All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cognitect.aws.client.impl.clj Maven / Gradle / Ivy

The newest version!
;; Copyright (c) Cognitect, Inc.
;; All rights reserved.

(ns ^:skip-wiki cognitect.aws.client.impl
  "Impl, don't call directly."
  (:require [clojure.core.async :as a]
            [cognitect.aws.client.protocol :as client.protocol]
            [cognitect.aws.client.shared :as shared]
            [cognitect.aws.client.validation :as validation]
            [cognitect.aws.credentials :as credentials]
            [cognitect.aws.endpoint :as endpoint]
            [cognitect.aws.http :as http]
            [cognitect.aws.interceptors :as interceptors]
            [cognitect.aws.protocols :as aws.protocols]
            [cognitect.aws.region :as region]
            [cognitect.aws.retry :as retry]
            [cognitect.aws.signers :as signers]
            [cognitect.aws.util :as util])
  (:import (clojure.lang ILookup)))

(set! *warn-on-reflection* true)

;; TODO convey throwable back from impl
(defn ^:private handle-http-response
  [service op-map {:keys [status] :as http-response}]
  (try
    (if (:cognitect.anomalies/category http-response)
      http-response
      (if (< status 300)
        (aws.protocols/parse-http-response service op-map http-response)
        (aws.protocols/parse-http-error-response http-response)))
    (catch Throwable t
      {:cognitect.anomalies/category :cognitect.anomalies/fault
       ::throwable t})))

(defn ^:private with-endpoint
  "Updates the request map `req` with data returned by the endpoint provider.

   The request map is created with reasonable defaults by `cognitect.aws.protocols/build-http-request`,
   and the `:scheme`, `:server-port` and `:uri` may be overridden here with
   data from the endpoint provider.

   `:server-name` is always set based on the endpoint provider data.

   Also computes and sets the `Host` header with the appropriate authority."
  [req {:keys [protocol hostname port path]}]
  (let [scheme (or protocol (:scheme req) (throw (IllegalArgumentException. "missing protocol/scheme")))
        server-name (or hostname (throw (IllegalArgumentException. "missing hostname")))
        server-port (or port (:server-port req) (throw (IllegalArgumentException. "missing port/server-port")))
        uri (or path (:uri req) (throw (IllegalArgumentException. "missing path/uri")))
        authority (http/uri-authority scheme server-name server-port)]
    (-> req
        (assoc-in [:headers "host"] authority)
        (assoc :scheme scheme
               :server-name server-name
               :server-port server-port
               :uri uri))))

(defn ^:private put-throwable [result-ch t response-meta op-map]
  (a/put! result-ch (with-meta
                      {:cognitect.anomalies/category :cognitect.anomalies/fault
                       ::throwable                   t}
                      (swap! response-meta
                             assoc :op-map op-map))))

(defn ^:private send-request
  [client op-map http-request]
  (let [{:keys [service http-client region-provider credentials-provider endpoint-provider]}
        (client.protocol/-get-info client)
        response-meta (atom {})
        region-ch     (region/fetch-async region-provider)
        creds-ch      (credentials/fetch-async credentials-provider)
        response-ch   (a/chan 1)
        result-ch     (a/promise-chan)]
    (a/go
      (let [region   (a/! result-ch region)
          (:cognitect.anomalies/category creds)
          (a/>! result-ch creds)
          (:cognitect.anomalies/category endpoint)
          (a/>! result-ch endpoint)
          :else
          (try
            (let [http-request (signers/sign-http-request
                                service endpoint
                                creds
                                (-> http-request
                                    (with-endpoint endpoint)
                                    ((partial interceptors/modify-http-request service op-map))))]
              (swap! response-meta assoc :http-request http-request)
              (http/submit http-client http-request response-ch))
            (catch Throwable t
              (put-throwable result-ch t response-meta op-map))))))
    (a/go
      (try
        (let [response (a/! result-ch (with-meta
                            (handle-http-response service op-map response)
                            (swap! response-meta assoc
                                   :http-response (update response :body util/bbuf->input-stream)))))
        (catch Throwable t
          (put-throwable result-ch t response-meta op-map))))
    result-ch))

(deftype Client [client-meta info]
  clojure.lang.IObj
  (meta [_] @client-meta)
  (withMeta [this m] (swap! client-meta merge m) this)

  ILookup
  (valAt [this k]
    (.valAt this k nil))

  (valAt [this k default]
    (case k
      :api
      (-> info :service :metadata :cognitect.aws/service-name)
      :region
      (some-> info :region-provider region/fetch)
      :endpoint
      (some-> info :endpoint-provider (endpoint/fetch (.valAt this :region)))
      :credentials
      (some-> info :credentials-provider credentials/fetch)
      :service
      (some-> info :service (select-keys [:metadata]))
      :http-client
      (:http-client info)
      default))

  client.protocol/Client
  (-get-info [_] info)

  (-invoke [client op-map]
    (a/ (aws.protocols/build-http-request service op-map)
                      (update :body util/->bbuf))]
          (retry/with-retry
            #(send-request client op-map req)
            result-chan
            (or (:retriable? op-map) retriable?)
            (or (:backoff op-map) backoff))))

      result-chan))

  (-stop [aws-client]
    (let [{:keys [http-client]} (client.protocol/-get-info aws-client)]
      (when-not (#'shared/shared-http-client? http-client)
        (http/stop http-client)))))




© 2015 - 2025 Weber Informatics LLC | Privacy Policy