httpadapter 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,633 @@
1
+ # Copyright (C) 2010 Google Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'spec_helper'
16
+ require 'spec/httpadapter/adapter_type_checking_spec'
17
+
18
+ require 'httpadapter/adapters/net_http'
19
+
20
+ if ([:enable_post_connection_check=, 'enable_post_connection_check='] &
21
+ Net::HTTP.instance_methods).empty?
22
+ class Net::HTTP
23
+ def enable_post_connection_check=(value)
24
+ # No-op
25
+ end
26
+ end
27
+ end
28
+
29
+ describe HTTPAdapter::NetHTTPAdapter do
30
+ before do
31
+ @adapter = HTTPAdapter::NetHTTPAdapter.new
32
+ end
33
+
34
+ it_should_behave_like 'adapter type-checking example'
35
+
36
+ describe 'when adapting a request' do
37
+ describe 'from Net::HTTP::Get' do
38
+ before do
39
+ @request = Net::HTTP::Get.new('/path/to/resource')
40
+ @request['Accept'] = 'application/json'
41
+ @result = @adapter.adapt_request(@request)
42
+ @method, @uri, @headers, @body = @result
43
+ end
44
+
45
+ it 'should convert the HTTP method properly' do
46
+ @method.should == 'GET'
47
+ end
48
+
49
+ it 'should convert the URI properly' do
50
+ @uri.should == '/path/to/resource'
51
+ end
52
+
53
+ it 'should convert the headers properly' do
54
+ accept = nil
55
+ @headers.each do |header, value|
56
+ header.should be_kind_of(String)
57
+ value.should be_kind_of(String)
58
+ accept = value if header == 'Accept'
59
+ end
60
+ accept.should == 'application/json'
61
+ end
62
+
63
+ it 'should convert the body properly' do
64
+ @body.each do |chunk|
65
+ chunk.should be_kind_of(String)
66
+ end
67
+ end
68
+ end
69
+
70
+ describe 'from Net::HTTP::Get with http' do
71
+ before do
72
+ @request = Net::HTTP::Get.new('/path/to/resource')
73
+ @request['Host'] = 'www.example.com'
74
+ @request['Accept'] = 'application/json'
75
+ @request['X-Forwarded-Proto'] = 'http'
76
+ @result = @adapter.adapt_request(@request)
77
+ @method, @uri, @headers, @body = @result
78
+ end
79
+
80
+ it 'should convert the HTTP method properly' do
81
+ @method.should == 'GET'
82
+ end
83
+
84
+ it 'should convert the URI properly' do
85
+ @uri.should == 'http://www.example.com/path/to/resource'
86
+ end
87
+
88
+ it 'should convert the headers properly' do
89
+ accept = nil
90
+ @headers.each do |header, value|
91
+ header.should be_kind_of(String)
92
+ value.should be_kind_of(String)
93
+ accept = value if header == 'Accept'
94
+ end
95
+ accept.should == 'application/json'
96
+ end
97
+
98
+ it 'should convert the body properly' do
99
+ @body.each do |chunk|
100
+ chunk.should be_kind_of(String)
101
+ end
102
+ end
103
+ end
104
+
105
+ describe 'from Net::HTTP::Get with https' do
106
+ before do
107
+ @request = Net::HTTP::Get.new('/path/to/resource')
108
+ @request['Host'] = 'www.example.com'
109
+ @request['Accept'] = 'application/json'
110
+ @request['X-Forwarded-Proto'] = 'https'
111
+ @result = @adapter.adapt_request(@request)
112
+ @method, @uri, @headers, @body = @result
113
+ end
114
+
115
+ it 'should convert the HTTP method properly' do
116
+ @method.should == 'GET'
117
+ end
118
+
119
+ it 'should convert the URI properly' do
120
+ @uri.should == 'https://www.example.com/path/to/resource'
121
+ end
122
+
123
+ it 'should convert the headers properly' do
124
+ accept = nil
125
+ @headers.each do |header, value|
126
+ header.should be_kind_of(String)
127
+ value.should be_kind_of(String)
128
+ accept = value if header == 'Accept'
129
+ end
130
+ accept.should == 'application/json'
131
+ end
132
+
133
+ it 'should convert the body properly' do
134
+ @body.each do |chunk|
135
+ chunk.should be_kind_of(String)
136
+ end
137
+ end
138
+ end
139
+
140
+ describe 'from Net::HTTP::Post' do
141
+ before do
142
+ @request = Net::HTTP::Post.new('/path/to/resource')
143
+ @request['Accept'] = 'application/json'
144
+ @request['Content-Type'] = 'application/json; charset=utf-8'
145
+ @request.body = '{"three":3,"two":2,"one":1}'
146
+ @result = @adapter.adapt_request(@request)
147
+ @method, @uri, @headers, @body = @result
148
+ end
149
+
150
+ it 'should convert the HTTP method properly' do
151
+ @method.should == 'POST'
152
+ end
153
+
154
+ it 'should convert the URI properly' do
155
+ @uri.should == '/path/to/resource'
156
+ end
157
+
158
+ it 'should convert the headers properly' do
159
+ accept = nil
160
+ content_type = nil
161
+ @headers.each do |header, value|
162
+ header.should be_kind_of(String)
163
+ value.should be_kind_of(String)
164
+ accept = value if header == 'Accept'
165
+ content_type = value if header == 'Content-Type'
166
+ end
167
+ accept.should == 'application/json'
168
+ content_type.should == 'application/json; charset=utf-8'
169
+ end
170
+
171
+ it 'should convert the body properly' do
172
+ merged_body = ""
173
+ @body.each do |chunk|
174
+ chunk.should be_kind_of(String)
175
+ merged_body += chunk
176
+ end
177
+ merged_body.should == '{"three":3,"two":2,"one":1}'
178
+ end
179
+ end
180
+ end
181
+
182
+ describe 'when specializing a request' do
183
+ it 'should raise an error for converting from an invalid tuple' do
184
+ # Can't put this in the generic type-checking specs; this type-check
185
+ # is implementation-specific.
186
+ (lambda do
187
+ @adapter.specialize_request(['GET', '/', [], [42]])
188
+ end).should raise_error(TypeError)
189
+ end
190
+
191
+ it 'should raise an error for the bogus HTTP method' do
192
+ (lambda do
193
+ tuple = [
194
+ 'BOGUS',
195
+ 'http://www.example.com/path?query',
196
+ [
197
+ ['Accept', 'application/json'],
198
+ ['Content-Type', 'application/json; charset=utf-8'],
199
+ ['Content-Length', '27']
200
+ ],
201
+ ['{"three":3,', '"two":2,', '"one":1}']
202
+ ]
203
+ @adapter.specialize_request(tuple)
204
+ end).should raise_error(ArgumentError)
205
+ end
206
+
207
+ describe 'from a GET tuple' do
208
+ before do
209
+ @tuple = [
210
+ 'GET',
211
+ 'http://www.example.com/path?query',
212
+ [
213
+ ['Accept', 'application/json']
214
+ ],
215
+ []
216
+ ]
217
+ @request = @adapter.specialize_request(@tuple)
218
+ end
219
+
220
+ it 'should convert the HTTP method properly' do
221
+ @request.method.should == 'GET'
222
+ end
223
+
224
+ it 'should convert the URI properly' do
225
+ @request.path.should == '/path?query'
226
+ end
227
+
228
+ it 'should convert the headers properly' do
229
+ accept = nil
230
+ @request.canonical_each do |header, value|
231
+ header.should be_kind_of(String)
232
+ value.should be_kind_of(String)
233
+ accept = value if header == 'Accept'
234
+ end
235
+ accept.should == 'application/json'
236
+ end
237
+
238
+ it 'should convert the body properly' do
239
+ # Net::HTTP is weird in that it treats nils like empty strings.
240
+ [nil, ''].should include(@request.body)
241
+ [nil, 0].should include(@request.content_length)
242
+ end
243
+ end
244
+
245
+ describe 'from a GET tuple with a Host header' do
246
+ before do
247
+ @tuple = [
248
+ 'GET',
249
+ '/path?query',
250
+ [
251
+ ['Host', 'www.example.com'],
252
+ ['Accept', 'application/json']
253
+ ],
254
+ []
255
+ ]
256
+ @request = @adapter.specialize_request(@tuple)
257
+ end
258
+
259
+ it 'should convert the HTTP method properly' do
260
+ @request.method.should == 'GET'
261
+ end
262
+
263
+ it 'should convert the URI properly' do
264
+ @request.path.should == '/path?query'
265
+ end
266
+
267
+ it 'should convert the headers properly' do
268
+ accept = nil
269
+ host = nil
270
+ @request.canonical_each do |header, value|
271
+ header.should be_kind_of(String)
272
+ value.should be_kind_of(String)
273
+ host = value if header == 'Host'
274
+ accept = value if header == 'Accept'
275
+ end
276
+ host.should == 'www.example.com'
277
+ accept.should == 'application/json'
278
+ end
279
+
280
+ it 'should convert the body properly' do
281
+ # Net::HTTP is weird in that it treats nils like empty strings.
282
+ [nil, ''].should include(@request.body)
283
+ [nil, 0].should include(@request.content_length)
284
+ end
285
+ end
286
+
287
+ describe 'from a POST tuple' do
288
+ before do
289
+ @tuple = [
290
+ 'POST',
291
+ 'http://www.example.com/path?query',
292
+ [
293
+ ['Accept', 'application/json'],
294
+ ['Content-Type', 'application/json; charset=utf-8'],
295
+ ['Content-Length', '27']
296
+ ],
297
+ ['{"three":3,', '"two":2,', '"one":1}']
298
+ ]
299
+ @request = @adapter.specialize_request(@tuple)
300
+ end
301
+
302
+ it 'should convert the HTTP method properly' do
303
+ @request.method.should == 'POST'
304
+ end
305
+
306
+ it 'should convert the URI properly' do
307
+ @request.path.should == '/path?query'
308
+ end
309
+
310
+ it 'should convert the headers properly' do
311
+ accept = nil
312
+ content_type = nil
313
+ @request.canonical_each do |header, value|
314
+ header.should be_kind_of(String)
315
+ value.should be_kind_of(String)
316
+ accept = value if header == 'Accept'
317
+ content_type = value if header == 'Content-Type'
318
+ end
319
+ accept.should == 'application/json'
320
+ content_type.should == 'application/json; charset=utf-8'
321
+ end
322
+
323
+ it 'should convert the body properly' do
324
+ @request.body.should == '{"three":3,"two":2,"one":1}'
325
+ end
326
+ end
327
+
328
+ describe 'from a POST tuple with no body' do
329
+ before do
330
+ @tuple = [
331
+ 'POST',
332
+ 'http://www.example.com/path?query',
333
+ [
334
+ ['Accept', 'application/json']
335
+ ],
336
+ []
337
+ ]
338
+ @request = @adapter.specialize_request(@tuple)
339
+ end
340
+
341
+ it 'should convert the HTTP method properly' do
342
+ @request.method.should == 'POST'
343
+ end
344
+
345
+ it 'should convert the URI properly' do
346
+ @request.path.should == '/path?query'
347
+ end
348
+
349
+ it 'should convert the headers properly' do
350
+ accept = nil
351
+ @request.canonical_each do |header, value|
352
+ header.should be_kind_of(String)
353
+ value.should be_kind_of(String)
354
+ accept = value if header == 'Accept'
355
+ end
356
+ accept.should == 'application/json'
357
+ end
358
+
359
+ it 'should have the correct content length' do
360
+ @request.content_length.should == 0
361
+ end
362
+ end
363
+ end
364
+
365
+ describe 'when adapting a response' do
366
+ describe 'from Net::HTTPOK' do
367
+ before do
368
+ @response = Net::HTTPOK.new('1.1', '200', 'OK')
369
+ @response['Content-Type'] = 'application/json; charset=utf-8'
370
+ @response['Content-Length'] = '27'
371
+
372
+ # Ugh
373
+ @response.instance_variable_set('@read', true)
374
+ @response.instance_variable_set('@body', '{"three":3,"two":2,"one":1}')
375
+
376
+ @result = @adapter.adapt_response(@response)
377
+ @status, @headers, @body = @result
378
+ end
379
+
380
+ it 'should convert the HTTP status properly' do
381
+ @status.should == 200
382
+ end
383
+
384
+ it 'should convert the headers properly' do
385
+ content_type = nil
386
+ @headers.each do |header, value|
387
+ header.should be_kind_of(String)
388
+ value.should be_kind_of(String)
389
+ content_type = value if header == 'Content-Type'
390
+ end
391
+ content_type.should == 'application/json; charset=utf-8'
392
+ end
393
+
394
+ it 'should convert the body properly' do
395
+ merged_body = ""
396
+ @body.each do |chunk|
397
+ merged_body += chunk
398
+ chunk.should be_kind_of(String)
399
+ end
400
+ merged_body.should == '{"three":3,"two":2,"one":1}'
401
+ end
402
+ end
403
+ end
404
+
405
+ describe 'when specializing a response' do
406
+ it 'should raise an error for the bogus HTTP status code' do
407
+ (lambda do
408
+ tuple = [
409
+ 'BOGUS',
410
+ [
411
+ ['Content-Type', 'application/json; charset=utf-8'],
412
+ ['Content-Length', '27']
413
+ ],
414
+ ['{"three":3,', '"two":2,', '"one":1}']
415
+ ]
416
+ @adapter.specialize_response(tuple)
417
+ end).should raise_error(ArgumentError)
418
+ end
419
+
420
+ it 'should raise an error for converting from an invalid tuple' do
421
+ # Can't put this in the generic type-checking specs; this type-check
422
+ # is implementation-specific.
423
+ (lambda do
424
+ @adapter.specialize_response([200, [], [42]])
425
+ end).should raise_error(TypeError)
426
+ end
427
+
428
+ describe 'from a 200 tuple' do
429
+ before do
430
+ @tuple = [
431
+ 200,
432
+ [
433
+ ['Content-Type', 'application/json; charset=utf-8'],
434
+ ['Content-Length', '27']
435
+ ],
436
+ ['{"three":3,"two":2,"one":1}']
437
+ ]
438
+ @response = @adapter.specialize_response(@tuple)
439
+ end
440
+
441
+ it 'should be the correct type' do
442
+ @response.should be_kind_of(Net::HTTPOK)
443
+ end
444
+
445
+ it 'should convert the HTTP status properly' do
446
+ @response.code.to_i.should == 200
447
+ end
448
+
449
+ it 'should convert the headers properly' do
450
+ content_type = nil
451
+ @response.canonical_each do |header, value|
452
+ header.should be_kind_of(String)
453
+ value.should be_kind_of(String)
454
+ content_type = value if header == 'Content-Type'
455
+ end
456
+ content_type.should == 'application/json; charset=utf-8'
457
+ end
458
+
459
+ it 'should convert the body properly' do
460
+ @response.body.should == '{"three":3,"two":2,"one":1}'
461
+ end
462
+ end
463
+
464
+ describe 'from a 200 tuple' do
465
+ before do
466
+ @tuple = [
467
+ 500,
468
+ [
469
+ ['Content-Type', 'text/html'],
470
+ ['Content-Length', '28']
471
+ ],
472
+ ['<html><body>', '42', '</body></html>']
473
+ ]
474
+ @response = @adapter.specialize_response(@tuple)
475
+ end
476
+
477
+ it 'should be the correct type' do
478
+ @response.should be_kind_of(Net::HTTPInternalServerError)
479
+ end
480
+
481
+ it 'should convert the HTTP status properly' do
482
+ @response.code.to_i.should == 500
483
+ end
484
+
485
+ it 'should convert the headers properly' do
486
+ content_type = nil
487
+ @response.canonical_each do |header, value|
488
+ header.should be_kind_of(String)
489
+ value.should be_kind_of(String)
490
+ content_type = value if header == 'Content-Type'
491
+ end
492
+ content_type.should == 'text/html'
493
+ end
494
+
495
+ it 'should convert the body properly' do
496
+ @response.body.should == '<html><body>42</body></html>'
497
+ end
498
+ end
499
+ end
500
+
501
+ describe 'when transmitting a request' do
502
+ describe 'with a GET tuple' do
503
+ before do
504
+ @tuple = [
505
+ 'GET',
506
+ 'http://www.google.com/',
507
+ [],
508
+ []
509
+ ]
510
+ @response = @adapter.transmit(@tuple)
511
+ @status, @headers, @chunked_body = @response
512
+ @body = ''
513
+ @chunked_body.each do |chunk|
514
+ @body += chunk
515
+ end
516
+ end
517
+
518
+ it 'should have the correct status' do
519
+ @status.should == 200
520
+ end
521
+
522
+ it 'should have response headers' do
523
+ @headers.should_not be_empty
524
+ end
525
+
526
+ it 'should have a response body' do
527
+ @body.length.should > 0
528
+ end
529
+ end
530
+
531
+ describe 'with a GET tuple' do
532
+ before do
533
+ @tuple = [
534
+ 'GET',
535
+ 'https://encrypted.google.com/',
536
+ [],
537
+ []
538
+ ]
539
+ @response = @adapter.transmit(@tuple)
540
+ @status, @headers, @chunked_body = @response
541
+ @body = ''
542
+ @chunked_body.each do |chunk|
543
+ @body += chunk
544
+ end
545
+ end
546
+
547
+ it 'should have the correct status' do
548
+ @status.should == 200
549
+ end
550
+
551
+ it 'should have response headers' do
552
+ @headers.should_not be_empty
553
+ end
554
+
555
+ it 'should have a response body' do
556
+ @body.length.should > 0
557
+ end
558
+ end
559
+
560
+ describe 'with a connection' do
561
+ before do
562
+ @connection = HTTPAdapter::Connection.new(
563
+ 'www.google.com', 80,
564
+ Net::HTTP.new('www.google.com', 80),
565
+ :open => [:start],
566
+ :close => [:finish]
567
+ )
568
+ @connection.open
569
+ @tuple = [
570
+ 'GET',
571
+ 'http://www.google.com/',
572
+ [],
573
+ []
574
+ ]
575
+ @response = @adapter.transmit(@tuple, @connection)
576
+ @status, @headers, @chunked_body = @response
577
+ @body = ''
578
+ @chunked_body.each do |chunk|
579
+ @body += chunk
580
+ end
581
+ end
582
+
583
+ after do
584
+ @connection.close
585
+ end
586
+
587
+ it 'should have the correct status' do
588
+ @status.should == 200
589
+ end
590
+
591
+ it 'should have response headers' do
592
+ @headers.should_not be_empty
593
+ end
594
+
595
+ it 'should have a response body' do
596
+ @body.length.should > 0
597
+ end
598
+ end
599
+
600
+ describe 'with a custom configuration block' do
601
+ before do
602
+ @adapter = HTTPAdapter::NetHTTPAdapter.new do |http|
603
+ # You should never actually do this. But you could.
604
+ http.connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
605
+ end
606
+ @tuple = [
607
+ 'GET',
608
+ 'https://gmail.com/.well-known/host-meta',
609
+ [],
610
+ []
611
+ ]
612
+ @response = @adapter.transmit(@tuple, @connection)
613
+ @status, @headers, @chunked_body = @response
614
+ @body = ''
615
+ @chunked_body.each do |chunk|
616
+ @body += chunk
617
+ end
618
+ end
619
+
620
+ it 'should have the correct status' do
621
+ @status.should == 200
622
+ end
623
+
624
+ it 'should have response headers' do
625
+ @headers.should_not be_empty
626
+ end
627
+
628
+ it 'should have a response body' do
629
+ @body.length.should > 0
630
+ end
631
+ end
632
+ end
633
+ end