├── VERSION_PREFIX ├── .gitignore ├── test ├── resources │ ├── aws-sig-v4-test-suite │ │ ├── get-utf8 │ │ │ ├── get-utf8.req │ │ │ ├── get-utf8.sts │ │ │ ├── get-utf8.creq │ │ │ ├── get-utf8.authz │ │ │ └── get-utf8.sreq │ │ ├── get-vanilla │ │ │ ├── get-vanilla.req │ │ │ ├── get-vanilla.sts │ │ │ ├── get-vanilla.creq │ │ │ ├── get-vanilla.authz │ │ │ └── get-vanilla.sreq │ │ ├── post-vanilla │ │ │ ├── post-vanilla.req │ │ │ ├── post-vanilla.sts │ │ │ ├── post-vanilla.creq │ │ │ ├── post-vanilla.authz │ │ │ └── post-vanilla.sreq │ │ ├── get-vanilla-query │ │ │ ├── get-vanilla-query.req │ │ │ ├── get-vanilla-query.sts │ │ │ ├── get-vanilla-query.creq │ │ │ ├── get-vanilla-query.authz │ │ │ └── get-vanilla-query.sreq │ │ ├── normalize-path │ │ │ ├── get-slash │ │ │ │ ├── get-slash.req │ │ │ │ ├── get-slash.sts │ │ │ │ ├── get-slash.creq │ │ │ │ ├── get-slash.authz │ │ │ │ └── get-slash.sreq │ │ │ ├── get-relative │ │ │ │ ├── get-relative.req │ │ │ │ ├── get-relative.sts │ │ │ │ ├── get-relative.creq │ │ │ │ ├── get-relative.authz │ │ │ │ └── get-relative.sreq │ │ │ ├── get-slashes │ │ │ │ ├── get-slashes.req │ │ │ │ ├── get-slashes.sts │ │ │ │ ├── get-slashes.creq │ │ │ │ ├── get-slashes.authz │ │ │ │ └── get-slashes.sreq │ │ │ ├── get-space │ │ │ │ ├── get-space.req │ │ │ │ ├── get-space.sts │ │ │ │ ├── get-space.authz │ │ │ │ ├── get-space.creq │ │ │ │ └── get-space.sreq │ │ │ ├── get-slash-dot-slash │ │ │ │ ├── get-slash-dot-slash.req │ │ │ │ ├── get-slash-dot-slash.sts │ │ │ │ ├── get-slash-dot-slash.creq │ │ │ │ ├── get-slash-dot-slash.authz │ │ │ │ └── get-slash-dot-slash.sreq │ │ │ ├── get-slash-pointless-dot │ │ │ │ ├── get-slash-pointless-dot.req │ │ │ │ ├── get-slash-pointless-dot.sts │ │ │ │ ├── get-slash-pointless-dot.creq │ │ │ │ ├── get-slash-pointless-dot.authz │ │ │ │ └── get-slash-pointless-dot.sreq │ │ │ ├── get-relative-relative │ │ │ │ ├── get-relative-relative.req │ │ │ │ ├── get-relative-relative.sts │ │ │ │ ├── get-relative-relative.creq │ │ │ │ ├── get-relative-relative.authz │ │ │ │ └── get-relative-relative.sreq │ │ │ └── normalize-path.txt │ │ ├── post-header-key-case │ │ │ ├── post-header-key-case.req │ │ │ ├── post-header-key-case.sts │ │ │ ├── post-header-key-case.creq │ │ │ ├── post-header-key-case.authz │ │ │ └── post-header-key-case.sreq │ │ ├── get-vanilla-utf8-query │ │ │ ├── get-vanilla-utf8-query.req │ │ │ ├── get-vanilla-utf8-query.sts │ │ │ ├── get-vanilla-utf8-query.creq │ │ │ ├── get-vanilla-utf8-query.authz │ │ │ └── get-vanilla-utf8-query.sreq │ │ ├── post-vanilla-query │ │ │ ├── post-vanilla-query.req │ │ │ ├── post-vanilla-query.sts │ │ │ ├── post-vanilla-query.authz │ │ │ ├── post-vanilla-query.creq │ │ │ └── post-vanilla-query.sreq │ │ ├── post-sts-token │ │ │ ├── post-sts-header-after │ │ │ │ ├── post-sts-header-after.req │ │ │ │ ├── post-sts-header-after.sts │ │ │ │ ├── post-sts-header-after.creq │ │ │ │ ├── post-sts-header-after.authz │ │ │ │ └── post-sts-header-after.sreq │ │ │ ├── post-sts-header-before │ │ │ │ ├── post-sts-header-before.sts │ │ │ │ ├── post-sts-header-before.authz │ │ │ │ ├── post-sts-header-before.req │ │ │ │ ├── post-sts-header-before.creq │ │ │ │ └── post-sts-header-before.sreq │ │ │ └── readme.txt │ │ ├── post-header-key-sort │ │ │ ├── post-header-key-sort.req │ │ │ ├── post-header-key-sort.sts │ │ │ ├── post-header-key-sort.authz │ │ │ ├── post-header-key-sort.creq │ │ │ └── post-header-key-sort.sreq │ │ ├── get-vanilla-empty-query-key │ │ │ ├── get-vanilla-empty-query-key.req │ │ │ ├── get-vanilla-empty-query-key.sts │ │ │ ├── get-vanilla-empty-query-key.creq │ │ │ ├── get-vanilla-empty-query-key.authz │ │ │ └── get-vanilla-empty-query-key.sreq │ │ ├── post-header-value-case │ │ │ ├── post-header-value-case.req │ │ │ ├── post-header-value-case.sts │ │ │ ├── post-header-value-case.authz │ │ │ ├── post-header-value-case.creq │ │ │ └── post-header-value-case.sreq │ │ ├── post-vanilla-empty-query-value │ │ │ ├── post-vanilla-empty-query-value.req │ │ │ ├── post-vanilla-empty-query-value.sts │ │ │ ├── post-vanilla-empty-query-value.creq │ │ │ ├── post-vanilla-empty-query-value.authz │ │ │ └── post-vanilla-empty-query-value.sreq │ │ ├── get-vanilla-query-order-key │ │ │ ├── get-vanilla-query-order-key.req │ │ │ ├── get-vanilla-query-order-key.sts │ │ │ ├── get-vanilla-query-order-key.authz │ │ │ ├── get-vanilla-query-order-key.creq │ │ │ └── get-vanilla-query-order-key.sreq │ │ ├── get-vanilla-query-order-value │ │ │ ├── get-vanilla-query-order-value.req │ │ │ ├── get-vanilla-query-order-value.sts │ │ │ ├── get-vanilla-query-order-value.authz │ │ │ ├── get-vanilla-query-order-value.creq │ │ │ └── get-vanilla-query-order-value.sreq │ │ ├── get-header-value-trim │ │ │ ├── get-header-value-trim.req │ │ │ ├── get-header-value-trim.sts │ │ │ ├── get-header-value-trim.authz │ │ │ ├── get-header-value-trim.creq │ │ │ └── get-header-value-trim.sreq │ │ ├── get-unreserved │ │ │ ├── get-unreserved.req │ │ │ ├── get-unreserved.sts │ │ │ ├── get-unreserved.authz │ │ │ ├── get-unreserved.creq │ │ │ └── get-unreserved.sreq │ │ ├── get-vanilla-query-order-key-case │ │ │ ├── get-vanilla-query-order-key-case.req │ │ │ ├── get-vanilla-query-order-key-case.sts │ │ │ ├── get-vanilla-query-order-key-case.authz │ │ │ ├── get-vanilla-query-order-key-case.creq │ │ │ └── get-vanilla-query-order-key-case.sreq │ │ ├── get-header-value-multiline │ │ │ ├── get-header-value-multiline.req │ │ │ ├── get-header-value-multiline.sts │ │ │ ├── get-header-value-multiline.authz │ │ │ ├── get-header-value-multiline.creq │ │ │ └── get-header-value-multiline.sreq │ │ ├── get-header-key-duplicate │ │ │ ├── get-header-key-duplicate.req │ │ │ ├── get-header-key-duplicate.sts │ │ │ ├── get-header-key-duplicate.authz │ │ │ ├── get-header-key-duplicate.creq │ │ │ └── get-header-key-duplicate.sreq │ │ ├── get-header-value-order │ │ │ ├── get-header-value-order.sts │ │ │ ├── get-header-value-order.req │ │ │ ├── get-header-value-order.authz │ │ │ ├── get-header-value-order.creq │ │ │ └── get-header-value-order.sreq │ │ ├── get-vanilla-query-unreserved │ │ │ ├── get-vanilla-query-unreserved.sts │ │ │ ├── get-vanilla-query-unreserved.authz │ │ │ ├── get-vanilla-query-unreserved.req │ │ │ ├── get-vanilla-query-unreserved.creq │ │ │ └── get-vanilla-query-unreserved.sreq │ │ ├── post-x-www-form-urlencoded │ │ │ ├── post-x-www-form-urlencoded.req │ │ │ ├── post-x-www-form-urlencoded.sts │ │ │ ├── post-x-www-form-urlencoded.authz │ │ │ ├── post-x-www-form-urlencoded.creq │ │ │ └── post-x-www-form-urlencoded.sreq │ │ └── post-x-www-form-urlencoded-parameters │ │ │ ├── post-x-www-form-urlencoded-parameters.sts │ │ │ ├── post-x-www-form-urlencoded-parameters.req │ │ │ ├── post-x-www-form-urlencoded-parameters.authz │ │ │ ├── post-x-www-form-urlencoded-parameters.creq │ │ │ └── post-x-www-form-urlencoded-parameters.sreq │ ├── .aws │ │ ├── credentials │ │ └── config │ ├── cognitect │ │ └── protocols │ │ │ ├── output │ │ │ ├── json.json │ │ │ ├── rest-json.json │ │ │ └── rest-xml.json │ │ │ └── input │ │ │ ├── rest-xml.json │ │ │ └── query.json │ └── botocore │ │ └── protocols │ │ └── output │ │ └── event-stream.json └── src │ ├── cognitect │ ├── aws │ │ ├── integration │ │ │ ├── fixtures.clj │ │ │ └── error_codes_test.clj │ │ ├── test │ │ │ ├── utils.clj │ │ │ └── ec2_metadata_utils_server.clj │ │ ├── interceptors_test.clj │ │ ├── http │ │ │ └── default_test.clj │ │ ├── config_test.clj │ │ ├── client │ │ │ └── shared_test.clj │ │ ├── shape_test.clj │ │ ├── protocols │ │ │ └── rest_test.clj │ │ ├── endpoint_test.clj │ │ ├── ec2_metadata_utils_test.clj │ │ ├── api_test.clj │ │ ├── generators.clj │ │ ├── http_test.clj │ │ ├── retry_test.clj │ │ ├── region_test.clj │ │ └── util_test.clj │ └── client │ │ └── test_double_test.clj │ └── bb_test_runner.clj ├── bb.edn ├── examples ├── autoscaling.clj ├── iam_examples.clj ├── ec2_examples.clj ├── lambda_examples.clj ├── README.md ├── resources │ └── dynamodb │ │ ├── Forum.json │ │ ├── Reply.json │ │ └── Thread.json ├── ssm_examples.clj ├── s3_examples.clj └── assume_role_example.clj ├── dev └── resources │ └── log4j.properties ├── .github ├── pull_request_template.md ├── issue_template.md └── workflows │ ├── release.yml │ └── test.yml ├── src └── cognitect │ └── aws │ ├── client │ ├── api │ │ └── async.clj │ ├── protocol.clj │ ├── shared.clj │ ├── validation.clj │ └── test_double.clj │ ├── json.cljc │ ├── dynaload.clj │ ├── http │ ├── cognitect.clj │ └── default.clj │ ├── resources.clj │ ├── protocols │ ├── rest_xml.clj │ ├── rest_json.clj │ ├── json.clj │ ├── ec2.clj │ └── query.clj │ ├── interceptors.clj │ ├── retry.clj │ ├── config.clj │ ├── service.clj │ ├── endpoint.clj │ ├── http.clj │ └── region.clj ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── doc ├── types.md └── testing.md ├── deps.edn ├── UPGRADE.md └── pom.xml /VERSION_PREFIX: -------------------------------------------------------------------------------- 1 | 0.8 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cpcache 2 | target 3 | logs 4 | log 5 | .nrepl-port 6 | VERSION_SUFFIX 7 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.req: -------------------------------------------------------------------------------- 1 | GET /ሴ HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req: -------------------------------------------------------------------------------- 1 | GET // HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req: -------------------------------------------------------------------------------- 1 | GET /example/.. HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req: -------------------------------------------------------------------------------- 1 | GET //example// HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req: -------------------------------------------------------------------------------- 1 | GET /example space/ HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req: -------------------------------------------------------------------------------- 1 | GET /?ሴ=bar HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req: -------------------------------------------------------------------------------- 1 | POST /?Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req: -------------------------------------------------------------------------------- 1 | GET /./ HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:value1 4 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req: -------------------------------------------------------------------------------- 1 | GET /?Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:VALUE1 4 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req: -------------------------------------------------------------------------------- 1 | GET /./example HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req: -------------------------------------------------------------------------------- 1 | POST /?Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req: -------------------------------------------------------------------------------- 1 | GET /?Param1=value2&Param1=Value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req: -------------------------------------------------------------------------------- 1 | GET /example1/example2/../.. HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 2a0a97d02205e45ce2e994789806b19270cfbbb0921b278ccf58f5249ac42102 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req: -------------------------------------------------------------------------------- 1 | GET /?Param1=value2&Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1: value1 4 | My-Header2: "a b c" 5 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req: -------------------------------------------------------------------------------- 1 | GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req: -------------------------------------------------------------------------------- 1 | GET /?Param2=value2&Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:value1 4 | value2 5 | value3 6 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 6a968768eefaa713e2a6b16b589a8ea192661f098f37349f4e2c0082757446f9 -------------------------------------------------------------------------------- /bb.edn: -------------------------------------------------------------------------------- 1 | {:deps {com.cognitect.aws/api {:local/root "."}} 2 | :tasks 3 | {test-bb {:doc "Run Babashka tests" 4 | :extra-paths ["src" "test/src" "test/resources"] 5 | :task bb-test-runner/run-tests}}} 6 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 63ee75631ed7234ae61b5f736dfc7754cdccfedbff4b5128a915706ee9390d86 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:value2 4 | My-Header1:value2 5 | My-Header1:value1 6 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | cb96b4ac96d501f7c5c15bc6d67b3035061cfced4af6585ad927f7e6c985c015 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 9368318c2967cf6de74404b30c65a91e8f6253e0a8659d6d5319f1a812f87d65 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 31ce73cd3f3d9f66977ad3dd957dc47af14df92fcd8509f59b349e9137c58b86 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | a726db9b0df21c14f559d0a978e563112acb1b9e05476f0a6a1c7d68f28605c7 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | eb30c5bed55734080471a834cc727ae56beb50e5f39d1bff6d0d38cb192a7073 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | d51ced243e649e3de6ef63afbbdcbca03131a21a7103a1583706a64618606a93 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | dc7f04a3abfde8d472b0ab1a418b741b7c67174dad1551b4117b15527fbe966c -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.creq: -------------------------------------------------------------------------------- 1 | GET 2 | /%E1%88%B4 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | b7b6cbfd8a0430b78891e986784da2630c8a135a8595cec25b26ea94f926ee55 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 1e24db194ed7d0eec2de28d7369675a243488e08526e8c1c73571282f7c517ab -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 704b4cef673542d84cdff252633f065e8daeba5f168b77116f8b1bcaf3d38f89 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | c30d4703d9f799439be92736156d47ccfb2d879ddf56f5befa6d1d6aab979177 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Content-Type:application/x-www-form-urlencoded 3 | Host:example.amazonaws.com 4 | X-Amz-Date:20150830T123600Z 5 | 6 | Param1=value1 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 42a5e5bb34198acb3e84da4f085bb7927f2bc277ca766e6d19c73c2154021281 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:value4 4 | My-Header1:value1 5 | My-Header1:value3 6 | My-Header1:value2 7 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | c968629d70850097a2d8781c9bf7edcb988b04cac14cca9be4acc3595f884606 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 214d50c111a8edc4819da6a636336472c916b5240f51e9a51b5c3305180cf702 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | c237e1b440d4c63c32ca95b5b99481081cb7b13c7e40434868e71567c1a882f6 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq: -------------------------------------------------------------------------------- 1 | GET 2 | /example/ 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq: -------------------------------------------------------------------------------- 1 | GET 2 | /example%20space/ 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | Param1=value1 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 2 | 20150830T123600Z 3 | 20150830/us-east-1/service/aws4_request 4 | 2e1cf7ed91881a30569e46552437e4156c823447bf1781b921b5d486c568dd1c -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | %E1%88%B4=bar 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | Param1=value1 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq: -------------------------------------------------------------------------------- 1 | GET 2 | /example 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Content-Type:application/x-www-form-urlencoded; charset=utf8 3 | Host:example.amazonaws.com 4 | X-Amz-Date:20150830T123600Z 5 | 6 | Param1=value1 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | 4 | host:example.amazonaws.com 5 | my-header1:value1 6 | x-amz-date:20150830T123600Z 7 | 8 | host;my-header1;x-amz-date 9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | Param1=value1 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /examples/autoscaling.clj: -------------------------------------------------------------------------------- 1 | (ns autoscaling 2 | (:require [cognitect.aws.client.api :as aws])) 3 | 4 | (comment 5 | 6 | (def client (aws/client {:api :autoscaling})) 7 | (aws/invoke client {:op :DescribeAutoScalingGroups 8 | :request {}}) 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | 4 | host:example.amazonaws.com 5 | my-header1:VALUE1 6 | x-amz-date:20150830T123600Z 7 | 8 | host;my-header1;x-amz-date 9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | Param1=Value1&Param1=value2 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473a -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | Param1=value1&Param1=value2 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq: -------------------------------------------------------------------------------- 1 | GET 2 | /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | Param1=value1&Param2=value2 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | my-header1:value2,value2,value1 6 | x-amz-date:20150830T123600Z 7 | 8 | host;my-header1;x-amz-date 9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | my-header1:value1,value2,value3 6 | x-amz-date:20150830T123600Z 7 | 8 | host;my-header1;x-amz-date 9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | my-header1:value4,value1,value3,value2 6 | x-amz-date:20150830T123600Z 7 | 8 | host;my-header1;x-amz-date 9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req: -------------------------------------------------------------------------------- 1 | GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz: -------------------------------------------------------------------------------- 1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fe -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | 4 | host:example.amazonaws.com 5 | my-header1:value1 6 | my-header2:"a b c" 7 | x-amz-date:20150830T123600Z 8 | 9 | host;my-header1;my-header2;x-amz-date 10 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | 4 | content-type:application/x-www-form-urlencoded 5 | host:example.amazonaws.com 6 | x-amz-date:20150830T123600Z 7 | 8 | content-type;host;x-amz-date 9 | 9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6e -------------------------------------------------------------------------------- /test/resources/.aws/credentials: -------------------------------------------------------------------------------- 1 | [default] 2 | aws_access_key_id = DEFAULT_AWS_ACCESS_KEY 3 | aws_secret_access_key = DEFAULT_AWS_SECRET_ACCESS_KEY 4 | 5 | [tardigrade] 6 | aws_access_key_id = TARDIGRADE_AWS_ACCESS_KEY 7 | aws_secret_access_key = TARDIGRADE_AWS_SECRET_ACCESS_KEY 8 | aws_session_token = TARDIGRADE_AWS_SESSION_TOKEN 9 | -------------------------------------------------------------------------------- /dev/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG, file 2 | 3 | log4j.appender.file=org.apache.log4j.RollingFileAppender 4 | log4j.appender.file.File=log/aws-api.log 5 | log4j.appender.file.maxFileSize=20MB 6 | log4j.appender.file.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.file.layout.ConversionPattern=%5p [%c] %m%n 8 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq: -------------------------------------------------------------------------------- 1 | GET /ሴ HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq: -------------------------------------------------------------------------------- 1 | GET // HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | 4 | content-type:application/x-www-form-urlencoded; charset=utf8 5 | host:example.amazonaws.com 6 | x-amz-date:20150830T123600Z 7 | 8 | content-type;host;x-amz-date 9 | 9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6e -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Thank you for your interest in contributing to Cognitect's aws-api! 2 | 3 | As we incorporate this library into products and client projects, we 4 | do not accept pull requests or patches. 5 | 6 | We do, however, want to know how we can make it better, so please file 7 | an issue at https://github.com/cognitect-labs/aws-api/issues. 8 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq: -------------------------------------------------------------------------------- 1 | GET //example// HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq: -------------------------------------------------------------------------------- 1 | GET /example space/ HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq: -------------------------------------------------------------------------------- 1 | GET /?ሴ=bar HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq: -------------------------------------------------------------------------------- 1 | GET /example/.. HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq: -------------------------------------------------------------------------------- 1 | POST /?Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq: -------------------------------------------------------------------------------- 1 | GET /./ HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq: -------------------------------------------------------------------------------- 1 | GET /?Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq: -------------------------------------------------------------------------------- 1 | GET /./example HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq: -------------------------------------------------------------------------------- 1 | GET 2 | / 3 | -._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | 7 | host;x-amz-date 8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:value1 4 | X-Amz-Date:20150830T123600Z 5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq: -------------------------------------------------------------------------------- 1 | POST /?Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq: -------------------------------------------------------------------------------- 1 | GET /?Param1=value2&Param1=Value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq: -------------------------------------------------------------------------------- 1 | GET /example1/example2/../.. HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:VALUE1 4 | X-Amz-Date:20150830T123600Z 5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq: -------------------------------------------------------------------------------- 1 | GET /?Param1=value2&Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq: -------------------------------------------------------------------------------- 1 | GET /?Param2=value2&Param1=value1 HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq: -------------------------------------------------------------------------------- 1 | GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f -------------------------------------------------------------------------------- /examples/iam_examples.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns iam-examples 5 | (:require [cognitect.aws.client.api :as aws])) 6 | 7 | (comment 8 | 9 | ;; make a client 10 | (def iam (aws/client {:api :iam})) 11 | 12 | (aws/ops iam) 13 | 14 | (-> (aws/ops iam) keys sort) 15 | 16 | (aws/doc iam :ListRoles) 17 | 18 | (aws/invoke iam {:op :ListRoles}) 19 | 20 | ) 21 | -------------------------------------------------------------------------------- /src/cognitect/aws/client/api/async.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.client.api.async 5 | "DEPRECATED" 6 | (:require 7 | [cognitect.aws.client.protocol :as client.protocol])) 8 | 9 | (defn ^:deprecated invoke 10 | "DEPRECATED: use cognitect.aws.client.api/invoke-async instead" 11 | [client op-map] 12 | (client.protocol/-invoke-async client op-map)) -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:value1 4 | value2 5 | value3 6 | X-Amz-Date:20150830T123600Z 7 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1: value1 4 | My-Header2: "a b c" 5 | X-Amz-Date:20150830T123600Z 6 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736 -------------------------------------------------------------------------------- /examples/ec2_examples.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ec2-examples 5 | (:require [cognitect.aws.client.api :as aws])) 6 | 7 | (comment 8 | 9 | (def ec2 (aws/client {:api :ec2})) 10 | 11 | (aws/ops ec2) 12 | 13 | (-> (aws/ops ec2) keys sort) 14 | 15 | (aws/doc ec2 :DescribeAvailabilityZones) 16 | 17 | (aws/invoke ec2 {:op :DescribeAvailabilityZones}) 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /examples/lambda_examples.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns lambda-examples 5 | (:require [cognitect.aws.client.api :as aws])) 6 | 7 | (comment 8 | 9 | (def lambda (aws/client {:api :lambda})) 10 | 11 | (aws/ops lambda) 12 | 13 | (-> (aws/ops lambda) keys sort) 14 | 15 | (aws/doc lambda :ListFunctions) 16 | 17 | (aws/invoke lambda {:op :ListFunctions}) 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:value2 4 | My-Header1:value2 5 | My-Header1:value1 6 | X-Amz-Date:20150830T123600Z 7 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Content-Type:application/x-www-form-urlencoded 3 | Host:example.amazonaws.com 4 | X-Amz-Date:20150830T123600Z 5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473a 6 | 7 | Param1=value1 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq: -------------------------------------------------------------------------------- 1 | GET / HTTP/1.1 2 | Host:example.amazonaws.com 3 | My-Header1:value4 4 | My-Header1:value1 5 | My-Header1:value3 6 | My-Header1:value2 7 | X-Amz-Date:20150830T123600Z 8 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01 -------------------------------------------------------------------------------- /test/src/cognitect/aws/integration/fixtures.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.integration.fixtures) 2 | 3 | (defn ensure-test-profile 4 | "Ensure AWS_PROFILE env var is aws-api-test 5 | 6 | To use it, add this at beginning of your test namespace: 7 | 8 | (use-fixtures :once aux.integration/ensure-test-profile)" 9 | [f] 10 | (if (= "aws-api-test" (System/getenv "AWS_PROFILE")) 11 | (f) 12 | (println "AWS_PROFILE is not configured, so not running integration tests."))) 13 | -------------------------------------------------------------------------------- /src/cognitect/aws/json.cljc: -------------------------------------------------------------------------------- 1 | (ns ^:skip-wiki cognitect.aws.json 2 | "Impl, don't call directly." 3 | (:require #?(:bb [cheshire.core] 4 | :clj [clojure.data.json]))) 5 | 6 | (defn read-str [string & {:as options}] 7 | #?(:bb (cheshire.core/parse-string string (:key-fn options)) 8 | :clj (clojure.data.json/read-str string options))) 9 | 10 | (defn write-str [x] 11 | #?(:bb (cheshire.core/generate-string x) 12 | :clj (clojure.data.json/write-str x))) 13 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Content-Type:application/x-www-form-urlencoded; charset=utf8 3 | Host:example.amazonaws.com 4 | X-Amz-Date:20150830T123600Z 5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fe 6 | 7 | Param1=value1 -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # aws-api examples 2 | 3 | This directory contains examples of usage of aws-api. To explore, 4 | clone this repo and start a repl in your preferred environment 5 | using the deps.edn in the project root, e.g. 6 | 7 | ``` 8 | clj -A:dev 9 | ``` 10 | 11 | Then you can open any of the xxx_examples.clj files and start evaluating forms. In each example 12 | namespace, forms are contained within a `comment` block to prevent their accidental side-effectful 13 | evaluation. 14 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq: -------------------------------------------------------------------------------- 1 | GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197 -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== -------------------------------------------------------------------------------- /src/cognitect/aws/dynaload.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.dynaload) 5 | 6 | (set! *warn-on-reflection* true) 7 | 8 | (defonce ^:private dynalock (Object.)) 9 | 10 | (defn load-ns [ns] 11 | (locking dynalock 12 | (require (symbol ns)))) 13 | 14 | (defn load-var 15 | [s] 16 | (let [ns (namespace s)] 17 | (assert ns) 18 | (load-ns ns) 19 | (or (resolve s) 20 | (throw (RuntimeException. (str "Var " s " is not on the classpath")))))) 21 | -------------------------------------------------------------------------------- /src/cognitect/aws/http/cognitect.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.http.cognitect 5 | (:require [cognitect.http-client :as impl] 6 | [cognitect.aws.http :as aws])) 7 | 8 | (set! *warn-on-reflection* true) 9 | 10 | (defn create 11 | [] 12 | (let [c (impl/create {:follow-redirects false})] 13 | (reify aws/HttpClient 14 | (-submit [_ request channel] 15 | (impl/submit c request channel)) 16 | (-stop [_] 17 | (impl/stop c))))) 18 | -------------------------------------------------------------------------------- /test/resources/.aws/config: -------------------------------------------------------------------------------- 1 | [default] 2 | region = us-east-1 3 | 4 | [tardigrade] 5 | region = us-west-1 6 | 7 | [profile waterbear] 8 | region = eu-west-1 9 | credential_process=awsprocesscreds-saml -e 'https://url.com?with-query-string=true' -u some-user -a arn:aws:iam::1234:role/system/specialness 10 | 11 | [nested] 12 | s3 = 13 | max_concurrent_requests=10 14 | s3_key = s3_val 15 | region = eu-west-1 16 | s3 = 17 | max_queue_size=1000 18 | foo.bar = baz 19 | 20 | [temp-credentials] 21 | aws_session_token = FQoG/Ehj40mh/xf0TR+xLl+cp/xGWC+haIy+fJh6/fD+LFW= 22 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/normalize-path/normalize-path.txt: -------------------------------------------------------------------------------- 1 | A note about signing requests to Amazon S3: 2 | 3 | In exception to this, you do not normalize URI paths for requests to Amazon S3. For example, if you have a bucket with an object named my-object//example//photo.user, use that path. Normalizing the path to my-object/example/photo.user will cause the request to fail. For more information, see Task 1: Create a Canonical Request in the Amazon Simple Storage Service API Reference: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-request -------------------------------------------------------------------------------- /test/src/cognitect/aws/test/utils.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.test.utils 5 | (:require [clojure.string :as str])) 6 | 7 | (defn stub-getenv [env] 8 | (fn 9 | ([] env) 10 | ([k] (get env k)))) 11 | 12 | (defn stub-getProperty [props] 13 | (fn [k] 14 | (get props k))) 15 | 16 | (defn major-java-version [] 17 | (-> (System/getProperty "java.version") (str/split #"[\.-]") first read-string)) 18 | 19 | (defmacro when-java11 [& body] 20 | (when (<= 11 (major-java-version)) 21 | `(do ~@body))) 22 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/interceptors_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.interceptors-test 2 | (:require [clojure.test :refer [deftest is]] 3 | [cognitect.aws.interceptors :as interceptors])) 4 | 5 | (deftest test-apigatewaymanagementapi 6 | (is (= "prefixsuffix" 7 | (:uri 8 | (interceptors/modify-http-request {:metadata {:uid "apigatewaymanagementapi"}} 9 | {:op :PostToConnection 10 | :request {:ConnectionId "suffix"}} 11 | {:uri "prefix"}))))) -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq: -------------------------------------------------------------------------------- 1 | POST 2 | / 3 | 4 | host:example.amazonaws.com 5 | x-amz-date:20150830T123600Z 6 | x-amz-security-token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== 7 | 8 | host;x-amz-date;x-amz-security-token 9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -------------------------------------------------------------------------------- /examples/resources/dynamodb/Forum.json: -------------------------------------------------------------------------------- 1 | { 2 | "Forum": [ 3 | { 4 | "PutRequest": { 5 | "Item": { 6 | "Name": {"S":"Amazon DynamoDB"}, 7 | "Category": {"S":"Amazon Web Services"}, 8 | "Threads": {"N":"2"}, 9 | "Messages": {"N":"4"}, 10 | "Views": {"N":"1000"} 11 | } 12 | } 13 | }, 14 | { 15 | "PutRequest": { 16 | "Item": { 17 | "Name": {"S":"Amazon S3"}, 18 | "Category": {"S":"Amazon Web Services"} 19 | } 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== 5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq: -------------------------------------------------------------------------------- 1 | POST / HTTP/1.1 2 | Host:example.amazonaws.com 3 | X-Amz-Date:20150830T123600Z 4 | X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== 5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | Thank you for your interest in helping to improve Cognitect's aws-api! 2 | 3 | ## Dependencies 4 | 5 | Be sure to list the precise libs and versions you are using ("the 6 | latest" might change by the time we're looking at your issue). 7 | 8 | e.g. 9 | 10 | ``` clojure 11 | {:deps {com.cognitect.aws/api {:mvn/version "0.8.292"} 12 | com.cognitect.aws/endpoints {:mvn/version "1.1.11.532"} 13 | com.cognitect.aws/s3 {:mvn/version "714.2.430.0"}}} 14 | ``` 15 | 16 | ## Description with failing test case 17 | 18 | Please describe what you are trying to do, what you expect to see, and 19 | what you're seeing instead. 20 | 21 | *Important*: Please include a complete minimal code example that we 22 | can paste as/is into a REPL, execute, and watch fail. 23 | 24 | ## Stack traces 25 | 26 | If you're including a strack trace, please be sure to format it using 27 | markdown formatting (wrap it in "```"). 28 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/http/default_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.http.default-test 2 | (:require [clojure.string :as str] 3 | [clojure.test :refer [deftest is]] 4 | [cognitect.aws.http.default :as default-http-client] 5 | [cognitect.aws.test.utils :as utils])) 6 | 7 | (deftest http-client-defaults-parity-with-cognitect-client 8 | (let [client (default-http-client/create) 9 | client-class-name (.getName (class client)) 10 | java-version (utils/major-java-version) 11 | expected (if (< java-version 11) 12 | "cognitect.aws.http.cognitect$create" 13 | "cognitect.aws.http.java$create")] 14 | ; NOTE: this is matched via starts-with?, because the returned client 15 | ; is created via reify, and the class name is not fully predictable 16 | ; (e.g. cognitect.aws.http.java$create$reify__11484) 17 | (is (str/starts-with? client-class-name expected)))) 18 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/config_test.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.config-test 5 | (:require [clojure.test :refer [deftest is]] 6 | [clojure.java.io :as io] 7 | [cognitect.aws.config :as config])) 8 | 9 | (deftest read-config 10 | (let [config (config/parse (io/resource ".aws/config"))] 11 | (is (= {"region" "us-west-1"} 12 | (get config "tardigrade"))) 13 | (is (= {"s3" {"max_concurrent_requests" "10" 14 | "max_queue_size" "1000" 15 | "s3_key" "s3_val"} 16 | "region" "eu-west-1" 17 | "foo.bar" "baz"} 18 | (get config "nested"))) 19 | (is (re-matches 20 | #"^awsprocesscreds.*specialness$" 21 | (get-in config ["waterbear" "credential_process"]))) 22 | (is (= "FQoG/Ehj40mh/xf0TR+xLl+cp/xGWC+haIy+fJh6/fD+LFW=" 23 | (get-in config ["temp-credentials" "aws_session_token"]))))) -------------------------------------------------------------------------------- /test/src/cognitect/aws/client/shared_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.client.shared-test 2 | (:require [clojure.test :as t :refer [deftest is testing]] 3 | [cognitect.aws.client.api :as aws] 4 | [cognitect.aws.client.shared :as shared] 5 | [cognitect.aws.http :as http])) 6 | 7 | (deftest test-shared-delays-are-not-realized-unnecessarily 8 | ;; issue 262 9 | (testing "Creating a client with custom attributes does not deref the default delayed shared resources." 10 | ;; As a precondition of this test, the delays must be unrealized. 11 | (require 'cognitect.aws.client.shared :reload) 12 | (aws/client {:api :s3 13 | :http-client (reify http/HttpClient) 14 | :region-provider :test-provider 15 | :credentials-provider :test-provider}) 16 | (is (not (realized? @#'shared/shared-http-client))) 17 | (is (not (realized? @#'shared/shared-region-provider))) 18 | (is (not (realized? @#'shared/shared-credentials-provider))))) 19 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /src/cognitect/aws/client/protocol.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.client.protocol 5 | "Impl, don't call directly.") 6 | 7 | (defprotocol Client 8 | (-get-info [_] "Used by fns in cognitect.aws.client.api. Implementors must supply 9 | :service, and may also supply anything else needed by the other 10 | protocol functions. 11 | 12 | The cognitect.aws.client.api/client uses the following: 13 | :retriable? 14 | :backoff 15 | :http-client 16 | :endpoint-provider 17 | :region-provider 18 | :credentials-provider 19 | :validate-requests?") 20 | (-invoke [this op-map] "Packages and ships a request and returns a response") 21 | (-invoke-async [this op-map] "Packages and ships a request and returns a channel that will contain a response") 22 | (-stop [this] "Release resources managed by this client")) -------------------------------------------------------------------------------- /src/cognitect/aws/resources.clj: -------------------------------------------------------------------------------- 1 | (ns ^:skip-wiki cognitect.aws.resources 2 | "Impl, don't call directly." 3 | (:import (clojure.lang RT))) 4 | 5 | (def loader 6 | "Clojure's base class loader, used to load all resources. 7 | 8 | This ensures the same class loader is used, no matter which thread invokes `resource`. 9 | This is needed to guard against the scenario where the application is running in an 10 | environment that uses a non-default class loader, and the `resource` function is invoked 11 | from a thread that does not inherit the custom class loader (e.g. ForkJoinPool/commonPool threads). 12 | 13 | See https://github.com/cognitect-labs/aws-api/issues/265 for details" 14 | (RT/baseLoader)) 15 | 16 | (defn resource 17 | "Returns the URL for a named resource, always using Clojure's base class loader." 18 | [n] 19 | (.getResource ^ClassLoader loader n)) 20 | 21 | (defn resources 22 | "Returns a seq of URLs for a named resource, always using Clojure's base class loader." 23 | [n] 24 | (enumeration-seq (.getResources ^ClassLoader loader n))) 25 | -------------------------------------------------------------------------------- /src/cognitect/aws/protocols/rest_xml.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.protocols.rest-xml 5 | "Impl, don't call directly." 6 | (:require [cognitect.aws.protocols :as aws.protocols] 7 | [cognitect.aws.protocols.rest :as rest] 8 | [cognitect.aws.shape :as shape])) 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | (defn serialize 13 | "xml body args serializer passed to rest/build-http-request" 14 | [shapes shape-name shape data] 15 | (when data 16 | (shape/xml-serialize shapes 17 | shape 18 | data 19 | (or (:locationName shape) shape-name)))) 20 | 21 | (defmethod aws.protocols/build-http-request "rest-xml" 22 | [service op-map] 23 | (rest/build-http-request service op-map serialize)) 24 | 25 | (defmethod aws.protocols/parse-http-response "rest-xml" 26 | [service op-map http-response] 27 | (rest/parse-http-response service 28 | op-map 29 | http-response 30 | shape/xml-parse)) 31 | -------------------------------------------------------------------------------- /examples/ssm_examples.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ssm-examples 5 | (:require [clojure.spec.alpha :as s] 6 | [clojure.pprint :as pp] 7 | [cognitect.aws.client.api :as aws])) 8 | 9 | (comment 10 | 11 | (def ssm-client (aws/client {:api :ssm})) 12 | 13 | ;; guard against invalid :request map 14 | (aws/validate-requests ssm-client true) 15 | 16 | ;; what ops are available 17 | (aws/ops ssm-client) 18 | 19 | (-> (aws/ops ssm-client) keys sort) 20 | 21 | ;; print docs 22 | (aws/doc ssm-client :PutParameter) 23 | 24 | ;; or describe args as data 25 | (pp/pprint (s/describe (aws/request-spec-key ssm-client :PutParameter))) 26 | 27 | ;; describe an op param 28 | (s/form :cognitect.aws.ssm.PutParameterRequest/Type) 29 | 30 | ;; jam! 31 | (aws/invoke ssm-client {:op :PutParameter 32 | :request {:Name "aws-api-example" 33 | :Value "example-value" 34 | :Type "SecureString"}}) 35 | 36 | ;; see spec fail 37 | (aws/invoke ssm-client {:op :PutParameter 38 | :request {:Value "example-value" 39 | :Type "SecureString"}}) 40 | 41 | ) 42 | -------------------------------------------------------------------------------- /src/cognitect/aws/http/default.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.http.default 2 | (:require [cognitect.aws.dynaload :as dynaload])) 3 | 4 | (def ^:private native-client-available 5 | (delay (try (Class/forName "java.net.http.HttpClient") true 6 | (catch Exception _ false)))) 7 | 8 | (def ^:private cognitect-client-available 9 | (delay (try (require 'cognitect.http-client) true 10 | (catch Exception _ false)))) 11 | 12 | (defn ^:private load-and-create 13 | [sym] 14 | (let [f @(dynaload/load-var sym)] 15 | (f))) 16 | 17 | (defn create 18 | "Returns a new cognitect.aws.http/HttpClient. 19 | 20 | - If running JVM is JDK 11+, returns a client based on java.net.http.HttpClient. 21 | - If cognitect.http-client ns is available, returns a client based on it. 22 | 23 | If none of these requirements are met, throws." 24 | [] 25 | (cond 26 | @native-client-available 27 | (load-and-create 'cognitect.aws.http.java/create) 28 | 29 | @cognitect-client-available 30 | (load-and-create 'cognitect.aws.http.cognitect/create) 31 | 32 | :else 33 | (throw (ex-info "aws-api requires JDK 11+, or you must have an explicit dependency on com.cognitect/http-client in your project" 34 | {:details "See https://github.com/cognitect-labs/aws-api/blob/main/UPGRADE.md if you are upgrading from a previous aws-api version"})))) 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | permissions: 3 | contents: write 4 | 5 | on: 6 | workflow_dispatch: 7 | 8 | jobs: 9 | 10 | release: 11 | name: 'Publish to Maven Central' 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | ssh-key: ${{secrets.AWS_API_DEPLOY_PRIVATE_KEY}} 18 | 19 | - name: Set Github identity 20 | run: | 21 | git config --global user.name nubank-cicd 22 | git config --global user.email "nubank-cicd@users.noreply.github.com" 23 | 24 | - uses: actions/setup-java@v4 25 | with: 26 | distribution: 'temurin' 27 | java-version: 21 28 | cache: 'maven' 29 | server-id: central 30 | server-username: MAVEN_CENTRAL_TOKEN 31 | server-password: MAVEN_CENTRAL_TOKEN_PASSWORD 32 | gpg-private-key: ${{ secrets.COGNITECT_SECRET_GPG_KEY }} 33 | gpg-passphrase: COGNITECT_SECRET_GPG_KEY_PASSPHRASE 34 | 35 | - name: Install clojure 36 | uses: DeLaGuardo/setup-clojure@master 37 | with: 38 | cli: '1.12.1.1550' 39 | 40 | - name: Publish to Maven OSS Server 41 | run: ./build/release 42 | env: 43 | MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }} 44 | MAVEN_CENTRAL_TOKEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }} 45 | COGNITECT_SECRET_GPG_KEY_PASSPHRASE: ${{ secrets.COGNITECT_SECRET_GPG_KEY_PASSPHRASE }} 46 | -------------------------------------------------------------------------------- /test/resources/aws-sig-v4-test-suite/post-sts-token/readme.txt: -------------------------------------------------------------------------------- 1 | A note about using temporary security credentials: 2 | 3 | You can use temporary security credentials provided by the AWS Security Token Service (AWS STS) to sign a request. The process is the same as using long-term credentials but requires an additional HTTP header or query string parameter for the security token. The name of the header or query string parameter is X-Amz-Security-Token, and the value is the session token (the string that you received from AWS STS when you obtained temporary security credentials). 4 | 5 | When you add X-Amz-Security-Token, some services require that you include this parameter in the canonical (signed) request. For other services, you add this parameter at the end, after you calculate the signature. For details see the API reference documentation for that service. 6 | 7 | The test suite has 2 examples: 8 | 9 | post-sts-header-before - The X-Amz-Security-Token header is part of the canonical request. 10 | 11 | post-sts-header-after - The X-Amz-Security-Token header is added to the request after you calculate the signature. 12 | 13 | The test suite uses this example value for X-Amz-Security-Token: 14 | 15 | AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== -------------------------------------------------------------------------------- /test/src/bb_test_runner.clj: -------------------------------------------------------------------------------- 1 | (ns bb-test-runner 2 | (:require [clojure.test :as t] 3 | [clojure.edn :as edn])) 4 | 5 | ; Load dev dependencies from deps.edn 6 | (require '[babashka.deps :as deps]) 7 | (deps/add-deps (edn/read-string (slurp "deps.edn")) 8 | {:aliases [:dev]}) 9 | 10 | ; NOTE: some tests won't run in babashka: 11 | ; cognitect.aws.http.default-test - all reify instances start with `babashka.impl.reify`, test won't pass 12 | ; cognitect.aws.signers-test - requires loading AWS SDK, which is not supported (no Java libs) 13 | ; cognitect.client.test-double-test - test double not supported in babashka 14 | (def test-namespaces 15 | ['cognitect.aws.api-test 16 | 'cognitect.aws.client.shared-test 17 | 'cognitect.aws.config-test 18 | 'cognitect.aws.credentials-test 19 | 'cognitect.aws.ec2-metadata-utils-test 20 | 'cognitect.aws.endpoint-test 21 | 'cognitect.aws.http-test 22 | 'cognitect.aws.http.java-test 23 | 'cognitect.aws.integration.error-codes-test 24 | 'cognitect.aws.integration.s3-test 25 | 'cognitect.aws.interceptors-test 26 | 'cognitect.aws.protocols-test 27 | 'cognitect.aws.protocols.rest-test 28 | 'cognitect.aws.region-test 29 | 'cognitect.aws.retry-test 30 | 'cognitect.aws.shape-test 31 | 'cognitect.aws.util-test 32 | 'cognitect.client.impl-test]) 33 | 34 | (defn run-tests [& _args] 35 | (apply require test-namespaces) 36 | 37 | (let [{:keys [fail error]} 38 | (apply t/run-tests test-namespaces)] 39 | (when (or (pos? fail) 40 | (pos? error)) 41 | (System/exit 1)))) 42 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/integration/error_codes_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.integration.error-codes-test 2 | (:require [clojure.test :refer [deftest is testing use-fixtures]] 3 | [cognitect.aws.client.api :as aws] 4 | [cognitect.aws.integration.fixtures :as fixtures])) 5 | 6 | (use-fixtures :once fixtures/ensure-test-profile) 7 | 8 | (deftest ^:integration error-codes-for-protocols 9 | (testing "rest-xml" 10 | (is (= "NoSuchBucket" 11 | (:cognitect.aws.error/code 12 | (aws/invoke (aws/client {:api :s3}) 13 | {:op :GetObject 14 | :request {:Bucket "i-do-not-exist" 15 | :Key "neither-do-i.txt"}}))))) 16 | 17 | (testing "rest-json" 18 | (is (= "ValidationException" 19 | (:cognitect.aws.error/code 20 | (aws/invoke (aws/client {:api :lambda}) 21 | {:op :CreateFunction}))))) 22 | 23 | (testing "ec2" 24 | (is (= "MissingParameter" 25 | (:cognitect.aws.error/code 26 | (aws/invoke (aws/client {:api :ec2}) 27 | {:op :CreateVolume}))))) 28 | 29 | (testing "query" 30 | (is (= "ValidationError" 31 | (:cognitect.aws.error/code 32 | (aws/invoke (aws/client {:api :autoscaling}) 33 | {:op :DescribeTrafficSources}))))) 34 | 35 | (testing "json" 36 | (is (= "ValidationException" 37 | (:cognitect.aws.error/code 38 | (aws/invoke (aws/client {:api :ssm}) 39 | {:op :DescribePatchGroupState})))))) 40 | -------------------------------------------------------------------------------- /src/cognitect/aws/protocols/rest_json.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.protocols.rest-json 5 | "Impl, don't call directly." 6 | (:require [cognitect.aws.protocols :as aws.protocols] 7 | [cognitect.aws.protocols.rest :as rest] 8 | [cognitect.aws.shape :as shape] 9 | [cognitect.aws.util :as util])) 10 | 11 | (set! *warn-on-reflection* true) 12 | 13 | (defmulti serialize 14 | "json body args serializer passed to rest/build-http-request 15 | 16 | Obs: this fn doesn't use the 2nd arg, but the one in rest-xml 17 | does, and this function gets invoked by rest/build-http-request, 18 | which requires a 3 arg serialize fn." 19 | (fn [_shapes _ shape _data] (:type shape))) 20 | 21 | (defmethod serialize :default 22 | [shapes _ shape data] 23 | (shape/json-serialize shapes shape data)) 24 | 25 | (defmethod serialize "structure" 26 | [shapes _ shape data] 27 | (some->> (util/with-defaults shape data) 28 | not-empty 29 | (shape/json-serialize shapes shape))) 30 | 31 | (defmethod serialize "timestamp" 32 | [_shapes _ shape data] 33 | (shape/format-date shape data)) 34 | 35 | (defmethod aws.protocols/build-http-request "rest-json" 36 | [service op-map] 37 | (rest/build-http-request service op-map serialize)) 38 | 39 | (defmethod aws.protocols/parse-http-response "rest-json" 40 | [service op-map http-response] 41 | (rest/parse-http-response service 42 | op-map 43 | http-response 44 | shape/json-parse)) 45 | -------------------------------------------------------------------------------- /src/cognitect/aws/protocols/json.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.protocols.json 5 | "Impl, don't call directly." 6 | (:require [cognitect.aws.protocols :as aws.protocols] 7 | [cognitect.aws.shape :as shape] 8 | [cognitect.aws.util :as util])) 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | (defmulti serialize 13 | (fn [_shapes shape _data] (:type shape))) 14 | 15 | (defmethod serialize :default 16 | [shapes shape data] 17 | (shape/json-serialize shapes shape data)) 18 | 19 | (defmethod serialize "structure" 20 | [shapes shape data] 21 | (->> (util/with-defaults shape data) 22 | (shape/json-serialize shapes shape))) 23 | 24 | (defmethod aws.protocols/build-http-request "json" 25 | [service {:keys [op request]}] 26 | (let [operation (get-in service [:operations op]) 27 | shapes (:shapes service) 28 | input-shape (shape/resolve shapes (:input operation))] 29 | {:request-method :post 30 | :scheme :https 31 | :server-port 443 32 | :uri "/" 33 | :headers (aws.protocols/headers service operation) 34 | :body (serialize shapes input-shape (or request {}))})) 35 | 36 | (defmethod aws.protocols/parse-http-response "json" 37 | [service {:keys [op]} {:keys [body]}] 38 | (let [operation (get-in service [:operations op]) 39 | shapes (:shapes service) 40 | output-shape (shape/resolve shapes (:output operation)) 41 | body-str (util/bbuf->str body)] 42 | (if output-shape 43 | (shape/json-parse shapes output-shape body-str) 44 | {}))) 45 | -------------------------------------------------------------------------------- /test/resources/cognitect/protocols/output/json.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "No output", 4 | "metadata": { 5 | "metadata": { 6 | "protocol": "json" 7 | }, 8 | "shapes": { 9 | "OutputShape": { 10 | "type": "structure", 11 | "members": { 12 | "StrType": { 13 | "shape": "StrType" 14 | } 15 | } 16 | }, 17 | "StrType": { 18 | "type": "string" 19 | } 20 | }, 21 | "cases": [ 22 | { 23 | "given": { 24 | "name": "OperationName" 25 | }, 26 | "result": {}, 27 | "response": { 28 | "status_code": 200, 29 | "headers": {} 30 | } 31 | } 32 | ] 33 | } 34 | }, 35 | { 36 | "description": "Timestamp members with doubles", 37 | "metadata": { 38 | "protocol": "json" 39 | }, 40 | "shapes": { 41 | "OutputShape": { 42 | "type": "structure", 43 | "members": { 44 | "TimeArg": { 45 | "shape": "TimestampType" 46 | } 47 | } 48 | }, 49 | "TimestampType": { 50 | "type": "timestamp" 51 | } 52 | }, 53 | "cases": [ 54 | { 55 | "given": { 56 | "output": { 57 | "shape": "OutputShape" 58 | }, 59 | "name": "OperationName" 60 | }, 61 | "result": { 62 | "TimeArg": 1398796238 63 | }, 64 | "response": { 65 | "status_code": 200, 66 | "headers": {}, 67 | "body": "{\"TimeArg\": 1.398796238E9}" 68 | } 69 | } 70 | ] 71 | } 72 | ] 73 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/shape_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.shape-test 2 | (:require [clojure.test :refer [deftest testing is]] 3 | [cognitect.aws.shape :as shape])) 4 | 5 | (deftest test-parse-date 6 | (testing "returns nil for nil" 7 | (is (nil? (shape/parse-date {} nil))) 8 | (is (nil? (shape/parse-date {:timestampFormat "rfc822"} nil))) 9 | (is (nil? (shape/parse-date {:timestampFormat "iso8601"} nil)))) 10 | (testing "returns nil for incorrect format" 11 | (is (nil? (shape/parse-date {:timestampFormat "rfc822"} "wrong"))) 12 | (is (nil? (shape/parse-date {:timestampFormat "iso8601"} "wrong")))) 13 | (testing "iso8601 format handles presence and absence of fractional seconds" 14 | (is (= #inst "2020-07-06T10:59:13.000-00:00" 15 | (shape/parse-date {:timestampFormat "iso8601"} "2020-07-06T10:59:13Z"))) 16 | (is (= #inst "2020-07-06T10:59:13.417-00:00" 17 | (shape/parse-date {:timestampFormat "iso8601"} "2020-07-06T10:59:13.417Z"))))) 18 | 19 | (deftest parse-json-structure 20 | (testing "no shape" 21 | (is (= {} 22 | (shape/json-parse* {} 23 | {:type "structure"} 24 | [{:this "is" :a "doc"}])))) 25 | (testing "ignores unspecified members" 26 | (is (= {:a "b"} 27 | (shape/json-parse* {} 28 | {:type "structure" :members {:a {:type "string"}}} 29 | {:a "b" :extra "whatever"})))) 30 | (testing ":document true" 31 | (is (= [{:this "is" :a "doc"}] 32 | (shape/json-parse* {} 33 | {:type "structure" :document true} 34 | [{:this "is" :a "doc"}]))))) 35 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/protocols/rest_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.protocols.rest-test 2 | (:require [clojure.test :refer [deftest is testing]] 3 | [cognitect.aws.protocols.rest :as protocols.rest])) 4 | 5 | (deftest test-serialize-url 6 | (testing "ensures no double-slash" 7 | (is (= "/a/b/c/d/e/f" 8 | (protocols.rest/serialize-uri "/{Foo+}/{Bar+}" {} {:Foo "a/b/c" :Bar "d/e/f"}) 9 | (protocols.rest/serialize-uri "/{Foo+}/{Bar+}" {} {:Foo "a/b/c" :Bar "/d/e/f"}) 10 | (protocols.rest/serialize-uri "/{Foo+}/{Bar+}" {} {:Foo "/a/b/c" :Bar "/d/e/f"}) 11 | (protocols.rest/serialize-uri "/{Foo+}/{Bar+}" {} {:Foo "/a/b/c" :Bar "d/e/f"})))) 12 | 13 | (testing "throws when required key is missing" 14 | (is (thrown-with-msg? Exception 15 | #"missing" 16 | (protocols.rest/serialize-uri "/{Bucket}" {:required ["Bucket"]} {}))) 17 | (is (thrown-with-msg? Exception 18 | #"missing" 19 | (protocols.rest/serialize-uri "/{Bucket}" {:required ["Bucket"]} {:BucketName "wrong key"}))) 20 | (is (thrown-with-msg? Exception 21 | #"missing" 22 | (protocols.rest/serialize-uri "/{Bucket}/{Key+}" {:required ["Bucket" "Key"]} {:Bucket "foo"})))) 23 | 24 | (testing "serializes longs" 25 | (is (= "/a/1/b" 26 | (protocols.rest/serialize-uri "/a/{count}/b" {:type "structure" 27 | :members {:Count {:shape "__long", :location "uri", :locationName "count"}} 28 | :required ["Count"]} 29 | {:count 1}))))) 30 | -------------------------------------------------------------------------------- /src/cognitect/aws/client/shared.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.client.shared 5 | "Default, globally shared resources which are by default shared among all AWS clients and whose 6 | lifecycles and resources are automatically managed." 7 | (:require [cognitect.aws.http :as http] 8 | [cognitect.aws.credentials :as credentials] 9 | [cognitect.aws.region :as region])) 10 | 11 | (set! *warn-on-reflection* true) 12 | 13 | (declare http-client) 14 | 15 | (def ^:private shared-http-client 16 | (delay (http/resolve-http-client nil))) 17 | 18 | (def ^:private shared-credentials-provider 19 | (delay (credentials/default-credentials-provider (http-client)))) 20 | 21 | (def ^:private shared-region-provider 22 | (delay (region/default-region-provider (http-client)))) 23 | 24 | (defn http-client 25 | "Returns the globally shared instance of http-client (created on the 26 | first call). 27 | 28 | Alpha. Subject to change." 29 | [] 30 | @shared-http-client) 31 | 32 | (defn credentials-provider 33 | "Returns the globally shared instance of credentials-provider, which 34 | uses the globally shared instance of http-client. 35 | 36 | Alpha. Subject to change." 37 | [] 38 | @shared-credentials-provider) 39 | 40 | (defn region-provider 41 | "Returns the globally shared instance of region-provider, which 42 | uses the globally shared instance of http-client. 43 | 44 | Alpha. Subject to change." 45 | [] 46 | @shared-region-provider) 47 | 48 | #_:clj-kondo/ignore 49 | (defn ^:private shared-http-client? 50 | "For internal use. 51 | 52 | Alpha. Subject to change." 53 | [candidate-http-client] 54 | (identical? candidate-http-client 55 | (and (realized? shared-http-client) @shared-http-client))) 56 | -------------------------------------------------------------------------------- /test/resources/cognitect/protocols/input/rest-xml.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "Lists in headers", 4 | "metadata": { 5 | "protocol": "rest-xml", 6 | "apiVersion": "2014-01-01" 7 | }, 8 | "shapes": { 9 | "ObjectAttributes": { 10 | "type": "string", 11 | "enum": [ 12 | "ETag", 13 | "Checksum", 14 | "ObjectParts", 15 | "StorageClass", 16 | "ObjectSize" 17 | ] 18 | }, 19 | "ObjectAttributesList": { 20 | "type": "list", 21 | "member": { 22 | "shape": "ObjectAttributes" 23 | } 24 | }, 25 | "GetObjectAttributesRequest": { 26 | "type": "structure", 27 | "members": { 28 | "ObjectAttributes": { 29 | "shape": "ObjectAttributesList", 30 | "location": "header", 31 | "locationName": "x-amz-object-attributes" 32 | } 33 | } 34 | } 35 | }, 36 | "cases": [ 37 | { 38 | "given": { 39 | "http": { 40 | "method": "GET", 41 | "requestUri": "/2014-01-01/hostedzone" 42 | }, 43 | "input": { 44 | "shape": "GetObjectAttributesRequest" 45 | }, 46 | "name": "GetObjectAttributes" 47 | }, 48 | "params": { 49 | "ObjectAttributes": [ 50 | "Checksum", 51 | "ObjectParts" 52 | ] 53 | }, 54 | "serialized": { 55 | "method": "GET", 56 | "uri": "/2014-01-01/hostedzone", 57 | "headers": {"x-amz-object-attributes": "Checksum,ObjectParts"} 58 | } 59 | } 60 | ] 61 | } 62 | ] 63 | 64 | 65 | -------------------------------------------------------------------------------- /doc/types.md: -------------------------------------------------------------------------------- 1 | # Data Types 2 | 3 | AWS service descriptions define a number of data types for use in `:request` maps passed to `cognitect.aws.client.api/invoke` and responses. aws-api supports them as follows: 4 | 5 | | AWS type | Clojure/java input type | Clojure/java output type (if different) | 6 | |-----------|-----------------------------------|-----------------------------------------| 7 | | structure | map with keyword keys | | 8 | | map | map with arbitrary types for keys | | 9 | | list | sequence | | 10 | | string | string | | 11 | | character | character | | 12 | | boolean | boolean | | 13 | | double | double | | 14 | | float | double | | 15 | | long | long | | 16 | | integer | long | | 17 | | blob | byte[] or java.io.InputStream* | java.io.InputStream | 18 | | timestamp | java.util.Date | | 19 | 20 | * for the time being, if you submit a `java.io.InputStream` for a `blob`, 21 | it must fit in memory. 22 | 23 | You can validate that your request maps conform to these types by enabling `clojure.spec`-backed request validation during development: 24 | 25 | ``` clojure 26 | (cognitect.aws.client.api/validate-requests client true) 27 | ``` 28 | -------------------------------------------------------------------------------- /src/cognitect/aws/interceptors.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.interceptors 5 | "Impl, don't call directly." 6 | (:require [cognitect.aws.service :as service] 7 | [cognitect.aws.util :as util])) 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | (defmulti modify-http-request (fn [service _op-map _http-request] 12 | (service/service-name service))) 13 | 14 | (defmethod modify-http-request :default [_service _op-map http-request] http-request) 15 | 16 | (def md5-blacklist 17 | "Set of ops that should not get the Content-MD5 header. 18 | 19 | See https://github.com/aws/aws-sdk-java-v2/blob/master/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AddContentMd5HeaderInterceptor.java " 20 | #{:PutObject :UploadPart}) 21 | 22 | (defmethod modify-http-request "s3" [service op-map http-request] 23 | (if (and (= "md5" (get-in service [:metadata :checksumFormat])) 24 | (not (md5-blacklist (:op op-map))) 25 | (:body http-request)) 26 | (update http-request :headers assoc "Content-MD5" (-> http-request :body util/md5 util/base64-encode)) 27 | http-request)) 28 | 29 | (defmethod modify-http-request "apigatewaymanagementapi" [_service op-map http-request] 30 | (if (= :PostToConnection (:op op-map)) 31 | (update http-request :uri str (-> op-map :request :ConnectionId)) 32 | http-request)) 33 | 34 | ;; See https://github.com/aws/aws-sdk-java-v2/blob/985ec92c0dfac868b33791fe4623296c68e2feab/services/glacier/src/main/java/software/amazon/awssdk/services/glacier/internal/GlacierExecutionInterceptor.java#L40 35 | (defmethod modify-http-request "glacier" [service _op-map http-request] 36 | (assoc-in http-request 37 | [:headers "x-amz-glacier-version"] 38 | (get-in service [:metadata :apiVersion]))) -------------------------------------------------------------------------------- /test/src/cognitect/aws/endpoint_test.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.endpoint-test 5 | (:require [clojure.test :refer [deftest is testing]] 6 | [cognitect.aws.endpoint :as endpoint])) 7 | 8 | (def endpoints-excerpt 9 | {:partitions 10 | [{:defaults 11 | {:hostname "{service}.{region}.{dnsSuffix}" 12 | :protocols ["https"] 13 | :signatureVersions ["v4"]} 14 | :dnsSuffix "amazonaws.com" 15 | :partition "aws" 16 | :partitionName "AWS Standard" 17 | :regionRegex "^(us|eu|ap|sa|ca)\\-\\w+\\-\\d+$" 18 | :regions 19 | {:us-east-1 {:description "US East (N. Virginia)"}} 20 | :services 21 | {:ec2 22 | {:defaults {:protocols ["http" "https"]} 23 | :endpoints 24 | {:us-east-1 {}}} 25 | :marketplacecommerceanalytics {:endpoints {:us-east-1 {}}} 26 | :iam 27 | {:endpoints 28 | {:aws-global 29 | {:credentialScope {:region "us-east-1"} 30 | :hostname "iam.amazonaws.com"}} 31 | :isRegionalized false 32 | :partitionEndpoint "aws-global"}}}]}) 33 | 34 | (deftest test-resolve-endpoints 35 | (testing "resolves regionalized endpoints" 36 | (with-redefs [endpoint/resolver (constantly endpoints-excerpt)] 37 | (is (= "ec2.us-east-1.amazonaws.com" (:hostname (endpoint/resolve* :ec2 :us-east-1)))))) 38 | (testing "resolves global endpoints" 39 | (with-redefs [endpoint/resolver (constantly endpoints-excerpt)] 40 | (is (= "iam.amazonaws.com" 41 | (:hostname (endpoint/resolve* :iam :us-east-1)))))) 42 | (testing "uses defaults to resolve unspecified endpoints" 43 | (with-redefs [endpoint/resolver (constantly endpoints-excerpt)] 44 | (is (= "i-do-not-exist.us-east-1.amazonaws.com" 45 | (:hostname (endpoint/resolve* :i-do-not-exist :us-east-1))))))) 46 | -------------------------------------------------------------------------------- /src/cognitect/aws/client/validation.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.client.validation 5 | "For internal use. Don't call directly." 6 | (:require [cognitect.aws.client.protocol :as client.protocol] 7 | [cognitect.aws.dynaload :as dynaload] 8 | [cognitect.aws.service :as service])) 9 | 10 | (set! *warn-on-reflection* true) 11 | 12 | (defn validate-requests? 13 | "For internal use. Don't call directly." 14 | [client] 15 | (some-> client client.protocol/-get-info :validate-requests? deref)) 16 | 17 | (def ^:private registry-ref (delay (dynaload/load-var 'clojure.spec.alpha/registry))) 18 | (defn registry 19 | "For internal use. Don't call directly." 20 | [& args] (apply @registry-ref args)) 21 | 22 | (def ^:private valid?-ref (delay (dynaload/load-var 'clojure.spec.alpha/valid?))) 23 | (defn valid? 24 | "For internal use. Don't call directly." 25 | [& args] (apply @valid?-ref args)) 26 | 27 | (def ^:private explain-data-ref (delay (dynaload/load-var 'clojure.spec.alpha/explain-data))) 28 | (defn explain-data 29 | "For internal use. Don't call directly." 30 | [& args] (apply @explain-data-ref args)) 31 | 32 | (defn request-spec 33 | "For internal use. Don't call directly." 34 | [service op] 35 | (when-let [spec (service/request-spec-key service op)] 36 | (when (contains? (-> (registry) keys set) spec) 37 | spec))) 38 | 39 | (defn invalid-request-anomaly 40 | "For internal use. Don't call directly." 41 | [spec request] 42 | (assoc (explain-data spec request) 43 | :cognitect.anomalies/category :cognitect.anomalies/incorrect)) 44 | 45 | (defn unsupported-op-anomaly 46 | "For internal use. Don't call directly." 47 | [service op] 48 | {:cognitect.anomalies/category :cognitect.anomalies/unsupported 49 | :cognitect.anomalies/message "Operation not supported" 50 | :service (keyword (service/service-name service)) 51 | :op op}) -------------------------------------------------------------------------------- /test/resources/cognitect/protocols/output/rest-json.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "No output", 4 | "metadata": { 5 | "metadata": { 6 | "protocol": "rest-json" 7 | }, 8 | "shapes": { 9 | "OutputShape": { 10 | "type": "structure", 11 | "members": { 12 | "StrType": { 13 | "shape": "StrType" 14 | } 15 | } 16 | }, 17 | "StrType": { 18 | "type": "string" 19 | } 20 | }, 21 | "cases": [ 22 | { 23 | "given": { 24 | "name": "OperationName" 25 | }, 26 | "result": {}, 27 | "response": { 28 | "status_code": 200, 29 | "headers": {} 30 | } 31 | } 32 | ] 33 | } 34 | }, 35 | { 36 | "description": "Named locations in JSON body", 37 | "metadata": { 38 | "protocol": "rest-json" 39 | }, 40 | "shapes": { 41 | "OutputShape": { 42 | "type": "structure", 43 | "payload": "Data", 44 | "members": { 45 | "Data": { 46 | "shape": "BodyStructure" 47 | } 48 | } 49 | }, 50 | "BodyStructure": { 51 | "type": "structure", 52 | "members": { 53 | "Foo": { 54 | "shape": "StringType", 55 | "locationName": "FOO" 56 | } 57 | } 58 | }, 59 | "StringType": { 60 | "type": "string" 61 | } 62 | }, 63 | "cases": [ 64 | { 65 | "given": { 66 | "output": { 67 | "shape": "OutputShape" 68 | }, 69 | "name": "OperationName" 70 | }, 71 | "result": { 72 | "Data": { 73 | "Foo": "abc" 74 | } 75 | }, 76 | "response": { 77 | "status_code": 200, 78 | "headers": {}, 79 | "body": "{\"FOO\": \"abc\"}" 80 | } 81 | } 82 | ] 83 | } 84 | ] 85 | -------------------------------------------------------------------------------- /src/cognitect/aws/retry.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.retry 5 | (:require [clojure.core.async :as a])) 6 | 7 | (set! *warn-on-reflection* true) 8 | 9 | (defn ^:skip-wiki with-retry 10 | "For internal use. Do not call directly. 11 | 12 | Calls req-fn, a *non-blocking* function that wraps some operation 13 | and returns a channel. When the response to req-fn is retriable? 14 | and backoff returns an int, waits backoff ms and retries, otherwise 15 | puts response on resp-chan." 16 | [req-fn resp-chan retriable? backoff] 17 | (a/go-loop [retries 0] 18 | (let [resp (a/! resp-chan resp)) 25 | (a/>! resp-chan resp)))) 26 | resp-chan) 27 | 28 | (defn capped-exponential-backoff 29 | "Returns a function of the num-retries (so far), which returns the 30 | lesser of max-backoff and an exponentially increasing multiple of 31 | base, or nil when (>= num-retries max-retries). 32 | See with-retry to see how it is used. 33 | 34 | Alpha. Subject to change." 35 | [base max-backoff max-retries] 36 | (fn [num-retries] 37 | (when (< num-retries max-retries) 38 | (min max-backoff 39 | (* base (bit-shift-left 1 num-retries)))))) 40 | 41 | (def default-backoff 42 | "Returns (capped-exponential-backoff 100 20000 3). 43 | 44 | Alpha. Subject to change." 45 | (capped-exponential-backoff 100 20000 3)) 46 | 47 | (def default-retriable? 48 | "A fn of an http-response map which returns a truthy value 49 | if (:cognitect.anomalies/category http-response) is any of: 50 | - :cognitect.anomalies/busy 51 | - :cognitect.anomalies/interrupted 52 | - :cognitect.anomalies/unavailable 53 | 54 | Alpha. Subject to change." 55 | (comp #{:cognitect.anomalies/busy 56 | :cognitect.anomalies/interrupted 57 | :cognitect.anomalies/unavailable} 58 | :cognitect.anomalies/category)) 59 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/ec2_metadata_utils_test.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.ec2-metadata-utils-test 5 | (:require [clojure.core.async :as a] 6 | [clojure.test :refer [deftest is testing]] 7 | [cognitect.aws.ec2-metadata-utils :as ec2-metadata-utils] 8 | [cognitect.aws.http :as http] 9 | [cognitect.aws.util :as u]) 10 | (:import [java.net URI])) 11 | 12 | 13 | (deftest returns-nil-after-retries 14 | ;; Using a mock http client that keeps track of the number of http requests and always returns a 15 | ;; busy response, assert that the expected number of requests were made and that ultimately a nil 16 | ;; response was returned. 17 | (let [expected-submit-count 4 ;; Expected to equal `max-retries` plus one. 18 | actual-submit-count (atom 0) 19 | response-chan (doto (a/promise-chan) 20 | (a/>!! {:cognitect.anomalies/category :cognitect.anomalies/busy})) 21 | mock-http-client (reify http/HttpClient 22 | (-submit [_ _request _channel] 23 | (swap! actual-submit-count inc) 24 | response-chan) 25 | (-stop [_]))] 26 | (is (nil? (ec2-metadata-utils/get-ec2-instance-region mock-http-client))) 27 | (is (= expected-submit-count @actual-submit-count)))) 28 | 29 | (deftest request-map 30 | (testing "server-port" 31 | (is (= 443 (:server-port (#'ec2-metadata-utils/request-map (URI/create "https://169.254.169.254"))))) 32 | (is (= 80 (:server-port (#'ec2-metadata-utils/request-map (URI/create "http://169.254.169.254"))))) 33 | (is (= 8081 (:server-port (#'ec2-metadata-utils/request-map (URI/create "http://169.254.169.254:8081")))))) 34 | (testing "auth token" 35 | (is (nil? (get-in (#'ec2-metadata-utils/request-map (URI/create "http://localhost")) [:headers "Authorization"]))) 36 | (with-redefs [u/getenv {"AWS_CONTAINER_AUTHORIZATION_TOKEN" "this-is-the-token"}] 37 | (is (#{"this-is-the-token"} (get-in (#'ec2-metadata-utils/request-map (URI/create "http://localhost")) [:headers "Authorization"])))))) 38 | -------------------------------------------------------------------------------- /src/cognitect/aws/protocols/ec2.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.protocols.ec2 5 | "Impl, don't call directly." 6 | (:require [cognitect.aws.protocols :as aws.protocols] 7 | [cognitect.aws.protocols.query :as query] 8 | [cognitect.aws.shape :as shape] 9 | [cognitect.aws.util :as util])) 10 | 11 | (set! *warn-on-reflection* true) 12 | 13 | (defn serialized-name 14 | [shape default] 15 | (or (:queryName shape) 16 | (when-let [name (:locationName shape)] 17 | (apply str (Character/toUpperCase ^Character (first name)) (rest name))) 18 | default)) 19 | 20 | (defmulti serialize 21 | (fn [_shapes shape _args _serialized _prefix] (:type shape))) 22 | 23 | (defmethod serialize :default 24 | [shapes shape args serialized prefix] 25 | (query/serialize shapes shape args serialized prefix)) 26 | 27 | (defmethod serialize "structure" 28 | [shapes shape args serialized prefix] 29 | (let [args (util/with-defaults shape args)] 30 | (reduce (fn [serialized k] 31 | (let [member-shape (shape/resolve shapes (shape/structure-member-shape-ref shape k)) 32 | member-name (serialized-name member-shape (name k))] 33 | (if (contains? args k) 34 | (serialize shapes member-shape (k args) serialized (conj prefix member-name)) 35 | serialized))) 36 | serialized 37 | (keys (:members shape))))) 38 | 39 | (defmethod serialize "list" 40 | [shapes shape args serialized prefix] 41 | (let [member-shape (shape/resolve shapes (shape/list-member-shape-ref shape))] 42 | (reduce (fn [serialized [i member]] 43 | (serialize shapes member-shape member serialized (conj prefix (str i)))) 44 | serialized 45 | (map-indexed (fn [i member] [(inc i) member]) args)))) 46 | 47 | (defmethod aws.protocols/build-http-request "ec2" 48 | [service op-map] 49 | (query/build-query-http-request op-map service serialize)) 50 | 51 | (defmethod aws.protocols/parse-http-response "ec2" 52 | [service op-map http-response] 53 | (query/build-query-http-response service op-map http-response)) 54 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/api_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.api-test 2 | (:require [clojure.datafy :as datafy] 3 | [clojure.test :as t :refer [deftest is testing]] 4 | [cognitect.aws.client.api :as aws] 5 | [cognitect.aws.client.protocol :as client.protocol] 6 | [cognitect.aws.client.shared :as shared] 7 | [cognitect.aws.http :as http])) 8 | 9 | (deftest test-underlying-http-client 10 | (testing "defaults to shared client" 11 | (let [clients (repeatedly 5 #(aws/client {:api :s3 :region "us-east-1"}))] 12 | (is (= #{(shared/http-client)} 13 | (into #{(shared/http-client)} 14 | (->> clients (map (fn [c] (-> c client.protocol/-get-info :http-client)))))))))) 15 | 16 | (deftest test-datafy 17 | (let [client (aws/client {:api :s3}) 18 | data (datafy/datafy client)] 19 | (is (= "s3" (:api data))) 20 | (is (map? (:service data))) 21 | (is (map? (:metadata (:service data)))) 22 | (is (map? (:endpoint data))) 23 | (is (string? (:region data))) 24 | (is (map? (:ops data))) 25 | (is (map? (:endpoint data))))) 26 | 27 | (deftest test-stop 28 | (let [call-count (atom 0) 29 | default (aws/client {:api :s3 :region "us-east-1"}) 30 | supplied-shared (aws/client {:api :s3 :region "us-east-1" 31 | :http-client (shared/http-client)}) 32 | supplied-unshared (aws/client {:api :s3 :region "us-east-1" 33 | :http-client (http/resolve-http-client nil)})] 34 | (with-redefs [http/stop (fn [_] (swap! call-count inc))] 35 | (testing "has no effect when aws-client uses the default shared http-client" 36 | (aws/stop default) 37 | (is (zero? @call-count))) 38 | (testing "has no effect when user supplies the shared http-client" 39 | (aws/stop supplied-shared) 40 | (is (zero? @call-count))) 41 | (testing "forwards to http/stop when user supplies a different http-client" 42 | (aws/stop supplied-unshared) 43 | (is (= 1 @call-count)))) 44 | ;; now actually stop the aws-client with the non-shared http-client 45 | (aws/stop supplied-unshared))) 46 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/generators.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.generators 2 | (:require [clojure.string :as str] 3 | [clojure.test.check.generators :as gen] 4 | [cognitect.aws.util :as util])) 5 | 6 | ;; see cognitect.aws.protocols.rest/serialize-uri 7 | ;; we want to mimic inputs to cognitect.aws.signers/sign-http-request 8 | (defn ^:private serialize-path-part 9 | [part] 10 | (-> part 11 | (util/url-encode) 12 | (str/replace "%2F" "/") 13 | (str/replace "%7E" "~"))) 14 | 15 | (defn gen-service 16 | [sig-ver] 17 | (gen/let [service-name (gen/such-that seq gen/string-alphanumeric)] 18 | {:metadata {:signatureVersion sig-ver 19 | :signingName service-name}})) 20 | 21 | (def gen-request 22 | (gen/let [host (gen/fmap #(str % ".com") (gen/such-that seq gen/string-alphanumeric)) 23 | path-parts (gen/vector (gen/fmap serialize-path-part (gen/such-that (complement str/blank?) gen/string-ascii)) 1 10) 24 | path-separator (gen/elements ["/" "//"]) 25 | query-ks (gen/vector (gen/such-that seq gen/string-alphanumeric)) 26 | query-vs (gen/vector (gen/such-that seq gen/string-alphanumeric) (count query-ks)) 27 | method (gen/elements [:get :post]) 28 | ;; https://github.com/aws/aws-sdk-java-v2/blob/61d16e0/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerKey.java#L44 29 | ;; date will be >1 day past epoch and <= today 30 | ;; 1735838904634 == 2025-01-02 31 | epoch (gen/large-integer* {:min 86400000 :max 1735838904634}) 32 | body gen/string] 33 | {:request-method method 34 | :body (.getBytes ^String body "UTF-8") 35 | :headers {"x-amz-date" (util/format-date util/x-amz-date-format (java.util.Date. epoch)) 36 | "host" host} 37 | :uri (str path-separator 38 | (str/join path-separator path-parts) 39 | (when (seq query-ks) 40 | (str "?" (str/join "&" (map #(str %1 "=" %2) 41 | query-ks 42 | query-vs)))))})) 43 | -------------------------------------------------------------------------------- /test/resources/cognitect/protocols/output/rest-xml.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "Single text node", 4 | "metadata": {"protocol": "rest-xml"}, 5 | "shapes": { 6 | "OutputShape": { 7 | "type": "structure", 8 | "members": { 9 | "LocationConstraint": { 10 | "shape": "StringType" 11 | } 12 | } 13 | }, 14 | "StringType": { 15 | "type": "string" 16 | } 17 | }, 18 | "cases": [ 19 | { 20 | "given": { 21 | "output": { 22 | "shape": "OutputShape" 23 | }, 24 | "name": "OperationName" 25 | }, 26 | "result": { 27 | "LocationConstraint": "us-west-1" 28 | }, 29 | "response": { 30 | "status_code": 200, 31 | "body": "us-west-1" 32 | } 33 | } 34 | ] 35 | }, 36 | { 37 | "description": "Single string in payload", 38 | "metadata": {"protocol": "rest-xml"}, 39 | "shapes": { 40 | "OutputShape": { 41 | "type": "structure", 42 | "members": { 43 | "Data": { 44 | "shape": "DataShape" 45 | } 46 | }, 47 | "payload" : "Data" 48 | }, 49 | "DataShape": { 50 | "type": "string" 51 | } 52 | }, 53 | "cases": [ 54 | { 55 | "given": { 56 | "output": { 57 | "shape": "OutputShape" 58 | }, 59 | "name": "OperationName" 60 | }, 61 | "result": { 62 | "Data": "{\"a\":1}" 63 | }, 64 | "response": { 65 | "status_code": 200, 66 | "body": "{\"a\":1}" 67 | } 68 | } 69 | ] 70 | } 71 | ] 72 | -------------------------------------------------------------------------------- /src/cognitect/aws/config.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.config 5 | (:require [clojure.string :as str])) 6 | 7 | (set! *warn-on-reflection* true) 8 | 9 | ;;; predicates 10 | 11 | (defn comment? [s] 12 | (str/starts-with? s "#")) 13 | 14 | (defn start-profile? [s] 15 | (str/starts-with? s "[")) 16 | 17 | (defn start-nested? [s] 18 | (re-find #"^[\w-_\.]+\s*=$" s)) 19 | 20 | (defn add-profile-kv? [s] 21 | (re-find #"^[\w-_\.]+\s*=.*\w+" s)) 22 | 23 | (defn add-nested-kv? [s] 24 | (re-find #"^\s+[\w-_\.]+\s*=.*\w+" s)) 25 | 26 | ;;; helpers 27 | 28 | (defn split-lines [s] 29 | (into [] 30 | (comp (map str/trimr) 31 | (remove str/blank?) 32 | (remove comment?)) 33 | (str/split-lines s))) 34 | 35 | (defn split-kv [s] 36 | (->> (str/split s #"=" 2) 37 | (map str/trim))) 38 | 39 | ;;; actions 40 | 41 | (defn set-profile-path [m line] 42 | (assoc m :path [:profiles (second (re-find #"\[(?:profile)?\s*(.+)\]" line))])) 43 | 44 | (defn ensure-profile-path [m] 45 | (update m :path (comp vec (partial take 2)))) 46 | 47 | (defn set-nested-path [m line] 48 | (update m :path #(conj (vec (take 2 %)) 49 | (-> line (str/replace #"=" "") (str/trim))))) 50 | 51 | (defn add-profile-kv [m line] 52 | (let [[k v] (split-kv line)] 53 | (update-in m (take 2 (:path m)) assoc k v))) 54 | 55 | (defn add-nested-kv [m line] 56 | (let [[k v] (split-kv line)] 57 | (update-in m (:path m) assoc k v))) 58 | 59 | ;;; main 60 | 61 | (defn parse 62 | "Return the profiles in the configuration file." 63 | [file] 64 | (->> file 65 | slurp 66 | split-lines 67 | (reduce (fn [m ^String line] 68 | (cond (start-profile? line) 69 | (set-profile-path m line) 70 | 71 | (start-nested? line) 72 | (set-nested-path m line) 73 | 74 | (add-profile-kv? line) 75 | (-> m 76 | (add-profile-kv line) 77 | (ensure-profile-path)) 78 | 79 | (add-nested-kv? line) 80 | (add-nested-kv m line) 81 | 82 | :else 83 | (throw (ex-info "Invalid format in config" {:file file})))) 84 | {:profiles {}}) 85 | :profiles)) 86 | -------------------------------------------------------------------------------- /test/resources/cognitect/protocols/input/query.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "URL Encoded values in body", 4 | "metadata": { 5 | "protocol": "query", 6 | "apiVersion": "2014-01-01" 7 | }, 8 | "shapes": { 9 | "InputShape": { 10 | "type": "structure", 11 | "members": { 12 | "Integer": { 13 | "shape": "IntegerType" 14 | }, 15 | "Long": { 16 | "shape": "LongType" 17 | }, 18 | "Double": { 19 | "shape" : "DoubleType" 20 | }, 21 | "Float": { 22 | "shape" : "FloatType" 23 | }, 24 | "String": { 25 | "shape": "StringType" 26 | }, 27 | "Boolean": { 28 | "shape": "BooleanType" 29 | }, 30 | "Timestamp": { 31 | "shape" : "TimestampType" 32 | }, 33 | "Blob": { 34 | "shape": "BlobType" 35 | } 36 | } 37 | }, 38 | "IntegerType": { 39 | "type": "integer" 40 | }, 41 | "LongType": { 42 | "type": "long" 43 | }, 44 | "DoubleType": { 45 | "type": "double" 46 | }, 47 | "FloatType": { 48 | "type": "float" 49 | }, 50 | "StringType": { 51 | "type": "string" 52 | }, 53 | "BooleanType": { 54 | "type": "boolean" 55 | }, 56 | "TimestampType": { 57 | "type": "timestamp" 58 | }, 59 | "BlobType": { 60 | "type": "blob" 61 | } 62 | }, 63 | "cases": [ 64 | { 65 | "given": { 66 | "input": { 67 | "shape": "InputShape" 68 | }, 69 | "name": "OperationName" 70 | }, 71 | "params": { 72 | "Integer": 22, 73 | "Long": 37, 74 | "Double": 1.2, 75 | "Float": 3.4, 76 | "String": "a+b", 77 | "Boolean": true, 78 | "Timestamp": 1422172800, 79 | "Blob": "foo" 80 | }, 81 | "serialized": { 82 | "uri": "/", 83 | "headers": { 84 | "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" 85 | }, 86 | "body": "Action=OperationName&Version=2014-01-01&Integer=22&Long=37&Double=1.2&Float=3.4&String=a%2Bb&Boolean=true&Timestamp=2015-01-25T08%3A00%3A00Z&Blob=Zm9v" 87 | } 88 | } 89 | ] 90 | } 91 | ] 92 | -------------------------------------------------------------------------------- /examples/resources/dynamodb/Reply.json: -------------------------------------------------------------------------------- 1 | { 2 | "Reply": [ 3 | { 4 | "PutRequest": { 5 | "Item": { 6 | "Id": { 7 | "S": "Amazon DynamoDB#DynamoDB Thread 1" 8 | }, 9 | "ReplyDateTime": { 10 | "S": "2015-09-15T19:58:22.947Z" 11 | }, 12 | "Message": { 13 | "S": "DynamoDB Thread 1 Reply 1 text" 14 | }, 15 | "PostedBy": { 16 | "S": "User A" 17 | } 18 | } 19 | } 20 | }, 21 | { 22 | "PutRequest": { 23 | "Item": { 24 | "Id": { 25 | "S": "Amazon DynamoDB#DynamoDB Thread 1" 26 | }, 27 | "ReplyDateTime": { 28 | "S": "2015-09-22T19:58:22.947Z" 29 | }, 30 | "Message": { 31 | "S": "DynamoDB Thread 1 Reply 2 text" 32 | }, 33 | "PostedBy": { 34 | "S": "User B" 35 | } 36 | } 37 | } 38 | }, 39 | { 40 | "PutRequest": { 41 | "Item": { 42 | "Id": { 43 | "S": "Amazon DynamoDB#DynamoDB Thread 2" 44 | }, 45 | "ReplyDateTime": { 46 | "S": "2015-09-29T19:58:22.947Z" 47 | }, 48 | "Message": { 49 | "S": "DynamoDB Thread 2 Reply 1 text" 50 | }, 51 | "PostedBy": { 52 | "S": "User A" 53 | } 54 | } 55 | } 56 | }, 57 | { 58 | "PutRequest": { 59 | "Item": { 60 | "Id": { 61 | "S": "Amazon DynamoDB#DynamoDB Thread 2" 62 | }, 63 | "ReplyDateTime": { 64 | "S": "2015-10-05T19:58:22.947Z" 65 | }, 66 | "Message": { 67 | "S": "DynamoDB Thread 2 Reply 2 text" 68 | }, 69 | "PostedBy": { 70 | "S": "User A" 71 | } 72 | } 73 | } 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /test/src/cognitect/aws/http_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.http-test 2 | (:require [clojure.java.io :as io] 3 | [clojure.test :refer :all] 4 | [cognitect.aws.http :as aws-http])) 5 | 6 | (deftest configured-client-test 7 | (testing "Returns the default http client" 8 | (is (= 'cognitect.aws.http.default/create 9 | (#'aws-http/configured-client)))) 10 | 11 | (testing "Returns new http client on cognitect_aws_http.edn file" 12 | (spit (io/file "test/resources/cognitect_aws_http.edn") 13 | "{:constructor-var cognitect.aws.http.java/create}") 14 | 15 | (is (= 'cognitect.aws.http.java/create 16 | (#'aws-http/configured-client))) 17 | 18 | (is (= true 19 | (io/delete-file "test/resources/cognitect_aws_http.edn" :failed)) 20 | "Accidentally failed to delete file"))) 21 | 22 | (deftest uri-authority-test 23 | (testing "http scheme, default port" 24 | (is (= "a-server.example.com" (aws-http/uri-authority :http "a-server.example.com" 80))) 25 | (is (= "a-server.example.com" (aws-http/uri-authority :http "A-sErvEr.example.com" nil))) 26 | (is (= "a-server.example.com" (aws-http/uri-authority "HTTP" "a-server.example.com" 80))) 27 | (is (= "a-server.example.com" (aws-http/uri-authority "http" "a-server.example.com" nil)))) 28 | 29 | (testing "https scheme, default port" 30 | (is (= "a-server.example.com" (aws-http/uri-authority :https "a-server.example.com" 443))) 31 | (is (= "a-server.example.com" (aws-http/uri-authority :https "A-sErvEr.example.com" nil))) 32 | (is (= "a-server.example.com" (aws-http/uri-authority "https" "a-server.example.com" 443))) 33 | (is (= "a-server.example.com" (aws-http/uri-authority "HTTPs" "a-server.example.com" nil)))) 34 | 35 | (testing "http scheme, custom port" 36 | (is (= "a-server.example.com:8080" (aws-http/uri-authority :http "a-server.example.com" 8080))) 37 | (is (= "a-server.example.com:4080" (aws-http/uri-authority :http "a-server.example.com" 4080))) 38 | (is (= "a-server.example.com:443" (aws-http/uri-authority :http "a-server.example.com" 443))) 39 | (is (= "a-server.example.com:1" (aws-http/uri-authority :http "a-server.example.com" 1)))) 40 | 41 | (testing "https scheme, custom port" 42 | (is (= "a-server.example.com:8080" (aws-http/uri-authority :https "a-server.example.com" 8080))) 43 | (is (= "a-server.example.com:4443" (aws-http/uri-authority :https "a-server.example.com" 4443))) 44 | (is (= "a-server.example.com:80" (aws-http/uri-authority :https "a-server.example.com" 80))) 45 | (is (= "a-server.example.com:1" (aws-http/uri-authority :https "a-server.example.com" 1))))) 46 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | AWS_REGION: us-east-1 11 | 12 | jobs: 13 | tests-using-java: 14 | runs-on: ubuntu-24.04 15 | strategy: 16 | matrix: 17 | java-version: [ '8', '11', '17', '21'] 18 | 19 | name: Java version ${{ matrix.java-version }} 20 | 21 | services: 22 | s3: 23 | image: bitnamilegacy/minio 24 | ports: 25 | - 9000:9000 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | with: 31 | persist-credentials: false 32 | 33 | - name: Cache build materials 34 | uses: actions/cache@v4 35 | with: 36 | path: ~/.m2/repository 37 | key: ${{ runner.os }}-maven-${{ hashFiles('deps.edn') }} 38 | restore-keys: | 39 | ${{ runner.os }}-maven- 40 | 41 | - name: Setup Java 42 | uses: actions/setup-java@v4 43 | with: 44 | distribution: 'temurin' 45 | java-version: ${{ matrix.java-version }} 46 | 47 | - name: Print java version 48 | run: java -version 49 | 50 | - name: Setup Clojure 51 | uses: DeLaGuardo/setup-clojure@13.1 52 | with: 53 | cli: 1.12.0.1488 54 | bb: 1.12.200 55 | 56 | - name: Cache clojure dependencies 57 | uses: actions/cache@v4 58 | with: 59 | path: | 60 | ~/.m2/repository 61 | ~/.gitlibs 62 | ~/.deps.clj 63 | key: cljdeps-${{ hashFiles('deps.edn') }} 64 | restore-keys: cljdeps- 65 | 66 | - name: Run unit tests 67 | run: clojure -M:dev:test 68 | 69 | - name: Run integration tests 70 | env: 71 | # AWS_PROFILE is only required to comply with fixture check, 72 | # it's not actually used to load credentials 73 | AWS_PROFILE: aws-api-test 74 | AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TEST_AWS_ACCESS_KEY_ID }} 75 | AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TEST_AWS_SECRET_ACCESS_KEY }} 76 | 77 | run: clojure -M:dev:test-integration 78 | 79 | - name: Run babashka tests 80 | env: 81 | # AWS_PROFILE is only required to comply with fixture check, 82 | # it's not actually used to load credentials 83 | AWS_PROFILE: aws-api-test 84 | AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TEST_AWS_ACCESS_KEY_ID }} 85 | AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TEST_AWS_SECRET_ACCESS_KEY }} 86 | 87 | run: bb test-bb 88 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | {:paths ["src"] 5 | :deps {org.clojure/clojure {:mvn/version "1.12.0"} 6 | org.clojure/core.async {:mvn/version "1.8.741"} 7 | org.clojure/tools.logging {:mvn/version "1.3.0"} 8 | org.clojure/data.json {:mvn/version "2.5.1"} 9 | org.clojure/data.xml {:mvn/version "0.2.0-alpha9"}} 10 | :aliases {:update-versions {:extra-paths ["build/src"] 11 | :main-opts ["-m" "cognitect.aws.version-updater"]} 12 | :dev {:extra-paths ["dev/src" "dev/resources" "test/src" "test/resources" "examples" "examples/resources"] 13 | :extra-deps {commons-io/commons-io {:mvn/version "2.18.0"} 14 | org.clojure/test.check {:mvn/version "1.1.1"} 15 | org.slf4j/slf4j-reload4j {:mvn/version "2.0.16"} 16 | http-kit/http-kit {:mvn/version "2.8.0"} 17 | software.amazon.awssdk/s3 {:mvn/version "2.31.46"} 18 | com.cognitect/http-client {:mvn/version "1.0.127"} 19 | com.cognitect.aws/endpoints {:mvn/version "871.2.40.14"} 20 | com.cognitect.aws/autoscaling {:mvn/version "871.2.39.3"} 21 | com.cognitect.aws/dynamodb {:mvn/version "871.2.39.3"} 22 | com.cognitect.aws/ec2 {:mvn/version "871.2.40.14"} 23 | com.cognitect.aws/iam {:mvn/version "871.2.40.4"} 24 | com.cognitect.aws/lambda {:mvn/version "871.2.40.9"} 25 | com.cognitect.aws/s3 {:mvn/version "871.2.40.9"} 26 | com.cognitect.aws/ssm {:mvn/version "871.2.38.3"} 27 | com.cognitect.aws/sts {:mvn/version "871.2.39.3"}}} 28 | :test {:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}} 29 | :main-opts ["-m" "cognitect.test-runner" 30 | "-e" "integration"]} 31 | :test-integration {:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}} 32 | :main-opts ["-m" "cognitect.test-runner" 33 | "-i" "integration"]}}} 34 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | # Upgrade Notes 2 | 3 | ## 0.8.711 / 2024-12-03 4 | 5 | ### New Java-native HTTP client 6 | 7 | This release provides a new `java.net.http`-based HTTP client. 8 | 9 | This release changes the default type of http client to be the new Java 10 | native client, when the version of Java is recent enough (11 or newer). 11 | 12 | With this release, the `com.cognitect/http-client` is no longer 13 | a required dependency for this library. If you need to run on Java 8 14 | (which predates the `java.net.http` module), you must add an explicit 15 | dependency on `com.cognitect/http-client` in your project, to make the 16 | previous HTTP client implementation available again - it will be 17 | automatically used when running on Java 8, as long as it is in the 18 | classpath. 19 | 20 | If you only need to run on java 11+, the new HTTP client is now the 21 | default, and the change should be transparent. In case you want to 22 | revert to the previous HTTP client implementation, please see the 23 | README section about [overriding the http 24 | client](README.md#overriding-the-http-client). 25 | 26 | This fixes issues 27 | [181](https://github.com/cognitect-labs/aws-api/issues/181) and 28 | [250](https://github.com/cognitect-labs/aws-api/issues/250). 29 | 30 | ## 0.8.430 31 | 32 | This release changed the behavior of the following functions: 33 | 34 | ### com.cognitect.aws.api/client 35 | 36 | As of 0.8.430, each aws-api client uses a single shared http-client by 37 | default. Before this release, each aws-client got its own instance of 38 | http-client by default, which caused the number of threads consumed to 39 | increase linearly in relation to the number of aws-clients created. 40 | To reduce resource consumption in the case of many aws-clients, we 41 | recommended that you create a single instance of the http-client and 42 | explicitly share it across all aws-clients. This is no longer 43 | necessary. 44 | 45 | ### com.cognitect.aws.api/stop 46 | 47 | With the introduction of a shared http-client, this function was 48 | updated so that it has no effect when using the shared http-client, 49 | but will continue to call `cognitect.aws.http/stop` on any other 50 | http-client instance. 51 | 52 | ### effects 53 | 54 | These changes have the following effects: 55 | 56 | Programs that were creating multiple aws-clients without supplying 57 | an http-client, and without ever calling stop, will see a reduction 58 | in resource consumption. 59 | 60 | Programs that were creating an instance of 61 | `cognitect.aws.client.api/default-http-client` and sharing it across 62 | aws-clients should see no change. You can, however, safely stop doing 63 | that. 64 | 65 | For programs that were using the default aws-client constructor and 66 | calling stop on each aws-client, the shared http-client will not be 67 | shut down. This should have no negative impact on resource consumption, 68 | as there is only one http-client in this case, and its resources are 69 | managed by aws-api. 70 | 71 | For programs that were creating multiple aws-clients in order to get 72 | around an ["Ops limit reached" 73 | error](https://github.com/cognitect-labs/aws-api/issues/98), this is a 74 | breaking change. For this case, we recommend, for now, that you supply 75 | a new http-client for each aws-client. 76 | -------------------------------------------------------------------------------- /examples/s3_examples.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns s3-examples 5 | (:require [clojure.core.async :as a] 6 | [clojure.spec.alpha :as s] 7 | [clojure.spec.gen.alpha :as gen] 8 | [clojure.java.io :as io] 9 | [clojure.repl :as repl] 10 | [cognitect.aws.client.api :as aws])) 11 | 12 | (comment 13 | 14 | ;; make a client 15 | (def s3 (aws/client {:api :s3})) 16 | 17 | ;; guard against invalid :request map 18 | (aws/validate-requests s3 true) 19 | 20 | ;; what can it do? 21 | (aws/ops s3) 22 | ;; op names 23 | (-> (aws/ops s3) keys sort) 24 | 25 | ;; op doc-data 26 | (-> (aws/ops s3) :CreateBucket) 27 | ;; a little more friendly 28 | (aws/doc s3 :CreateBucket) 29 | 30 | ;; specs 31 | (aws/request-spec-key s3 :CreateBucket) 32 | (s/describe (aws/request-spec-key s3 :CreateBucket)) 33 | (gen/sample (s/gen (aws/request-spec-key s3 :CreateBucket))) 34 | 35 | (aws/response-spec-key s3 :CreateBucket) 36 | (s/describe (aws/response-spec-key s3 :CreateBucket)) 37 | (gen/sample (s/gen (aws/response-spec-key s3 :CreateBucket))) 38 | 39 | ;; * use this bucket-name to avoid collisions with other devs 40 | ;; * don't forget to delete the bucket when done exploring! 41 | (def bucket-name (str "cognitect-aws-test-" (.getEpochSecond (java.time.Instant/now)))) 42 | 43 | ;; see how submit works 44 | (repl/doc aws/invoke) 45 | 46 | ;; doit 47 | (aws/invoke s3 {:op :ListBuckets}) 48 | 49 | ;; http-request and http-response are in metadata 50 | (meta *1) 51 | 52 | (aws/invoke s3 {:op :CreateBucket :request {:Bucket bucket-name}}) 53 | 54 | ;; now you should see the bucket you just added 55 | (aws/invoke s3 {:op :ListBuckets}) 56 | 57 | ;; no objects yet ... 58 | (aws/invoke s3 {:op :ListObjectsV2 :request {:Bucket bucket-name}}) 59 | 60 | ;; Body is blob type, for which we accept a byte-array or an InputStream 61 | (aws/invoke s3 {:op :PutObject :request {:Bucket bucket-name :Key "hello.txt" 62 | :Body (.getBytes "Oh hai!")}}) 63 | 64 | (aws/invoke s3 {:op :PutObject :request {:Bucket bucket-name :Key "hello.txt" 65 | :Body (io/input-stream (.getBytes "Oh hai!"))}}) 66 | 67 | ;; now you should see the object you just added 68 | (aws/invoke s3 {:op :ListObjectsV2 :request {:Bucket bucket-name}}) 69 | 70 | ;; Body is a blob type, which always returns an InputStream 71 | (aws/invoke s3 {:op :GetObject :request {:Bucket bucket-name :Key "hello.txt"}}) 72 | 73 | ;; check it! 74 | (slurp (:Body *1)) 75 | 76 | (aws/invoke s3 {:op :DeleteObject :request {:Bucket bucket-name :Key "hello.txt"}}) 77 | 78 | (aws/invoke s3 {:op :DeleteObjects, :request {:Delete {:Objects [{:Key "hello.txt"}]}, :Bucket bucket-name}}) 79 | 80 | ;; poof, the object is gone! 81 | (aws/invoke s3 {:op :ListObjectsV2 :request {:Bucket bucket-name}}) 82 | 83 | (aws/invoke s3 {:op :DeleteBucket :request {:Bucket bucket-name}}) 84 | 85 | ;; poof, the bucket is gone! 86 | (aws/invoke s3 {:op :ListBuckets}) 87 | 88 | ;;;;;;;;;;;;;;;;;;;;;;; 89 | 90 | ;; see how submit works w/ async 91 | (clojure.repl/doc aws/invoke-async) 92 | 93 | ;; async! 94 | (def c (aws/invoke-async s3 {:op :ListBuckets})) 95 | 96 | (a/!! c {:this :map}) 13 | response-ch (retry/with-retry 14 | (constantly c) 15 | (a/promise-chan) 16 | (constantly false) 17 | (constantly nil))] 18 | (a/!! c {:this :map}) 25 | response-ch (retry/with-retry 26 | (constantly c) 27 | (a/promise-chan) 28 | (constantly true) 29 | (constantly nil))] 30 | (a/!! c {:cognitect.anomalies/category :cognitect.anomalies/busy 36 | :test/attempt-number 1}) 37 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/busy 38 | :test/attempt-number 2}) 39 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/busy 40 | :test/attempt-number 3}) 41 | (a/!! c {:cognitect.anomalies/category :cognitect.anomalies/unavailable 51 | :test/attempt-number 1}) 52 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/unavailable 53 | :test/attempt-number 2}) 54 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/unavailable 55 | :test/attempt-number 3}) 56 | (a/!! c {:cognitect.anomalies/category :cognitect.anomalies/busy 66 | :test/attempt-number 1}) 67 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/busy 68 | :test/attempt-number 2}) 69 | (a/>!! c {:test/attempt-number 3}) 70 | (a/> (aws/invoke iam {:op :ListRoles}) :Roles (map :RoleName)) 31 | 32 | ;; who am I? 33 | (aws/invoke iam {:op :GetUser}) 34 | 35 | (def me (:User *1)) 36 | 37 | ;; make a role to use for this example 38 | (aws/invoke iam {:op :CreateRole 39 | :request {:RoleName "aws-api-example-role" 40 | :AssumeRolePolicyDocument 41 | (json/json-str 42 | {"Version" "2012-10-17", 43 | "Statement" [{"Effect" "Allow" 44 | "Principal" {"AWS" [(:Arn me)]} 45 | "Action" ["sts:AssumeRole"]}]})}}) 46 | 47 | (def new-role (:Role (aws/invoke iam {:op :GetRole 48 | :request {:RoleName "aws-api-example-role"}}))) 49 | 50 | ;; make a policy to use for this example 51 | (aws/invoke iam {:op :CreatePolicy 52 | :request {:PolicyName "IAMGetMe" 53 | :PolicyDocument 54 | (json/json-str 55 | {"Version" "2012-10-17", 56 | "Statement" [{"Effect" "Allow" 57 | "Action" ["iam:GetUser"] 58 | "Resource" [(:Arn me)]}]})}}) 59 | 60 | (def policy (->> (aws/invoke iam {:op :ListPolicies}) 61 | :Policies 62 | (filter #(re-find #"IAMGetMe" (:Arn %))) 63 | first)) 64 | 65 | ;; attach the new policy to the new role 66 | (aws/invoke iam {:op :AttachRolePolicy :request {:RoleName (:RoleName new-role) 67 | :PolicyArn (:Arn policy)}}) 68 | 69 | 70 | (def provider (assumed-role-credentials-provider (:Arn new-role))) 71 | 72 | ;; make a client using the assumed role credentials provider 73 | (def iam-with-assumed-role (aws/client {:api :iam :credentials-provider provider})) 74 | 75 | ;; use it! 76 | (aws/invoke iam-with-assumed-role {:op :GetUser :request {:UserName (:UserName me)}}) 77 | 78 | ;; clean up 79 | (aws/invoke iam {:op :DetachRolePolicy :request {:RoleName (:RoleName new-role) :PolicyArn (:Arn policy)}}) 80 | (aws/invoke iam {:op :DeletePolicy :request {:PolicyArn (:Arn policy)}}) 81 | (aws/invoke iam {:op :DeleteRole :request {:RoleName "aws-api-example-role"}}) 82 | 83 | ) 84 | -------------------------------------------------------------------------------- /src/cognitect/aws/service.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.service 5 | "Impl, don't call directly." 6 | (:require [clojure.edn :as edn] 7 | [clojure.string :as str] 8 | [clojure.walk :as walk] 9 | [cognitect.aws.resources :as resources])) 10 | 11 | (set! *warn-on-reflection* true) 12 | 13 | (def base-ns "cognitect.aws") 14 | 15 | (def base-resource-path "cognitect/aws") 16 | 17 | (defn descriptor-resource-path [service-name] 18 | (str base-resource-path "/" service-name "/service.edn")) 19 | 20 | (defn descriptor-resource [service-name] 21 | (resources/resource (descriptor-resource-path service-name))) 22 | 23 | (defn read-service-description 24 | "Return service description readerable source (anything supported by 25 | clojure.java.io/reader)." 26 | [readerable] 27 | (edn/read-string (slurp readerable))) 28 | 29 | (defn service-description [service-name] 30 | (if-let [resource (descriptor-resource service-name)] 31 | (read-service-description resource) 32 | (throw (ex-info (str "Cannot find resource " (descriptor-resource-path service-name) ".") {})))) 33 | 34 | (defn endpoint-prefix 35 | [service] 36 | (get-in service [:metadata :endpointPrefix])) 37 | 38 | (defn signing-name 39 | [service] 40 | (get-in service [:metadata :signingName])) 41 | 42 | (defn service-name [service] 43 | (-> service :metadata :uid 44 | (str/replace #"-\d{4}-\d{2}-\d{2}" "") 45 | (str/replace #"\s" "-") 46 | (str/replace #"\." "-"))) 47 | 48 | (defn ns-prefix 49 | "Returns the namespace prefix to use when looking up resources." 50 | [service] 51 | (format "%s.%s" base-ns (service-name service))) 52 | 53 | (defn spec-ns 54 | "The namespace for specs for service." 55 | [service] 56 | (symbol (format "%s.specs" (ns-prefix service)))) 57 | 58 | (defn load-specs [service] 59 | (require (spec-ns service))) 60 | 61 | (defonce svc-docs (atom {})) 62 | 63 | (defn with-ref-meta [m op doc] 64 | (let [ref-atom (atom nil) 65 | refs (:refs doc) 66 | updated-doc (walk/postwalk 67 | (fn [n] 68 | (if (contains? refs n) 69 | (with-meta n 70 | {'clojure.core.protocols/datafy #(-> ref-atom deref %)}) 71 | n)) 72 | doc)] 73 | (reset! ref-atom (:refs updated-doc)) 74 | (assoc m op (into {:name (name op)} updated-doc)))) 75 | 76 | (defn docs 77 | "Returns the docs for this service" 78 | [service] 79 | (let [k (service-name service)] 80 | (if-let [doc (get @svc-docs k)] 81 | doc 82 | (-> (swap! svc-docs 83 | assoc 84 | k 85 | (reduce-kv with-ref-meta 86 | {} 87 | (clojure.edn/read-string 88 | (slurp 89 | (resources/resource (format "%s/%s/docs.edn" base-resource-path (service-name service))))))) 90 | (get k))))) 91 | 92 | (defn request-spec-key 93 | "Returns the key to look up in the spec registry for the spec for 94 | the request body of op." 95 | [service op] 96 | (load-specs service) 97 | (when-let [shape-key (some->> service :operations op :input :shape)] 98 | (keyword (ns-prefix service) shape-key))) 99 | 100 | (defn response-spec-key 101 | "Returns the key to look up in the spec registry for the spec for 102 | the response body of op." 103 | [service op] 104 | (load-specs service) 105 | (when-let [shape-key (some->> service :operations op :output :shape)] 106 | (keyword (ns-prefix service) shape-key))) 107 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/region_test.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.region-test 5 | (:require [clojure.test :refer [deftest is testing]] 6 | [clojure.java.io :as io] 7 | [clojure.core.async :as a] 8 | [cognitect.aws.client.shared :as shared] 9 | [cognitect.aws.region :as region] 10 | [cognitect.aws.util :as u] 11 | [cognitect.aws.test.utils :as tu] 12 | [cognitect.aws.ec2-metadata-utils :as ec2-metadata-utils] 13 | [cognitect.aws.test.ec2-metadata-utils-server :as ec2-metadata-utils-test-server])) 14 | 15 | (deftest chain-region-provider-test 16 | (let [r "us-east-1" 17 | p1 (reify region/RegionProvider (region/fetch [_])) 18 | p2 (reify region/RegionProvider (region/fetch [_] r))] 19 | (testing "provider calls each provider until one returns a region" 20 | (is (= r (region/fetch (region/chain-region-provider [p1 p2]))))) 21 | (testing "provider throws if none of the providers returns a region." 22 | (is (thrown-with-msg? Exception #"No region found" (region/fetch (region/chain-region-provider [p1]))))))) 23 | 24 | (deftest profile-region-provider-test 25 | (let [config (io/file (io/resource ".aws/config"))] 26 | (testing "reads the default profile correctly." 27 | (is (= "us-east-1" 28 | (region/fetch (region/profile-region-provider "default" config))))) 29 | (testing "reads a custom profile correctly." 30 | (is (= "us-west-1" 31 | (region/fetch (region/profile-region-provider "tardigrade" config))))) 32 | 33 | (testing "uses env vars and sys props for credentials file location and profile" 34 | (with-redefs [u/getenv (tu/stub-getenv {"AWS_CONFIG_FILE" config})] 35 | (is (= "us-east-1" (region/fetch (region/profile-region-provider))))) 36 | (with-redefs [u/getenv (tu/stub-getenv {"AWS_CONFIG_FILE" config 37 | "AWS_PROFILE" "tardigrade"})] 38 | (is (= "us-west-1" (region/fetch (region/profile-region-provider))))) 39 | (with-redefs [u/getenv (tu/stub-getenv {"AWS_CONFIG_FILE" config}) 40 | u/getProperty (tu/stub-getProperty {"aws.profile" "tardigrade"})] 41 | (is (= "us-west-1" (region/fetch (region/profile-region-provider)))))))) 42 | 43 | (deftest instance-region-provider-test 44 | (testing "provider caches the fetched value" 45 | (ec2-metadata-utils-test-server/with-test-server 46 | (let [orig-get-region-fn ec2-metadata-utils/get-ec2-instance-region 47 | request-counter (atom 0) 48 | fetch-counter (atom 0)] 49 | (with-redefs [ec2-metadata-utils/get-ec2-instance-region 50 | (fn [http imdsv2-token] 51 | (swap! fetch-counter inc) 52 | (orig-get-region-fn http imdsv2-token))] 53 | (let [num-requests 10 54 | p (region/instance-region-provider (shared/http-client)) 55 | chans (repeatedly num-requests 56 | #(do 57 | (swap! request-counter inc) 58 | (region/fetch-async p)))] 59 | (is (apply = "us-east-1" (map #(a/ result 50 | (:hostname result) 51 | (update :hostname (partial render-uri uri-parts)) 52 | 53 | (:sslCommonName result) 54 | (update :sslCommonName (partial render-uri uri-parts))))) 55 | 56 | (defn partition-resolve 57 | [{:keys [services] :as partition} service-key region-key] 58 | (when (contains? (-> partition :regions keys set) region-key) 59 | (let [{:keys [partitionEndpoint isRegionalized] :as service} (get services service-key) 60 | endpoint-key (if (and partitionEndpoint (not isRegionalized)) 61 | (keyword partitionEndpoint) 62 | region-key)] 63 | (service-resolve partition (name service-key) service endpoint-key)))) 64 | 65 | (defn resolve* 66 | "Resolves an endpoint for a given service and region. 67 | 68 | service keyword Identify a AWS service (e.g. :s3) 69 | region keyword Identify a AWS region (e.g. :us-east-1). 70 | 71 | Return a map with the following keys: 72 | 73 | :partition The name of the partition. 74 | :region The region of the endpoint. 75 | :hostname The hostname to use. 76 | :sslCommonName The sslCommonName to use (optional). 77 | :credentialScope The Signature v4 credential scope (optional). 78 | :signatureVersions A list of possible signature versions (optional). 79 | :protocols A list of supported protocols." 80 | [service-key region] 81 | (some #(partition-resolve % service-key region) 82 | (:partitions (resolver)))) 83 | 84 | (def resolve (memoize resolve*)) 85 | 86 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 87 | 88 | (defprotocol EndpointProvider 89 | (-fetch [provider region])) 90 | 91 | (defn default-endpoint-provider [endpointPrefix endpoint-override] 92 | (reify EndpointProvider 93 | (-fetch [_ region] 94 | (if-let [ep (resolve (keyword endpointPrefix) (keyword region))] 95 | (merge ep (if (string? endpoint-override) 96 | {:hostname endpoint-override} 97 | endpoint-override)) 98 | {:cognitect.anomalies/category :cognitect.anomalies/fault 99 | :cognitect.anomalies/message "No known endpoint."})))) 100 | 101 | (defn fetch [provider region] 102 | (-fetch provider region)) 103 | -------------------------------------------------------------------------------- /src/cognitect/aws/client/test_double.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.client.test-double 5 | "Provides a test implementation of the aws client, which can be passed 6 | to the functions in the cognitect.aws.client.api ns." 7 | (:require [clojure.core.async :as a] 8 | [cognitect.aws.client.protocol :as client.protocol] 9 | [cognitect.aws.client.validation :as validation] 10 | [cognitect.aws.service :as service]) 11 | (:import (clojure.lang ILookup))) 12 | 13 | (set! *warn-on-reflection* true) 14 | 15 | (defn ^:private no-handler-provided-anomaly [op] 16 | {:cognitect.anomalies/category :cognitect.anomalies/incorrect 17 | :cognitect.anomalies/message "No handler or response provided for op" 18 | :op op}) 19 | 20 | (defprotocol TestDoubleClient 21 | (-instrument [c ops])) 22 | 23 | (deftype Client [info handlers] 24 | ILookup 25 | (valAt [this k] 26 | (.valAt this k nil)) 27 | 28 | (valAt [_this k default] 29 | (case k 30 | :api 31 | (-> info :service :metadata :cognitect.aws/service-name) 32 | :service 33 | (:service info) 34 | default)) 35 | 36 | client.protocol/Client 37 | (-get-info [_] info) 38 | 39 | (-invoke [this {:keys [op request] :as op-map}] 40 | (let [spec (validation/request-spec (:service info) op) 41 | handler (get @handlers op)] 42 | (cond 43 | (not (get-in info [:service :operations op])) 44 | (validation/unsupported-op-anomaly (:service info) op) 45 | 46 | (and (validation/validate-requests? this) 47 | spec 48 | (not (validation/valid? spec request))) 49 | (validation/invalid-request-anomaly spec request) 50 | 51 | (not handler) 52 | (no-handler-provided-anomaly op) 53 | 54 | :else 55 | (handler op-map)))) 56 | 57 | (-invoke-async [this {:keys [ch] :as op-map}] 58 | (let [response-chan (or ch (a/promise-chan))] 59 | (a/go 60 | (let [resp (.-invoke this op-map)] 61 | (a/>! response-chan resp))) 62 | response-chan)) 63 | 64 | (-stop [_aws-client]) 65 | 66 | TestDoubleClient 67 | (-instrument [client ops] 68 | (swap! (.handlers client) 69 | (fn [handlers] 70 | (reduce-kv 71 | (fn [m op handler] 72 | (when-not (some-> client :service :operations op) 73 | (throw (ex-info "Operation not supported" 74 | (validation/unsupported-op-anomaly (-> client :service) op)))) 75 | (assoc m op (if (fn? handler) handler (constantly handler)))) 76 | handlers 77 | ops))))) 78 | 79 | ;; ->Client is intended for internal use 80 | (alter-meta! #'->Client assoc :skip-wiki true) 81 | (alter-meta! #'TestDoubleClient assoc :skip-wiki true) 82 | 83 | (defn instrument 84 | "Given a test double client and a `:ops` map of operations to handlers, 85 | instruments the client with handlers. See `client` for more info about 86 | `:ops`." 87 | [client ops] 88 | (-instrument client ops)) 89 | 90 | (defn client 91 | "Given a map with :api and :ops (optional), returns a test double client that 92 | you can pass to `cognitect.aws.client.api/invoke` and `cognitect.aws.client.api/stop` 93 | in implementation code. 94 | 95 | You can provide :ops on creation or use `instrument` to add them later. 96 | 97 | :ops should be a map of operation (keyword) to one of 98 | - a function of op-map that returns a response map 99 | - a literal response map 100 | 101 | Notes: 102 | - you must instrument every op that will be invoked during a test 103 | - every op must be supported 104 | - See (keys (cognitect.aws.client.api/ops )) 105 | - will validate request payloads passed to `invoke` by default 106 | - you can disable request validation with (cognitect.aws.client.api/validate-requests client false) 107 | - will not validate response payloads" 108 | [{:keys [api ops]}] 109 | (let [service (service/service-description (name api))] 110 | (doto (->Client {:service service :validate-requests? (atom true)} (atom {})) 111 | (instrument ops)))) 112 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.aws.util-test 2 | (:require [clojure.test :as t :refer [deftest is testing]] 3 | [clojure.java.io :as io] 4 | [cognitect.aws.util :as util]) 5 | (:import [java.nio ByteBuffer] 6 | [java.util Random] 7 | [java.util Arrays])) 8 | 9 | (deftest test-input-stream->byte-array 10 | (is (= "hi" (slurp (util/input-stream->byte-array (io/input-stream (.getBytes "hi")))))) 11 | (testing "works with a 1mb array" 12 | (let [input (byte-array (int (Math/pow 2 20))) 13 | rng (Random.) 14 | _ (.nextBytes rng input) 15 | output (util/input-stream->byte-array (io/input-stream input))] 16 | (is (Arrays/equals ^bytes input ^bytes output))))) 17 | 18 | (deftest test-bbuff->byte-array 19 | (testing "return ByteBuffer starting at the current position" 20 | (let [bb (ByteBuffer/wrap (.getBytes "bye")) 21 | positioned-bb (-> (.getBytes "hi bye") 22 | ByteBuffer/wrap 23 | (.position 3)) 24 | direct-bb (-> (ByteBuffer/allocateDirect 10) 25 | (.put (ByteBuffer/wrap (.getBytes "bye"))) 26 | .flip)] 27 | (is (= (seq (util/bbuff->byte-array bb)) 28 | (seq (util/bbuff->byte-array positioned-bb)) 29 | (seq (util/bbuff->byte-array direct-bb))))))) 30 | 31 | (deftest test-sha-256 32 | (testing "returns sha for empty string if given nil" 33 | (is (= (seq (util/sha-256 nil)) 34 | (seq (util/sha-256 "")) 35 | (seq (util/sha-256 (.getBytes "")))))) 36 | (testing "accepts string, byte array, or ByteBuffer" 37 | (is (= (seq (util/sha-256 "hi")) 38 | (seq (util/sha-256 (.getBytes "hi"))) 39 | (seq (util/sha-256 (ByteBuffer/wrap (.getBytes "hi"))))))) 40 | (testing "does not consume a ByteBuffer" 41 | (let [bb (ByteBuffer/wrap (.getBytes "hi"))] 42 | (util/sha-256 bb) 43 | (is (= "hi" (util/bbuf->str bb))))) 44 | (testing "return ByteBuffer content starting at current the position" 45 | (let [bb (ByteBuffer/wrap (.getBytes "bye")) 46 | positioned-bb (-> (.getBytes "hi bye") 47 | ByteBuffer/wrap 48 | (.position 3)) 49 | direct-bb (-> (ByteBuffer/allocateDirect 10) 50 | (.put (ByteBuffer/wrap (.getBytes "bye"))) 51 | .flip)] 52 | (is (= (seq (util/sha-256 bb)) 53 | (seq (util/sha-256 positioned-bb)) 54 | (seq (util/sha-256 direct-bb))))))) 55 | 56 | (deftest test-md5 57 | (testing "Respects positioned ByteBuffer arrays and direct ByteBuffers" 58 | (let [bb (ByteBuffer/wrap (.getBytes "bye")) 59 | positioned-bb (-> (.getBytes "hi bye") 60 | ByteBuffer/wrap 61 | (.position 3)) 62 | direct-bb (-> (ByteBuffer/allocateDirect 10) 63 | (.put (ByteBuffer/wrap (.getBytes "bye"))) 64 | .flip)] 65 | (is (= (seq (util/md5 bb)) 66 | (seq (util/md5 positioned-bb)) 67 | (seq (util/md5 direct-bb))))))) 68 | 69 | (deftest test-xml-read 70 | (testing "removes whitespace-only nodes, preserving whitespace in single text nodes" 71 | (let [parsed (util/xml-read " 72 | outer-value 73 | 74 | inner-value 75 | 76 | ")] 77 | (is (= 2 (count (-> parsed :content)))) 78 | (is (re-matches #"\n\s+outer-value\s+" (-> parsed :content first))) 79 | (is (= 1 (count (-> parsed :content last :content)))) 80 | (is (re-matches #"\n\s+inner-value\s+" (-> parsed :content last :content first)))))) 81 | 82 | (deftest hex-encode 83 | (is (= (util/hex-encode (byte-array (range -128 128))) 84 | "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"))) 85 | 86 | (comment 87 | (t/run-tests)) 88 | -------------------------------------------------------------------------------- /examples/resources/dynamodb/Thread.json: -------------------------------------------------------------------------------- 1 | { 2 | "Thread": [ 3 | { 4 | "PutRequest": { 5 | "Item": { 6 | "ForumName": { 7 | "S": "Amazon DynamoDB" 8 | }, 9 | "Subject": { 10 | "S": "DynamoDB Thread 1" 11 | }, 12 | "Message": { 13 | "S": "DynamoDB thread 1 message" 14 | }, 15 | "LastPostedBy": { 16 | "S": "User A" 17 | }, 18 | "LastPostedDateTime": { 19 | "S": "2015-09-22T19:58:22.514Z" 20 | }, 21 | "Views": { 22 | "N": "0" 23 | }, 24 | "Replies": { 25 | "N": "0" 26 | }, 27 | "Answered": { 28 | "N": "0" 29 | }, 30 | "Tags": { 31 | "L": [ 32 | { 33 | "S": "index" 34 | }, 35 | { 36 | "S": "primarykey" 37 | }, 38 | { 39 | "S": "table" 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | }, 46 | { 47 | "PutRequest": { 48 | "Item": { 49 | "ForumName": { 50 | "S": "Amazon DynamoDB" 51 | }, 52 | "Subject": { 53 | "S": "DynamoDB Thread 2" 54 | }, 55 | "Message": { 56 | "S": "DynamoDB thread 2 message" 57 | }, 58 | "LastPostedBy": { 59 | "S": "User A" 60 | }, 61 | "LastPostedDateTime": { 62 | "S": "2015-09-15T19:58:22.514Z" 63 | }, 64 | "Views": { 65 | "N": "0" 66 | }, 67 | "Replies": { 68 | "N": "0" 69 | }, 70 | "Answered": { 71 | "N": "0" 72 | }, 73 | "Tags": { 74 | "L": [ 75 | { 76 | "S": "items" 77 | }, 78 | { 79 | "S": "attributes" 80 | }, 81 | { 82 | "S": "throughput" 83 | } 84 | ] 85 | } 86 | } 87 | } 88 | }, 89 | { 90 | "PutRequest": { 91 | "Item": { 92 | "ForumName": { 93 | "S": "Amazon S3" 94 | }, 95 | "Subject": { 96 | "S": "S3 Thread 1" 97 | }, 98 | "Message": { 99 | "S": "S3 thread 1 message" 100 | }, 101 | "LastPostedBy": { 102 | "S": "User A" 103 | }, 104 | "LastPostedDateTime": { 105 | "S": "2015-09-29T19:58:22.514Z" 106 | }, 107 | "Views": { 108 | "N": "0" 109 | }, 110 | "Replies": { 111 | "N": "0" 112 | }, 113 | "Answered": { 114 | "N": "0" 115 | }, 116 | "Tags": { 117 | "L": [ 118 | { 119 | "S": "largeobjects" 120 | }, 121 | { 122 | "S": "multipart upload" 123 | } 124 | ] 125 | } 126 | } 127 | } 128 | } 129 | ] 130 | } -------------------------------------------------------------------------------- /src/cognitect/aws/http.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.http 5 | "Impl, don't call directly." 6 | (:require [clojure.edn :as edn] 7 | [clojure.core.async :as a] 8 | [clojure.string :as str] 9 | [cognitect.aws.resources :as resources] 10 | [cognitect.aws.dynaload :as dynaload])) 11 | 12 | (set! *warn-on-reflection* true) 13 | 14 | (defprotocol HttpClient 15 | (-submit [_ request channel] 16 | "Submit an http request, channel will be filled with response. Returns ch. 17 | 18 | Request map: 19 | 20 | :scheme :http or :https 21 | :server-name string 22 | :server-port integer 23 | :uri string 24 | :query-string string, optional 25 | :request-method :get/:post/:put/:head/:delete 26 | :headers map from downcased string to string 27 | :body ByteBuffer, optional 28 | :timeout-msec opt, total request send/receive timeout 29 | :meta opt, data to be added to the response map 30 | 31 | content-type must be specified in the headers map 32 | content-length is derived from the ByteBuffer passed to body 33 | 34 | Response map: 35 | 36 | :status integer HTTP status code 37 | :body ByteBuffer, optional 38 | :headers map from downcased string to string 39 | :meta opt, data from the request 40 | 41 | On error, response map is per cognitect.anomalies. 42 | 43 | Alpha. This will absolutely change.") 44 | (-stop [_] "Stops the client, releasing resources")) 45 | 46 | (defn submit 47 | ([client request] 48 | (-submit client request (a/chan 1))) 49 | ([client request channel] 50 | (-submit client request channel))) 51 | 52 | (defn stop 53 | "Stops the client, releasing resources. 54 | 55 | Alpha. Subject to change." 56 | [client] 57 | (-stop client)) 58 | 59 | (defn client? 60 | [c] 61 | (satisfies? HttpClient c)) 62 | 63 | (defn read-config 64 | [url] 65 | (-> url slurp edn/read-string)) 66 | 67 | ;; TODO consider providing config arguments to http constructor 68 | (defn- configured-client 69 | "If a single `cognitect_aws_http.edn` is found on the classpath, 70 | returns the symbol bound to `:constructor-var`. 71 | 72 | If none are found, use this library's default. 73 | 74 | Throws if more than one `cognitect_aws_http.edn` files are found." 75 | [] 76 | (let [cfgs (resources/resources "cognitect_aws_http.edn")] 77 | (case (count cfgs) 78 | 0 'cognitect.aws.http.default/create 79 | 1 (-> cfgs first read-config :constructor-var) 80 | 81 | (throw (ex-info "Found more than one cognitect_aws_http.edn file in the classpath. There must be at most one." {:config cfgs}))))) 82 | 83 | (defn resolve-http-client 84 | [http-client-or-sym] 85 | (let [c (or (when (symbol? http-client-or-sym) 86 | (let [ctor @(dynaload/load-var http-client-or-sym)] 87 | (ctor))) 88 | http-client-or-sym 89 | (let [ctor @(dynaload/load-var (configured-client))] 90 | (ctor)))] 91 | (when-not (client? c) 92 | (throw (ex-info "not an http client" {:provided http-client-or-sym 93 | :resolved c}))) 94 | c)) 95 | 96 | (defn uri-authority 97 | "Returns the normalized URI authority (RFC 3986 section 3.2) for the given URI components, 98 | good for building the full request URI, signing the request, and computing the Host header, 99 | according to RFC 9112 section 3.2: 100 | 101 | A client MUST send a Host header field in all HTTP/1.1 request messages. If the target URI 102 | includes an authority component, then a client MUST send a field value for Host that is identical 103 | to that authority component, excluding any userinfo subcomponent and its \"@\" delimiter. 104 | 105 | `uri-scheme` and `uri-host` are required. `uri-port` may be nil in case the default port 106 | for the scheme is used. 107 | 108 | Normalization follows RFC 9110 section 4.2.3 (default port for the scheme is omitted; 109 | scheme and host are normalized to lowercase). The userinfo component of the URI is 110 | always omitted as per RFC 9110 section 4.2.4. 111 | 112 | authority = host [ \":\" port ]" 113 | [uri-scheme uri-host uri-port] 114 | (let [scheme (str/lower-case (name uri-scheme)) 115 | host (str/lower-case uri-host) 116 | is-default-port (case scheme 117 | "http" (= uri-port 80) 118 | "https" (= uri-port 443) 119 | false) 120 | should-include-port (and (some? uri-port) 121 | (not is-default-port))] 122 | (str host (when should-include-port (str ":" uri-port))))) 123 | -------------------------------------------------------------------------------- /src/cognitect/aws/region.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.region 5 | "Region providers. Primarily for internal use, and subject to change." 6 | (:require [clojure.string :as str] 7 | [clojure.java.io :as io] 8 | [clojure.tools.logging :as log] 9 | [cognitect.aws.util :as u] 10 | [cognitect.aws.config :as config] 11 | [cognitect.aws.ec2-metadata-utils :as ec2]) 12 | (:import (java.io File))) 13 | 14 | (set! *warn-on-reflection* true) 15 | 16 | (defn ^:skip-wiki valid-region 17 | "For internal use. Don't call directly. 18 | 19 | Return the credential region if valid, otherwise nil." 20 | [region] 21 | ;; TODO: (dchelimsky 2018-07-27) maybe validate this against known regions? 22 | (when-not (str/blank? region) region)) 23 | 24 | (defprotocol RegionProvider 25 | (fetch [_] "Returns the region found by this provider, or nil.")) 26 | 27 | (defn chain-region-provider 28 | "Chain together multiple region providers. 29 | 30 | `fetch` calls each provider in order until one returns a non-nil result, 31 | or returns nil. 32 | 33 | Alpha. Subject to change." 34 | [providers] 35 | (reify RegionProvider 36 | (fetch [_] 37 | (or (valid-region (some fetch providers)) 38 | (throw (ex-info "No region found by any region provider." 39 | {:providers (map class providers)})))))) 40 | 41 | (defn environment-region-provider 42 | "Returns the region from the AWS_REGION env var, or nil if not present. 43 | 44 | Alpha. Subject to change." 45 | [] 46 | (reify RegionProvider 47 | (fetch [_] (valid-region (u/getenv "AWS_REGION"))))) 48 | 49 | (defn system-property-region-provider 50 | "Returns the region from the aws.region system property, or nil if not present. 51 | 52 | Alpha. Subject to change." 53 | [] 54 | (reify RegionProvider 55 | (fetch [_] (valid-region (u/getProperty "aws.region"))))) 56 | 57 | (defn profile-region-provider 58 | "Returns the region from an AWS configuration profile. 59 | 60 | Arguments: 61 | 62 | f File The profile configuration file. (default: ~/.aws/config) 63 | profile-name string The name of the profile in the file. (default: default) 64 | 65 | Parsed properties: 66 | 67 | region required 68 | 69 | Alpha. Subject to change." 70 | ([] 71 | (profile-region-provider (or (u/getenv "AWS_PROFILE") 72 | (u/getProperty "aws.profile") 73 | "default"))) 74 | ([profile-name] 75 | (profile-region-provider profile-name (or (io/file (u/getenv "AWS_CONFIG_FILE")) 76 | (io/file (u/getProperty "user.home") ".aws" "config")))) 77 | ([profile-name ^File f] 78 | (reify RegionProvider 79 | (fetch [_] 80 | (when (.exists f) 81 | (try 82 | (let [profile (get (config/parse f) profile-name)] 83 | (valid-region (get profile "region"))) 84 | (catch Throwable t 85 | (log/error t "Unable to fetch region from the AWS config file " (str f))))))))) 86 | 87 | (defn ^:deprecated instance-region-provider 88 | "Returns the region from the ec2 instance's metadata service, 89 | or nil if the service can not be found. 90 | 91 | DEPRECATED use `instance-region-IMDS-v2-provider` 92 | 93 | Alpha. Subject to change." 94 | [http-client] 95 | (let [cached-region (atom nil) 96 | imdsv2-token nil] 97 | (reify RegionProvider 98 | (fetch [_] 99 | (or @cached-region 100 | (reset! cached-region (valid-region (ec2/get-ec2-instance-region http-client imdsv2-token)))))))) 101 | 102 | (defn instance-region-IMDS-v2-provider 103 | "Returns the region from the ec2 instance's IMDS v2 metadata service, 104 | or nil if the service can not be found. 105 | 106 | Alpha. Subject to change." 107 | [http-client] 108 | (let [cached-region (atom nil)] 109 | (reify RegionProvider 110 | (fetch [_] 111 | (or @cached-region 112 | (reset! cached-region (valid-region (when-let [IMDSv2-token (ec2/IMDSv2-token http-client)] 113 | (ec2/get-ec2-instance-region http-client IMDSv2-token))))))))) 114 | 115 | (defn default-region-provider 116 | "Returns a chain-region-provider with, in order: 117 | 118 | environment-region-provider 119 | system-property-region-provider 120 | profile-region-provider 121 | instance-region-IMDS-v2-provider 122 | instance-region-provider 123 | 124 | Alpha. Subject to change." 125 | [http-client] 126 | (chain-region-provider 127 | [(environment-region-provider) 128 | (system-property-region-provider) 129 | (profile-region-provider) 130 | (instance-region-IMDS-v2-provider http-client) 131 | (instance-region-provider http-client)])) 132 | 133 | (defn fetch-async 134 | "Returns a channel that will produce the result of calling fetch on 135 | the provider. 136 | 137 | Alpha. Subject to change." 138 | [provider] 139 | (u/fetch-async fetch provider "region")) 140 | -------------------------------------------------------------------------------- /test/src/cognitect/aws/test/ec2_metadata_utils_server.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns cognitect.aws.test.ec2-metadata-utils-server 5 | "Modeled after com.amazonaws.util.EC2MetadataUtilsServer" 6 | (:require [cognitect.aws.json :as json] 7 | [cognitect.aws.ec2-metadata-utils :as ec2-metadata-utils] 8 | [cognitect.aws.util :as u] 9 | [org.httpkit.server :as http-server])) 10 | 11 | (def iam-info 12 | (json/write-str 13 | {"Code" "Success" 14 | "LastUpdated" "2014-04-07T08 18 41Z" 15 | "InstanceProfileArn" "foobar" 16 | "InstanceProfileId" "moobily" 17 | "NewFeature" 12345})) 18 | 19 | (def iam-cred-list 20 | "test1\ntest2") 21 | 22 | (def iam-cred 23 | (json/write-str 24 | {"Code" "Success" 25 | "LastUpdated" "2014-04-07T08:18:41Z" 26 | "Type" "AWS-HMAC" 27 | "AccessKeyId" "foobar" 28 | "SecretAccessKey" "it^s4$3cret!" 29 | "Token" "norealvalue" 30 | "Expiration" "2014-04-08T23:16:53Z"})) 31 | 32 | (def instance-info 33 | (json/write-str 34 | {"pendingTime" "2014-08-07T22:07:46Z" 35 | "instanceType" "m1.small" 36 | "imageId" "ami-a49665cc" 37 | "instanceId" "i-6b2de041" 38 | "billingProducts" ["foo"] 39 | "architecture" "x86_64" 40 | "accountId" "599169622985" 41 | "kernelId" "aki-919dcaf8" 42 | "ramdiskId" "baz" 43 | "region" "us-east-1" 44 | "version" "2010-08-31" 45 | "availabilityZone" "us-east-1b" 46 | "privateIp" "10.201.215.38" 47 | "devpayProductCodes" ["bar"]})) 48 | 49 | (def ^:dynamic *test-server-port*) 50 | (def ^:private IMDSv2-token "a-secret-token") 51 | 52 | (defn IMDSv2-secure? 53 | "Returns truthy value if the request contains the expected IMDSv2 token in its 54 | `X-aws-ec2-metadata-token` request header." 55 | [{{imds-v2-token "x-aws-ec2-metadata-token"} :headers}] 56 | (= IMDSv2-token imds-v2-token)) 57 | 58 | (defn route-IMDSv2 59 | "Routes for an IMDSv2-enabled test service." 60 | [{:keys [uri request-method] :as req}] 61 | (cond 62 | (and (= uri "/latest/api/token") (= :put request-method)) IMDSv2-token 63 | (and (IMDSv2-secure? req) (= uri "/latest/meta-data/iam/info")) iam-info 64 | (or (u/getenv ec2-metadata-utils/container-credentials-relative-uri-env-var) 65 | (u/getenv ec2-metadata-utils/container-credentials-full-uri-env-var)) iam-cred 66 | (and (IMDSv2-secure? req) (= uri "/latest/meta-data/iam/security-credentials/")) iam-cred-list 67 | (and (IMDSv2-secure? req) (re-find #"/latest/meta-data/iam/security-credentials/.+" uri)) iam-cred 68 | (and (IMDSv2-secure? req) (= uri "/latest/dynamic/instance-identity/document")) instance-info 69 | :else nil)) 70 | 71 | (defn route 72 | "Routes for a legacy IMDSv1 test service." 73 | [{:keys [uri]}] 74 | (cond 75 | (= uri "/latest/meta-data/iam/info") iam-info 76 | (or (u/getenv ec2-metadata-utils/container-credentials-relative-uri-env-var) 77 | (u/getenv ec2-metadata-utils/container-credentials-full-uri-env-var)) iam-cred 78 | (= uri "/latest/meta-data/iam/security-credentials/") iam-cred-list 79 | (re-find #"/latest/meta-data/iam/security-credentials/.+" uri) iam-cred 80 | (= uri "/latest/dynamic/instance-identity/document") instance-info 81 | :else nil)) 82 | 83 | (defn handler 84 | [{::keys [IMDSv2-enabled?]}] 85 | (let [route-fn (if IMDSv2-enabled? route-IMDSv2 route)] 86 | (fn [req] 87 | (let [resp-body (route-fn req)] 88 | {:status (if resp-body 200 404) 89 | :body resp-body})))) 90 | 91 | (defn start 92 | "Starts a ec2 metadata utils server. Returns a no-arg stop function." 93 | [port opts] 94 | (http-server/run-server (handler opts) {:ip "127.0.0.1" :port port})) 95 | 96 | (defn with-test-server* 97 | "Create a test server, at `localhost` and a random port number, to mock the metadata service which 98 | runs inside an ECS or EC2 host. Invoke `f` function parameter, presumably because the function 99 | invocation includes http client requests to the metadata service. `opts` is a map to configure the 100 | test server; the only current option is `::IMDSv2-enabled?` which, when set to a truthy value, 101 | causes the test server to act IMDS v2 compliant." 102 | [opts f] 103 | ;; NOTE: starting w/ 0 generates a random port 104 | (let [server-stop-fn (start 0 opts) 105 | test-server-port (-> server-stop-fn meta :local-port)] 106 | (try 107 | (System/setProperty ec2-metadata-utils/ec2-metadata-service-override-system-property 108 | (str "http://localhost:" test-server-port)) 109 | (binding [*test-server-port* test-server-port] 110 | (f)) 111 | (finally 112 | (server-stop-fn) 113 | (System/clearProperty ec2-metadata-utils/ec2-metadata-service-override-system-property))))) 114 | 115 | (defmacro with-test-server 116 | [& body] 117 | `(with-test-server* {} (fn [] ~@body))) 118 | 119 | (defmacro with-IMDSv2-test-server 120 | [& body] 121 | `(with-test-server* {::IMDSv2-enabled? true} (fn [] ~@body))) 122 | 123 | 124 | (comment 125 | 126 | (def stop-fn (start 0 {})) 127 | 128 | (stop-fn)) 129 | -------------------------------------------------------------------------------- /test/src/cognitect/client/test_double_test.clj: -------------------------------------------------------------------------------- 1 | (ns cognitect.client.test-double-test 2 | "Tests for the test double client implementation." 3 | (:require [clojure.test :refer [deftest is testing]] 4 | [cognitect.client.impl-test :as client-test] 5 | [cognitect.aws.client.api :as aws] 6 | [cognitect.aws.client.test-double :as client.test])) 7 | 8 | (deftest keyword-access 9 | (let [c (client.test/client {:api :s3})] 10 | (is (= "s3" (:api c))) 11 | (is (map? (:service c))))) 12 | 13 | (deftest register-handlers-on-creation 14 | (testing "supports handler functions (sync and async)" 15 | (let [user-provided-response {:Location "abc"} 16 | test-s3 (client.test/client {:api :s3 :ops {:CreateBucket 17 | (constantly user-provided-response)}})] 18 | (doseq [invocation-response (client-test/invocations test-s3 19 | {:op :CreateBucket 20 | :request {:Bucket "bucket"}})] 21 | (is (= user-provided-response invocation-response)))))) 22 | 23 | (deftest register-handlers-with-instrument 24 | (let [user-provided-response {:Location "abc"} 25 | test-s3 (client.test/client {:api :s3})] 26 | (testing "register handler" 27 | (client.test/instrument test-s3 {:CreateBucket (constantly user-provided-response)}) 28 | (doseq [invocation-response (client-test/invocations test-s3 29 | {:op :CreateBucket 30 | :request {:Bucket "bucket"}})] 31 | (is (= user-provided-response invocation-response)))))) 32 | 33 | (deftest raw-response-values 34 | (let [test-s3 (client.test/client {:api :s3})] 35 | (let [response {:Buckets []}] 36 | (client.test/instrument test-s3 {:ListBuckets response}) 37 | (is (= response (aws/invoke test-s3 {:op :ListBuckets :request {}})))) 38 | (let [response {:Location "abc"}] 39 | (client.test/instrument test-s3 {:CreateBucket response}) 40 | (is (= response (aws/invoke test-s3 {:op :CreateBucket :request {:Bucket "bucket"}})))) 41 | (let [response {:cognitect.anomalies/category :cognitect.anomalies/busy}] 42 | (client.test/instrument test-s3 {:CreateBucket response}) 43 | (is (= response (aws/invoke test-s3 {:op :CreateBucket :request {:Bucket "bucket"}})))))) 44 | 45 | (deftest custom-testing-strategies 46 | (let [calls (atom []) 47 | test-s3 (client.test/client {:api :s3 48 | :ops {:CreateBucket 49 | (fn [op-map] 50 | (swap! calls conj op-map) 51 | {:Location "over the rainbow"})}})] 52 | (dotimes [n 5] 53 | (aws/invoke test-s3 {:op :CreateBucket 54 | :request {:Bucket (str "bucket-" n)}}) 55 | (is (= (take (inc n) [{:op :CreateBucket, :request {:Bucket "bucket-0"}} 56 | {:op :CreateBucket, :request {:Bucket "bucket-1"}} 57 | {:op :CreateBucket, :request {:Bucket "bucket-2"}} 58 | {:op :CreateBucket, :request {:Bucket "bucket-3"}} 59 | {:op :CreateBucket, :request {:Bucket "bucket-4"}}]) 60 | @calls))))) 61 | 62 | (deftest test-anomalies-and-exceptions 63 | (testing "returns an anomaly with spec report when invoked with invalid request" 64 | (let [res (aws/invoke 65 | (client.test/client {:api :s3}) 66 | {:op :CreateBucket 67 | :request {:BucketName "key should be :Bucket"}})] 68 | (is (= :cognitect.anomalies/incorrect (:cognitect.anomalies/category res))) 69 | (is (:clojure.spec.alpha/problems res)))) 70 | 71 | (testing "returns an anomaly when invoking op unsupported by service" 72 | (let [res (aws/invoke 73 | (client.test/client {:api :s3 :ops {}}) 74 | {:op :DoesNotExistInS3})] 75 | (is (= :cognitect.anomalies/unsupported (:cognitect.anomalies/category res))) 76 | (is (= "Operation not supported" (:cognitect.anomalies/message res))))) 77 | 78 | (testing "returns an anomaly when invoking undeclared operation" 79 | (let [res (aws/invoke 80 | (client.test/client {:api :s3 :ops {}}) 81 | {:op :CreateBucket 82 | :request {:Bucket "bucket"}})] 83 | (is (= :cognitect.anomalies/incorrect (:cognitect.anomalies/category res))) 84 | (is (= "No handler or response provided for op" (:cognitect.anomalies/message res))))) 85 | 86 | (testing "throws when instrumenting op unsupported by service" 87 | (is (thrown-with-msg? RuntimeException 88 | #"Operation not supported" 89 | (client.test/client {:api :s3 :ops {:DoesNotExistInS3 {}}}))) 90 | (let [client (client.test/client {:api :s3})] 91 | (is (thrown-with-msg? RuntimeException 92 | #"Operation not supported" 93 | (client.test/instrument client {:DoesNotExistInS3 {}})))))) 94 | -------------------------------------------------------------------------------- /src/cognitect/aws/protocols/query.clj: -------------------------------------------------------------------------------- 1 | ;; Copyright (c) Cognitect, Inc. 2 | ;; All rights reserved. 3 | 4 | (ns ^:skip-wiki cognitect.aws.protocols.query 5 | "Impl, don't call directly." 6 | (:require [clojure.string :as str] 7 | [cognitect.aws.protocols :as aws.protocols] 8 | [cognitect.aws.shape :as shape] 9 | [cognitect.aws.util :as util])) 10 | 11 | (set! *warn-on-reflection* true) 12 | 13 | ; ---------------------------------------------------------------------------------------- 14 | ;; Serializer 15 | ;; ---------------------------------------------------------------------------------------- 16 | 17 | (defn serialized-name 18 | [shape default] 19 | (or (:locationName shape) 20 | default)) 21 | 22 | (defn prefix-assoc 23 | [serialized prefix val] 24 | (assoc serialized (str/join "." prefix) val)) 25 | 26 | (defmulti serialize 27 | (fn [_shapes shape _args _serialized _prefix] (:type shape))) 28 | 29 | (defmethod serialize :default 30 | [_shapes _shape args serialized prefix] 31 | (prefix-assoc serialized prefix (str args))) 32 | 33 | (defmethod serialize "structure" 34 | [shapes shape args serialized prefix] 35 | (let [args (util/with-defaults shape args)] 36 | (reduce (fn [serialized k] 37 | (let [member-shape (shape/resolve shapes (shape/structure-member-shape-ref shape k)) 38 | member-name (serialized-name member-shape (name k))] 39 | (if (contains? args k) 40 | (serialize shapes member-shape (k args) serialized (conj prefix member-name)) 41 | serialized))) 42 | serialized 43 | (keys (:members shape))))) 44 | 45 | (defmethod serialize "list" 46 | [shapes shape args serialized prefix] 47 | (if (empty? args) 48 | (prefix-assoc serialized prefix "") 49 | (let [member-shape (shape/resolve shapes (shape/list-member-shape-ref shape)) 50 | list-prefix (if (:flattened shape) 51 | (conj (vec (butlast prefix)) (serialized-name member-shape (last prefix))) 52 | (conj prefix (serialized-name member-shape "member")))] 53 | (reduce (fn [serialized [i member]] 54 | (serialize shapes member-shape member serialized (conj list-prefix (str i)))) 55 | serialized 56 | (map-indexed (fn [i member] [(inc i) member]) args))))) 57 | 58 | (defmethod serialize "map" 59 | [shapes shape args serialized prefix] 60 | (let [map-prefix (if (:flattened shape) prefix (conj prefix "entry")) 61 | key-shape (shape/resolve shapes (shape/map-key-shape-ref shape)) 62 | key-suffix (serialized-name key-shape "key") 63 | value-shape (shape/resolve shapes (shape/map-value-shape-ref shape)) 64 | value-suffix (serialized-name value-shape "value")] 65 | (reduce (fn [serialized [i k v]] 66 | (as-> serialized $ 67 | (serialize shapes key-shape (name k) $ (conj map-prefix (str i) key-suffix)) 68 | (serialize shapes value-shape v $ (conj map-prefix (str i) value-suffix)))) 69 | serialized 70 | (map-indexed (fn [i [k v]] [(inc i) k v]) args)))) 71 | 72 | (defmethod serialize "blob" 73 | [_shapes _shape args serialized prefix] 74 | (prefix-assoc serialized prefix (util/base64-encode args))) 75 | 76 | (defmethod serialize "timestamp" 77 | [_shapes shape args serialized prefix] 78 | (prefix-assoc serialized prefix (shape/format-date shape 79 | args 80 | (partial util/format-date util/iso8601-date-format)))) 81 | 82 | (defmethod serialize "boolean" 83 | [_shapes _shape args serialized prefix] 84 | (prefix-assoc serialized prefix (if args "true" "false"))) 85 | 86 | (defn build-query-http-request 87 | [{:keys [op request]} service serialize] 88 | (let [operation (get-in service [:operations op]) 89 | shapes (:shapes service) 90 | input-shape (shape/resolve shapes (:input operation)) 91 | params {"Action" (name op) 92 | "Version" (get-in service [:metadata :apiVersion])}] 93 | {:request-method :post 94 | :scheme :https 95 | :server-port 443 96 | :uri "/" 97 | :headers (aws.protocols/headers service operation) 98 | :body (util/query-string 99 | (serialize shapes input-shape request params []))})) 100 | 101 | (defmethod aws.protocols/build-http-request "query" 102 | [service op-map] 103 | (build-query-http-request op-map service serialize)) 104 | 105 | (defn build-query-http-response 106 | [service {:keys [op]} {:keys [body]}] 107 | (let [operation (get-in service [:operations op]) 108 | shapes (:shapes service)] 109 | (if-let [output-shape (shape/resolve shapes (:output operation))] 110 | (shape/xml-parse shapes output-shape (util/bbuf->str body)) 111 | (util/xml->map (util/xml-read (util/bbuf->str body)))))) 112 | 113 | (defmethod aws.protocols/parse-http-response "query" 114 | [service op-map http-response] 115 | (build-query-http-response service op-map http-response)) 116 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.cognitect.aws 5 | api 6 | 0.8.663 7 | jar 8 | aws-api 9 | A Clojure API for the AWS API 10 | https://github.com/cognitect-labs/aws-api 11 | 12 | 13 | Apache-2.0 14 | https://www.apache.org/licenses/LICENSE-2.0.txt 15 | 16 | 17 | 18 | 19 | Cognitect 20 | labs@cognitect.com 21 | Cognitect 22 | https://cognitect.com 23 | 24 | 25 | 26 | scm:git:git@github.com:cognitect-labs/aws-api.git 27 | scm:git:git@github.com:cognitect-labs/aws-api.git 28 | https://github.com/cognitect-labs/aws-api 29 | 30 | 31 | 32 | org.clojure 33 | data.json 34 | 2.5.1 35 | 36 | 37 | org.clojure 38 | clojure 39 | 1.12.0 40 | 41 | 42 | org.clojure 43 | tools.logging 44 | 1.3.0 45 | 46 | 47 | org.clojure 48 | data.xml 49 | 0.2.0-alpha9 50 | 51 | 52 | org.clojure 53 | core.async 54 | 1.8.741 55 | 56 | 57 | 58 | 59 | autodoc 60 | 61 | 62 | autodoc 63 | autodoc 64 | 0.9.0 65 | test 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | src 74 | 75 | 76 | 77 | 78 | dev-resources 79 | 80 | 81 | resources 82 | 83 | 84 | target 85 | target/classes 86 | 87 | 88 | org.sonatype.central 89 | central-publishing-maven-plugin 90 | 0.7.0 91 | true 92 | 93 | central 94 | true 95 | published 96 | 97 | 98 | 99 | org.apache.maven.plugins 100 | maven-source-plugin 101 | 2.2.1 102 | 103 | 104 | attach-sources 105 | 106 | jar-no-fork 107 | 108 | 109 | 110 | 111 | 112 | org.apache.maven.plugins 113 | maven-gpg-plugin 114 | 1.6 115 | 116 | 117 | sign-artifacts 118 | verify 119 | 120 | sign 121 | 122 | 123 | gpg 124 | 125 | --pinentry-mode 126 | loopback 127 | 128 | ${gpg.passphrase} 129 | 130 | 131 | 132 | 133 | 134 | com.theoryinpractise 135 | clojure-maven-plugin 136 | 1.9.3 137 | true 138 | 139 | 140 | src 141 | 142 | 143 | test 144 | 145 | 146 | 147 | 148 | src 149 | 150 | 151 | 152 | clojars 153 | https://repo.clojars.org/ 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /test/resources/botocore/protocols/output/event-stream.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "REST XML Event Stream", 4 | "metadata": { 5 | "protocol": "rest-xml" 6 | }, 7 | "shapes": { 8 | "OutputShape": { 9 | "type": "structure", 10 | "members": { 11 | "Payload": {"shape": "EventStream"} 12 | }, 13 | "payload": "Payload" 14 | }, 15 | "EventStream": { 16 | "type": "structure", 17 | "eventstream": true, 18 | "members": { 19 | "TypeA": {"shape": "TypeAEvent"}, 20 | "TypeB": {"shape": "TypeBEvent"}, 21 | "TypeC": {"shape": "TypeCEvent"} 22 | } 23 | }, 24 | "TypeAEvent": { 25 | "type": "structure", 26 | "event": true, 27 | "members": { 28 | "Payload": { 29 | "shape": "BlobType", 30 | "eventpayload": true 31 | } 32 | } 33 | }, 34 | "TypeBEvent": { 35 | "type": "structure", 36 | "event": true, 37 | "members": { 38 | "Details": { 39 | "shape": "Details", 40 | "eventpayload": true 41 | } 42 | } 43 | }, 44 | "TypeCEvent": { 45 | "type": "structure", 46 | "event": true, 47 | "members": { 48 | "Details": { 49 | "shape": "Details", 50 | "eventpayload": true 51 | }, 52 | "Boolean": { 53 | "shape": "BooleanType", 54 | "eventheader": true 55 | }, 56 | "Integer": { 57 | "shape": "IntegerType", 58 | "eventheader": true 59 | }, 60 | "Blob": { 61 | "shape": "BlobType", 62 | "eventheader": true 63 | }, 64 | "String": { 65 | "shape": "StringType", 66 | "eventheader": true 67 | }, 68 | "Timestamp": { 69 | "shape": "TimestampType", 70 | "eventheader": true 71 | } 72 | } 73 | }, 74 | "Details": { 75 | "type": "structure", 76 | "members": { 77 | "StringField": {"shape": "StringType"}, 78 | "IntegerField": {"shape": "IntegerType"} 79 | } 80 | }, 81 | "StringType": { 82 | "type": "string" 83 | }, 84 | "IntegerType": { 85 | "type": "integer" 86 | }, 87 | "BooleanType": { 88 | "type": "boolean" 89 | }, 90 | "TimestampType": { 91 | "type": "timestamp" 92 | }, 93 | "BlobType": { 94 | "type": "blob" 95 | } 96 | }, 97 | "cases": [ 98 | { 99 | "given": { 100 | "output": { 101 | "shape": "OutputShape" 102 | }, 103 | "name": "OperationName" 104 | }, 105 | "result": { 106 | "Payload": [ 107 | { 108 | "TypeA": {"Payload": "somebytes"} 109 | }, 110 | { 111 | "TypeB": { 112 | "Details": { 113 | "StringField": "somestring", 114 | "IntegerField": 123 115 | } 116 | } 117 | } 118 | ] 119 | }, 120 | "response": { 121 | "status_code": 200, 122 | "headers": {}, 123 | "body": "AAAAbAAAAFPLgkVrDTptZXNzYWdlLXR5cGUHAAVldmVudAs6ZXZlbnQtdHlwZQcABVR5cGVBDTpjb250ZW50LXR5cGUHABhhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW1zb21lYnl0ZXMesj2HAAAAsAAAAEOaMMdXDTptZXNzYWdlLXR5cGUHAAVldmVudAs6ZXZlbnQtdHlwZQcABVR5cGVCDTpjb250ZW50LXR5cGUHAAh0ZXh0L3htbDxUeXBlQiB4bWxucz0iIj48U3RyaW5nRmllbGQ+c29tZXN0cmluZzwvU3RyaW5nRmllbGQ+PEludGVnZXJGaWVsZD4xMjM8L0ludGVnZXJGaWVsZD48L1R5cGVCPiwthPo=" 124 | } 125 | }, 126 | { 127 | "given": { 128 | "output": { 129 | "shape": "OutputShape" 130 | }, 131 | "name": "OperationName" 132 | }, 133 | "result": { 134 | "Payload": [ 135 | { 136 | "TypeC": { 137 | "Boolean": true, 138 | "Integer": 123, 139 | "Blob": "someblob", 140 | "String": "somestring", 141 | "Timestamp": 1422172800, 142 | "Details": { 143 | "StringField": "somestring", 144 | "IntegerField": 123 145 | } 146 | } 147 | } 148 | ] 149 | }, 150 | "response": { 151 | "status_code": 200, 152 | "headers": {}, 153 | "body": "AAABAQAAAJBjEbY4DTptZXNzYWdlLXR5cGUHAAVldmVudAs6ZXZlbnQtdHlwZQcABVR5cGVDDTpjb250ZW50LXR5cGUHAAh0ZXh0L3htbAdCb29sZWFuAAdJbnRlZ2VyBAAAAHsEQmxvYgYACHNvbWVibG9iBlN0cmluZwcACnNvbWVzdHJpbmcJVGltZXN0YW1wCAAAAUsgGsQAPERldGFpbHMgeG1sbnM9IiI+PFN0cmluZ0ZpZWxkPnNvbWVzdHJpbmc8L1N0cmluZ0ZpZWxkPjxJbnRlZ2VyRmllbGQ+MTIzPC9JbnRlZ2VyRmllbGQ+PC9EZXRhaWxzPhGUvKo=" 154 | } 155 | }, 156 | { 157 | "given": { 158 | "output": { 159 | "shape": "OutputShape" 160 | }, 161 | "name": "OperationName" 162 | }, 163 | "result": { 164 | "Payload": [] 165 | }, 166 | "response": { 167 | "status_code": 200, 168 | "headers": {}, 169 | "body": "" 170 | } 171 | } 172 | ] 173 | } 174 | ] 175 | --------------------------------------------------------------------------------