aws-sdk 2.0.1 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/.eslintrc +20 -0
  2. package/.gitignore +10 -0
  3. package/.travis.yml +20 -0
  4. package/.yardopts +20 -0
  5. package/.yardopts_guide +21 -0
  6. package/Gemfile +16 -0
  7. package/Gemfile.lock +34 -0
  8. package/README.md +5 -6
  9. package/Rakefile +14 -0
  10. package/UPGRADING.md +9 -4
  11. package/configuration.sample +5 -0
  12. package/dist/BUNDLE_LICENSE.txt +96 -0
  13. package/dist/aws-sdk.js +9594 -0
  14. package/dist/aws-sdk.min.js +21 -0
  15. package/dist-tools/.eslintrc +10 -0
  16. package/dist-tools/browser-builder.js +142 -0
  17. package/dist-tools/strategies/cache.js +68 -0
  18. package/dist-tools/strategies/default.js +165 -0
  19. package/dist-tools/test/browser-builder.mocha.spec.coffee +182 -0
  20. package/dist-tools/test/helpers.coffee +16 -0
  21. package/doc-src/guide/browser-building.md +93 -0
  22. package/doc-src/guide/browser-configuring-wif.md +287 -0
  23. package/doc-src/guide/browser-configuring.md +218 -0
  24. package/doc-src/guide/browser-examples.md +220 -0
  25. package/doc-src/guide/browser-intro.md +46 -0
  26. package/doc-src/guide/browser-making-requests.md +279 -0
  27. package/doc-src/guide/browser-services.md +75 -0
  28. package/doc-src/guide/index.md +41 -0
  29. package/doc-src/guide/node-configuring.md +272 -0
  30. package/doc-src/guide/node-examples.md +341 -0
  31. package/doc-src/guide/node-intro.md +32 -0
  32. package/doc-src/guide/node-making-requests.md +309 -0
  33. package/doc-src/guide/node-services.md +159 -0
  34. package/doc-src/templates/api-versions/model_documentor.rb +366 -0
  35. package/doc-src/templates/api-versions/plugin.rb +230 -0
  36. package/doc-src/templates/api-versions/templates/default/class/html/setup.rb +9 -0
  37. package/doc-src/templates/api-versions/templates/default/class/html/waiter_details_list.erb +7 -0
  38. package/doc-src/templates/api-versions/templates/default/class/html/waiter_summary.erb +7 -0
  39. package/doc-src/templates/api-versions/templates/default/docstring/html/experimental.erb +4 -0
  40. package/doc-src/templates/api-versions/templates/default/docstring/setup.rb +9 -0
  41. package/doc-src/templates/api-versions/templates/default/fulldoc/html/css/common.css +6 -0
  42. package/doc-src/templates/api-versions/templates/default/fulldoc/html/setup.rb +62 -0
  43. package/doc-src/templates/api-versions/templates/default/layout/html/services.erb +10 -0
  44. package/doc-src/templates/api-versions/templates/default/layout/html/setup.rb +28 -0
  45. package/doc-src/templates/api-versions/templates/default/module/html/box_info.erb +45 -0
  46. package/doc-src/templates/api-versions/templates/default/module/html/children.erb +8 -0
  47. package/doc-src/templates/api-versions/templates/default/tags/setup.rb +3 -0
  48. package/doc-src/templates/api-versions/templates/default/waiter_details/html/method_signature.erb +3 -0
  49. package/doc-src/templates/api-versions/templates/default/waiter_details/html/setup.rb +5 -0
  50. package/doc-src/templates/default/layout/html/footer.erb +31 -0
  51. package/doc-src/templates/default/layout/html/layout.erb +23 -0
  52. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/css/highlight.github.css +127 -0
  53. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/css/style.css +1192 -0
  54. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/img/logo.png +0 -0
  55. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/app.js +33 -0
  56. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/highlight.pack.js +27 -0
  57. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/AUTHORS +55 -0
  58. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/LICENSE +25 -0
  59. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/doctools.js +247 -0
  60. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/file.png +0 -0
  61. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/sphinx/searchtools.js +568 -0
  62. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/js/underscore.js +23 -0
  63. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/search.erb +29 -0
  64. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/search_index.erb +1 -0
  65. package/doc-src/templates/flasky_sphinx_guide/fulldoc/html/setup.rb +75 -0
  66. package/doc-src/templates/flasky_sphinx_guide/layout/html/layout.erb +93 -0
  67. package/doc-src/templates/flasky_sphinx_guide/layout/html/setup.rb +9 -0
  68. package/doc-src/templates/flasky_sphinx_guide/layout/html/sidebar.erb +45 -0
  69. package/doc-src/templates/flasky_sphinx_guide/onefile/html/layout.erb +51 -0
  70. package/doc-src/templates/flasky_sphinx_guide/onefile/html/setup.rb +1 -0
  71. package/eslint-rules/no-require-in-service.js +10 -0
  72. package/features/autoscaling/autoscaling.feature +21 -0
  73. package/features/autoscaling/step_definitions/autoscaling.js +49 -0
  74. package/features/cloudformation/cloudformation.feature +22 -0
  75. package/features/cloudformation/step_definitions/cloudformation.js +26 -0
  76. package/features/cloudfront/cloudfront.feature +28 -0
  77. package/features/cloudfront/step_definitions/cloudfront-latest.js +54 -0
  78. package/features/cloudfront/step_definitions/cloudfront.js +21 -0
  79. package/features/cloudsearch/cloudsearch.feature +34 -0
  80. package/features/cloudsearch/step_definitions/cloudsearch.js +42 -0
  81. package/features/cloudtrail/cloudtrail.feature +17 -0
  82. package/features/cloudtrail/step_definitions/cloudtrail.js +14 -0
  83. package/features/cloudwatch/cloudwatch.feature +15 -0
  84. package/features/cloudwatch/step_definitions/cloudwatch.js +48 -0
  85. package/features/datapipeline/datapipeline.feature +23 -0
  86. package/features/datapipeline/step_definitions/datapipeline.js +79 -0
  87. package/features/directconnect/directconnect.feature +20 -0
  88. package/features/directconnect/step_definitions/directconnect.js +44 -0
  89. package/features/dynamodb/crc32.feature +18 -0
  90. package/features/dynamodb/step_definitions/dynamodb.js +154 -0
  91. package/features/dynamodb/tables.feature +50 -0
  92. package/features/ec2/ec2.feature +28 -0
  93. package/features/ec2/step_definitions/ec2.js +65 -0
  94. package/features/elasticache/elasticache.feature +20 -0
  95. package/features/elasticache/step_definitions/elasticache.js +34 -0
  96. package/features/elasticbeanstalk/elasticbeanstalk.feature +22 -0
  97. package/features/elasticbeanstalk/step_definitions/elasticbeanstalk.js +38 -0
  98. package/features/elastictranscoder/elastictranscoder.feature +24 -0
  99. package/features/elastictranscoder/step_definitions/elastictranscoder.js +56 -0
  100. package/features/elb/elb.feature +19 -0
  101. package/features/elb/step_definitions/elb.js +37 -0
  102. package/features/emr/emr.feature +16 -0
  103. package/features/emr/step_definitions/emr.js +45 -0
  104. package/features/extra/assertions.js +29 -0
  105. package/features/extra/dummy.feature +0 -0
  106. package/features/extra/fixtures/testfile.txt +1 -0
  107. package/features/extra/helpers.js +113 -0
  108. package/features/extra/hooks.js +107 -0
  109. package/features/extra/world.js +12 -0
  110. package/features/glacier/glacier.feature +47 -0
  111. package/features/glacier/step_definitions/glacier.js +112 -0
  112. package/features/iam/iam.feature +24 -0
  113. package/features/iam/step_definitions/iam.js +66 -0
  114. package/features/importexport/importexport.feature +53 -0
  115. package/features/importexport/step_definitions/importexport.js +42 -0
  116. package/features/kinesis/kinesis.feature +9 -0
  117. package/features/kinesis/step_definitions/kinesis.js +10 -0
  118. package/features/opsworks/opsworks.feature +26 -0
  119. package/features/opsworks/step_definitions/opsworks.js +42 -0
  120. package/features/rds/rds.feature +32 -0
  121. package/features/rds/step_definitions/rds.js +72 -0
  122. package/features/redshift/redshift.feature +20 -0
  123. package/features/redshift/step_definitions/redshift.js +33 -0
  124. package/features/route53/route53.feature +41 -0
  125. package/features/route53/step_definitions/route53.js +97 -0
  126. package/features/s3/buckets.feature +40 -0
  127. package/features/s3/objects.feature +122 -0
  128. package/features/s3/step_definitions/buckets.js +136 -0
  129. package/features/s3/step_definitions/hooks.js +39 -0
  130. package/features/s3/step_definitions/objects.js +204 -0
  131. package/features/s3/step_definitions/proxy.js +44 -0
  132. package/features/ses/ses.feature +20 -0
  133. package/features/ses/step_definitions/ses.js +22 -0
  134. package/features/simpledb/simpledb.feature +29 -0
  135. package/features/simpledb/step_definitions/simpledb.js +46 -0
  136. package/features/sns/sns.feature +15 -0
  137. package/features/sns/step_definitions/sns.js +33 -0
  138. package/features/sqs/messages.feature +21 -0
  139. package/features/sqs/queues.feature +18 -0
  140. package/features/sqs/step_definitions/messages.js +46 -0
  141. package/features/sqs/step_definitions/queues.js +33 -0
  142. package/features/sqs/step_definitions/sqs.js +7 -0
  143. package/features/storagegateway/step_definitions/storagegateway.js +16 -0
  144. package/features/storagegateway/storagegateway.feature +13 -0
  145. package/features/sts/step_definitions/sts.js +35 -0
  146. package/features/sts/sts.feature +29 -0
  147. package/features/support/step_definitions/support.js +35 -0
  148. package/features/support/support.feature +18 -0
  149. package/features/swf/step_definitions/swf.js +38 -0
  150. package/features/swf/swf.feature +15 -0
  151. package/index.js +2 -0
  152. package/lib/core.js +2 -2
  153. package/lib/credentials/shared_ini_file_credentials.js +0 -1
  154. package/lib/event_listeners.js +13 -1
  155. package/lib/http/node.js +19 -30
  156. package/lib/model/resource_waiter.js +0 -4
  157. package/lib/model/shape.js +2 -1
  158. package/lib/protocol/rest_xml.js +1 -1
  159. package/lib/region_config.js +31 -0
  160. package/lib/region_config.json +56 -0
  161. package/lib/request.js +37 -45
  162. package/lib/sequential_executor.js +17 -34
  163. package/lib/service.js +17 -44
  164. package/lib/services/cloudsearchdomain.js +69 -0
  165. package/lib/services/route53.js +0 -12
  166. package/lib/services/s3.js +3 -19
  167. package/lib/signers/v4.js +2 -1
  168. package/lib/util.js +28 -3
  169. package/package.json +3 -3
  170. package/scripts/console +11 -3
  171. package/scripts/coverage +126 -0
  172. package/tasks/apis.rake +122 -0
  173. package/tasks/browser.rake +89 -0
  174. package/tasks/docs.rake +36 -0
  175. package/tasks/lib/cucumber_generator.rb +40 -0
  176. package/tasks/util.rake +33 -0
  177. package/test/browser/js/jasmine-1.3.1.js +2600 -0
  178. package/test/browser/js/jasmine-html.js +681 -0
  179. package/test/browser/runner.html +109 -0
  180. package/test/browser/runner.js +92 -0
  181. package/test/browser/sample/appinfo.sample.js +15 -0
  182. package/test/browser/sample/console.html +429 -0
  183. package/test/browser/sample/css/smoothness/images/animated-overlay.gif +0 -0
  184. package/test/browser/sample/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  185. package/test/browser/sample/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  186. package/test/browser/sample/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  187. package/test/browser/sample/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  188. package/test/browser/sample/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  189. package/test/browser/sample/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  190. package/test/browser/sample/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  191. package/test/browser/sample/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  192. package/test/browser/sample/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  193. package/test/browser/sample/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  194. package/test/browser/sample/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  195. package/test/browser/sample/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  196. package/test/browser/sample/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  197. package/test/browser/sample/css/smoothness/jquery-ui-1.10.1.custom.css +1175 -0
  198. package/test/browser/sample/css/smoothness/jquery-ui-1.10.1.custom.min.css +5 -0
  199. package/test/browser/sample/img/loading.gif +0 -0
  200. package/test/browser/sample/js/jquery-1.9.1.js +9597 -0
  201. package/test/browser/sample/js/jquery-ui-1.10.1.custom.js +14903 -0
  202. package/test/browser/sample/js/jquery-ui-1.10.1.custom.min.js +6 -0
  203. package/test/browser/sample/s3upload.html +111 -0
  204. package/test/browser.spec.coffee +207 -0
  205. package/test/config.spec.coffee +202 -0
  206. package/test/credential_provider_chain.spec.coffee +90 -0
  207. package/test/credentials.spec.coffee +452 -0
  208. package/test/endpoint.spec.coffee +80 -0
  209. package/test/event_listeners.spec.coffee +493 -0
  210. package/test/helpers.coffee +150 -0
  211. package/test/http_request.spec.coffee +55 -0
  212. package/test/json/builder.spec.coffee +129 -0
  213. package/test/json/parser.spec.coffee +108 -0
  214. package/test/metadata_service.spec.coffee +54 -0
  215. package/test/model/api.spec.coffee +67 -0
  216. package/test/model/shape.spec.coffee +23 -0
  217. package/test/node_http_client.spec.coffee +40 -0
  218. package/test/param_validator.spec.coffee +456 -0
  219. package/test/protocol/json.spec.coffee +167 -0
  220. package/test/protocol/query.spec.coffee +191 -0
  221. package/test/protocol/rest.spec.coffee +237 -0
  222. package/test/protocol/rest_json.spec.coffee +255 -0
  223. package/test/protocol/rest_xml.spec.coffee +329 -0
  224. package/test/query/query_param_serializer.spec.coffee +327 -0
  225. package/test/region_config.spec.coffee +50 -0
  226. package/test/request.spec.coffee +316 -0
  227. package/test/resource_waiter.spec.coffee +89 -0
  228. package/test/response.spec.coffee +81 -0
  229. package/test/sequential_executor.spec.coffee +118 -0
  230. package/test/service.spec.coffee +230 -0
  231. package/test/services/cloudfront.spec.coffee +44 -0
  232. package/test/services/cloudsearchdomain.spec.coffee +23 -0
  233. package/test/services/dynamodb.spec.coffee +32 -0
  234. package/test/services/ec2.spec.coffee +78 -0
  235. package/test/services/elastictranscoder.spec.coffee +43 -0
  236. package/test/services/glacier.spec.coffee +61 -0
  237. package/test/services/rds.spec.coffee +38 -0
  238. package/test/services/route53.spec.coffee +77 -0
  239. package/test/services/s3.spec.coffee +538 -0
  240. package/test/services/simpledb.spec.coffee +12 -0
  241. package/test/services/sqs.spec.coffee +130 -0
  242. package/test/services/sts.spec.coffee +72 -0
  243. package/test/services/swf.spec.coffee +6 -0
  244. package/test/signers/presign.spec.coffee +36 -0
  245. package/test/signers/s3.spec.coffee +297 -0
  246. package/test/signers/v2.spec.coffee +68 -0
  247. package/test/signers/v4.spec.coffee +135 -0
  248. package/test/util.spec.coffee +510 -0
  249. package/test/xml/builder.spec.coffee +529 -0
  250. package/test/xml/parser.spec.coffee +587 -0
  251. package/lib/services/simpledb.js +0 -15
@@ -0,0 +1,493 @@
1
+ helpers = require('./helpers')
2
+ AWS = helpers.AWS
3
+ MockService = helpers.MockService
4
+
5
+ describe 'AWS.EventListeners', ->
6
+
7
+ oldSetTimeout = setTimeout
8
+ config = null; service = null; totalWaited = null; delays = []
9
+ successHandler = null; errorHandler = null; completeHandler = null
10
+ retryHandler = null
11
+
12
+ beforeEach ->
13
+ # Mock the timer manually (jasmine.Clock does not work in node)
14
+ `setTimeout = jasmine.createSpy('setTimeout');`
15
+ setTimeout.andCallFake (callback, delay) ->
16
+ totalWaited += delay
17
+ delays.push(delay)
18
+ callback()
19
+
20
+ totalWaited = 0
21
+ delays = []
22
+ service = new MockService(maxRetries: 3)
23
+ service.config.credentials = AWS.util.copy(service.config.credentials)
24
+
25
+ # Helpful handlers
26
+ successHandler = jasmine.createSpy('success')
27
+ errorHandler = jasmine.createSpy('error')
28
+ completeHandler = jasmine.createSpy('complete')
29
+ retryHandler = jasmine.createSpy('retry')
30
+
31
+ # Safely tear down setTimeout hack
32
+ afterEach -> `setTimeout = oldSetTimeout`
33
+
34
+ makeRequest = (callback) ->
35
+ request = service.makeRequest('mockMethod', foo: 'bar')
36
+ request.on('retry', retryHandler)
37
+ request.on('error', errorHandler)
38
+ request.on('success', successHandler)
39
+ request.on('complete', completeHandler)
40
+ if callback
41
+ request.send(callback)
42
+ else
43
+ request
44
+
45
+ describe 'validate', ->
46
+ it 'takes the request object as a parameter', ->
47
+ request = makeRequest()
48
+ request.on 'validate', (req) ->
49
+ expect(req).toBe(request)
50
+ throw "ERROR"
51
+ response = request.send(->)
52
+ expect(response.error).toEqual("ERROR")
53
+
54
+ it 'sends error event if credentials are not set', ->
55
+ service.config.credentialProvider = null
56
+ service.config.credentials.accessKeyId = null
57
+ makeRequest(->)
58
+
59
+ expect(errorHandler).toHaveBeenCalled()
60
+ AWS.util.arrayEach errorHandler.calls, (call) ->
61
+ expect(call.args[0] instanceof Error).toBeTruthy()
62
+ expect(call.args[0].code).toEqual('CredentialsError')
63
+ expect(call.args[0].message).toMatch(/Missing credentials/)
64
+
65
+ it 'sends error event if credentials are not set', ->
66
+ service.config.credentials.accessKeyId = 'akid'
67
+ service.config.credentials.secretAccessKey = null
68
+ makeRequest(->)
69
+
70
+ expect(errorHandler).toHaveBeenCalled()
71
+ AWS.util.arrayEach errorHandler.calls, (call) ->
72
+ expect(call.args[0] instanceof Error).toBeTruthy()
73
+ expect(call.args[0].code).toEqual('CredentialsError')
74
+ expect(call.args[0].message).toMatch(/Missing credentials/)
75
+
76
+ it 'does not validate credentials if request is not signed', ->
77
+ helpers.mockHttpResponse 200, {}, ''
78
+ service.api = new AWS.Model.Api metadata:
79
+ endpointPrefix: 'mockservice'
80
+ signatureVersion: null
81
+ request = makeRequest()
82
+ request.send(->)
83
+ expect(errorHandler).not.toHaveBeenCalled()
84
+ expect(successHandler).toHaveBeenCalled()
85
+
86
+ it 'sends error event if region is not set', ->
87
+ service.config.region = null
88
+ request = makeRequest(->)
89
+
90
+ call = errorHandler.calls[0]
91
+ expect(errorHandler).toHaveBeenCalled()
92
+ expect(call.args[0] instanceof Error).toBeTruthy()
93
+ expect(call.args[0].code).toEqual('SigningError')
94
+ expect(call.args[0].message).toMatch(/Missing region in config/)
95
+
96
+ it 'ignores region validation if service has global endpoint', ->
97
+ helpers.mockHttpResponse 200, {}, ''
98
+ service.config.region = null
99
+ service.isGlobalEndpoint = true
100
+
101
+ makeRequest(->)
102
+ expect(errorHandler).not.toHaveBeenCalled()
103
+ delete service.isGlobalEndpoint
104
+
105
+ describe 'build', ->
106
+ it 'takes the request object as a parameter', ->
107
+ request = makeRequest()
108
+ request.on 'build', (req) ->
109
+ expect(req).toBe(request)
110
+ throw "ERROR"
111
+ response = request.send(->)
112
+ expect(response.error).toEqual("ERROR")
113
+
114
+ describe 'afterBuild', ->
115
+ sendRequest = (body) ->
116
+ request = makeRequest()
117
+ request.removeAllListeners('sign')
118
+ request.on('build', (req) -> req.httpRequest.body = body)
119
+ request.build()
120
+ request
121
+
122
+ contentLength = (body) ->
123
+ sendRequest(body).httpRequest.headers['Content-Length']
124
+
125
+ it 'builds Content-Length in the request headers for string content', ->
126
+ expect(contentLength('FOOBAR')).toEqual(6)
127
+
128
+ it 'builds Content-Length for string "0"', ->
129
+ expect(contentLength('0')).toEqual(1)
130
+
131
+ it 'builds Content-Length for utf-8 string body', ->
132
+ expect(contentLength('tï№')).toEqual(6)
133
+
134
+ it 'builds Content-Length for buffer body', ->
135
+ expect(contentLength(new AWS.util.Buffer('tï№'))).toEqual(6)
136
+
137
+ if AWS.util.isNode()
138
+ it 'builds Content-Length for file body', ->
139
+ fs = require('fs')
140
+ file = fs.createReadStream(__filename)
141
+ fileLen = fs.lstatSync(file.path).size
142
+ expect(contentLength(file)).toEqual(fileLen)
143
+
144
+ describe 'sign', ->
145
+ it 'takes the request object as a parameter', ->
146
+ request = makeRequest()
147
+ request.on 'sign', (req) ->
148
+ expect(req).toBe(request)
149
+ throw "ERROR"
150
+ response = request.send(->)
151
+ expect(response.error).toEqual("ERROR")
152
+
153
+ it 'uses the api.signingName if provided', ->
154
+ service.api.signingName = 'SIGNING_NAME'
155
+ spyOn(AWS.Signers.RequestSigner, 'getVersion').andCallFake ->
156
+ (req, signingName) -> throw signingName
157
+ request = makeRequest()
158
+ response = request.send(->)
159
+ expect(response.error).toEqual('SIGNING_NAME')
160
+ delete service.api.signingName
161
+
162
+ it 'uses the api.endpointPrefix if signingName not provided', ->
163
+ spyOn(AWS.Signers.RequestSigner, 'getVersion').andCallFake ->
164
+ (req, signingName) -> throw signingName
165
+ request = makeRequest()
166
+ response = request.send(->)
167
+ expect(response.error).toEqual('mockservice')
168
+
169
+ describe 'send', ->
170
+ it 'passes httpOptions from config', ->
171
+ options = {}
172
+ spyOn(AWS.HttpClient, 'getInstance').andReturn handleRequest: (req, opts) ->
173
+ options = opts
174
+ new AWS.SequentialExecutor()
175
+ service.config.httpOptions = timeout: 15
176
+ service.config.maxRetries = 0
177
+ makeRequest(->)
178
+ expect(options.timeout).toEqual(15)
179
+
180
+ it 'signs only once in normal case', ->
181
+ signHandler = jasmine.createSpy('sign')
182
+ helpers.mockHttpResponse 200, {}, ['data']
183
+
184
+ request = makeRequest()
185
+ request.on('sign', signHandler)
186
+ request.build()
187
+ request.signedAt = new Date(request.signedAt - 60 * 5 * 1000)
188
+ request.send()
189
+ expect(signHandler.callCount).toEqual(1)
190
+
191
+ it 'resigns if it took more than 10 min to get to send', ->
192
+ signHandler = jasmine.createSpy('sign')
193
+ helpers.mockHttpResponse 200, {}, ['data']
194
+
195
+ request = makeRequest()
196
+ request.on('sign', signHandler)
197
+ request.build()
198
+ request.signedAt = new Date(request.signedAt - 60 * 12 * 1000)
199
+ request.send()
200
+ expect(signHandler.callCount).toEqual(2)
201
+
202
+ describe 'httpData', ->
203
+ beforeEach ->
204
+ helpers.mockHttpResponse 200, {}, ['FOO', 'BAR', 'BAZ', 'QUX']
205
+
206
+ it 'emits httpData event on each chunk', ->
207
+ calls = []
208
+
209
+ # register httpData event
210
+ request = makeRequest()
211
+ request.on('httpData', (chunk) -> calls.push(chunk.toString()))
212
+ request.send()
213
+
214
+ expect(calls).toEqual(['FOO', 'BAR', 'BAZ', 'QUX'])
215
+
216
+ it 'does not clear default httpData event if another is added', ->
217
+ request = makeRequest()
218
+ request.on('httpData', ->)
219
+ response = request.send()
220
+
221
+ expect(response.httpResponse.body.toString()).toEqual('FOOBARBAZQUX')
222
+
223
+ if AWS.util.isNode() and AWS.HttpClient.streamsApiVersion > 1
224
+ describe 'httpDownloadProgress', ->
225
+ beforeEach ->
226
+ helpers.mockHttpResponse 200, {'content-length': 12}, ['FOO', 'BAR', 'BAZ', 'QUX']
227
+
228
+ it 'emits httpDownloadProgress for each chunk', ->
229
+ progress = []
230
+
231
+ # register httpData event
232
+ request = makeRequest()
233
+ request.on('httpDownloadProgress', (p) -> progress.push(p))
234
+ request.send()
235
+
236
+ expect(progress[0]).toEqual(loaded: 3, total: 12)
237
+ expect(progress[1]).toEqual(loaded: 6, total: 12)
238
+ expect(progress[2]).toEqual(loaded: 9, total: 12)
239
+ expect(progress[3]).toEqual(loaded: 12, total: 12)
240
+
241
+ describe 'retry', ->
242
+ it 'retries a request with a set maximum retries', ->
243
+ sendHandler = jasmine.createSpy('send')
244
+ service.config.maxRetries = 10
245
+
246
+ # fail every request with a fake networking error
247
+ helpers.mockHttpResponse
248
+ code: 'NetworkingError', message: 'Cannot connect'
249
+
250
+ request = makeRequest()
251
+ request.on('send', sendHandler)
252
+ response = request.send(->)
253
+
254
+ expect(retryHandler).toHaveBeenCalled()
255
+ expect(errorHandler).toHaveBeenCalled()
256
+ expect(completeHandler).toHaveBeenCalled()
257
+ expect(successHandler).not.toHaveBeenCalled()
258
+ expect(response.retryCount).toEqual(service.config.maxRetries);
259
+ expect(sendHandler.calls.length).toEqual(service.config.maxRetries + 1)
260
+
261
+ it 'retries with falloff', ->
262
+ helpers.mockHttpResponse
263
+ code: 'NetworkingError', message: 'Cannot connect'
264
+ makeRequest(->)
265
+ expect(delays).toEqual([30, 60, 120])
266
+
267
+ it 'uses retry from error.retryDelay property', ->
268
+ helpers.mockHttpResponse
269
+ code: 'NetworkingError', message: 'Cannot connect'
270
+ request = makeRequest()
271
+ request.on 'retry', (resp) -> resp.error.retryDelay = 17
272
+ response = request.send(->)
273
+ expect(delays).toEqual([17, 17, 17])
274
+
275
+ it 'retries if status code is >= 500', ->
276
+ helpers.mockHttpResponse 500, {}, ''
277
+
278
+ makeRequest (err) ->
279
+ expect(err.code).toEqual 500
280
+ expect(err.message).toEqual(null)
281
+ expect(err.statusCode).toEqual(500)
282
+ expect(err.retryable).toEqual(true)
283
+ expect(@retryCount).
284
+ toEqual(service.config.maxRetries)
285
+
286
+ it 'should not emit error if retried fewer than maxRetries', ->
287
+ helpers.mockIntermittentFailureResponse 2, 200, {}, 'foo'
288
+
289
+ response = makeRequest(->)
290
+
291
+ expect(totalWaited).toEqual(90)
292
+ expect(response.retryCount).toBeLessThan(service.config.maxRetries)
293
+ expect(response.data).toEqual('foo')
294
+ expect(errorHandler).not.toHaveBeenCalled()
295
+
296
+ ['ExpiredToken', 'ExpiredTokenException', 'RequestExpired'].forEach (name) ->
297
+ it 'invalidates expired credentials and retries', ->
298
+ spyOn(AWS.HttpClient, 'getInstance')
299
+ AWS.HttpClient.getInstance.andReturn handleRequest: (req, opts, cb, errCb) ->
300
+ if req.headers.Authorization.match('Credential=INVALIDKEY')
301
+ helpers.mockHttpSuccessfulResponse 403, {}, name, cb
302
+ else
303
+ helpers.mockHttpSuccessfulResponse 200, {}, 'DATA', cb
304
+ new AWS.SequentialExecutor()
305
+
306
+ creds =
307
+ numCalls: 0
308
+ expired: false
309
+ accessKeyId: 'INVALIDKEY'
310
+ secretAccessKey: 'INVALIDSECRET'
311
+ get: (cb) ->
312
+ if @expired
313
+ @numCalls += 1
314
+ @expired = false
315
+ @accessKeyId = 'VALIDKEY' + @numCalls
316
+ @secretAccessKey = 'VALIDSECRET' + @numCalls
317
+ cb()
318
+
319
+ service.config.credentials = creds
320
+
321
+ response = makeRequest(->)
322
+ expect(response.retryCount).toEqual(1)
323
+ expect(creds.accessKeyId).toEqual('VALIDKEY1')
324
+ expect(creds.secretAccessKey).toEqual('VALIDSECRET1')
325
+
326
+ [301, 307].forEach (code) ->
327
+ it 'attempts to redirect on ' + code + ' responses', ->
328
+ helpers.mockHttpResponse code, {location: 'http://redirected'}, ''
329
+ service.config.maxRetries = 0
330
+ service.config.maxRedirects = 5
331
+ response = makeRequest(->)
332
+ expect(response.request.httpRequest.endpoint.host).toEqual('redirected')
333
+ expect(response.error.retryable).toEqual(true)
334
+ expect(response.redirectCount).toEqual(service.config.maxRedirects)
335
+ expect(delays).toEqual([0, 0, 0, 0, 0])
336
+
337
+ it 'does not redirect if 3xx is missing location header', ->
338
+ helpers.mockHttpResponse 304, {}, ''
339
+ service.config.maxRetries = 0
340
+ response = makeRequest(->)
341
+ expect(response.request.httpRequest.endpoint.host).not.toEqual('redirected')
342
+ expect(response.error.retryable).toEqual(false)
343
+
344
+ describe 'success', ->
345
+ it 'emits success on a successful response', ->
346
+ # fail every request with a fake networking error
347
+ helpers.mockHttpResponse 200, {}, 'Success!'
348
+
349
+ response = makeRequest(->)
350
+
351
+ expect(retryHandler).not.toHaveBeenCalled()
352
+ expect(errorHandler).not.toHaveBeenCalled()
353
+ expect(completeHandler).toHaveBeenCalled()
354
+ expect(successHandler).toHaveBeenCalled()
355
+ expect(response.retryCount).toEqual(0);
356
+
357
+ describe 'error', ->
358
+ it 'emits error if error found and should not be retrying', ->
359
+ # fail every request with a fake networking error
360
+ helpers.mockHttpResponse 400, {}, ''
361
+
362
+ response = makeRequest(->)
363
+
364
+ expect(retryHandler).toHaveBeenCalled()
365
+ expect(errorHandler).toHaveBeenCalled()
366
+ expect(completeHandler).toHaveBeenCalled()
367
+ expect(successHandler).not.toHaveBeenCalled()
368
+ expect(response.retryCount).toEqual(0)
369
+
370
+ it 'emits error if an error is set in extractError', ->
371
+ error = code: 'ParseError', message: 'error message'
372
+ extractDataHandler = jasmine.createSpy('extractData')
373
+
374
+ helpers.mockHttpResponse 400, {}, ''
375
+
376
+ request = makeRequest()
377
+ request.on('extractData', extractDataHandler)
378
+ request.on('extractError', (resp) -> resp.error = error)
379
+ response = request.send(->)
380
+
381
+ expect(response.error).toBe(error)
382
+ expect(extractDataHandler).not.toHaveBeenCalled()
383
+ expect(retryHandler).toHaveBeenCalled()
384
+ expect(errorHandler).toHaveBeenCalled()
385
+ expect(completeHandler).toHaveBeenCalled()
386
+
387
+ describe 'logging', ->
388
+ data = null
389
+ logger = null
390
+ logfn = (d) -> data += d
391
+ match = /\[AWS mock 200 .* 0 retries\] mockMethod\(.*foo.*bar.*\)/
392
+
393
+ beforeEach ->
394
+ data = ''
395
+ logger = {}
396
+ service = new MockService(logger: logger)
397
+
398
+ it 'does nothing if logging is off', ->
399
+ service = new MockService(logger: null)
400
+ helpers.mockHttpResponse 200, {}, []
401
+ makeRequest().send()
402
+ expect(completeHandler).toHaveBeenCalled()
403
+
404
+ it 'calls .log() on logger if it is available', ->
405
+ helpers.mockHttpResponse 200, {}, []
406
+ logger.log = logfn
407
+ makeRequest().send()
408
+ expect(data).toMatch(match)
409
+
410
+ it 'calls .write() on logger if it is available', ->
411
+ helpers.mockHttpResponse 200, {}, []
412
+ logger.write = logfn
413
+ makeRequest().send()
414
+ expect(data).toMatch(match)
415
+
416
+ describe 'terminal callback error handling', ->
417
+ describe 'without domains', ->
418
+ it 'emits uncaughtException', ->
419
+ helpers.mockHttpResponse 200, {}, []
420
+ expect(-> (makeRequest -> invalidCode)).toThrow()
421
+ expect(completeHandler).toHaveBeenCalled()
422
+ expect(errorHandler).not.toHaveBeenCalled()
423
+ expect(retryHandler).not.toHaveBeenCalled()
424
+
425
+ ['error', 'complete'].forEach (evt) ->
426
+ it 'raise exceptions from terminal ' + evt + ' events', ->
427
+ helpers.mockHttpResponse 500, {}, []
428
+ request = makeRequest()
429
+ expect(-> request.send(-> invalidCode)).toThrow()
430
+ expect(completeHandler).toHaveBeenCalled()
431
+
432
+ if AWS.util.isNode()
433
+ describe 'with domains', ->
434
+ it 'sends error raised from complete event to a domain', ->
435
+ result = false
436
+ d = require('domain').create()
437
+ if d.run
438
+ d.enter()
439
+ d.on('error', (e) -> result = e)
440
+ d.run ->
441
+ helpers.mockHttpResponse 200, {}, []
442
+ request = makeRequest()
443
+ request.on 'complete', -> invalidCode
444
+ expect(-> request.send()).not.toThrow()
445
+ expect(completeHandler).toHaveBeenCalled()
446
+ expect(retryHandler).not.toHaveBeenCalled()
447
+ expect(result.name).toEqual('ReferenceError')
448
+ d.exit()
449
+
450
+ it 'does not leak service error into domain', ->
451
+ result = false
452
+ d = require('domain').create()
453
+ if d.run
454
+ d.on('error', (e) -> result = e)
455
+ d.enter()
456
+ d.run ->
457
+ helpers.mockHttpResponse 500, {}, []
458
+ makeRequest().send()
459
+ expect(completeHandler).toHaveBeenCalled()
460
+ expect(result).toEqual(false)
461
+ d.exit()
462
+
463
+ it 'supports inner domains', ->
464
+ helpers.mockHttpResponse 200, {}, []
465
+
466
+ done = false
467
+ err = new ReferenceError()
468
+ gotOuterError = false
469
+ gotInnerError = false
470
+ Domain = require("domain")
471
+ outerDomain = Domain.create()
472
+ outerDomain.on 'error', -> gotOuterError = true
473
+
474
+ if outerDomain.run
475
+ outerDomain.enter()
476
+ outerDomain.run ->
477
+ request = makeRequest()
478
+ innerDomain = Domain.create()
479
+ innerDomain.enter()
480
+ innerDomain.add(request)
481
+ innerDomain.on 'error', -> gotInnerError = true
482
+
483
+ runs ->
484
+ request.send ->
485
+ innerDomain.run -> done = true; throw err
486
+ waitsFor -> done
487
+ runs ->
488
+ expect(gotOuterError).toEqual(false)
489
+ expect(gotInnerError).toEqual(true)
490
+ expect(err.domainThrown).toEqual(false)
491
+ expect(err.domain).toEqual(innerDomain)
492
+ innerDomain.exit()
493
+ outerDomain.exit()
@@ -0,0 +1,150 @@
1
+ AWS = null
2
+ ignoreRequire = require
3
+ if typeof window == 'undefined'
4
+ AWS = ignoreRequire('../lib/aws')
5
+ else
6
+ AWS = window.AWS
7
+
8
+ EventEmitter = require('events').EventEmitter
9
+ Buffer = AWS.util.Buffer
10
+ semver = require('semver')
11
+
12
+ require('util').print = (data) ->
13
+ process.stdout.write(data)
14
+
15
+ # Mock credentials
16
+ AWS.config.update
17
+ paramValidation: false
18
+ region: 'mock-region'
19
+ credentials:
20
+ accessKeyId: 'akid'
21
+ secretAccessKey: 'secret'
22
+ sessionToken: 'session'
23
+
24
+ # Disable setTimeout for tests
25
+ # Warning: this might cause unpredictable results
26
+ # TODO: refactor this out.
27
+ `setTimeout = function(fn, delay) { fn(); }`
28
+
29
+ flattenXML = (xml) ->
30
+ if (!xml)
31
+ return xml
32
+ xml.split("\n").join(''). # remove newlines
33
+ replace(/>\s+</g, '><'). # prunes whitespace between elements
34
+ replace(/^\s+|\s+$/g, '') # trims whitespace from ends
35
+
36
+ matchXML = (xml1, xml2) ->
37
+ expect(flattenXML(xml1)).toEqual(flattenXML(xml2))
38
+
39
+ MockService = AWS.Service.defineService 'mockService',
40
+ serviceIdentifier: 'mock'
41
+ initialize: (config) ->
42
+ AWS.Service.prototype.initialize.call(this, config)
43
+ @config.credentials = accessKeyId: 'akid', secretAccessKey: 'secret'
44
+ @config.region = 'mock-region'
45
+ setupRequestListeners: (request) ->
46
+ request.on 'extractData', (resp) ->
47
+ resp.data = resp.httpResponse.body.toString()
48
+ request.on 'extractError', (resp) ->
49
+ resp.error =
50
+ code: resp.httpResponse.body.toString() || resp.httpResponse.statusCode
51
+ message: null
52
+ api: new AWS.Model.Api metadata:
53
+ endpointPrefix: 'mockservice'
54
+ signatureVersion: 'v4'
55
+
56
+ mockHttpSuccessfulResponse = (status, headers, data, cb) ->
57
+ if !Array.isArray(data)
58
+ data = [data]
59
+
60
+ httpResp = new EventEmitter()
61
+ httpResp.statusCode = status
62
+ httpResp.headers = headers
63
+
64
+ cb(httpResp)
65
+ httpResp.emit('headers', status, headers)
66
+
67
+ if AWS.util.isNode() && httpResp._events.readable
68
+ httpResp.read = ->
69
+ if data.length > 0
70
+ chunk = data.shift()
71
+ if chunk is null
72
+ null
73
+ else
74
+ new Buffer(chunk)
75
+ else
76
+ null
77
+
78
+ AWS.util.arrayEach data.slice(), (str) ->
79
+ if AWS.util.isNode() && (httpResp._events.readable || semver.gt(process.version, 'v0.11.3'))
80
+ httpResp.emit('readable')
81
+ else
82
+ httpResp.emit('data', new Buffer(str))
83
+
84
+ if httpResp._events['readable'] || httpResp._events['data']
85
+ httpResp.emit('end')
86
+ else
87
+ httpResp.emit('aborted')
88
+
89
+ mockHttpResponse = (status, headers, data) ->
90
+ stream = new EventEmitter()
91
+ stream.setMaxListeners(0)
92
+ spyOn(AWS.HttpClient, 'getInstance')
93
+ AWS.HttpClient.getInstance.andReturn handleRequest: (req, opts, cb, errCb) ->
94
+ if typeof status == 'number'
95
+ mockHttpSuccessfulResponse status, headers, data, cb
96
+ else
97
+ errCb(status)
98
+ stream
99
+
100
+ return stream
101
+
102
+ mockIntermittentFailureResponse = (numFailures, status, headers, data) ->
103
+ retryCount = 0
104
+ spyOn(AWS.HttpClient, 'getInstance')
105
+ AWS.HttpClient.getInstance.andReturn handleRequest: (req, opts, cb, errCb) ->
106
+ if retryCount < numFailures
107
+ retryCount += 1
108
+ errCb code: 'NetworkingError', message: 'FAIL!'
109
+ else
110
+ statusCode = retryCount < numFailures ? 500 : status
111
+ mockHttpSuccessfulResponse statusCode, headers, data, cb
112
+ new EventEmitter()
113
+
114
+ mockResponse = (svc, resp) ->
115
+ addAll = svc.addAllRequestListeners
116
+ spyOn(svc, 'addAllRequestListeners').andCallFake (req) ->
117
+ req.response.httpResponse.statusCode = 200
118
+ addAll.call(svc, req)
119
+ req.removeAllListeners('send')
120
+ req.removeAllListeners('extractError')
121
+ req.removeAllListeners('extractData')
122
+ req.on 'validateResponse', ->
123
+ AWS.util.update req.response, resp
124
+
125
+ mockResponses = (svc, resps) ->
126
+ index = 0
127
+ addAll = svc.addAllRequestListeners
128
+ spyOn(svc, 'addAllRequestListeners').andCallFake (req) ->
129
+ req.response.httpResponse.statusCode = 200
130
+ addAll.call(svc, req)
131
+ req.removeAllListeners('send')
132
+ req.removeAllListeners('extractError')
133
+ req.removeAllListeners('extractData')
134
+ req.on 'validateResponse', ->
135
+ resp = resps[index]
136
+ if resp
137
+ AWS.util.update req.response, resp
138
+ index += 1
139
+
140
+ module.exports =
141
+ AWS: AWS
142
+ util: AWS.util
143
+ matchXML: matchXML
144
+ mockHttpResponse: mockHttpResponse
145
+ mockIntermittentFailureResponse: mockIntermittentFailureResponse
146
+ mockHttpSuccessfulResponse: mockHttpSuccessfulResponse
147
+ mockResponse: mockResponse
148
+ mockResponses: mockResponses
149
+ MockService: MockService
150
+
@@ -0,0 +1,55 @@
1
+ AWS = require('./helpers').AWS
2
+
3
+ describe 'AWS.HttpRequest', ->
4
+
5
+ request = null
6
+ agentHeader = null
7
+ if AWS.util.isBrowser()
8
+ agentHeader = 'X-Amz-User-Agent'
9
+ else
10
+ agentHeader = 'User-Agent'
11
+
12
+ beforeEach ->
13
+ request = new AWS.HttpRequest('http://domain.com')
14
+
15
+ describe 'constructor', ->
16
+
17
+ it 'defaults to POST method requests', ->
18
+ expect(request.method).toEqual('POST')
19
+
20
+ it 'defaults the uri to /', ->
21
+ expect(request.path).toEqual('/')
22
+
23
+ it 'provides headers with a default user agent', ->
24
+ headers = {}
25
+ headers[agentHeader] = AWS.util.userAgent()
26
+ expect(request.headers).toEqual(headers)
27
+
28
+ it 'defaults body to empty string', ->
29
+ expect(request.body).toEqual('')
30
+
31
+ it 'defaults endpointPrefix to undefined', ->
32
+ expect(request.endpointPrefix).toEqual(undefined)
33
+
34
+ it 'uses the path from the endpoint if provided', ->
35
+ request = new AWS.HttpRequest('http://domain.com/path')
36
+ expect(request.path).toEqual('/path')
37
+
38
+ describe 'pathname', ->
39
+
40
+ it 'defaults to /', ->
41
+ expect(request.pathname()).toEqual('/')
42
+
43
+ it 'returns the path portion of the uri', ->
44
+ request.path = '/abc/xyz?mno=hjk'
45
+ expect(request.pathname()).toEqual('/abc/xyz')
46
+
47
+ describe 'search', ->
48
+
49
+ it 'defaults to an empty string', ->
50
+ expect(request.search()).toEqual('')
51
+
52
+ it 'returns the querystring portion of the uri', ->
53
+ request.path = '/abc/xyz?mno=hjk'
54
+ expect(request.search()).toEqual('mno=hjk')
55
+