em-http-request 1.0.0 → 1.0.1

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.

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