httpclient 2.1.5.2 → 2.1.6
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.
- data/lib/hexdump.rb +36 -0
- data/lib/httpclient.rb +83 -43
- data/lib/httpclient/auth.rb +260 -15
- data/lib/httpclient/cacert.p7s +545 -266
- data/lib/httpclient/cacert_sha1.p7s +552 -273
- data/lib/httpclient/connection.rb +5 -1
- data/lib/httpclient/cookie.rb +1 -2
- data/lib/httpclient/http.rb +106 -51
- data/lib/httpclient/session.rb +101 -21
- data/lib/httpclient/ssl_config.rb +3 -3
- data/lib/httpclient/timeout.rb +11 -9
- data/lib/httpclient/util.rb +17 -0
- data/lib/oauthclient.rb +110 -0
- metadata +27 -17
- data/lib/tags +0 -908
@@ -64,7 +64,11 @@ class HTTPClient
|
|
64
64
|
# Retrieves a HTTP::Message instance of HTTP response. Do not invoke this
|
65
65
|
# method twice for now. The second invocation will be blocked.
|
66
66
|
def pop
|
67
|
-
@queue.pop
|
67
|
+
response_or_exception = @queue.pop
|
68
|
+
if response_or_exception.is_a? Exception
|
69
|
+
raise response_or_exception
|
70
|
+
end
|
71
|
+
response_or_exception
|
68
72
|
end
|
69
73
|
|
70
74
|
def push(result) # :nodoc:
|
data/lib/httpclient/cookie.rb
CHANGED
@@ -337,7 +337,6 @@ class WebAgent
|
|
337
337
|
|
338
338
|
domainname = url.host
|
339
339
|
domain_orig, path_orig = domain, path
|
340
|
-
use_security = override
|
341
340
|
|
342
341
|
if domain
|
343
342
|
|
@@ -372,7 +371,7 @@ class WebAgent
|
|
372
371
|
# and IE does not check, too.
|
373
372
|
end
|
374
373
|
|
375
|
-
path ||= url.path.sub(%r|/[^/]
|
374
|
+
path ||= url.path.sub(%r|/[^/]*\z|, '')
|
376
375
|
domain ||= domainname
|
377
376
|
@cookies.synchronize do
|
378
377
|
cookie = find_cookie_info(domain, path, name)
|
data/lib/httpclient/http.rb
CHANGED
@@ -95,7 +95,7 @@ module HTTP
|
|
95
95
|
|
96
96
|
# Represents HTTP message header.
|
97
97
|
class Headers
|
98
|
-
# HTTP version in a HTTP header.
|
98
|
+
# HTTP version in a HTTP header. String.
|
99
99
|
attr_accessor :http_version
|
100
100
|
# Size of body. nil when size is unknown (e.g. chunked response).
|
101
101
|
attr_reader :body_size
|
@@ -109,7 +109,7 @@ module HTTP
|
|
109
109
|
# Request only. Requested query.
|
110
110
|
attr_accessor :request_query
|
111
111
|
# Request only. Requested via proxy or not.
|
112
|
-
attr_accessor :
|
112
|
+
attr_accessor :request_absolute_uri
|
113
113
|
|
114
114
|
# Response only. HTTP status
|
115
115
|
attr_reader :status_code
|
@@ -151,14 +151,14 @@ module HTTP
|
|
151
151
|
# Creates a Message::Headers. Use init_request, init_response, or
|
152
152
|
# init_connect_request for acutual initialize.
|
153
153
|
def initialize
|
154
|
-
@http_version = 1.1
|
154
|
+
@http_version = '1.1'
|
155
155
|
@body_size = nil
|
156
156
|
@chunked = false
|
157
157
|
|
158
158
|
@request_method = nil
|
159
159
|
@request_uri = nil
|
160
160
|
@request_query = nil
|
161
|
-
@
|
161
|
+
@request_absolute_uri = nil
|
162
162
|
|
163
163
|
@status_code = nil
|
164
164
|
@reason_phrase = nil
|
@@ -178,7 +178,7 @@ module HTTP
|
|
178
178
|
@request_method = 'CONNECT'
|
179
179
|
@request_uri = uri
|
180
180
|
@request_query = nil
|
181
|
-
@http_version = 1.0
|
181
|
+
@http_version = '1.0'
|
182
182
|
end
|
183
183
|
|
184
184
|
# Placeholder URI object for nil uri.
|
@@ -189,7 +189,7 @@ module HTTP
|
|
189
189
|
@request_method = method
|
190
190
|
@request_uri = uri || NIL_URI
|
191
191
|
@request_query = query
|
192
|
-
@
|
192
|
+
@request_absolute_uri = false
|
193
193
|
end
|
194
194
|
|
195
195
|
# Initialize this instance as a response.
|
@@ -285,11 +285,38 @@ module HTTP
|
|
285
285
|
get(key).collect { |item| item[1] }
|
286
286
|
end
|
287
287
|
|
288
|
+
def create_query_uri()
|
289
|
+
if @request_method == 'CONNECT'
|
290
|
+
return "#{@request_uri.host}:#{@request_uri.port}"
|
291
|
+
end
|
292
|
+
path = @request_uri.path
|
293
|
+
path = '/' if path.nil? or path.empty?
|
294
|
+
if query_str = create_query_part()
|
295
|
+
path += "?#{query_str}"
|
296
|
+
end
|
297
|
+
path
|
298
|
+
end
|
299
|
+
|
300
|
+
def create_query_part()
|
301
|
+
query_str = nil
|
302
|
+
if @request_uri.query
|
303
|
+
query_str = @request_uri.query
|
304
|
+
end
|
305
|
+
if @request_query
|
306
|
+
if query_str
|
307
|
+
query_str += "&#{Message.create_query_part_str(@request_query)}"
|
308
|
+
else
|
309
|
+
query_str = Message.create_query_part_str(@request_query)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
query_str
|
313
|
+
end
|
314
|
+
|
288
315
|
private
|
289
316
|
|
290
317
|
def request_line
|
291
|
-
path = create_query_uri(
|
292
|
-
if @
|
318
|
+
path = create_query_uri()
|
319
|
+
if @request_absolute_uri
|
293
320
|
path = "#{ @request_uri.scheme }://#{ @request_uri.host }:#{ @request_uri.port }#{ path }"
|
294
321
|
end
|
295
322
|
"#{ @request_method } #{ path } HTTP/#{ @http_version }#{ CRLF }"
|
@@ -323,7 +350,7 @@ module HTTP
|
|
323
350
|
elsif @body_size and (keep_alive or @body_size != 0)
|
324
351
|
set('Content-Length', @body_size.to_s)
|
325
352
|
end
|
326
|
-
if @http_version >= 1.1
|
353
|
+
if @http_version >= '1.1' and get('Host').empty?
|
327
354
|
if @request_uri.port == @request_uri.default_port
|
328
355
|
# GFE/1.3 dislikes default port number (returns 404)
|
329
356
|
set('Host', "#{@request_uri.host}")
|
@@ -358,29 +385,6 @@ module HTTP
|
|
358
385
|
def charset_label(charset)
|
359
386
|
CHARSET_MAP[charset] || 'us-ascii'
|
360
387
|
end
|
361
|
-
|
362
|
-
def create_query_uri(uri, query)
|
363
|
-
if @request_method == 'CONNECT'
|
364
|
-
return "#{uri.host}:#{uri.port}"
|
365
|
-
end
|
366
|
-
path = uri.path
|
367
|
-
path = '/' if path.nil? or path.empty?
|
368
|
-
query_str = nil
|
369
|
-
if uri.query
|
370
|
-
query_str = uri.query
|
371
|
-
end
|
372
|
-
if query
|
373
|
-
if query_str
|
374
|
-
query_str += "&#{Message.create_query_part_str(query)}"
|
375
|
-
else
|
376
|
-
query_str = Message.create_query_part_str(query)
|
377
|
-
end
|
378
|
-
end
|
379
|
-
if query_str
|
380
|
-
path += "?#{query_str}"
|
381
|
-
end
|
382
|
-
path
|
383
|
-
end
|
384
388
|
end
|
385
389
|
|
386
390
|
|
@@ -414,7 +418,9 @@ module HTTP
|
|
414
418
|
# Initialize this instance as a response.
|
415
419
|
def init_response(body = nil)
|
416
420
|
@body = body
|
417
|
-
if @body.respond_to?(:
|
421
|
+
if @body.respond_to?(:bytesize)
|
422
|
+
@size = @body.bytesize
|
423
|
+
elsif @body.respond_to?(:size)
|
418
424
|
@size = @body.size
|
419
425
|
else
|
420
426
|
@size = nil
|
@@ -438,6 +444,7 @@ module HTTP
|
|
438
444
|
while !part.read(@chunk_size, buf).nil?
|
439
445
|
dev << buf
|
440
446
|
end
|
447
|
+
part.rewind
|
441
448
|
else
|
442
449
|
dev << part
|
443
450
|
end
|
@@ -496,7 +503,7 @@ module HTTP
|
|
496
503
|
@size = @body.size
|
497
504
|
else
|
498
505
|
@body = Message.create_query_part_str(body)
|
499
|
-
@size = @body.
|
506
|
+
@size = @body.bytesize
|
500
507
|
end
|
501
508
|
end
|
502
509
|
|
@@ -517,7 +524,7 @@ module HTTP
|
|
517
524
|
end
|
518
525
|
|
519
526
|
def dump_chunk(str)
|
520
|
-
dump_chunk_size(str.
|
527
|
+
dump_chunk_size(str.bytesize) + (str + CRLF)
|
521
528
|
end
|
522
529
|
|
523
530
|
def dump_last_chunk
|
@@ -555,10 +562,10 @@ module HTTP
|
|
555
562
|
end
|
556
563
|
elsif @body[-1].is_a?(String)
|
557
564
|
@body[-1] += part.to_s
|
558
|
-
@size += part.to_s.
|
565
|
+
@size += part.to_s.bytesize if @size
|
559
566
|
else
|
560
567
|
@body << part.to_s
|
561
|
-
@size += part.to_s.
|
568
|
+
@size += part.to_s.bytesize if @size
|
562
569
|
end
|
563
570
|
end
|
564
571
|
|
@@ -583,11 +590,21 @@ module HTTP
|
|
583
590
|
}.join("; ")
|
584
591
|
if value.respond_to?(:mime_type)
|
585
592
|
content_type = value.mime_type
|
593
|
+
elsif value.respond_to?(:content_type)
|
594
|
+
content_type = value.content_type
|
586
595
|
else
|
587
|
-
|
596
|
+
path = value.respond_to?(:path) ? value.path : nil
|
597
|
+
content_type = Message.mime_type(path)
|
588
598
|
end
|
589
599
|
headers << %{Content-Disposition: form-data; name="#{attr}"; #{param_str}}
|
590
600
|
headers << %{Content-Type: #{content_type}}
|
601
|
+
elsif attr.is_a?(Hash)
|
602
|
+
h = attr
|
603
|
+
value = h[:content]
|
604
|
+
h.each do |h_key, h_val|
|
605
|
+
headers << %{#{h_key}: #{h_val}} if h_key != :content
|
606
|
+
end
|
607
|
+
remember_pos(value) if Message.file?(value)
|
591
608
|
else
|
592
609
|
headers << %{Content-Disposition: form-data; name="#{attr}"}
|
593
610
|
end
|
@@ -601,7 +618,8 @@ module HTTP
|
|
601
618
|
|
602
619
|
def params_from_file(value)
|
603
620
|
params = {}
|
604
|
-
|
621
|
+
path = value.respond_to?(:path) ? value.path : nil
|
622
|
+
params['filename'] = File.basename(path || '')
|
605
623
|
# Creation time is not available from File::Stat
|
606
624
|
if value.respond_to?(:mtime)
|
607
625
|
params['modification-date'] = value.mtime.rfc822
|
@@ -724,9 +742,9 @@ module HTTP
|
|
724
742
|
end
|
725
743
|
|
726
744
|
# Returns true if the given HTTP version allows keep alive connection.
|
727
|
-
# version::
|
745
|
+
# version:: String
|
728
746
|
def keep_alive_enabled?(version)
|
729
|
-
version >= 1.1
|
747
|
+
version >= '1.1'
|
730
748
|
end
|
731
749
|
|
732
750
|
# Returns true if the given query (or body) has a multiple parameter.
|
@@ -736,15 +754,14 @@ module HTTP
|
|
736
754
|
|
737
755
|
# Returns true if the given object is a File. In HTTPClient, a file is;
|
738
756
|
# * must respond to :read for retrieving String chunks.
|
739
|
-
# * must respond to :path and returns a path for Content-Disposition.
|
740
757
|
# * must respond to :pos and :pos= to rewind for reading.
|
741
758
|
# Rewinding is only needed for following HTTP redirect. Some IO impl
|
742
759
|
# defines :pos= but raises an Exception for pos= such as StringIO
|
743
760
|
# but there's no problem as far as using it for non-following methods
|
744
761
|
# (get/post/etc.)
|
745
762
|
def file?(obj)
|
746
|
-
obj.respond_to?(:read) and obj.respond_to?(:
|
747
|
-
obj.respond_to?(:pos
|
763
|
+
obj.respond_to?(:read) and obj.respond_to?(:pos) and
|
764
|
+
obj.respond_to?(:pos=)
|
748
765
|
end
|
749
766
|
|
750
767
|
def create_query_part_str(query) # :nodoc:
|
@@ -758,7 +775,7 @@ module HTTP
|
|
758
775
|
end
|
759
776
|
|
760
777
|
def escape_query(query) # :nodoc:
|
761
|
-
query.collect { |attr, value|
|
778
|
+
query.sort_by { |attr, value| attr.to_s }.collect { |attr, value|
|
762
779
|
if value.respond_to?(:read)
|
763
780
|
value = value.read
|
764
781
|
end
|
@@ -768,9 +785,36 @@ module HTTP
|
|
768
785
|
|
769
786
|
# from CGI.escape
|
770
787
|
def escape(str) # :nodoc:
|
771
|
-
|
772
|
-
|
773
|
-
|
788
|
+
if defined?(Encoding::ASCII_8BIT)
|
789
|
+
str.dup.force_encoding(Encoding::ASCII_8BIT).gsub(/([^ a-zA-Z0-9_.-]+)/) {
|
790
|
+
'%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
|
791
|
+
}.tr(' ', '+')
|
792
|
+
else
|
793
|
+
str.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
794
|
+
'%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
|
795
|
+
}.tr(' ', '+')
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
799
|
+
# from CGI.parse
|
800
|
+
def parse(query)
|
801
|
+
params = Hash.new([].freeze)
|
802
|
+
query.split(/[&;]/n).each do |pairs|
|
803
|
+
key, value = pairs.split('=',2).collect{|v| unescape(v) }
|
804
|
+
if params.has_key?(key)
|
805
|
+
params[key].push(value)
|
806
|
+
else
|
807
|
+
params[key] = [value]
|
808
|
+
end
|
809
|
+
end
|
810
|
+
params
|
811
|
+
end
|
812
|
+
|
813
|
+
# from CGI.unescape
|
814
|
+
def unescape(string)
|
815
|
+
string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
|
816
|
+
[$1.delete('%')].pack('H*')
|
817
|
+
end
|
774
818
|
end
|
775
819
|
end
|
776
820
|
|
@@ -813,13 +857,24 @@ module HTTP
|
|
813
857
|
@header.body_size = @body.size if @header
|
814
858
|
end
|
815
859
|
|
816
|
-
# Returns HTTP version in a HTTP header.
|
817
|
-
def
|
860
|
+
# Returns HTTP version in a HTTP header. String.
|
861
|
+
def http_version
|
818
862
|
@header.http_version
|
819
863
|
end
|
820
864
|
|
821
|
-
# Sets HTTP version in a HTTP header.
|
865
|
+
# Sets HTTP version in a HTTP header. String.
|
866
|
+
def http_version=(http_version)
|
867
|
+
@header.http_version = http_version
|
868
|
+
end
|
869
|
+
|
870
|
+
VERSION_WARNING = 'Message#version (Float) is deprecated. Use Message#http_version (String) instead.'
|
871
|
+
def version
|
872
|
+
warn(VERSION_WARNING)
|
873
|
+
@header.http_version.to_f
|
874
|
+
end
|
875
|
+
|
822
876
|
def version=(version)
|
877
|
+
warn(VERSION_WARNING)
|
823
878
|
@header.http_version = version
|
824
879
|
end
|
825
880
|
|
data/lib/httpclient/session.rb
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
require 'socket'
|
14
14
|
require 'thread'
|
15
15
|
require 'stringio'
|
16
|
+
require 'zlib'
|
16
17
|
|
17
18
|
require 'httpclient/timeout'
|
18
19
|
require 'httpclient/ssl_config'
|
@@ -106,6 +107,8 @@ class HTTPClient
|
|
106
107
|
|
107
108
|
attr_reader :test_loopback_http_response
|
108
109
|
|
110
|
+
attr_accessor :transparent_gzip_decompression
|
111
|
+
|
109
112
|
def initialize(client)
|
110
113
|
@client = client
|
111
114
|
@proxy = client.proxy
|
@@ -128,6 +131,7 @@ class HTTPClient
|
|
128
131
|
@ssl_config = nil
|
129
132
|
@test_loopback_http_response = []
|
130
133
|
|
134
|
+
@transparent_gzip_decompression = false
|
131
135
|
@sess_pool = []
|
132
136
|
@sess_pool_mutex = Mutex.new
|
133
137
|
end
|
@@ -165,6 +169,16 @@ class HTTPClient
|
|
165
169
|
add_cached_session(sess)
|
166
170
|
end
|
167
171
|
|
172
|
+
def invalidate(site)
|
173
|
+
@sess_pool_mutex.synchronize do
|
174
|
+
@sess_pool.each do |sess|
|
175
|
+
if sess.dest == site
|
176
|
+
sess.invalidate
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
168
182
|
private
|
169
183
|
|
170
184
|
def open(uri, via_proxy = false)
|
@@ -185,6 +199,7 @@ class HTTPClient
|
|
185
199
|
sess.ssl_config = @ssl_config
|
186
200
|
sess.debug_dev = @debug_dev
|
187
201
|
sess.test_loopback_http_response = @test_loopback_http_response
|
202
|
+
sess.transparent_gzip_decompression = @transparent_gzip_decompression
|
188
203
|
end
|
189
204
|
sess
|
190
205
|
end
|
@@ -198,6 +213,7 @@ class HTTPClient
|
|
198
213
|
@sess_pool.clear
|
199
214
|
end
|
200
215
|
|
216
|
+
# This method might not work as you expected...
|
201
217
|
def close(dest)
|
202
218
|
if cached = get_cached_session(dest)
|
203
219
|
cached.close
|
@@ -212,7 +228,9 @@ class HTTPClient
|
|
212
228
|
@sess_pool_mutex.synchronize do
|
213
229
|
new_pool = []
|
214
230
|
@sess_pool.each do |s|
|
215
|
-
if s.
|
231
|
+
if s.invalidated?
|
232
|
+
s.close # close & remove from the pool
|
233
|
+
elsif !cached && s.dest.match(uri)
|
216
234
|
cached = s
|
217
235
|
else
|
218
236
|
new_pool << s
|
@@ -435,7 +453,15 @@ class HTTPClient
|
|
435
453
|
private
|
436
454
|
|
437
455
|
def debug(str)
|
438
|
-
|
456
|
+
if str && @debug_dev
|
457
|
+
if str.index("\0")
|
458
|
+
require 'hexdump'
|
459
|
+
str.force_encoding('BINARY') if str.respond_to?(:force_encoding)
|
460
|
+
@debug_dev << HexDump.encode(str).join("\n")
|
461
|
+
else
|
462
|
+
@debug_dev << str
|
463
|
+
end
|
464
|
+
end
|
439
465
|
end
|
440
466
|
end
|
441
467
|
|
@@ -459,6 +485,7 @@ class HTTPClient
|
|
459
485
|
# Manages a HTTP session with a Site.
|
460
486
|
class Session
|
461
487
|
include HTTPClient::Timeout
|
488
|
+
include Util
|
462
489
|
|
463
490
|
# Destination site
|
464
491
|
attr_reader :dest
|
@@ -482,9 +509,12 @@ class HTTPClient
|
|
482
509
|
attr_reader :ssl_peer_cert
|
483
510
|
attr_accessor :test_loopback_http_response
|
484
511
|
|
512
|
+
attr_accessor :transparent_gzip_decompression
|
513
|
+
|
485
514
|
def initialize(client, dest, agent_name, from)
|
486
515
|
@client = client
|
487
516
|
@dest = dest
|
517
|
+
@invalidated = false
|
488
518
|
@proxy = nil
|
489
519
|
@socket_sync = true
|
490
520
|
@requested_version = nil
|
@@ -515,12 +545,15 @@ class HTTPClient
|
|
515
545
|
|
516
546
|
@socket = nil
|
517
547
|
@readbuf = nil
|
548
|
+
|
549
|
+
@transparent_gzip_decompression = false
|
518
550
|
end
|
519
551
|
|
520
552
|
# Send a request to the server
|
521
553
|
def query(req)
|
522
554
|
connect if @state == :INIT
|
523
|
-
|
555
|
+
# Use absolute URI (not absolute path) iif via proxy AND not HTTPS.
|
556
|
+
req.header.request_absolute_uri = !@proxy.nil? and !https?(@dest)
|
524
557
|
begin
|
525
558
|
timeout(@send_timeout, SendTimeoutError) do
|
526
559
|
set_header(req)
|
@@ -529,14 +562,16 @@ class HTTPClient
|
|
529
562
|
@socket.flush unless @socket_sync
|
530
563
|
end
|
531
564
|
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
|
565
|
+
# JRuby can raise IOError instead of ECONNRESET for now
|
532
566
|
close
|
533
|
-
raise KeepAliveDisconnected.new
|
567
|
+
raise KeepAliveDisconnected.new(self)
|
534
568
|
rescue HTTPClient::TimeoutError
|
535
569
|
close
|
536
570
|
raise
|
537
571
|
rescue
|
572
|
+
close
|
538
573
|
if SSLEnabled and $!.is_a?(OpenSSL::SSL::SSLError)
|
539
|
-
raise KeepAliveDisconnected.new
|
574
|
+
raise KeepAliveDisconnected.new(self)
|
540
575
|
else
|
541
576
|
raise
|
542
577
|
end
|
@@ -560,6 +595,14 @@ class HTTPClient
|
|
560
595
|
@state == :INIT
|
561
596
|
end
|
562
597
|
|
598
|
+
def invalidate
|
599
|
+
@invalidated = true
|
600
|
+
end
|
601
|
+
|
602
|
+
def invalidated?
|
603
|
+
@invalidated
|
604
|
+
end
|
605
|
+
|
563
606
|
def get_header
|
564
607
|
begin
|
565
608
|
if @state != :META
|
@@ -585,6 +628,19 @@ class HTTPClient
|
|
585
628
|
begin
|
586
629
|
read_header if @state == :META
|
587
630
|
return nil if @state != :DATA
|
631
|
+
if @gzipped and @transparent_gzip_decompression
|
632
|
+
# zlib itself has a functionality to decompress gzip stream.
|
633
|
+
# - zlib 1.2.5 Manual
|
634
|
+
# http://www.zlib.net/manual.html#Advanced
|
635
|
+
# > windowBits can also be greater than 15 for optional gzip decoding. Add 32 to
|
636
|
+
# > windowBits to enable zlib and gzip decoding with automatic header detection,
|
637
|
+
# > or add 16 to decode only the gzip format
|
638
|
+
inflate_stream = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
|
639
|
+
original_block = block
|
640
|
+
block = Proc.new { |buf|
|
641
|
+
original_block.call(inflate_stream.inflate(buf))
|
642
|
+
}
|
643
|
+
end
|
588
644
|
if @chunked
|
589
645
|
read_body_chunked(&block)
|
590
646
|
elsif @content_length
|
@@ -611,7 +667,7 @@ class HTTPClient
|
|
611
667
|
def set_header(req)
|
612
668
|
if @requested_version
|
613
669
|
if /^(?:HTTP\/|)(\d+.\d+)$/ =~ @requested_version
|
614
|
-
req.
|
670
|
+
req.http_version = $1
|
615
671
|
end
|
616
672
|
end
|
617
673
|
if @agent_name
|
@@ -620,6 +676,9 @@ class HTTPClient
|
|
620
676
|
if @from
|
621
677
|
req.header.set('From', @from)
|
622
678
|
end
|
679
|
+
if @transparent_gzip_decompression
|
680
|
+
req.header.set('Accept-Encoding', 'gzip,deflate')
|
681
|
+
end
|
623
682
|
req.header.set('Date', Time.now.httpdate)
|
624
683
|
end
|
625
684
|
|
@@ -630,7 +689,7 @@ class HTTPClient
|
|
630
689
|
begin
|
631
690
|
timeout(@connect_timeout, ConnectTimeoutError) do
|
632
691
|
@socket = create_socket(site)
|
633
|
-
if @dest
|
692
|
+
if https?(@dest)
|
634
693
|
if @socket.is_a?(LoopBackSocket)
|
635
694
|
connect_ssl_proxy(@socket, URI.parse(@dest.to_s)) if @proxy
|
636
695
|
else
|
@@ -704,7 +763,7 @@ class HTTPClient
|
|
704
763
|
@socket.flush unless @socket_sync
|
705
764
|
res = HTTP::Message.new_response('')
|
706
765
|
parse_header
|
707
|
-
res.
|
766
|
+
res.http_version, res.status, res.reason = @version, @status, @reason
|
708
767
|
@headers.each do |key, value|
|
709
768
|
res.header.set(key, value)
|
710
769
|
end
|
@@ -723,14 +782,13 @@ class HTTPClient
|
|
723
782
|
def read_header
|
724
783
|
@content_length = nil
|
725
784
|
@chunked = false
|
785
|
+
@gzipped = false
|
726
786
|
@chunk_length = 0
|
727
787
|
parse_header
|
728
|
-
|
729
|
-
# Head of the request has been parsed.
|
788
|
+
# Header of the request has been parsed.
|
730
789
|
@state = :DATA
|
731
790
|
req = @requests.shift
|
732
|
-
|
733
|
-
if req.header.request_method == 'HEAD'
|
791
|
+
if req.header.request_method == 'HEAD' or no_message_body?(@status)
|
734
792
|
@content_length = 0
|
735
793
|
if @next_connection
|
736
794
|
@state = :WAIT
|
@@ -744,11 +802,19 @@ class HTTPClient
|
|
744
802
|
StatusParseRegexp = %r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?\r?\n\z)
|
745
803
|
def parse_header
|
746
804
|
timeout(@receive_timeout, ReceiveTimeoutError) do
|
805
|
+
initial_line = nil
|
747
806
|
begin
|
748
807
|
initial_line = @socket.gets("\n")
|
749
808
|
if initial_line.nil?
|
750
|
-
|
809
|
+
close
|
810
|
+
raise KeepAliveDisconnected.new(self)
|
751
811
|
end
|
812
|
+
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError
|
813
|
+
# JRuby can raise IOError instead of ECONNRESET for now
|
814
|
+
close
|
815
|
+
raise KeepAliveDisconnected.new(self)
|
816
|
+
end
|
817
|
+
begin
|
752
818
|
if StatusParseRegexp !~ initial_line
|
753
819
|
@version = '0.9'
|
754
820
|
@status = nil
|
@@ -759,7 +825,7 @@ class HTTPClient
|
|
759
825
|
break
|
760
826
|
end
|
761
827
|
@version, @status, @reason = $1, $2.to_i, $3
|
762
|
-
@next_connection = HTTP::Message.keep_alive_enabled?(@version
|
828
|
+
@next_connection = HTTP::Message.keep_alive_enabled?(@version)
|
763
829
|
@headers = []
|
764
830
|
while true
|
765
831
|
line = @socket.gets("\n")
|
@@ -768,18 +834,32 @@ class HTTPClient
|
|
768
834
|
end
|
769
835
|
line.chomp!
|
770
836
|
break if line.empty?
|
771
|
-
|
772
|
-
|
773
|
-
|
837
|
+
if line[0] == ?\ or line[0] == ?\t
|
838
|
+
last = @headers.last[1]
|
839
|
+
last << ' ' unless last.empty?
|
840
|
+
last << line.strip
|
841
|
+
else
|
842
|
+
key, value = line.strip.split(/\s*:\s*/, 2)
|
843
|
+
parse_keepalive_header(key, value)
|
844
|
+
@headers << [key, value]
|
845
|
+
end
|
774
846
|
end
|
775
847
|
end while (@version == '1.1' && @status == 100)
|
776
848
|
end
|
777
849
|
end
|
778
850
|
|
851
|
+
def no_message_body?(status)
|
852
|
+
!status.nil? && # HTTP/0.9
|
853
|
+
((status >= 100 && status < 200) || status == 204 || status == 304)
|
854
|
+
end
|
855
|
+
|
779
856
|
def parse_keepalive_header(key, value)
|
780
857
|
key = key.downcase
|
781
858
|
if key == 'content-length'
|
782
859
|
@content_length = value.to_i
|
860
|
+
elsif key == 'content-encoding' and ( value.downcase == 'gzip' or
|
861
|
+
value.downcase == 'x-gzip' or value.downcase == 'deflate' )
|
862
|
+
@gzipped = true
|
783
863
|
elsif key == 'transfer-encoding' and value.downcase == 'chunked'
|
784
864
|
@chunked = true
|
785
865
|
@chunk_length = 0
|
@@ -806,8 +886,8 @@ class HTTPClient
|
|
806
886
|
buf = nil
|
807
887
|
end
|
808
888
|
end
|
809
|
-
if buf && buf.
|
810
|
-
@content_length -= buf.
|
889
|
+
if buf && buf.bytesize > 0
|
890
|
+
@content_length -= buf.bytesize
|
811
891
|
yield buf
|
812
892
|
else
|
813
893
|
@content_length = 0
|
@@ -837,7 +917,7 @@ class HTTPClient
|
|
837
917
|
end
|
838
918
|
|
839
919
|
def read_body_rest
|
840
|
-
if @readbuf and @readbuf.
|
920
|
+
if @readbuf and @readbuf.bytesize > 0
|
841
921
|
yield @readbuf
|
842
922
|
@readbuf = nil
|
843
923
|
end
|
@@ -850,7 +930,7 @@ class HTTPClient
|
|
850
930
|
buf = nil
|
851
931
|
end
|
852
932
|
end
|
853
|
-
if buf && buf.
|
933
|
+
if buf && buf.bytesize > 0
|
854
934
|
yield buf
|
855
935
|
else
|
856
936
|
return
|