em-http-request 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of em-http-request might be problematic. Click here for more details.

data/.gitignore CHANGED
@@ -4,3 +4,5 @@ Makefile
4
4
  mkmf.log
5
5
  Gemfile.lock
6
6
  misc
7
+ gems/
8
+ vendor/
data/README.md CHANGED
@@ -46,9 +46,11 @@ Several higher-order Ruby projects have incorporated em-http and other Ruby HTTP
46
46
 
47
47
  ## Other libraries & applications using EM-HTTP
48
48
 
49
+ - [VMWare CloudFoundry](https://github.com/cloudfoundry) - The open platform-as-a-service project
50
+ - [PubSubHubbub](https://github.com/igrigorik/PubSubHubbub) - Asynchronous PubSubHubbub ruby client
51
+ - [em-net-http](https://github.com/jfairbairn/em-net-http) - Monkeypatching Net::HTTP to play ball with EventMachine
49
52
  - [chirpstream](https://github.com/joshbuddy/chirpstream) - EM client for Twitters Chirpstream API
50
53
  - [rsolr-async](https://github.com/mwmitchell/rsolr-async) - An asynchronus connection adapter for RSolr
51
- - [PubSubHubbub](https://github.com/igrigorik/PubSubHubbub) - Asynchronous PubSubHubbub ruby client
52
54
  - [Firering](https://github.com/EmmanuelOga/firering) - Eventmachine powered Campfire API
53
55
  - [RDaneel](https://github.com/hasmanydevelopers/RDaneel) - Ruby crawler which respects robots.txt
54
56
  - [em-eventsource](https://github.com/AF83/em-eventsource) - EventSource client for EventMachine
@@ -57,4 +59,4 @@ Several higher-order Ruby projects have incorporated em-http and other Ruby HTTP
57
59
 
58
60
  ### License
59
61
 
60
- (MIT License) - Copyright (c) 2011 Ilya Grigorik
62
+ (MIT License) - Copyright (c) 2011 Ilya Grigorik
@@ -1,33 +1,33 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "em-http/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "em-http-request"
7
- s.version = EventMachine::HttpRequest::VERSION
8
-
9
- s.platform = Gem::Platform::RUBY
10
- s.authors = ["Ilya Grigorik"]
11
- s.email = ["ilya@igvita.com"]
12
- s.homepage = "http://github.com/igrigorik/em-http-request"
13
- s.summary = "EventMachine based, async HTTP Request client"
14
- s.description = s.summary
15
- s.rubyforge_project = "em-http-request"
16
-
17
- s.add_dependency "eventmachine", ">= 1.0.0.beta.3"
18
- s.add_dependency "addressable", ">= 2.2.3"
19
- s.add_dependency "http_parser.rb", ">= 0.5.2"
20
- s.add_dependency "em-socksify"
21
-
22
- s.add_development_dependency "rspec"
23
- s.add_development_dependency "rake"
24
- s.add_development_dependency "rack"
25
- s.add_development_dependency "yajl-ruby"
26
- s.add_development_dependency "cookiejar"
27
- s.add_development_dependency "mongrel", "~> 1.2.0.pre2"
28
-
29
- s.files = `git ls-files`.split("\n")
30
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
31
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
32
- s.require_paths = ["lib"]
33
- end
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "em-http/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "em-http-request"
7
+ s.version = EventMachine::HttpRequest::VERSION
8
+
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Ilya Grigorik"]
11
+ s.email = ["ilya@igvita.com"]
12
+ s.homepage = "http://github.com/igrigorik/em-http-request"
13
+ s.summary = "EventMachine based, async HTTP Request client"
14
+ s.description = s.summary
15
+ s.rubyforge_project = "em-http-request"
16
+
17
+ s.add_dependency "eventmachine", ">= 1.0.0.beta.4"
18
+ s.add_dependency "addressable", ">= 2.2.3"
19
+ s.add_dependency "http_parser.rb", ">= 0.5.3"
20
+ s.add_dependency "em-socksify"
21
+ s.add_dependency "cookiejar"
22
+
23
+ s.add_development_dependency "rspec"
24
+ s.add_development_dependency "rake"
25
+ s.add_development_dependency "rack"
26
+ s.add_development_dependency "yajl-ruby"
27
+ s.add_development_dependency "mongrel", "~> 1.2.0.pre2"
28
+
29
+ s.files = `git ls-files`.split("\n")
30
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
31
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
32
+ s.require_paths = ["lib"]
33
+ end
@@ -1,47 +1,51 @@
1
- $: << 'lib' << '../lib'
2
-
3
- require 'eventmachine'
4
- require 'em-http'
5
- require 'fiber'
6
-
7
- # Using Fibers in Ruby 1.9 to simulate blocking IO / IO scheduling
8
- # while using the async EventMachine API's
9
-
10
- def async_fetch(url)
11
- f = Fiber.current
12
- http = EventMachine::HttpRequest.new(url).get :timeout => 10
13
-
14
- http.callback { f.resume(http) }
15
- http.errback { f.resume(http) }
16
-
17
- Fiber.yield
18
-
19
- if http.error
20
- p [:HTTP_ERROR, http.error]
21
- end
22
-
23
- http
24
- end
25
-
26
- EventMachine.run do
27
- Fiber.new{
28
-
29
- puts "Setting up HTTP request #1"
30
- data = async_fetch('http://0.0.0.0/')
31
- puts "Fetched page #1: #{data.response_header.status}"
32
-
33
- puts "Setting up HTTP request #2"
34
- data = async_fetch('http://www.yahoo.com/')
35
- puts "Fetched page #2: #{data.response_header.status}"
36
-
37
- EventMachine.stop
38
- }.resume
39
- end
40
-
41
- puts "Done"
42
-
43
- # Setting up HTTP request #1
44
- # Fetched page #1: 302
45
- # Setting up HTTP request #2
46
- # Fetched page #2: 200
47
- # Done
1
+ $: << 'lib' << '../lib'
2
+
3
+ require 'eventmachine'
4
+ require 'em-http'
5
+ require 'fiber'
6
+
7
+ # Using Fibers in Ruby 1.9 to simulate blocking IO / IO scheduling
8
+ # while using the async EventMachine API's
9
+
10
+ def async_fetch(url)
11
+ f = Fiber.current
12
+ http = EventMachine::HttpRequest.new(url).get :timeout => 10
13
+
14
+ http.callback { f.resume(http) }
15
+ http.errback { f.resume(http) }
16
+
17
+ Fiber.yield
18
+
19
+ if http.error
20
+ p [:HTTP_ERROR, http.error]
21
+ end
22
+
23
+ http
24
+ end
25
+
26
+ EventMachine.run do
27
+ Fiber.new{
28
+
29
+ puts "Setting up HTTP request #1"
30
+ data = async_fetch('http://0.0.0.0/')
31
+ puts "Fetched page #1: #{data.response_header.status}"
32
+
33
+ puts "Setting up HTTP request #2"
34
+ data = async_fetch('http://www.yahoo.com/')
35
+ puts "Fetched page #2: #{data.response_header.status}"
36
+
37
+ puts "Setting up HTTP request #3"
38
+ data = async_fetch('http://non-existing.domain/')
39
+ puts "Fetched page #3: #{data.response_header.status}"
40
+
41
+ EventMachine.stop
42
+ }.resume
43
+ end
44
+
45
+ puts "Done"
46
+
47
+ # Setting up HTTP request #1
48
+ # Fetched page #1: 302
49
+ # Setting up HTTP request #2
50
+ # Fetched page #2: 200
51
+ # Done
@@ -1,275 +1,307 @@
1
- module EventMachine
2
-
3
- class HttpClient
4
- include Deferrable
5
- include HttpEncoding
6
- include HttpStatus
7
-
8
- TRANSFER_ENCODING="TRANSFER_ENCODING"
9
- CONTENT_ENCODING="CONTENT_ENCODING"
10
- CONTENT_LENGTH="CONTENT_LENGTH"
11
- CONTENT_TYPE="CONTENT_TYPE"
12
- LAST_MODIFIED="LAST_MODIFIED"
13
- KEEP_ALIVE="CONNECTION"
14
- SET_COOKIE="SET_COOKIE"
15
- LOCATION="LOCATION"
16
- HOST="HOST"
17
- ETAG="ETAG"
18
-
19
- CRLF="\r\n"
20
-
21
- attr_accessor :state, :response
22
- attr_reader :response_header, :error, :content_charset, :req, :cookies
23
-
24
- def initialize(conn, options)
25
- @conn = conn
26
- @req = options
27
-
28
- @stream = nil
29
- @headers = nil
30
- @cookies = []
31
-
32
- reset!
33
- end
34
-
35
- def reset!
36
- @response_header = HttpResponseHeader.new
37
- @state = :response_header
38
-
39
- @response = ''
40
- @error = nil
41
- @content_decoder = nil
42
- @content_charset = nil
43
- end
44
-
45
- def last_effective_url; @req.uri; end
46
- def redirects; @req.followed; end
47
- def peer; @conn.peer; end
48
-
49
- def connection_completed
50
- @state = :response_header
51
-
52
- head, body = build_request, @req.body
53
- @conn.middleware.each do |m|
54
- head, body = m.request(self, head, body) if m.respond_to?(:request)
55
- end
56
-
57
- send_request(head, body)
58
- end
59
-
60
- def on_request_complete
61
- begin
62
- @content_decoder.finalize! if @content_decoder
63
- rescue HttpDecoders::DecoderError
64
- on_error "Content-decoder error"
65
- end
66
-
67
- unbind
68
- end
69
-
70
- def continue?
71
- @response_header.status == 100 && (@req.method == 'POST' || @req.method == 'PUT')
72
- end
73
-
74
- def finished?
75
- @state == :finished || (@state == :body && @response_header.content_length.nil?)
76
- end
77
-
78
- def redirect?
79
- @response_header.location && @req.follow_redirect?
80
- end
81
-
82
- def unbind(reason = nil)
83
- if finished?
84
- if redirect?
85
-
86
- begin
87
- @conn.middleware.each do |m|
88
- m.response(self) if m.respond_to?(:response)
89
- end
90
-
91
- # one of the injected middlewares could have changed
92
- # our redirect settings, check if we still want to
93
- # follow the location header
94
- if redirect?
95
- @req.followed += 1
96
- @req.set_uri(@response_header.location)
97
- @conn.redirect(self)
98
- else
99
- succeed(self)
100
- end
101
-
102
- rescue Exception => e
103
- on_error(e.message)
104
- end
105
- else
106
- succeed(self)
107
- end
108
-
109
- else
110
- on_error(reason)
111
- end
112
- end
113
-
114
- def on_error(msg = nil)
115
- @error = msg
116
- fail(self)
117
- end
118
- alias :close :on_error
119
-
120
- def stream(&blk); @stream = blk; end
121
- def headers(&blk); @headers = blk; end
122
-
123
- def normalize_body(body)
124
- body.is_a?(Hash) ? form_encode_body(body) : body
125
- end
126
-
127
- def build_request
128
- head = @req.headers ? munge_header_keys(@req.headers) : {}
129
- proxy = @req.proxy
130
-
131
- if @req.http_proxy?
132
- head['proxy-authorization'] = @req.proxy[:authorization] if @req.proxy[:authorization]
133
- end
134
-
135
- # Set the cookie header if provided
136
- if cookie = head['cookie']
137
- @cookies << encode_cookie(cookie) if cookie
138
- end
139
- head['cookie'] = @cookies.compact.uniq.join("; ").squeeze(";") unless @cookies.empty?
140
-
141
- # Set connection close unless keepalive
142
- if !@req.keepalive
143
- head['connection'] = 'close'
144
- end
145
-
146
- # Set the Host header if it hasn't been specified already
147
- head['host'] ||= encode_host
148
-
149
- # Set the User-Agent if it hasn't been specified
150
- head['user-agent'] ||= "EventMachine HttpClient"
151
-
152
- head
153
- end
154
-
155
- def send_request(head, body)
156
- body = normalize_body(body)
157
- file = @req.file
158
- query = @req.query
159
-
160
- # Set the Content-Length if file is given
161
- head['content-length'] = File.size(file) if file
162
-
163
- # Set the Content-Length if body is given
164
- head['content-length'] = body.bytesize if body
165
-
166
- # Set content-type header if missing and body is a Ruby hash
167
- if not head['content-type'] and @req.body.is_a? Hash
168
- head['content-type'] = 'application/x-www-form-urlencoded'
169
- end
170
-
171
- request_header ||= encode_request(@req.method, @req.uri, query, @conn.connopts.proxy)
172
- request_header << encode_headers(head)
173
- request_header << CRLF
174
- @conn.send_data request_header
175
-
176
- if body
177
- @conn.send_data body
178
- elsif @req.file
179
- @conn.stream_file_data @req.file, :http_chunks => false
180
- end
181
- end
182
-
183
- def on_body_data(data)
184
- if @content_decoder
185
- begin
186
- @content_decoder << data
187
- rescue HttpDecoders::DecoderError
188
- on_error "Content-decoder error"
189
- end
190
- else
191
- on_decoded_body_data(data)
192
- end
193
- end
194
-
195
- def on_decoded_body_data(data)
196
- data.force_encoding @content_charset if @content_charset
197
- if @stream
198
- @stream.call(data)
199
- else
200
- @response << data
201
- end
202
- end
203
-
204
- def parse_response_header(header, version, status)
205
- header.each do |key, val|
206
- @response_header[key.upcase.gsub('-','_')] = val
207
- end
208
-
209
- @response_header.http_version = version.join('.')
210
- @response_header.http_status = status
211
- @response_header.http_reason = CODE[status] || 'unknown'
212
-
213
- # invoke headers callback after full parse
214
- # if one is specified by the user
215
- @headers.call(@response_header) if @headers
216
-
217
- unless @response_header.http_status and @response_header.http_reason
218
- @state = :invalid
219
- on_error "no HTTP response"
220
- return
221
- end
222
-
223
- # add set-cookie's to cookie list
224
- @cookies << @response_header.cookie if @response_header.cookie && @req.pass_cookies
225
-
226
- # correct location header - some servers will incorrectly give a relative URI
227
- if @response_header.location
228
- begin
229
- location = Addressable::URI.parse(@response_header.location)
230
-
231
- if location.relative?
232
- location = @req.uri.join(location)
233
- @response_header[LOCATION] = location.to_s
234
- else
235
- # if redirect is to an absolute url, check for correct URI structure
236
- raise if location.host.nil?
237
- end
238
-
239
- rescue
240
- on_error "Location header format error"
241
- return
242
- end
243
- end
244
-
245
- # Fire callbacks immediately after recieving header requests
246
- # if the request method is HEAD. In case of a redirect, terminate
247
- # current connection and reinitialize the process.
248
- if @req.method == "HEAD"
249
- @state = :finished
250
- return
251
- end
252
-
253
- if @response_header.chunked_encoding?
254
- @state = :chunk_header
255
- elsif @response_header.content_length
256
- @state = :body
257
- else
258
- @state = :body
259
- end
260
-
261
- if @req.decoding && decoder_class = HttpDecoders.decoder_for_encoding(response_header[CONTENT_ENCODING])
262
- begin
263
- @content_decoder = decoder_class.new do |s| on_decoded_body_data(s) end
264
- rescue HttpDecoders::DecoderError
265
- on_error "Content-decoder error"
266
- end
267
- end
268
-
269
- if String.method_defined?(:force_encoding) && /;\s*charset=\s*(.+?)\s*(;|$)/.match(response_header[CONTENT_TYPE])
270
- @content_charset = Encoding.find($1.gsub(/^\"|\"$/, '')) rescue Encoding.default_external
271
- end
272
- end
273
-
274
- end
275
- end
1
+ require 'cookiejar'
2
+
3
+ module EventMachine
4
+
5
+
6
+ class HttpClient
7
+ include Deferrable
8
+ include HttpEncoding
9
+ include HttpStatus
10
+
11
+ TRANSFER_ENCODING="TRANSFER_ENCODING"
12
+ CONTENT_ENCODING="CONTENT_ENCODING"
13
+ CONTENT_LENGTH="CONTENT_LENGTH"
14
+ CONTENT_TYPE="CONTENT_TYPE"
15
+ LAST_MODIFIED="LAST_MODIFIED"
16
+ KEEP_ALIVE="CONNECTION"
17
+ SET_COOKIE="SET_COOKIE"
18
+ LOCATION="LOCATION"
19
+ HOST="HOST"
20
+ ETAG="ETAG"
21
+
22
+ CRLF="\r\n"
23
+
24
+ attr_accessor :state, :response
25
+ attr_reader :response_header, :error, :content_charset, :req, :cookies
26
+
27
+ def initialize(conn, options)
28
+ @conn = conn
29
+ @req = options
30
+
31
+ @stream = nil
32
+ @headers = nil
33
+ @cookies = []
34
+ @cookiejar = CookieJar.new
35
+
36
+ reset!
37
+ end
38
+
39
+ def reset!
40
+ @response_header = HttpResponseHeader.new
41
+ @state = :response_header
42
+
43
+ @response = ''
44
+ @error = nil
45
+ @content_decoder = nil
46
+ @content_charset = nil
47
+ end
48
+
49
+ def last_effective_url; @req.uri; end
50
+ def redirects; @req.followed; end
51
+ def peer; @conn.peer; end
52
+
53
+ def connection_completed
54
+ @state = :response_header
55
+
56
+ head, body = build_request, @req.body
57
+ @conn.middleware.each do |m|
58
+ head, body = m.request(self, head, body) if m.respond_to?(:request)
59
+ end
60
+
61
+ send_request(head, body)
62
+ end
63
+
64
+ def on_request_complete
65
+ begin
66
+ @content_decoder.finalize! if @content_decoder
67
+ rescue HttpDecoders::DecoderError
68
+ on_error "Content-decoder error"
69
+ end
70
+
71
+ unbind
72
+ end
73
+
74
+ def continue?
75
+ @response_header.status == 100 && (@req.method == 'POST' || @req.method == 'PUT')
76
+ end
77
+
78
+ def finished?
79
+ @state == :finished || (@state == :body && @response_header.content_length.nil?)
80
+ end
81
+
82
+ def redirect?
83
+ @response_header.location && @req.follow_redirect?
84
+ end
85
+
86
+ def unbind(reason = nil)
87
+ if finished?
88
+ if redirect?
89
+
90
+ begin
91
+ @conn.middleware.each do |m|
92
+ m.response(self) if m.respond_to?(:response)
93
+ end
94
+
95
+ # one of the injected middlewares could have changed
96
+ # our redirect settings, check if we still want to
97
+ # follow the location header
98
+ if redirect?
99
+ @req.followed += 1
100
+
101
+ @cookies.clear
102
+ @cookies = @cookiejar.get(@response_header.location).map(&:to_s) if @req.pass_cookies
103
+ @req.set_uri(@response_header.location)
104
+ @conn.redirect(self)
105
+ else
106
+ succeed(self)
107
+ end
108
+
109
+ rescue Exception => e
110
+ on_error(e.message)
111
+ end
112
+ else
113
+ succeed(self)
114
+ end
115
+
116
+ else
117
+ on_error(reason)
118
+ end
119
+ end
120
+
121
+ def on_error(msg = nil)
122
+ @error = msg
123
+ fail(self)
124
+ end
125
+ alias :close :on_error
126
+
127
+ def stream(&blk); @stream = blk; end
128
+ def headers(&blk); @headers = blk; end
129
+
130
+ def normalize_body(body)
131
+ body.is_a?(Hash) ? form_encode_body(body) : body
132
+ end
133
+
134
+ def build_request
135
+ head = @req.headers ? munge_header_keys(@req.headers) : {}
136
+ proxy = @req.proxy
137
+
138
+ if @req.http_proxy?
139
+ head['proxy-authorization'] = @req.proxy[:authorization] if @req.proxy[:authorization]
140
+ end
141
+
142
+ # Set the cookie header if provided
143
+ if cookie = head['cookie']
144
+ @cookies << encode_cookie(cookie) if cookie
145
+ end
146
+ head['cookie'] = @cookies.compact.uniq.join("; ").squeeze(";") unless @cookies.empty?
147
+
148
+ # Set connection close unless keepalive
149
+ if !@req.keepalive
150
+ head['connection'] = 'close'
151
+ end
152
+
153
+ # Set the Host header if it hasn't been specified already
154
+ head['host'] ||= encode_host
155
+
156
+ # Set the User-Agent if it hasn't been specified
157
+ head['user-agent'] ||= "EventMachine HttpClient"
158
+
159
+ head
160
+ end
161
+
162
+ def send_request(head, body)
163
+ body = normalize_body(body)
164
+ file = @req.file
165
+ query = @req.query
166
+
167
+ # Set the Content-Length if file is given
168
+ head['content-length'] = File.size(file) if file
169
+
170
+ # Set the Content-Length if body is given,
171
+ # or we're doing an empty post or put
172
+ if body
173
+ head['content-length'] = body.bytesize
174
+ elsif @req.method == 'POST' or @req.method == 'PUT'
175
+ # wont happen if body is set and we already set content-length above
176
+ head['content-length'] = 0
177
+ end
178
+
179
+ # Set content-type header if missing and body is a Ruby hash
180
+ if not head['content-type'] and @req.body.is_a? Hash
181
+ head['content-type'] = 'application/x-www-form-urlencoded'
182
+ end
183
+
184
+ request_header ||= encode_request(@req.method, @req.uri, query, @conn.connopts.proxy)
185
+ request_header << encode_headers(head)
186
+ request_header << CRLF
187
+ @conn.send_data request_header
188
+
189
+ if body
190
+ @conn.send_data body
191
+ elsif @req.file
192
+ @conn.stream_file_data @req.file, :http_chunks => false
193
+ end
194
+ end
195
+
196
+ def on_body_data(data)
197
+ if @content_decoder
198
+ begin
199
+ @content_decoder << data
200
+ rescue HttpDecoders::DecoderError
201
+ on_error "Content-decoder error"
202
+ end
203
+ else
204
+ on_decoded_body_data(data)
205
+ end
206
+ end
207
+
208
+ def on_decoded_body_data(data)
209
+ data.force_encoding @content_charset if @content_charset
210
+ if @stream
211
+ @stream.call(data)
212
+ else
213
+ @response << data
214
+ end
215
+ end
216
+
217
+ def parse_response_header(header, version, status)
218
+ header.each do |key, val|
219
+ @response_header[key.upcase.gsub('-','_')] = val
220
+ end
221
+
222
+ @response_header.http_version = version.join('.')
223
+ @response_header.http_status = status
224
+ @response_header.http_reason = CODE[status] || 'unknown'
225
+
226
+ # invoke headers callback after full parse
227
+ # if one is specified by the user
228
+ @headers.call(@response_header) if @headers
229
+
230
+ unless @response_header.http_status and @response_header.http_reason
231
+ @state = :invalid
232
+ on_error "no HTTP response"
233
+ return
234
+ end
235
+
236
+ # add set-cookie's to cookie list
237
+ if @response_header.cookie && @req.pass_cookies
238
+ [@response_header.cookie].flatten.each {|cookie| @cookiejar.set(cookie, @req.uri)}
239
+ end
240
+
241
+ # correct location header - some servers will incorrectly give a relative URI
242
+ if @response_header.location
243
+ begin
244
+ location = Addressable::URI.parse(@response_header.location)
245
+
246
+ if location.relative?
247
+ location = @req.uri.join(location)
248
+ @response_header[LOCATION] = location.to_s
249
+ else
250
+ # if redirect is to an absolute url, check for correct URI structure
251
+ raise if location.host.nil?
252
+ end
253
+
254
+ rescue
255
+ on_error "Location header format error"
256
+ return
257
+ end
258
+ end
259
+
260
+ # Fire callbacks immediately after recieving header requests
261
+ # if the request method is HEAD. In case of a redirect, terminate
262
+ # current connection and reinitialize the process.
263
+ if @req.method == "HEAD"
264
+ @state = :finished
265
+ return
266
+ end
267
+
268
+ if @response_header.chunked_encoding?
269
+ @state = :chunk_header
270
+ elsif @response_header.content_length
271
+ @state = :body
272
+ else
273
+ @state = :body
274
+ end
275
+
276
+ if @req.decoding && decoder_class = HttpDecoders.decoder_for_encoding(response_header[CONTENT_ENCODING])
277
+ begin
278
+ @content_decoder = decoder_class.new do |s| on_decoded_body_data(s) end
279
+ rescue HttpDecoders::DecoderError
280
+ on_error "Content-decoder error"
281
+ end
282
+ end
283
+
284
+ # handle malformed header - Content-Type repetitions.
285
+ content_type = [response_header[CONTENT_TYPE]].flatten.first
286
+
287
+ if String.method_defined?(:force_encoding) && /;\s*charset=\s*(.+?)\s*(;|$)/.match(content_type)
288
+ @content_charset = Encoding.find($1.gsub(/^\"|\"$/, '')) rescue Encoding.default_external
289
+ end
290
+ end
291
+
292
+ class CookieJar
293
+ def initialize
294
+ @jar = ::CookieJar::Jar.new
295
+ end
296
+
297
+ def set string, uri
298
+ @jar.set_cookie(uri, string) rescue nil # drop invalid cookies
299
+ end
300
+
301
+ def get uri
302
+ uri = URI.parse(uri) rescue nil
303
+ uri ? @jar.get_cookies(uri) : []
304
+ end
305
+ end # CookieJar
306
+ end
307
+ end