diff --git a/README.md b/README.md index 0a6985b8..a08c0eac 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Gem Version](https://badge.fury.io/rb/ably-em-http-request.svg)](http://rubygems.org/gems/ably-em-http-request) [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/ably-forks/em-http-request/ci.yml)](https://github.com/ably-forks/em-http-request/actions/workflows/ci.yml) -**Note:** This is Ably’s fork of https://github.com/igrigorik/em-http-request. We created it to fix a TLS-related issue in the original library, which seems to be no longer maintained. This fork exists to be used inside our [ably-ruby SDK](https://github.com/ably/ably-ruby). We have only made the changes required to be able to distribute this library as a separate gem; in particular, most of the documentation still refers to the original library. +**Note:** This is Ably’s fork of https://github.com/igrigorik/em-http-request. We created it to fix a TLS-related issue in the original library, which seems to be no longer maintained. This fork exists to be used inside our [ably-ruby SDK](https://github.com/ably/ably-ruby). We have only made the changes required to be able to distribute this library as a separate gem; in particular, most of the documentation still refers to the original library. The constant names used in this fork have been changed so as not to clash with the original library. Async (EventMachine) HTTP client, with support for: diff --git a/ably-em-http-request.gemspec b/ably-em-http-request.gemspec index f6735be1..13e44cf1 100644 --- a/ably-em-http-request.gemspec +++ b/ably-em-http-request.gemspec @@ -4,7 +4,7 @@ require 'em-http/version' Gem::Specification.new do |s| s.name = 'ably-em-http-request' - s.version = EventMachine::HttpRequest::VERSION + s.version = EventMachine::AblyHttpRequest::HttpRequest::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Ilya Grigorik"] diff --git a/benchmarks/clients.rb b/benchmarks/clients.rb index 0340cb6c..fcb0e2ed 100644 --- a/benchmarks/clients.rb +++ b/benchmarks/clients.rb @@ -30,7 +30,7 @@ error = 0 n.times do - http = EventMachine::HttpRequest.new(url).get + http = EventMachine::AblyHttpRequest::HttpRequest.new(url).get http.callback { count += 1 @@ -57,7 +57,7 @@ count = 0 error = 0 - conn = EventMachine::HttpRequest.new(url) + conn = EventMachine::AblyHttpRequest::HttpRequest.new(url) n.times do http = conn.get :keepalive => true diff --git a/benchmarks/em-excon.rb b/benchmarks/em-excon.rb index 5ed82de7..243bd89f 100644 --- a/benchmarks/em-excon.rb +++ b/benchmarks/em-excon.rb @@ -21,7 +21,7 @@ error = 0 n.times do EM.next_tick do - http = EventMachine::HttpRequest.new(url, :connect_timeout => 1).get + http = EventMachine::AblyHttpRequest::HttpRequest.new(url, :connect_timeout => 1).get http.callback { count += 1 @@ -48,7 +48,7 @@ EventMachine.run { count = 0 error = 0 - conn = EventMachine::HttpRequest.new(url) + conn = EventMachine::AblyHttpRequest::HttpRequest.new(url) n.times do http = conn.get :keepalive => true diff --git a/examples/digest_auth/client.rb b/examples/digest_auth/client.rb index 2fccf782..7ee0cb5c 100644 --- a/examples/digest_auth/client.rb +++ b/examples/digest_auth/client.rb @@ -10,12 +10,12 @@ EM.run do - conn_handshake = EM::HttpRequest.new('http://localhost:3000') + conn_handshake = EM::AblyHttpRequest::HttpRequest.new('http://localhost:3000') http_handshake = conn_handshake.get http_handshake.callback do - conn = EM::HttpRequest.new('http://localhost:3000') - conn.use EM::Middleware::DigestAuth, http_handshake.response_header['WWW_AUTHENTICATE'], digest_config + conn = EM::AblyHttpRequest::HttpRequest.new('http://localhost:3000') + conn.use EM::AblyHttpRequest::Middleware::DigestAuth, http_handshake.response_header['WWW_AUTHENTICATE'], digest_config http = conn.get http.callback do puts http.response diff --git a/examples/fetch.rb b/examples/fetch.rb index 32bacce1..ffe622e5 100644 --- a/examples/fetch.rb +++ b/examples/fetch.rb @@ -12,7 +12,7 @@ EM.run do urls.each do |url| - http = EM::HttpRequest.new(url).get + http = EM::AblyHttpRequest::HttpRequest.new(url).get http.callback { puts "#{url}\n#{http.response_header.status} - #{http.response.length} bytes\n" puts http.response diff --git a/examples/fibered-http.rb b/examples/fibered-http.rb index 4e5bf270..6018718c 100644 --- a/examples/fibered-http.rb +++ b/examples/fibered-http.rb @@ -9,7 +9,7 @@ def async_fetch(url) f = Fiber.current - http = EventMachine::HttpRequest.new(url, :connect_timeout => 10, :inactivity_timeout => 20).get + http = EventMachine::AblyHttpRequest::HttpRequest.new(url, :connect_timeout => 10, :inactivity_timeout => 20).get http.callback { f.resume(http) } http.errback { f.resume(http) } diff --git a/examples/multi.rb b/examples/multi.rb index f0288259..1c9f211d 100644 --- a/examples/multi.rb +++ b/examples/multi.rb @@ -4,7 +4,7 @@ require 'em-http' EventMachine.run { - multi = EventMachine::MultiRequest.new + multi = EventMachine::AblyHttpRequest::MultiRequest.new reqs = [ 'http://google.com/', @@ -12,7 +12,7 @@ ] reqs.each_with_index do |url, idx| - http = EventMachine::HttpRequest.new(url, :connect_timeout => 1) + http = EventMachine::AblyHttpRequest::HttpRequest.new(url, :connect_timeout => 1) req = http.get multi.add idx, req end diff --git a/examples/oauth-tweet.rb b/examples/oauth-tweet.rb index fb08483d..34fa8603 100644 --- a/examples/oauth-tweet.rb +++ b/examples/oauth-tweet.rb @@ -15,11 +15,11 @@ EM.run do # automatically parse the JSON response into a Ruby object - EventMachine::HttpRequest.use EventMachine::Middleware::JSONResponse + EventMachine::AblyHttpRequest::HttpRequest.use EventMachine::AblyHttpRequest::Middleware::JSONResponse # sign the request with OAuth credentials - conn = EventMachine::HttpRequest.new('http://api.twitter.com/1/statuses/home_timeline.json') - conn.use EventMachine::Middleware::OAuth, OAuthConfig + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://api.twitter.com/1/statuses/home_timeline.json') + conn.use EventMachine::AblyHttpRequest::Middleware::OAuth, OAuthConfig http = conn.get http.callback do diff --git a/examples/socks5.rb b/examples/socks5.rb index 593df42f..9c2552b5 100644 --- a/examples/socks5.rb +++ b/examples/socks5.rb @@ -7,7 +7,7 @@ # ssh -D 8000 some_remote_machine connection_options = {:proxy => {:host => '127.0.0.1', :port => 8000, :type => :socks5}} - http = EM::HttpRequest.new('http://igvita.com/', connection_options).get :redirects => 2 + http = EM::AblyHttpRequest::HttpRequest.new('http://igvita.com/', connection_options).get :redirects => 2 http.callback { puts "#{http.response_header.status} - #{http.response.length} bytes\n" diff --git a/lib/em-http/client.rb b/lib/em-http/client.rb index ab25bb97..e0ad0e27 100644 --- a/lib/em-http/client.rb +++ b/lib/em-http/client.rb @@ -1,341 +1,343 @@ require 'cookiejar' module EventMachine + module AblyHttpRequest - class HttpClient - include Deferrable - include HttpEncoding - include HttpStatus + class HttpClient + include Deferrable + include HttpEncoding + include HttpStatus - TRANSFER_ENCODING="TRANSFER_ENCODING" - CONTENT_ENCODING="CONTENT_ENCODING" - CONTENT_LENGTH="CONTENT_LENGTH" - CONTENT_TYPE="CONTENT_TYPE" - LAST_MODIFIED="LAST_MODIFIED" - KEEP_ALIVE="CONNECTION" - SET_COOKIE="SET_COOKIE" - LOCATION="LOCATION" - HOST="HOST" - ETAG="ETAG" + TRANSFER_ENCODING="TRANSFER_ENCODING" + CONTENT_ENCODING="CONTENT_ENCODING" + CONTENT_LENGTH="CONTENT_LENGTH" + CONTENT_TYPE="CONTENT_TYPE" + LAST_MODIFIED="LAST_MODIFIED" + KEEP_ALIVE="CONNECTION" + SET_COOKIE="SET_COOKIE" + LOCATION="LOCATION" + HOST="HOST" + ETAG="ETAG" - CRLF="\r\n" + CRLF="\r\n" - attr_accessor :state, :response, :conn - attr_reader :response_header, :error, :content_charset, :req, :cookies + attr_accessor :state, :response, :conn + attr_reader :response_header, :error, :content_charset, :req, :cookies - def initialize(conn, options) - @conn = conn - @req = options + def initialize(conn, options) + @conn = conn + @req = options - @stream = nil - @headers = nil - @cookies = [] - @cookiejar = CookieJar.new + @stream = nil + @headers = nil + @cookies = [] + @cookiejar = CookieJar.new - reset! - end + reset! + end - def reset! - @response_header = HttpResponseHeader.new - @state = :response_header + def reset! + @response_header = HttpResponseHeader.new + @state = :response_header - @response = '' - @error = nil - @content_decoder = nil - @content_charset = nil - end + @response = '' + @error = nil + @content_decoder = nil + @content_charset = nil + end - def last_effective_url; @req.uri; end - def redirects; @req.followed; end - def peer; @conn.peer; end + def last_effective_url; @req.uri; end + def redirects; @req.followed; end + def peer; @conn.peer; end - def connection_completed - @state = :response_header + def connection_completed + @state = :response_header - head, body = build_request, @req.body - @conn.middleware.each do |m| - head, body = m.request(self, head, body) if m.respond_to?(:request) + head, body = build_request, @req.body + @conn.middleware.each do |m| + head, body = m.request(self, head, body) if m.respond_to?(:request) + end + + send_request(head, body) end - send_request(head, body) - end + def on_request_complete + begin + @content_decoder.finalize! if @content_decoder + rescue HttpDecoders::DecoderError + on_error "Content-decoder error" + end - def on_request_complete - begin - @content_decoder.finalize! if @content_decoder - rescue HttpDecoders::DecoderError - on_error "Content-decoder error" + unbind end - unbind - end + def continue? + @response_header.status == 100 && (@req.method == 'POST' || @req.method == 'PUT') + end - def continue? - @response_header.status == 100 && (@req.method == 'POST' || @req.method == 'PUT') - end + def finished? + @state == :finished || (@state == :body && @response_header.content_length.nil?) + end - def finished? - @state == :finished || (@state == :body && @response_header.content_length.nil?) - end + def redirect? + @response_header.redirection? && @req.follow_redirect? + end - def redirect? - @response_header.redirection? && @req.follow_redirect? - end + def unbind(reason = nil) + if finished? + if redirect? - def unbind(reason = nil) - if finished? - if redirect? + begin + @conn.middleware.each do |m| + m.response(self) if m.respond_to?(:response) + end - begin - @conn.middleware.each do |m| - m.response(self) if m.respond_to?(:response) - end + # one of the injected middlewares could have changed + # our redirect settings, check if we still want to + # follow the location header + if redirect? + @req.followed += 1 - # one of the injected middlewares could have changed - # our redirect settings, check if we still want to - # follow the location header - if redirect? - @req.followed += 1 + @cookies.clear + @cookies = @cookiejar.get(@response_header.location).map(&:to_s) if @req.pass_cookies - @cookies.clear - @cookies = @cookiejar.get(@response_header.location).map(&:to_s) if @req.pass_cookies + @conn.redirect(self, @response_header.location) + else + succeed(self) + end - @conn.redirect(self, @response_header.location) - else - succeed(self) + rescue => e + on_error(e.message) end - - rescue => e - on_error(e.message) + else + succeed(self) end + else - succeed(self) + on_error(reason || 'connection closed by server') end + end - else - on_error(reason || 'connection closed by server') + def on_error(msg = nil) + @error = msg + fail(self) end - end + alias :close :on_error - def on_error(msg = nil) - @error = msg - fail(self) - end - alias :close :on_error + def stream(&blk); @stream = blk; end + def headers(&blk); @headers = blk; end - def stream(&blk); @stream = blk; end - def headers(&blk); @headers = blk; end + def normalize_body(body) + body.is_a?(Hash) ? form_encode_body(body) : body + end - def normalize_body(body) - body.is_a?(Hash) ? form_encode_body(body) : body - end + def build_request + head = @req.headers ? munge_header_keys(@req.headers) : {} - def build_request - head = @req.headers ? munge_header_keys(@req.headers) : {} + if @conn.connopts.http_proxy? + proxy = @conn.connopts.proxy + head['proxy-authorization'] = proxy[:authorization] if proxy[:authorization] + end - if @conn.connopts.http_proxy? - proxy = @conn.connopts.proxy - head['proxy-authorization'] = proxy[:authorization] if proxy[:authorization] - end + # Set the cookie header if provided + if cookie = head['cookie'] + @cookies << encode_cookie(cookie) if cookie + end + head['cookie'] = @cookies.compact.uniq.join("; ").squeeze(";") unless @cookies.empty? - # Set the cookie header if provided - if cookie = head['cookie'] - @cookies << encode_cookie(cookie) if cookie - end - head['cookie'] = @cookies.compact.uniq.join("; ").squeeze(";") unless @cookies.empty? + # Set connection close unless keepalive + if !@req.keepalive + head['connection'] = 'close' + end - # Set connection close unless keepalive - if !@req.keepalive - head['connection'] = 'close' - end + # Set the Host header if it hasn't been specified already + head['host'] ||= encode_host - # Set the Host header if it hasn't been specified already - head['host'] ||= encode_host + # Set the User-Agent if it hasn't been specified + if !head.key?('user-agent') + head['user-agent'] = 'EventMachine HttpClient' + elsif head['user-agent'].nil? + head.delete('user-agent') + end - # Set the User-Agent if it hasn't been specified - if !head.key?('user-agent') - head['user-agent'] = 'EventMachine HttpClient' - elsif head['user-agent'].nil? - head.delete('user-agent') - end + # Set the Accept-Encoding header if none is provided + if !head.key?('accept-encoding') && req.compressed + head['accept-encoding'] = 'gzip, compressed' + end + + # Set the auth from the URI if given + head['Authorization'] = @req.uri.userinfo.split(/:/, 2) if @req.uri.userinfo - # Set the Accept-Encoding header if none is provided - if !head.key?('accept-encoding') && req.compressed - head['accept-encoding'] = 'gzip, compressed' + head end - # Set the auth from the URI if given - head['Authorization'] = @req.uri.userinfo.split(/:/, 2) if @req.uri.userinfo + def send_request(head, body) + body = normalize_body(body) + file = @req.file + query = @req.query + + # Set the Content-Length if file is given + head['content-length'] = File.size(file) if file + + # Set the Content-Length if body is given, + # or we're doing an empty post or put + if body + head['content-length'] ||= body.respond_to?(:bytesize) ? body.bytesize : body.size + elsif @req.method == 'POST' or @req.method == 'PUT' + # wont happen if body is set and we already set content-length above + head['content-length'] ||= 0 + end - head - end + # Set content-type header if missing and body is a Ruby hash + if !head['content-type'] and @req.body.is_a? Hash + head['content-type'] = 'application/x-www-form-urlencoded' + end - def send_request(head, body) - body = normalize_body(body) - file = @req.file - query = @req.query - - # Set the Content-Length if file is given - head['content-length'] = File.size(file) if file - - # Set the Content-Length if body is given, - # or we're doing an empty post or put - if body - head['content-length'] ||= body.respond_to?(:bytesize) ? body.bytesize : body.size - elsif @req.method == 'POST' or @req.method == 'PUT' - # wont happen if body is set and we already set content-length above - head['content-length'] ||= 0 - end + request_header ||= encode_request(@req.method, @req.uri, query, @conn.connopts) + request_header << encode_headers(head) + request_header << CRLF + @conn.send_data request_header - # Set content-type header if missing and body is a Ruby hash - if !head['content-type'] and @req.body.is_a? Hash - head['content-type'] = 'application/x-www-form-urlencoded' + @req_body = body || (@req.file && Pathname.new(@req.file)) + send_request_body unless @req.headers['expect'] == '100-continue' end - request_header ||= encode_request(@req.method, @req.uri, query, @conn.connopts) - request_header << encode_headers(head) - request_header << CRLF - @conn.send_data request_header - - @req_body = body || (@req.file && Pathname.new(@req.file)) - send_request_body unless @req.headers['expect'] == '100-continue' - end - - def on_body_data(data) - if @content_decoder - begin - @content_decoder << data - rescue HttpDecoders::DecoderError - on_error "Content-decoder error" + def on_body_data(data) + if @content_decoder + begin + @content_decoder << data + rescue HttpDecoders::DecoderError + on_error "Content-decoder error" + end + else + on_decoded_body_data(data) end - else - on_decoded_body_data(data) end - end - def on_decoded_body_data(data) - data.force_encoding @content_charset if @content_charset - if @stream - @stream.call(data) - else - @response << data + def on_decoded_body_data(data) + data.force_encoding @content_charset if @content_charset + if @stream + @stream.call(data) + else + @response << data + end end - end - def request_body_pending? - !!@req_body - end + def request_body_pending? + !!@req_body + end - def send_request_body - return if @req_body.nil? + def send_request_body + return if @req_body.nil? - if @req_body.is_a?(String) - @conn.send_data @req_body + if @req_body.is_a?(String) + @conn.send_data @req_body - elsif @req_body.is_a?(Pathname) - @conn.stream_file_data @req_body.to_path, http_chunks: false + elsif @req_body.is_a?(Pathname) + @conn.stream_file_data @req_body.to_path, http_chunks: false - elsif @req_body.respond_to?(:read) && @req_body.respond_to?(:eof?) # IO or IO-like object - @conn.stream_data @req_body + elsif @req_body.respond_to?(:read) && @req_body.respond_to?(:eof?) # IO or IO-like object + @conn.stream_data @req_body - else - raise "Don't know how to send request body: #{@req_body.inspect}" + else + raise "Don't know how to send request body: #{@req_body.inspect}" + end + @req_body = nil end - @req_body = nil - end - def parse_response_header(header, version, status) - @response_header.raw = header - header.each do |key, val| - @response_header[key.upcase.gsub('-','_')] = val - end + def parse_response_header(header, version, status) + @response_header.raw = header + header.each do |key, val| + @response_header[key.upcase.gsub('-','_')] = val + end - @response_header.http_version = version.join('.') - @response_header.http_status = status - @response_header.http_reason = CODE[status] || 'unknown' + @response_header.http_version = version.join('.') + @response_header.http_status = status + @response_header.http_reason = CODE[status] || 'unknown' - # invoke headers callback after full parse - # if one is specified by the user - @headers.call(@response_header) if @headers + # invoke headers callback after full parse + # if one is specified by the user + @headers.call(@response_header) if @headers - unless @response_header.http_status and @response_header.http_reason - @state = :invalid - on_error "no HTTP response" - return - end + unless @response_header.http_status and @response_header.http_reason + @state = :invalid + on_error "no HTTP response" + return + end - # add set-cookie's to cookie list - if @response_header.cookie && @req.pass_cookies - [@response_header.cookie].flatten.each {|cookie| @cookiejar.set(cookie, @req.uri)} - end + # add set-cookie's to cookie list + if @response_header.cookie && @req.pass_cookies + [@response_header.cookie].flatten.each {|cookie| @cookiejar.set(cookie, @req.uri)} + end - # correct location header - some servers will incorrectly give a relative URI - if @response_header.location - begin - location = Addressable::URI.parse(@response_header.location) - location.path = "/" if location.path.empty? + # correct location header - some servers will incorrectly give a relative URI + if @response_header.location + begin + location = Addressable::URI.parse(@response_header.location) + location.path = "/" if location.path.empty? - if location.relative? - location = @req.uri.join(location) - else - # if redirect is to an absolute url, check for correct URI structure - raise if location.host.nil? - end + if location.relative? + location = @req.uri.join(location) + else + # if redirect is to an absolute url, check for correct URI structure + raise if location.host.nil? + end - @response_header[LOCATION] = location.to_s + @response_header[LOCATION] = location.to_s - rescue - on_error "Location header format error" - return + rescue + on_error "Location header format error" + return + end end - end - # Fire callbacks immediately after recieving header requests - # if the request method is HEAD. In case of a redirect, terminate - # current connection and reinitialize the process. - if @req.method == "HEAD" - @state = :finished - return - end + # Fire callbacks immediately after recieving header requests + # if the request method is HEAD. In case of a redirect, terminate + # current connection and reinitialize the process. + if @req.method == "HEAD" + @state = :finished + return + end - if @response_header.chunked_encoding? - @state = :chunk_header - elsif @response_header.content_length - @state = :body - else - @state = :body - end + if @response_header.chunked_encoding? + @state = :chunk_header + elsif @response_header.content_length + @state = :body + else + @state = :body + end - if @req.decoding && decoder_class = HttpDecoders.decoder_for_encoding(response_header[CONTENT_ENCODING]) - begin - @content_decoder = decoder_class.new do |s| on_decoded_body_data(s) end - rescue HttpDecoders::DecoderError - on_error "Content-decoder error" + if @req.decoding && decoder_class = HttpDecoders.decoder_for_encoding(response_header[CONTENT_ENCODING]) + begin + @content_decoder = decoder_class.new do |s| on_decoded_body_data(s) end + rescue HttpDecoders::DecoderError + on_error "Content-decoder error" + end end - end - # handle malformed header - Content-Type repetitions. - content_type = [response_header[CONTENT_TYPE]].flatten.first + # handle malformed header - Content-Type repetitions. + content_type = [response_header[CONTENT_TYPE]].flatten.first - if String.method_defined?(:force_encoding) && /;\s*charset=\s*(.+?)\s*(;|$)/.match(content_type) - @content_charset = Encoding.find($1.gsub(/^\"|\"$/, '')) rescue Encoding.default_external + if String.method_defined?(:force_encoding) && /;\s*charset=\s*(.+?)\s*(;|$)/.match(content_type) + @content_charset = Encoding.find($1.gsub(/^\"|\"$/, '')) rescue Encoding.default_external + end end - end - class CookieJar - def initialize - @jar = ::CookieJar::Jar.new - end + class CookieJar + def initialize + @jar = ::CookieJar::Jar.new + end - def set string, uri - @jar.set_cookie(uri, string) rescue nil # drop invalid cookies - end + def set string, uri + @jar.set_cookie(uri, string) rescue nil # drop invalid cookies + end - def get uri - uri = URI.parse(uri) rescue nil - uri ? @jar.get_cookies(uri) : [] - end - end # CookieJar + def get uri + uri = URI.parse(uri) rescue nil + uri ? @jar.get_cookies(uri) : [] + end + end # CookieJar + end end end diff --git a/lib/em-http/decoders.rb b/lib/em-http/decoders.rb index 0fcac465..67fef5be 100644 --- a/lib/em-http/decoders.rb +++ b/lib/em-http/decoders.rb @@ -3,7 +3,7 @@ ## # Provides a unified callback interface to decompression libraries. -module EventMachine::HttpDecoders +module EventMachine::AblyHttpRequest::HttpDecoders class DecoderError < StandardError end diff --git a/lib/em-http/http_client_options.rb b/lib/em-http/http_client_options.rb index 6d3edb23..52c5150d 100644 --- a/lib/em-http/http_client_options.rb +++ b/lib/em-http/http_client_options.rb @@ -1,49 +1,51 @@ -class HttpClientOptions - attr_reader :uri, :method, :host, :port - attr_reader :headers, :file, :body, :query, :path - attr_reader :keepalive, :pass_cookies, :decoding, :compressed +module AblyHttpRequest + class HttpClientOptions + attr_reader :uri, :method, :host, :port + attr_reader :headers, :file, :body, :query, :path + attr_reader :keepalive, :pass_cookies, :decoding, :compressed - attr_accessor :followed, :redirects + attr_accessor :followed, :redirects - def initialize(uri, options, method) - @keepalive = options[:keepalive] || false # default to single request per connection - @redirects = options[:redirects] ||= 0 # default number of redirects to follow - @followed = options[:followed] ||= 0 # keep track of number of followed requests + def initialize(uri, options, method) + @keepalive = options[:keepalive] || false # default to single request per connection + @redirects = options[:redirects] ||= 0 # default number of redirects to follow + @followed = options[:followed] ||= 0 # keep track of number of followed requests - @method = method.to_s.upcase - @headers = options[:head] || {} + @method = method.to_s.upcase + @headers = options[:head] || {} - @file = options[:file] - @body = options[:body] + @file = options[:file] + @body = options[:body] - @pass_cookies = options.fetch(:pass_cookies, true) # pass cookies between redirects - @decoding = options.fetch(:decoding, true) # auto-decode compressed response - @compressed = options.fetch(:compressed, true) # auto-negotiated compressed response + @pass_cookies = options.fetch(:pass_cookies, true) # pass cookies between redirects + @decoding = options.fetch(:decoding, true) # auto-decode compressed response + @compressed = options.fetch(:compressed, true) # auto-negotiated compressed response - set_uri(uri, options[:path], options[:query]) - end - - def follow_redirect?; @followed < @redirects; end - def ssl?; @uri.scheme == "https" || @uri.port == 443; end - def no_body?; @method == "HEAD"; end - - def set_uri(uri, path = nil, query = nil) - uri = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI::parse(uri.to_s) - uri.path = path if path - uri.path = '/' if uri.path.empty? - - @uri = uri - @path = uri.path - @host = uri.hostname - @port = uri.port - @query = query - - # Make sure the ports are set as Addressable::URI doesn't - # set the port if it isn't there - if @port.nil? - @port = @uri.scheme == "https" ? 443 : 80 + set_uri(uri, options[:path], options[:query]) end - uri + def follow_redirect?; @followed < @redirects; end + def ssl?; @uri.scheme == "https" || @uri.port == 443; end + def no_body?; @method == "HEAD"; end + + def set_uri(uri, path = nil, query = nil) + uri = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI::parse(uri.to_s) + uri.path = path if path + uri.path = '/' if uri.path.empty? + + @uri = uri + @path = uri.path + @host = uri.hostname + @port = uri.port + @query = query + + # Make sure the ports are set as Addressable::URI doesn't + # set the port if it isn't there + if @port.nil? + @port = @uri.scheme == "https" ? 443 : 80 + end + + uri + end end end diff --git a/lib/em-http/http_connection.rb b/lib/em-http/http_connection.rb index 42a1d482..3627a28f 100644 --- a/lib/em-http/http_connection.rb +++ b/lib/em-http/http_connection.rb @@ -1,322 +1,324 @@ require 'em/io_streamer' module EventMachine + module AblyHttpRequest + + module HTTPMethods + def get options = {}, &blk; setup_request(:get, options, &blk); end + def head options = {}, &blk; setup_request(:head, options, &blk); end + def delete options = {}, &blk; setup_request(:delete, options, &blk); end + def put options = {}, &blk; setup_request(:put, options, &blk); end + def post options = {}, &blk; setup_request(:post, options, &blk); end + def patch options = {}, &blk; setup_request(:patch, options, &blk); end + def options options = {}, &blk; setup_request(:options, options, &blk); end + end - module HTTPMethods - def get options = {}, &blk; setup_request(:get, options, &blk); end - def head options = {}, &blk; setup_request(:head, options, &blk); end - def delete options = {}, &blk; setup_request(:delete, options, &blk); end - def put options = {}, &blk; setup_request(:put, options, &blk); end - def post options = {}, &blk; setup_request(:post, options, &blk); end - def patch options = {}, &blk; setup_request(:patch, options, &blk); end - def options options = {}, &blk; setup_request(:options, options, &blk); end - end + class HttpStubConnection < Connection + include Deferrable + attr_reader :parent - class HttpStubConnection < Connection - include Deferrable - attr_reader :parent + def parent=(p) + @parent = p + @parent.conn = self + end - def parent=(p) - @parent = p - @parent.conn = self - end + def receive_data(data) + begin + @parent.receive_data data + rescue EventMachine::Connectify::CONNECTError => e + @parent.close(e.message) + end + end - def receive_data(data) - begin - @parent.receive_data data - rescue EventMachine::Connectify::CONNECTError => e - @parent.close(e.message) + def connection_completed + @parent.connection_completed end - end - def connection_completed - @parent.connection_completed - end + def unbind(reason=nil) + @parent.unbind(reason) + end - def unbind(reason=nil) - @parent.unbind(reason) - end + # TLS verification support, original implementation by Mislav Marohnić + # https://github.com/lostisland/faraday/blob/63cf47c95b573539f047c729bd9ad67560bc83ff/lib/faraday/adapter/em_http_ssl_patch.rb + def ssl_verify_peer(cert_string) + cert = nil + begin + cert = OpenSSL::X509::Certificate.new(cert_string) + rescue OpenSSL::X509::CertificateError + return false + end + + @last_seen_cert = cert - # TLS verification support, original implementation by Mislav Marohnić - # https://github.com/lostisland/faraday/blob/63cf47c95b573539f047c729bd9ad67560bc83ff/lib/faraday/adapter/em_http_ssl_patch.rb - def ssl_verify_peer(cert_string) - cert = nil - begin - cert = OpenSSL::X509::Certificate.new(cert_string) - rescue OpenSSL::X509::CertificateError - return false + if certificate_store.verify(@last_seen_cert) + begin + certificate_store.add_cert(@last_seen_cert) + rescue OpenSSL::X509::StoreError => e + raise e unless e.message == 'cert already in hash table' + end + true + else + raise OpenSSL::SSL::SSLError.new(%(unable to verify the server certificate for "#{host}")) + end end - @last_seen_cert = cert + def ssl_handshake_completed + unless verify_peer? + warn "[WARNING; ably-em-http-request] TLS hostname validation is disabled (use 'tls: {verify_peer: true}'), see" + + " CVE-2020-13482 and https://github.com/igrigorik/em-http-request/issues/339 for details" unless parent.connopts.tls.has_key?(:verify_peer) + return true + end - if certificate_store.verify(@last_seen_cert) - begin - certificate_store.add_cert(@last_seen_cert) - rescue OpenSSL::X509::StoreError => e - raise e unless e.message == 'cert already in hash table' + unless OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host) + raise OpenSSL::SSL::SSLError.new(%(host "#{host}" does not match the server certificate)) + else + true end - true - else - raise OpenSSL::SSL::SSLError.new(%(unable to verify the server certificate for "#{host}")) end - end - def ssl_handshake_completed - unless verify_peer? - warn "[WARNING; ably-em-http-request] TLS hostname validation is disabled (use 'tls: {verify_peer: true}'), see" + - " CVE-2020-13482 and https://github.com/igrigorik/em-http-request/issues/339 for details" unless parent.connopts.tls.has_key?(:verify_peer) - return true + def verify_peer? + parent.connopts.tls[:verify_peer] end - unless OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host) - raise OpenSSL::SSL::SSLError.new(%(host "#{host}" does not match the server certificate)) - else - true + def host + parent.connopts.host end - end - def verify_peer? - parent.connopts.tls[:verify_peer] - end - - def host - parent.connopts.host - end - - def certificate_store - @certificate_store ||= begin - store = OpenSSL::X509::Store.new - store.set_default_paths - ca_file = parent.connopts.tls[:cert_chain_file] - store.add_file(ca_file) if ca_file - store + def certificate_store + @certificate_store ||= begin + store = OpenSSL::X509::Store.new + store.set_default_paths + ca_file = parent.connopts.tls[:cert_chain_file] + store.add_file(ca_file) if ca_file + store + end end end - end - class HttpConnection - include HTTPMethods - include Socksify - include Connectify + class HttpConnection + include HTTPMethods + include Socksify + include Connectify - attr_reader :deferred, :conn - attr_accessor :error, :connopts, :uri + attr_reader :deferred, :conn + attr_accessor :error, :connopts, :uri - def initialize - @deferred = true - @middleware = [] - end + def initialize + @deferred = true + @middleware = [] + end - def conn=(c) - @conn = c - @deferred = false - end + def conn=(c) + @conn = c + @deferred = false + end - def activate_connection(client) - begin - EventMachine.bind_connect(@connopts.bind, @connopts.bind_port, - @connopts.host, @connopts.port, - HttpStubConnection) do |conn| - post_init + def activate_connection(client) + begin + EventMachine.bind_connect(@connopts.bind, @connopts.bind_port, + @connopts.host, @connopts.port, + HttpStubConnection) do |conn| + post_init + + @deferred = false + @conn = conn - @deferred = false - @conn = conn + conn.parent = self + conn.pending_connect_timeout = @connopts.connect_timeout + conn.comm_inactivity_timeout = @connopts.inactivity_timeout + end - conn.parent = self - conn.pending_connect_timeout = @connopts.connect_timeout - conn.comm_inactivity_timeout = @connopts.inactivity_timeout + finalize_request(client) + rescue EventMachine::ConnectionError => e + # + # Currently, this can only fire on initial connection setup + # since #connect is a synchronous method. Hence, rescue the exception, + # and return a failed deferred which fail any client request at next + # tick. We fail at next tick to keep a consistent API when the newly + # created HttpClient is failed. This approach has the advantage to + # remove a state check of @deferred_status after creating a new + # HttpRequest. The drawback is that users may setup a callback which we + # know won't be used. + # + # Once there is async-DNS, then we'll iterate over the outstanding + # client requests and fail them in order. + # + # Net outcome: failed connection will invoke the same ConnectionError + # message on the connection deferred, and on the client deferred. + # + EM.next_tick{client.close(e.message)} end + end - finalize_request(client) - rescue EventMachine::ConnectionError => e - # - # Currently, this can only fire on initial connection setup - # since #connect is a synchronous method. Hence, rescue the exception, - # and return a failed deferred which fail any client request at next - # tick. We fail at next tick to keep a consistent API when the newly - # created HttpClient is failed. This approach has the advantage to - # remove a state check of @deferred_status after creating a new - # HttpRequest. The drawback is that users may setup a callback which we - # know won't be used. - # - # Once there is async-DNS, then we'll iterate over the outstanding - # client requests and fail them in order. - # - # Net outcome: failed connection will invoke the same ConnectionError - # message on the connection deferred, and on the client deferred. - # - EM.next_tick{client.close(e.message)} + def setup_request(method, options = {}, c = nil) + c ||= HttpClient.new(self, ::AblyHttpRequest::HttpClientOptions.new(@uri, options, method)) + @deferred ? activate_connection(c) : finalize_request(c) + c end - end - def setup_request(method, options = {}, c = nil) - c ||= HttpClient.new(self, HttpClientOptions.new(@uri, options, method)) - @deferred ? activate_connection(c) : finalize_request(c) - c - end + def finalize_request(c) + @conn.callback { c.connection_completed } - def finalize_request(c) - @conn.callback { c.connection_completed } + middleware.each do |m| + c.callback(&m.method(:response)) if m.respond_to?(:response) + end - middleware.each do |m| - c.callback(&m.method(:response)) if m.respond_to?(:response) + @clients.push c end - @clients.push c - end + def middleware + [HttpRequest.middleware, @middleware].flatten + end - def middleware - [HttpRequest.middleware, @middleware].flatten - end + def post_init + @clients = [] + @pending = [] + + @p = Http::Parser.new + @p.header_value_type = :mixed + @p.on_headers_complete = proc do |h| + if client + if @p.status_code == 100 + client.send_request_body + @p.reset! + else + client.parse_response_header(h, @p.http_version, @p.status_code) + :reset if client.req.no_body? + end + else + # if we receive unexpected data without a pending client request + # reset the parser to avoid firing any further callbacks and close + # the connection because we're processing invalid HTTP + @p.reset! + unbind + :stop + end + end - def post_init - @clients = [] - @pending = [] + @p.on_body = proc do |b| + client.on_body_data(b) + end - @p = Http::Parser.new - @p.header_value_type = :mixed - @p.on_headers_complete = proc do |h| - if client - if @p.status_code == 100 - client.send_request_body - @p.reset! - else - client.parse_response_header(h, @p.http_version, @p.status_code) - :reset if client.req.no_body? + @p.on_message_complete = proc do + if !client.continue? + c = @clients.shift + c.state = :finished + c.on_request_complete end - else - # if we receive unexpected data without a pending client request - # reset the parser to avoid firing any further callbacks and close - # the connection because we're processing invalid HTTP - @p.reset! - unbind - :stop end end - @p.on_body = proc do |b| - client.on_body_data(b) + def use(klass, *args, &block) + @middleware << klass.new(*args, &block) + end + + def peer + Socket.unpack_sockaddr_in(@peer)[1] rescue nil end - @p.on_message_complete = proc do - if !client.continue? + def receive_data(data) + begin + @p << data + rescue HTTP::Parser::Error => e c = @clients.shift - c.state = :finished - c.on_request_complete + c.nil? ? unbind(e.message) : c.on_error(e.message) end end - end - - def use(klass, *args, &block) - @middleware << klass.new(*args, &block) - end - def peer - Socket.unpack_sockaddr_in(@peer)[1] rescue nil - end + def connection_completed + @peer = @conn.get_peername - def receive_data(data) - begin - @p << data - rescue HTTP::Parser::Error => e - c = @clients.shift - c.nil? ? unbind(e.message) : c.on_error(e.message) + if @connopts.socks_proxy? + socksify(client.req.uri.hostname, client.req.uri.inferred_port, *@connopts.proxy[:authorization]) { start } + elsif @connopts.connect_proxy? + connectify(client.req.uri.hostname, client.req.uri.inferred_port, *@connopts.proxy[:authorization]) { start } + else + start + end end - end - - def connection_completed - @peer = @conn.get_peername - if @connopts.socks_proxy? - socksify(client.req.uri.hostname, client.req.uri.inferred_port, *@connopts.proxy[:authorization]) { start } - elsif @connopts.connect_proxy? - connectify(client.req.uri.hostname, client.req.uri.inferred_port, *@connopts.proxy[:authorization]) { start } - else - start + def start + @conn.start_tls(@connopts.tls) if client && client.req.ssl? + @conn.succeed end - end - - def start - @conn.start_tls(@connopts.tls) if client && client.req.ssl? - @conn.succeed - end - def redirect(client, new_location) - old_location = client.req.uri - new_location = client.req.set_uri(new_location) - - if client.req.keepalive - # Application requested a keep-alive connection but one of the requests - # hits a cross-origin redirect. We need to open a new connection and - # let both connections proceed simultaneously. - if old_location.origin != new_location.origin - conn = HttpConnection.new - client.conn = conn - conn.connopts = @connopts - conn.connopts.https = new_location.scheme == "https" - conn.uri = client.req.uri - conn.activate_connection(client) - - # If the redirect is a same-origin redirect on a keep-alive request - # then immidiately dispatch the request over existing connection. + def redirect(client, new_location) + old_location = client.req.uri + new_location = client.req.set_uri(new_location) + + if client.req.keepalive + # Application requested a keep-alive connection but one of the requests + # hits a cross-origin redirect. We need to open a new connection and + # let both connections proceed simultaneously. + if old_location.origin != new_location.origin + conn = HttpConnection.new + client.conn = conn + conn.connopts = @connopts + conn.connopts.https = new_location.scheme == "https" + conn.uri = client.req.uri + conn.activate_connection(client) + + # If the redirect is a same-origin redirect on a keep-alive request + # then immidiately dispatch the request over existing connection. + else + @clients.push client + client.connection_completed + end else - @clients.push client - client.connection_completed + # If connection is not keep-alive the unbind will fire and we'll + # reconnect using the same connection object. + @pending.push client end - else - # If connection is not keep-alive the unbind will fire and we'll - # reconnect using the same connection object. - @pending.push client end - end - def unbind(reason = nil) - @clients.map { |c| c.unbind(reason) } + def unbind(reason = nil) + @clients.map { |c| c.unbind(reason) } - if r = @pending.shift - @clients.push r + if r = @pending.shift + @clients.push r - r.reset! - @p.reset! + r.reset! + @p.reset! - begin - @conn.set_deferred_status :unknown + begin + @conn.set_deferred_status :unknown - if @connopts.proxy - @conn.reconnect(@connopts.host, @connopts.port) - else - @conn.reconnect(r.req.host, r.req.port) - end + if @connopts.proxy + @conn.reconnect(@connopts.host, @connopts.port) + else + @conn.reconnect(r.req.host, r.req.port) + end - @conn.pending_connect_timeout = @connopts.connect_timeout - @conn.comm_inactivity_timeout = @connopts.inactivity_timeout - @conn.callback { r.connection_completed } - rescue EventMachine::ConnectionError => e - @clients.pop.close(e.message) + @conn.pending_connect_timeout = @connopts.connect_timeout + @conn.comm_inactivity_timeout = @connopts.inactivity_timeout + @conn.callback { r.connection_completed } + rescue EventMachine::ConnectionError => e + @clients.pop.close(e.message) + end + else + @deferred = true + @conn.close_connection end - else - @deferred = true - @conn.close_connection end - end - alias :close :unbind + alias :close :unbind - def send_data(data) - @conn.send_data data - end + def send_data(data) + @conn.send_data data + end - def stream_file_data(filename, args = {}) - @conn.stream_file_data filename, args - end + def stream_file_data(filename, args = {}) + @conn.stream_file_data filename, args + end - def stream_data(io, opts = {}) - EventMachine::IOStreamer.new(self, io, opts) - end + def stream_data(io, opts = {}) + EventMachine::AblyHttpRequest::IOStreamer.new(self, io, opts) + end - private + private - def client - @clients.first - end + def client + @clients.first + end + end end end diff --git a/lib/em-http/http_connection_options.rb b/lib/em-http/http_connection_options.rb index 7036d1d5..3cbd9951 100644 --- a/lib/em-http/http_connection_options.rb +++ b/lib/em-http/http_connection_options.rb @@ -1,70 +1,72 @@ -class HttpConnectionOptions - attr_reader :host, :port, :tls, :proxy, :bind, :bind_port - attr_reader :connect_timeout, :inactivity_timeout - attr_writer :https +module AblyHttpRequest + class HttpConnectionOptions + attr_reader :host, :port, :tls, :proxy, :bind, :bind_port + attr_reader :connect_timeout, :inactivity_timeout + attr_writer :https - def initialize(uri, options) - @connect_timeout = options[:connect_timeout] || 5 # default connection setup timeout - @inactivity_timeout = options[:inactivity_timeout] ||= 10 # default connection inactivity (post-setup) timeout + def initialize(uri, options) + @connect_timeout = options[:connect_timeout] || 5 # default connection setup timeout + @inactivity_timeout = options[:inactivity_timeout] ||= 10 # default connection inactivity (post-setup) timeout - @tls = options[:tls] || options[:ssl] || {} + @tls = options[:tls] || options[:ssl] || {} - if bind = options[:bind] - @bind = bind[:host] || '0.0.0.0' + if bind = options[:bind] + @bind = bind[:host] || '0.0.0.0' - # Eventmachine will open a UNIX socket if bind :port - # is explicitly set to nil - @bind_port = bind[:port] - end + # Eventmachine will open a UNIX socket if bind :port + # is explicitly set to nil + @bind_port = bind[:port] + end - uri = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI::parse(uri.to_s) - @https = uri.scheme == "https" - uri.port ||= (@https ? 443 : 80) - @tls[:sni_hostname] = uri.hostname + uri = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI::parse(uri.to_s) + @https = uri.scheme == "https" + uri.port ||= (@https ? 443 : 80) + @tls[:sni_hostname] = uri.hostname - @proxy = options[:proxy] || proxy_from_env + @proxy = options[:proxy] || proxy_from_env - if proxy - @host = proxy[:host] - @port = proxy[:port] - else - @host = uri.hostname - @port = uri.port + if proxy + @host = proxy[:host] + @port = proxy[:port] + else + @host = uri.hostname + @port = uri.port + end end - end - def http_proxy? - @proxy && (@proxy[:type] == :http || @proxy[:type].nil?) && !@https - end + def http_proxy? + @proxy && (@proxy[:type] == :http || @proxy[:type].nil?) && !@https + end - def connect_proxy? - @proxy && (@proxy[:type] == :http || @proxy[:type].nil?) && @https - end + def connect_proxy? + @proxy && (@proxy[:type] == :http || @proxy[:type].nil?) && @https + end - def socks_proxy? - @proxy && (@proxy[:type] == :socks5) - end + def socks_proxy? + @proxy && (@proxy[:type] == :socks5) + end - def proxy_from_env - # TODO: Add support for $http_no_proxy or $no_proxy ? - proxy_str = if @https - ENV['HTTPS_PROXY'] || ENV['https_proxy'] - else - ENV['HTTP_PROXY'] || ENV['http_proxy'] + def proxy_from_env + # TODO: Add support for $http_no_proxy or $no_proxy ? + proxy_str = if @https + ENV['HTTPS_PROXY'] || ENV['https_proxy'] + else + ENV['HTTP_PROXY'] || ENV['http_proxy'] - # Fall-back to $ALL_PROXY if none of the above env-vars have values - end || ENV['ALL_PROXY'] + # Fall-back to $ALL_PROXY if none of the above env-vars have values + end || ENV['ALL_PROXY'] - # Addressable::URI::parse will return `nil` if given `nil` and an empty URL for an empty string - # so, let's short-circuit that: - return if !proxy_str || proxy_str.empty? + # Addressable::URI::parse will return `nil` if given `nil` and an empty URL for an empty string + # so, let's short-circuit that: + return if !proxy_str || proxy_str.empty? - proxy_env_uri = Addressable::URI::parse(proxy_str) - { :host => proxy_env_uri.host, :port => proxy_env_uri.port, :type => :http } + proxy_env_uri = Addressable::URI::parse(proxy_str) + { :host => proxy_env_uri.host, :port => proxy_env_uri.port, :type => :http } - rescue Addressable::URI::InvalidURIError - # An invalid env-var shouldn't crash the config step, IMHO. - # We should somehow log / warn about this, perhaps... - return + rescue Addressable::URI::InvalidURIError + # An invalid env-var shouldn't crash the config step, IMHO. + # We should somehow log / warn about this, perhaps... + return + end end end diff --git a/lib/em-http/http_encoding.rb b/lib/em-http/http_encoding.rb index cf7e647c..e9b9f42b 100644 --- a/lib/em-http/http_encoding.rb +++ b/lib/em-http/http_encoding.rb @@ -1,148 +1,150 @@ module EventMachine - module HttpEncoding - HTTP_REQUEST_HEADER="%s %s HTTP/1.1\r\n" - FIELD_ENCODING = "%s: %s\r\n" - - def escape(s) - if defined?(EscapeUtils) - EscapeUtils.escape_url(s.to_s) - else - s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/) { - '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase - } + module AblyHttpRequest + module HttpEncoding + HTTP_REQUEST_HEADER="%s %s HTTP/1.1\r\n" + FIELD_ENCODING = "%s: %s\r\n" + + def escape(s) + if defined?(EscapeUtils) + EscapeUtils.escape_url(s.to_s) + else + s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/) { + '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase + } + end end - end - def unescape(s) - if defined?(EscapeUtils) - EscapeUtils.unescape_url(s.to_s) - else - s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/) { - [$1.delete('%')].pack('H*') - } + def unescape(s) + if defined?(EscapeUtils) + EscapeUtils.unescape_url(s.to_s) + else + s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/) { + [$1.delete('%')].pack('H*') + } + end end - end - if ''.respond_to?(:bytesize) - def bytesize(string) - string.bytesize - end - else - def bytesize(string) - string.size + if ''.respond_to?(:bytesize) + def bytesize(string) + string.bytesize + end + else + def bytesize(string) + string.size + end end - end - # Map all header keys to a downcased string version - def munge_header_keys(head) - head.inject({}) { |h, (k, v)| h[k.to_s.downcase] = v; h } - end - - def encode_host - if @req.uri.port.nil? || @req.uri.port == 80 || @req.uri.port == 443 - return @req.uri.host - else - @req.uri.host + ":#{@req.uri.port}" + # Map all header keys to a downcased string version + def munge_header_keys(head) + head.inject({}) { |h, (k, v)| h[k.to_s.downcase] = v; h } end - end - def encode_request(method, uri, query, connopts) - query = encode_query(uri, query) - - # Non CONNECT proxies require that you provide the full request - # uri in request header, as opposed to a relative path. - # Don't modify the header with CONNECT proxies. It's unneeded and will - # cause 400 Bad Request errors with many standard setups. - if connopts.proxy && !connopts.connect_proxy? - query = uri.join(query) - # Drop the userinfo, it's been converted to a header and won't be - # accepted by the proxy - query.userinfo = nil + def encode_host + if @req.uri.port.nil? || @req.uri.port == 80 || @req.uri.port == 443 + return @req.uri.host + else + @req.uri.host + ":#{@req.uri.port}" + end end - HTTP_REQUEST_HEADER % [method.to_s.upcase, query] - end + def encode_request(method, uri, query, connopts) + query = encode_query(uri, query) + + # Non CONNECT proxies require that you provide the full request + # uri in request header, as opposed to a relative path. + # Don't modify the header with CONNECT proxies. It's unneeded and will + # cause 400 Bad Request errors with many standard setups. + if connopts.proxy && !connopts.connect_proxy? + query = uri.join(query) + # Drop the userinfo, it's been converted to a header and won't be + # accepted by the proxy + query.userinfo = nil + end - def encode_query(uri, query) - encoded_query = if query.kind_of?(Hash) - query.map { |k, v| encode_param(k, v) }.join('&') - else - query.to_s + HTTP_REQUEST_HEADER % [method.to_s.upcase, query] end - if uri && !uri.query.to_s.empty? - encoded_query = [encoded_query, uri.query].reject {|part| part.empty?}.join("&") + def encode_query(uri, query) + encoded_query = if query.kind_of?(Hash) + query.map { |k, v| encode_param(k, v) }.join('&') + else + query.to_s + end + + if uri && !uri.query.to_s.empty? + encoded_query = [encoded_query, uri.query].reject {|part| part.empty?}.join("&") + end + encoded_query.to_s.empty? ? uri.path : "#{uri.path}?#{encoded_query}" end - encoded_query.to_s.empty? ? uri.path : "#{uri.path}?#{encoded_query}" - end - # URL encodes query parameters: - # single k=v, or a URL encoded array, if v is an array of values - def encode_param(k, v) - if v.is_a?(Array) - v.map { |e| escape(k) + "[]=" + escape(e) }.join("&") - else - escape(k) + "=" + escape(v) + # URL encodes query parameters: + # single k=v, or a URL encoded array, if v is an array of values + def encode_param(k, v) + if v.is_a?(Array) + v.map { |e| escape(k) + "[]=" + escape(e) }.join("&") + else + escape(k) + "=" + escape(v) + end end - end - def form_encode_body(obj) - pairs = [] - recursive = Proc.new do |h, prefix| - h.each do |k,v| - key = prefix == '' ? escape(k) : "#{prefix}[#{escape(k)}]" - - if v.is_a? Array - nh = Hash.new - v.size.times { |t| nh[t] = v[t] } - recursive.call(nh, key) - - elsif v.is_a? Hash - recursive.call(v, key) - else - pairs << "#{key}=#{escape(v)}" + def form_encode_body(obj) + pairs = [] + recursive = Proc.new do |h, prefix| + h.each do |k,v| + key = prefix == '' ? escape(k) : "#{prefix}[#{escape(k)}]" + + if v.is_a? Array + nh = Hash.new + v.size.times { |t| nh[t] = v[t] } + recursive.call(nh, key) + + elsif v.is_a? Hash + recursive.call(v, key) + else + pairs << "#{key}=#{escape(v)}" + end end end - end - recursive.call(obj, '') - return pairs.join('&') - end + recursive.call(obj, '') + return pairs.join('&') + end - # Encode a field in an HTTP header - def encode_field(k, v) - FIELD_ENCODING % [k, v] - end + # Encode a field in an HTTP header + def encode_field(k, v) + FIELD_ENCODING % [k, v] + end - # Encode basic auth in an HTTP header - # In: Array ([user, pass]) - for basic auth - # String - custom auth string (OAuth, etc) - def encode_auth(k,v) - if v.is_a? Array - FIELD_ENCODING % [k, ["Basic", Base64.strict_encode64(v.join(":")).split.join].join(" ")] - else - encode_field(k,v) + # Encode basic auth in an HTTP header + # In: Array ([user, pass]) - for basic auth + # String - custom auth string (OAuth, etc) + def encode_auth(k,v) + if v.is_a? Array + FIELD_ENCODING % [k, ["Basic", Base64.strict_encode64(v.join(":")).split.join].join(" ")] + else + encode_field(k,v) + end end - end - def encode_headers(head) - head.inject('') do |result, (key, value)| - # Munge keys from foo-bar-baz to Foo-Bar-Baz - key = key.split('-').map { |k| k.to_s.capitalize }.join('-') - result << case key - when 'Authorization', 'Proxy-Authorization' - encode_auth(key, value) - else - encode_field(key, value) + def encode_headers(head) + head.inject('') do |result, (key, value)| + # Munge keys from foo-bar-baz to Foo-Bar-Baz + key = key.split('-').map { |k| k.to_s.capitalize }.join('-') + result << case key + when 'Authorization', 'Proxy-Authorization' + encode_auth(key, value) + else + encode_field(key, value) + end end end - end - def encode_cookie(cookie) - if cookie.is_a? Hash - cookie.inject('') { |result, (k, v)| result << encode_param(k, v) + ";" } - else - cookie + def encode_cookie(cookie) + if cookie.is_a? Hash + cookie.inject('') { |result, (k, v)| result << encode_param(k, v) + ";" } + else + cookie + end end end end diff --git a/lib/em-http/http_header.rb b/lib/em-http/http_header.rb index 9a3f271a..d1fcd18e 100644 --- a/lib/em-http/http_header.rb +++ b/lib/em-http/http_header.rb @@ -1,83 +1,85 @@ module EventMachine - # A simple hash is returned for each request made by HttpClient with the - # headers that were given by the server for that request. - class HttpResponseHeader < Hash - # The reason returned in the http response (string - e.g. "OK") - attr_accessor :http_reason - - # The HTTP version returned (string - e.g. "1.1") - attr_accessor :http_version - - # The status code (integer - e.g. 200) - attr_accessor :http_status - - # Raw headers - attr_accessor :raw - - # E-Tag - def etag - self[HttpClient::ETAG] - end - - def last_modified - self[HttpClient::LAST_MODIFIED] - end - - # HTTP response status - def status - Integer(http_status) rescue 0 - end - - # Length of content as an integer, or nil if chunked/unspecified - def content_length - @content_length ||= ((s = self[HttpClient::CONTENT_LENGTH]) && - (s =~ /^(\d+)$/)) ? $1.to_i : nil - end - - # Cookie header from the server - def cookie - self[HttpClient::SET_COOKIE] - end - - # Is the transfer encoding chunked? - def chunked_encoding? - /chunked/i === self[HttpClient::TRANSFER_ENCODING] - end - - def keepalive? - /keep-alive/i === self[HttpClient::KEEP_ALIVE] - end - - def compressed? - /gzip|compressed|deflate/i === self[HttpClient::CONTENT_ENCODING] - end - - def location - self[HttpClient::LOCATION] - end - - def [](key) - super(key) || super(key.upcase.gsub('-','_')) - end - - def informational? - 100 <= status && 200 > status - end - - def successful? - 200 <= status && 300 > status - end - - def redirection? - 300 <= status && 400 > status - end - - def client_error? - 400 <= status && 500 > status - end - - def server_error? - 500 <= status && 600 > status + module AblyHttpRequest + # A simple hash is returned for each request made by HttpClient with the + # headers that were given by the server for that request. + class HttpResponseHeader < Hash + # The reason returned in the http response (string - e.g. "OK") + attr_accessor :http_reason + + # The HTTP version returned (string - e.g. "1.1") + attr_accessor :http_version + + # The status code (integer - e.g. 200) + attr_accessor :http_status + + # Raw headers + attr_accessor :raw + + # E-Tag + def etag + self[HttpClient::ETAG] + end + + def last_modified + self[HttpClient::LAST_MODIFIED] + end + + # HTTP response status + def status + Integer(http_status) rescue 0 + end + + # Length of content as an integer, or nil if chunked/unspecified + def content_length + @content_length ||= ((s = self[HttpClient::CONTENT_LENGTH]) && + (s =~ /^(\d+)$/)) ? $1.to_i : nil + end + + # Cookie header from the server + def cookie + self[HttpClient::SET_COOKIE] + end + + # Is the transfer encoding chunked? + def chunked_encoding? + /chunked/i === self[HttpClient::TRANSFER_ENCODING] + end + + def keepalive? + /keep-alive/i === self[HttpClient::KEEP_ALIVE] + end + + def compressed? + /gzip|compressed|deflate/i === self[HttpClient::CONTENT_ENCODING] + end + + def location + self[HttpClient::LOCATION] + end + + def [](key) + super(key) || super(key.upcase.gsub('-','_')) + end + + def informational? + 100 <= status && 200 > status + end + + def successful? + 200 <= status && 300 > status + end + + def redirection? + 300 <= status && 400 > status + end + + def client_error? + 400 <= status && 500 > status + end + + def server_error? + 500 <= status && 600 > status + end end end end diff --git a/lib/em-http/http_status_codes.rb b/lib/em-http/http_status_codes.rb index c99ef252..be4a442d 100644 --- a/lib/em-http/http_status_codes.rb +++ b/lib/em-http/http_status_codes.rb @@ -1,57 +1,59 @@ module EventMachine - module HttpStatus - CODE = { - 100 => 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 226 => 'IM Used', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 306 => 'Reserved', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 426 => 'Upgrade Required', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 506 => 'Variant Also Negotiates', - 507 => 'Insufficient Storage', - 510 => 'Not Extended' - } + module AblyHttpRequest + module HttpStatus + CODE = { + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Reserved', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 426 => 'Upgrade Required', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 510 => 'Not Extended' + } + end end end diff --git a/lib/em-http/middleware/digest_auth.rb b/lib/em-http/middleware/digest_auth.rb index 7610a449..c96fe74a 100644 --- a/lib/em-http/middleware/digest_auth.rb +++ b/lib/em-http/middleware/digest_auth.rb @@ -1,111 +1,113 @@ module EventMachine - module Middleware - require 'digest' - require 'securerandom' + module AblyHttpRequest + module Middleware + require 'digest' + require 'securerandom' - class DigestAuth - include EventMachine::HttpEncoding + class DigestAuth + include EventMachine::AblyHttpRequest::HttpEncoding - attr_accessor :auth_digest, :is_digest_auth + attr_accessor :auth_digest, :is_digest_auth - def initialize(www_authenticate, opts = {}) - @nonce_count = -1 - @opts = opts - @digest_params = { - algorithm: 'MD5' # MD5 is the default hashing algorithm - } - if (@is_digest_auth = www_authenticate =~ /^Digest/) - get_params(www_authenticate) + def initialize(www_authenticate, opts = {}) + @nonce_count = -1 + @opts = opts + @digest_params = { + algorithm: 'MD5' # MD5 is the default hashing algorithm + } + if (@is_digest_auth = www_authenticate =~ /^Digest/) + get_params(www_authenticate) + end end - end - def request(client, head, body) - # Allow HTTP basic auth fallback - if @is_digest_auth - head['Authorization'] = build_auth_digest(client.req.method, client.req.uri.path, @opts.merge(@digest_params)) - else - head['Authorization'] = [@opts[:username], @opts[:password]] + def request(client, head, body) + # Allow HTTP basic auth fallback + if @is_digest_auth + head['Authorization'] = build_auth_digest(client.req.method, client.req.uri.path, @opts.merge(@digest_params)) + else + head['Authorization'] = [@opts[:username], @opts[:password]] + end + [head, body] end - [head, body] - end - def response(resp) - # If the server responds with the Authentication-Info header, set the nonce to the new value - if @is_digest_auth && (authentication_info = resp.response_header['Authentication-Info']) - authentication_info =~ /nextnonce="?(.*?)"?(,|\z)/ - @digest_params[:nonce] = $1 + def response(resp) + # If the server responds with the Authentication-Info header, set the nonce to the new value + if @is_digest_auth && (authentication_info = resp.response_header['Authentication-Info']) + authentication_info =~ /nextnonce="?(.*?)"?(,|\z)/ + @digest_params[:nonce] = $1 + end end - end - def build_auth_digest(method, uri, params = nil) - params = @opts.merge(@digest_params) if !params - nonce_count = next_nonce + def build_auth_digest(method, uri, params = nil) + params = @opts.merge(@digest_params) if !params + nonce_count = next_nonce - user = unescape params[:username] - password = unescape params[:password] + user = unescape params[:username] + password = unescape params[:password] - splitted_algorithm = params[:algorithm].split('-') - sess = "-sess" if splitted_algorithm[1] - raw_algorithm = splitted_algorithm[0] - if %w(MD5 SHA1 SHA2 SHA256 SHA384 SHA512 RMD160).include? raw_algorithm - algorithm = eval("Digest::#{raw_algorithm}") - else - raise "Unknown algorithm: #{raw_algorithm}" - end - qop = params[:qop] - cnonce = make_cnonce if qop or sess - a1 = if sess - [ - algorithm.hexdigest("#{params[:username]}:#{params[:realm]}:#{params[:password]}"), - params[:nonce], - cnonce, - ].join ':' - else - "#{params[:username]}:#{params[:realm]}:#{params[:password]}" - end - ha1 = algorithm.hexdigest a1 - ha2 = algorithm.hexdigest "#{method}:#{uri}" + splitted_algorithm = params[:algorithm].split('-') + sess = "-sess" if splitted_algorithm[1] + raw_algorithm = splitted_algorithm[0] + if %w(MD5 SHA1 SHA2 SHA256 SHA384 SHA512 RMD160).include? raw_algorithm + algorithm = eval("Digest::#{raw_algorithm}") + else + raise "Unknown algorithm: #{raw_algorithm}" + end + qop = params[:qop] + cnonce = make_cnonce if qop or sess + a1 = if sess + [ + algorithm.hexdigest("#{params[:username]}:#{params[:realm]}:#{params[:password]}"), + params[:nonce], + cnonce, + ].join ':' + else + "#{params[:username]}:#{params[:realm]}:#{params[:password]}" + end + ha1 = algorithm.hexdigest a1 + ha2 = algorithm.hexdigest "#{method}:#{uri}" - request_digest = [ha1, params[:nonce]] - request_digest.push(('%08x' % @nonce_count), cnonce, qop) if qop - request_digest << ha2 - request_digest = request_digest.join ':' - header = [ - "Digest username=\"#{params[:username]}\"", - "realm=\"#{params[:realm]}\"", - "algorithm=#{raw_algorithm}#{sess}", - "uri=\"#{uri}\"", - "nonce=\"#{params[:nonce]}\"", - "response=\"#{algorithm.hexdigest(request_digest)[0, 32]}\"", - ] - if params[:qop] - header << "qop=#{qop}" - header << "nc=#{'%08x' % @nonce_count}" - header << "cnonce=\"#{cnonce}\"" + request_digest = [ha1, params[:nonce]] + request_digest.push(('%08x' % @nonce_count), cnonce, qop) if qop + request_digest << ha2 + request_digest = request_digest.join ':' + header = [ + "Digest username=\"#{params[:username]}\"", + "realm=\"#{params[:realm]}\"", + "algorithm=#{raw_algorithm}#{sess}", + "uri=\"#{uri}\"", + "nonce=\"#{params[:nonce]}\"", + "response=\"#{algorithm.hexdigest(request_digest)[0, 32]}\"", + ] + if params[:qop] + header << "qop=#{qop}" + header << "nc=#{'%08x' % @nonce_count}" + header << "cnonce=\"#{cnonce}\"" + end + header << "opaque=\"#{params[:opaque]}\"" if params.key? :opaque + header.join(', ') end - header << "opaque=\"#{params[:opaque]}\"" if params.key? :opaque - header.join(', ') - end - # Process the WWW_AUTHENTICATE header to get the authentication parameters - def get_params(www_authenticate) - www_authenticate.scan(/(\w+)="?(.*?)"?(,|\z)/).each do |match| - @digest_params[match[0].to_sym] = match[1] + # Process the WWW_AUTHENTICATE header to get the authentication parameters + def get_params(www_authenticate) + www_authenticate.scan(/(\w+)="?(.*?)"?(,|\z)/).each do |match| + @digest_params[match[0].to_sym] = match[1] + end end - end - # Generate a client nonce - def make_cnonce - Digest::MD5.hexdigest [ - Time.now.to_i, - $$, - SecureRandom.random_number(2**32), - ].join ':' - end + # Generate a client nonce + def make_cnonce + Digest::MD5.hexdigest [ + Time.now.to_i, + $$, + SecureRandom.random_number(2**32), + ].join ':' + end - # Keep track of the nounce count - def next_nonce - @nonce_count += 1 + # Keep track of the nounce count + def next_nonce + @nonce_count += 1 + end end end end diff --git a/lib/em-http/middleware/json_response.rb b/lib/em-http/middleware/json_response.rb index 1d749345..fcf089a9 100644 --- a/lib/em-http/middleware/json_response.rb +++ b/lib/em-http/middleware/json_response.rb @@ -1,13 +1,15 @@ require 'multi_json' module EventMachine - module Middleware - class JSONResponse - def response(resp) - begin - body = MultiJson.load(resp.response) - resp.response = body - rescue => e + module AblyHttpRequest + module Middleware + class JSONResponse + def response(resp) + begin + body = MultiJson.load(resp.response) + resp.response = body + rescue => e + end end end end diff --git a/lib/em-http/middleware/oauth.rb b/lib/em-http/middleware/oauth.rb index b583f05e..e9d058fa 100644 --- a/lib/em-http/middleware/oauth.rb +++ b/lib/em-http/middleware/oauth.rb @@ -1,39 +1,41 @@ require 'simple_oauth' module EventMachine - module Middleware + module AblyHttpRequest + module Middleware - class OAuth - include HttpEncoding + class OAuth + include HttpEncoding - def initialize(opts = {}) - @opts = opts.dup - # Allow both `oauth` gem and `simple_oauth` gem opts formats - @opts[:token] ||= @opts.delete(:access_token) - @opts[:token_secret] ||= @opts.delete(:access_token_secret) - end - - def request(client, head, body) - request = client.req - uri = request.uri.join(encode_query(request.uri, request.query)) - params = {} - - # from https://github.com/oauth/oauth-ruby/blob/master/lib/oauth/request_proxy/em_http_request.rb - if ["POST", "PUT"].include?(request.method) - head["content-type"] ||= "application/x-www-form-urlencoded" if body.is_a? Hash - form_encoded = head["content-type"].to_s.downcase.start_with?("application/x-www-form-urlencoded") + def initialize(opts = {}) + @opts = opts.dup + # Allow both `oauth` gem and `simple_oauth` gem opts formats + @opts[:token] ||= @opts.delete(:access_token) + @opts[:token_secret] ||= @opts.delete(:access_token_secret) + end - if form_encoded - CGI.parse(client.normalize_body(body)).each do |k,v| - # Since `CGI.parse` always returns values as an array - params[k] = v.size == 1 ? v.first : v + def request(client, head, body) + request = client.req + uri = request.uri.join(encode_query(request.uri, request.query)) + params = {} + + # from https://github.com/oauth/oauth-ruby/blob/master/lib/oauth/request_proxy/em_http_request.rb + if ["POST", "PUT"].include?(request.method) + head["content-type"] ||= "application/x-www-form-urlencoded" if body.is_a? Hash + form_encoded = head["content-type"].to_s.downcase.start_with?("application/x-www-form-urlencoded") + + if form_encoded + CGI.parse(client.normalize_body(body)).each do |k,v| + # Since `CGI.parse` always returns values as an array + params[k] = v.size == 1 ? v.first : v + end end end - end - head["Authorization"] = SimpleOAuth::Header.new(request.method, uri, params, @opts) + head["Authorization"] = SimpleOAuth::Header.new(request.method, uri, params, @opts) - [head,body] + [head,body] + end end end end diff --git a/lib/em-http/middleware/oauth2.rb b/lib/em-http/middleware/oauth2.rb index 0d30a0bc..b5235f59 100644 --- a/lib/em-http/middleware/oauth2.rb +++ b/lib/em-http/middleware/oauth2.rb @@ -1,26 +1,28 @@ module EventMachine - module Middleware - class OAuth2 - include EM::HttpEncoding - attr_accessor :access_token + module AblyHttpRequest + module Middleware + class OAuth2 + include EM::AblyHttpRequest::HttpEncoding + attr_accessor :access_token - def initialize(opts={}) - self.access_token = opts[:access_token] or raise "No :access_token provided" - end + def initialize(opts={}) + self.access_token = opts[:access_token] or raise "No :access_token provided" + end - def request(client, head, body) - uri = client.req.uri.dup - update_uri! uri - client.req.set_uri uri + def request(client, head, body) + uri = client.req.uri.dup + update_uri! uri + client.req.set_uri uri - [head, body] - end + [head, body] + end - def update_uri!(uri) - if uri.query.nil? - uri.query = encode_param(:access_token, access_token) - else - uri.query += "&#{encode_param(:access_token, access_token)}" + def update_uri!(uri) + if uri.query.nil? + uri.query = encode_param(:access_token, access_token) + else + uri.query += "&#{encode_param(:access_token, access_token)}" + end end end end diff --git a/lib/em-http/multi.rb b/lib/em-http/multi.rb index 7583c4f5..a7b4bb18 100644 --- a/lib/em-http/multi.rb +++ b/lib/em-http/multi.rb @@ -1,57 +1,59 @@ module EventMachine + module AblyHttpRequest + + # EventMachine based Multi request client, based on a streaming HTTPRequest class, + # which allows you to open multiple parallel connections and return only when all + # of them finish. (i.e. ideal for parallelizing workloads) + # + # == Example + # + # EventMachine.run { + # + # multi = EventMachine::AblyHttpRequest::MultiRequest.new + # + # # add multiple requests to the multi-handler + # multi.add(:a, EventMachine::AblyHttpRequest::HttpRequest.new('http://www.google.com/').get) + # multi.add(:b, EventMachine::AblyHttpRequest::HttpRequest.new('http://www.yahoo.com/').get) + # + # multi.callback { + # p multi.responses[:callback] + # p multi.responses[:errback] + # + # EventMachine.stop + # } + # } + # + + class MultiRequest + include EventMachine::Deferrable + + attr_reader :requests, :responses + + def initialize + @requests = {} + @responses = {:callback => {}, :errback => {}} + end + + def add(name, conn) + raise 'Duplicate Multi key' if @requests.key? name + + @requests[name] = conn + + conn.callback { @responses[:callback][name] = conn; check_progress } + conn.errback { @responses[:errback][name] = conn; check_progress } + end + + def finished? + (@responses[:callback].size + @responses[:errback].size) == @requests.size + end + + protected + + # invoke callback if all requests have completed + def check_progress + succeed(self) if finished? + end - # EventMachine based Multi request client, based on a streaming HTTPRequest class, - # which allows you to open multiple parallel connections and return only when all - # of them finish. (i.e. ideal for parallelizing workloads) - # - # == Example - # - # EventMachine.run { - # - # multi = EventMachine::MultiRequest.new - # - # # add multiple requests to the multi-handler - # multi.add(:a, EventMachine::HttpRequest.new('http://www.google.com/').get) - # multi.add(:b, EventMachine::HttpRequest.new('http://www.yahoo.com/').get) - # - # multi.callback { - # p multi.responses[:callback] - # p multi.responses[:errback] - # - # EventMachine.stop - # } - # } - # - - class MultiRequest - include EventMachine::Deferrable - - attr_reader :requests, :responses - - def initialize - @requests = {} - @responses = {:callback => {}, :errback => {}} end - - def add(name, conn) - raise 'Duplicate Multi key' if @requests.key? name - - @requests[name] = conn - - conn.callback { @responses[:callback][name] = conn; check_progress } - conn.errback { @responses[:errback][name] = conn; check_progress } - end - - def finished? - (@responses[:callback].size + @responses[:errback].size) == @requests.size - end - - protected - - # invoke callback if all requests have completed - def check_progress - succeed(self) if finished? - end - end end diff --git a/lib/em-http/request.rb b/lib/em-http/request.rb index e9e2308b..ef310117 100644 --- a/lib/em-http/request.rb +++ b/lib/em-http/request.rb @@ -1,23 +1,25 @@ module EventMachine - class HttpRequest - @middleware = [] + module AblyHttpRequest + class HttpRequest + @middleware = [] - def self.new(uri, options={}) - uri = uri.clone - connopt = HttpConnectionOptions.new(uri, options) + def self.new(uri, options={}) + uri = uri.clone + connopt = ::AblyHttpRequest::HttpConnectionOptions.new(uri, options) - c = HttpConnection.new - c.connopts = connopt - c.uri = uri - c - end + c = HttpConnection.new + c.connopts = connopt + c.uri = uri + c + end - def self.use(klass, *args, &block) - @middleware << klass.new(*args, &block) - end + def self.use(klass, *args, &block) + @middleware << klass.new(*args, &block) + end - def self.middleware - @middleware + def self.middleware + @middleware + end end end end diff --git a/lib/em-http/version.rb b/lib/em-http/version.rb index 171c79b7..90eddde1 100644 --- a/lib/em-http/version.rb +++ b/lib/em-http/version.rb @@ -1,5 +1,7 @@ module EventMachine - class HttpRequest - VERSION = "1.1.7" + module AblyHttpRequest + class HttpRequest + VERSION = "1.1.7" + end end end diff --git a/lib/em/io_streamer.rb b/lib/em/io_streamer.rb index fc2f2f19..57fa4eb1 100644 --- a/lib/em/io_streamer.rb +++ b/lib/em/io_streamer.rb @@ -2,46 +2,48 @@ # similar to EventMachine::FileStreamer, but for any IO object module EventMachine - class IOStreamer - include Deferrable - CHUNK_SIZE = 16384 + module AblyHttpRequest + class IOStreamer + include Deferrable + CHUNK_SIZE = 16384 - # @param [EventMachine::Connection] connection - # @param [IO] io Data source - # @param [Integer] Data size - # - # @option opts [Boolean] :http_chunks (false) Use HTTP 1.1 style chunked-encoding semantics. - def initialize(connection, io, opts = {}) - @connection = connection - @io = io - @http_chunks = opts[:http_chunks] + # @param [EventMachine::Connection] connection + # @param [IO] io Data source + # @param [Integer] Data size + # + # @option opts [Boolean] :http_chunks (false) Use HTTP 1.1 style chunked-encoding semantics. + def initialize(connection, io, opts = {}) + @connection = connection + @io = io + @http_chunks = opts[:http_chunks] - @buff = String.new - @io.binmode if @io.respond_to?(:binmode) - stream_one_chunk - end + @buff = String.new + @io.binmode if @io.respond_to?(:binmode) + stream_one_chunk + end - private + private - # Used internally to stream one chunk at a time over multiple reactor ticks - # @private - def stream_one_chunk - loop do - if @io.eof? - @connection.send_data "0\r\n\r\n" if @http_chunks - succeed - break - end + # Used internally to stream one chunk at a time over multiple reactor ticks + # @private + def stream_one_chunk + loop do + if @io.eof? + @connection.send_data "0\r\n\r\n" if @http_chunks + succeed + break + end - if @connection.respond_to?(:get_outbound_data_size) && (@connection.get_outbound_data_size > FileStreamer::BackpressureLevel) - EventMachine::next_tick { stream_one_chunk } - break - end + if @connection.respond_to?(:get_outbound_data_size) && (@connection.get_outbound_data_size > FileStreamer::BackpressureLevel) + EventMachine::next_tick { stream_one_chunk } + break + end - if @io.read(CHUNK_SIZE, @buff) - @connection.send_data("#{@buff.length.to_s(16)}\r\n") if @http_chunks - @connection.send_data(@buff) - @connection.send_data("\r\n") if @http_chunks + if @io.read(CHUNK_SIZE, @buff) + @connection.send_data("#{@buff.length.to_s(16)}\r\n") if @http_chunks + @connection.send_data(@buff) + @connection.send_data("\r\n") if @http_chunks + end end end end diff --git a/spec/client_fiber_spec.rb b/spec/client_fiber_spec.rb index b7a58aca..19fa7051 100644 --- a/spec/client_fiber_spec.rb +++ b/spec/client_fiber_spec.rb @@ -1,7 +1,7 @@ require 'helper' require 'fiber' -describe EventMachine::HttpRequest do +describe EventMachine::AblyHttpRequest::HttpRequest do context "with fibers" do it "should be transparent to connection errors" do @@ -9,7 +9,7 @@ Fiber.new do f = Fiber.current fired = false - http = EventMachine::HttpRequest.new('http://non-existing.domain/', :connection_timeout => 0.1).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://non-existing.domain/', :connection_timeout => 0.1).get http.callback { failed(http) } http.errback { f.resume :errback } diff --git a/spec/client_spec.rb b/spec/client_spec.rb index c8bfe456..b513075c 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -1,6 +1,6 @@ require 'helper' -describe EventMachine::HttpRequest do +describe EventMachine::AblyHttpRequest::HttpRequest do def failed(http=nil) EventMachine.stop @@ -9,7 +9,7 @@ def failed(http=nil) it "should perform successful GET" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').get http.errback { failed(http) } http.callback { @@ -23,7 +23,7 @@ def failed(http=nil) it "should perform successful GET with a URI passed as argument" do EventMachine.run { uri = URI.parse('http://127.0.0.1:8090/') - http = EventMachine::HttpRequest.new(uri).get + http = EventMachine::AblyHttpRequest::HttpRequest.new(uri).get http.errback { failed(http) } http.callback { @@ -37,7 +37,7 @@ def failed(http=nil) it "should succeed GET on missing path" do EventMachine.run { lambda { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090').get http.callback { http.response.should match(/Hello/) EventMachine.stop @@ -50,7 +50,7 @@ def failed(http=nil) it "should raise error on invalid URL" do EventMachine.run { lambda { - EventMachine::HttpRequest.new('random?text').get + EventMachine::AblyHttpRequest::HttpRequest.new('random?text').get }.should raise_error(Addressable::URI::InvalidURIError) EM.stop @@ -60,7 +60,7 @@ def failed(http=nil) it "should perform successful HEAD with a URI passed as argument" do EventMachine.run { uri = URI.parse('http://127.0.0.1:8090/') - http = EventMachine::HttpRequest.new(uri).head + http = EventMachine::AblyHttpRequest::HttpRequest.new(uri).head http.errback { failed(http) } http.callback { @@ -74,7 +74,7 @@ def failed(http=nil) it "should perform successful DELETE with a URI passed as argument" do EventMachine.run { uri = URI.parse('http://127.0.0.1:8090/') - http = EventMachine::HttpRequest.new(uri).delete + http = EventMachine::AblyHttpRequest::HttpRequest.new(uri).delete http.errback { failed(http) } http.callback { @@ -87,7 +87,7 @@ def failed(http=nil) it "should return 404 on invalid path" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/fail').get http.errback { failed(http) } http.callback { @@ -99,7 +99,7 @@ def failed(http=nil) it "should return HTTP reason" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/fail').get http.errback { failed(http) } http.callback { @@ -112,7 +112,7 @@ def failed(http=nil) it "should return HTTP reason 'unknown' on a non-standard status code" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail_with_nonstandard_response').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/fail_with_nonstandard_response').get http.errback { failed(http) } http.callback { @@ -125,7 +125,7 @@ def failed(http=nil) it "should build query parameters from Hash" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :query => {:q => 'test'} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').get :query => {:q => 'test'} http.errback { failed(http) } http.callback { @@ -138,7 +138,7 @@ def failed(http=nil) it "should pass query parameters string" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :query => "q=test" + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').get :query => "q=test" http.errback { failed(http) } http.callback { @@ -151,7 +151,7 @@ def failed(http=nil) it "should encode an array of query parameters" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_query').get :query => {:hash =>['value1','value2']} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_query').get :query => {:hash =>['value1','value2']} http.errback { failed(http) } http.callback { @@ -164,7 +164,7 @@ def failed(http=nil) it "should perform successful PUT" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').put :body => "data" + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').put :body => "data" http.errback { failed(http) } http.callback { @@ -177,7 +177,7 @@ def failed(http=nil) it "should perform successful POST" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => "data" + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').post :body => "data" http.errback { failed(http) } http.callback { @@ -190,7 +190,7 @@ def failed(http=nil) it "should perform successful PATCH" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').patch :body => "data" + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').patch :body => "data" http.errback { failed(http) } http.callback { @@ -203,7 +203,7 @@ def failed(http=nil) it "should escape body on POST" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => {:stuff => 'string&string'} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').post :body => {:stuff => 'string&string'} http.errback { failed(http) } http.callback { @@ -216,7 +216,7 @@ def failed(http=nil) it "should perform successful POST with Ruby Hash/Array as params" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => {"key1" => 1, "key2" => [2,3]} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').post :body => {"key1" => 1, "key2" => [2,3]} http.errback { failed(http) } http.callback { @@ -230,7 +230,7 @@ def failed(http=nil) it "should set content-length to 0 on posts with empty bodies" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_length_from_header').post + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_content_length_from_header').post http.errback { failed(http) } http.callback { @@ -244,7 +244,7 @@ def failed(http=nil) it "should perform successful POST with Ruby Hash/Array as params and with the correct content length" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_length').post :body => {"key1" => "data1"} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_content_length').post :body => {"key1" => "data1"} http.errback { failed(http) } http.callback { @@ -258,7 +258,7 @@ def failed(http=nil) xit "should support expect-continue header" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090').post :body => "data", :head => { 'expect' => '100-continue' } + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090').post :body => "data", :head => { 'expect' => '100-continue' } http.errback { failed(http) } http.callback { @@ -271,7 +271,7 @@ def failed(http=nil) it "should perform successful GET with custom header" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :head => {'if-none-match' => 'evar!'} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').get :head => {'if-none-match' => 'evar!'} http.errback { p http; failed(http) } http.callback { @@ -284,7 +284,7 @@ def failed(http=nil) it "should perform basic auth" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/authtest').get :head => {'authorization' => ['user', 'pass']} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/authtest').get :head => {'authorization' => ['user', 'pass']} http.errback { failed(http) } http.callback { @@ -297,7 +297,7 @@ def failed(http=nil) it "should perform basic auth via the URL" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://user:pass@127.0.0.1:8090/authtest').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://user:pass@127.0.0.1:8090/authtest').get http.errback { failed(http) } http.callback { @@ -310,7 +310,7 @@ def failed(http=nil) it "should return peer's IP address" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/') conn.peer.should be_nil http = conn.get @@ -329,7 +329,7 @@ def failed(http=nil) it "should remove all newlines from long basic auth header" do EventMachine.run { auth = {'authorization' => ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz']} - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => auth + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => auth http.errback { failed(http) } http.callback { http.response_header.status.should == 200 @@ -342,7 +342,7 @@ def failed(http=nil) it "should send proper OAuth auth header" do EventMachine.run { oauth_header = 'OAuth oauth_nonce="oqwgSYFUD87MHmJJDv7bQqOF2EPnVus7Wkqj5duNByU", b=c, d=e' - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => { + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => { 'authorization' => oauth_header } @@ -357,7 +357,7 @@ def failed(http=nil) it "should return ETag and Last-Modified headers" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_query').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_query').get http.errback { failed(http) } http.callback { @@ -371,7 +371,7 @@ def failed(http=nil) it "should return raw headers in a hash" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_headers').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_headers').get http.errback { failed(http) } http.callback { @@ -386,7 +386,7 @@ def failed(http=nil) it "should detect deflate encoding" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => {"accept-encoding" => "deflate"} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => {"accept-encoding" => "deflate"} http.errback { failed(http) } http.callback { @@ -402,7 +402,7 @@ def failed(http=nil) it "should auto-detect and decode gzip encoding" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {"accept-encoding" => "gzip, compressed"} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {"accept-encoding" => "gzip, compressed"} http.errback { failed(http) } http.callback { @@ -421,7 +421,7 @@ def failed(http=nil) EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip-large').get :head => {"accept-encoding" => "gzip, compressed"} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/gzip-large').get :head => {"accept-encoding" => "gzip, compressed"} http.errback { failed(http) } http.callback { @@ -442,7 +442,7 @@ def failed(http=nil) it "should not decode the response when configured so" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => { + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => { "accept-encoding" => "gzip, compressed" }, :decoding => false @@ -462,7 +462,7 @@ def failed(http=nil) it "should default to requesting compressed response" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_accept_encoding').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_accept_encoding').get http.errback { failed(http) } http.callback { @@ -477,7 +477,7 @@ def failed(http=nil) it "should default to requesting compressed response" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_accept_encoding').get :compressed => false + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_accept_encoding').get :compressed => false http.errback { failed(http) } http.callback { @@ -493,7 +493,7 @@ def failed(http=nil) EventMachine.run { t = Time.now.to_i EventMachine.heartbeat_interval = 0.1 - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/timeout', :inactivity_timeout => 0.1).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/timeout', :inactivity_timeout => 0.1).get http.errback { http.error.should == Errno::ETIMEDOUT @@ -506,7 +506,7 @@ def failed(http=nil) it "should complete a Location: with a relative path" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/relative-location').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/relative-location').get http.errback { failed(http) } http.callback { @@ -519,7 +519,7 @@ def failed(http=nil) context "body content-type encoding" do it "should not set content type on string in body" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => "data" + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => "data" http.errback { failed(http) } http.callback { @@ -532,7 +532,7 @@ def failed(http=nil) it "should set content-type automatically when passed a ruby hash/array for body" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => {:a => :b} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => {:a => :b} http.errback { failed(http) } http.callback { @@ -546,7 +546,7 @@ def failed(http=nil) it "should not override content-type when passing in ruby hash/array for body" do EventMachine.run { ct = 'text; charset=utf-8' - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({ + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({ :body => {:a => :b}, :head => {'content-type' => ct}}) http.errback { failed(http) } @@ -562,7 +562,7 @@ def failed(http=nil) it "should default to external encoding on invalid encoding" do EventMachine.run { ct = 'text/html; charset=utf-8lias' - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({ + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({ :body => {:a => :b}, :head => {'content-type' => ct}}) http.errback { failed(http) } @@ -578,7 +578,7 @@ def failed(http=nil) it "should processed escaped content-type" do EventMachine.run { ct = "text/html; charset=\"ISO-8859-4\"" - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({ + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({ :body => {:a => :b}, :head => {'content-type' => ct}}) http.errback { failed(http) } @@ -595,7 +595,7 @@ def failed(http=nil) context "optional header callback" do it "should optionally pass the response headers" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').get http.errback { failed(http) } http.headers { |hash| @@ -614,7 +614,7 @@ def failed(http=nil) it "should allow to terminate current connection from header callback" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').get http.callback { failed(http) } http.headers { |hash| @@ -638,7 +638,7 @@ def failed(http=nil) it "should optionally pass the response body progressively" do EventMachine.run { body = '' - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').get http.errback { failed(http) } http.stream { |chunk| body += chunk } @@ -655,7 +655,7 @@ def failed(http=nil) it "should optionally pass the deflate-encoded response body progressively" do EventMachine.run { body = '' - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => { + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => { "accept-encoding" => "deflate, compressed" } @@ -674,7 +674,7 @@ def failed(http=nil) it "should accept & return cookie header to user" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/set_cookie').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/set_cookie').get http.errback { failed(http) } http.callback { @@ -687,7 +687,7 @@ def failed(http=nil) it "should return array of cookies on multiple Set-Cookie headers" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/set_multiple_cookies').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/set_multiple_cookies').get http.errback { failed(http) } http.callback { @@ -703,7 +703,7 @@ def failed(http=nil) it "should pass cookie header to server from string" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => 'id=2;'} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => 'id=2;'} http.errback { failed(http) } http.callback { @@ -715,7 +715,7 @@ def failed(http=nil) it "should pass cookie header to server from Hash" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => {'id' => 2}} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => {'id' => 2}} http.errback { failed(http) } http.callback { @@ -729,7 +729,7 @@ def failed(http=nil) EventMachine.run { @s = StubServer.new("HTTP/1.1 200 OK\r\n\r\nFoo") - http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8081/').get http.errback { failed(http) } http.callback { http.response.should match(/Foo/) @@ -747,7 +747,7 @@ def failed(http=nil) EventMachine.run { @s = StubServer.new("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nFoo") - http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8081/').get http.errback { failed(http) } http.callback { http.response.should match(/Foo/) @@ -763,7 +763,7 @@ def failed(http=nil) EventMachine.run { @s = StubServer.new("HTTP/1.0 200 OK\nContent-Type: text/plain\nContent-Length: 3\nConnection: close\n\nFoo") - http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8081/').get http.errback { failed(http) } http.callback { http.response_header.status.should == 200 @@ -780,7 +780,7 @@ def failed(http=nil) EventMachine.run { @s = StubServer.new("") - http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8081/').get http.callback { failed(http) } http.errback { http.error.should_not be_nil @@ -792,7 +792,7 @@ def failed(http=nil) it "should stream a file off disk" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :file => 'spec/fixtures/google.ca' + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').post :file => 'spec/fixtures/google.ca' http.errback { failed(http) } http.callback { @@ -804,7 +804,7 @@ def failed(http=nil) it "streams POST request from disk via Pathname" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => Pathname.new('spec/fixtures/google.ca') + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').post :body => Pathname.new('spec/fixtures/google.ca') http.errback { failed(http) } http.callback { http.response.should match('google') @@ -815,7 +815,7 @@ def failed(http=nil) it "streams POST request from IO object" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => StringIO.new(File.read('spec/fixtures/google.ca')) + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').post :body => StringIO.new(File.read('spec/fixtures/google.ca')) http.errback { failed(http) } http.callback { http.response.should match('google') @@ -826,7 +826,7 @@ def failed(http=nil) it "should reconnect if connection was closed between requests" do EventMachine.run { - conn = EM::HttpRequest.new('http://127.0.0.1:8090/') + conn = EM::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/') req = conn.get req.callback do @@ -846,7 +846,7 @@ def failed(http=nil) it "should report error if connection was closed by server on client keepalive requests" do EventMachine.run { - conn = EM::HttpRequest.new('http://127.0.0.1:8090/') + conn = EM::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/') req = conn.get :keepalive => true req.callback do @@ -874,7 +874,7 @@ def failed(http=nil) HTTP @s = StubServer.new(response) - http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8081/').get http.errback { failed(http) } http.callback { http.content_charset.should == Encoding::ISO_8859_1 if defined? Encoding @@ -896,7 +896,7 @@ def failed(http=nil) HTTP @s = StubServer.new(response) - http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8081/').get http.errback { failed(http) } http.callback { http.response_header["Content-Type"].should == "text/plain; charset=utf-8" @@ -931,7 +931,7 @@ def failed(http=nil) @s = StubServer.new(response) lambda { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8081/') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8081/') req = conn.get req.errback { failed(http) } req.callback { EM.stop } @@ -943,7 +943,7 @@ def failed(http=nil) context "User-Agent" do it 'should default to "EventMachine HttpClient"' do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get http.errback { failed(http) } http.callback { @@ -955,7 +955,7 @@ def failed(http=nil) it 'should keep header if given empty string' do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get(:head => { 'user-agent'=>'' }) + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get(:head => { 'user-agent'=>'' }) http.errback { failed(http) } http.callback { @@ -967,7 +967,7 @@ def failed(http=nil) it 'should ommit header if given nil' do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get(:head => { 'user-agent'=>nil }) + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get(:head => { 'user-agent'=>nil }) http.errback { failed(http) } http.callback { @@ -986,7 +986,7 @@ def failed(http=nil) port: 8091, host: '::1', }) - http = EventMachine::HttpRequest.new('http://[::1]:8091/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://[::1]:8091/').get http.errback { failed(http) } http.callback { diff --git a/spec/digest_auth_spec.rb b/spec/digest_auth_spec.rb index 85d1a37e..a52bf222 100644 --- a/spec/digest_auth_spec.rb +++ b/spec/digest_auth_spec.rb @@ -17,7 +17,7 @@ password: 'digest_password' } - middleware = EM::Middleware::DigestAuth.new(www_authenticate, params) + middleware = EM::AblyHttpRequest::Middleware::DigestAuth.new(www_authenticate, params) middleware.build_auth_digest('GET', '/').should == @reference_header end @@ -29,7 +29,7 @@ password: 'digest_password' } - middleware = EM::Middleware::DigestAuth.new(www_authenticate, params) + middleware = EM::AblyHttpRequest::Middleware::DigestAuth.new(www_authenticate, params) middleware.build_auth_digest('GET', '/').should_not == @reference_header end @@ -41,7 +41,7 @@ password: 'digest_password' } - middleware = EM::Middleware::DigestAuth.new(www_authenticate, params) + middleware = EM::AblyHttpRequest::Middleware::DigestAuth.new(www_authenticate, params) middleware.build_auth_digest('GET', '/').should_not == @reference_header end diff --git a/spec/dns_spec.rb b/spec/dns_spec.rb index 7aae6b36..2f23e71b 100644 --- a/spec/dns_spec.rb +++ b/spec/dns_spec.rb @@ -1,10 +1,10 @@ require 'helper' -describe EventMachine::HttpRequest do +describe EventMachine::AblyHttpRequest::HttpRequest do it "should fail gracefully on an invalid host in Location header" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/badhost', :connect_timeout => 0.1).get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/badhost', :connect_timeout => 0.1).get :redirects => 1 http.callback { failed(http) } http.errback { http.error.should match(/unable to resolve (server |)address/) @@ -16,7 +16,7 @@ it "should fail GET on DNS timeout" do EventMachine.run { EventMachine.heartbeat_interval = 0.1 - http = EventMachine::HttpRequest.new('http://127.1.1.1/', :connect_timeout => 0.1).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.1.1.1/', :connect_timeout => 0.1).get http.callback { failed(http) } http.errback { http.response_header.status.should == 0 @@ -28,7 +28,7 @@ it "should fail GET on invalid host" do EventMachine.run { EventMachine.heartbeat_interval = 0.1 - http = EventMachine::HttpRequest.new('http://somethinglocal/', :connect_timeout => 0.1).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://somethinglocal/', :connect_timeout => 0.1).get http.callback { failed(http) } http.errback { http.error.should match(/unable to resolve (server |)address/) diff --git a/spec/encoding_spec.rb b/spec/encoding_spec.rb index 29b8171d..6c77b5be 100644 --- a/spec/encoding_spec.rb +++ b/spec/encoding_spec.rb @@ -2,8 +2,8 @@ require 'helper' -describe EventMachine::HttpEncoding do - include EventMachine::HttpEncoding +describe EventMachine::AblyHttpRequest::HttpEncoding do + include EventMachine::AblyHttpRequest::HttpEncoding it "should transform a basic hash into HTTP POST Params" do form_encode_body({:a => "alpha", :b => "beta"}).should == "a=alpha&b=beta" diff --git a/spec/external_spec.rb b/spec/external_spec.rb index ec609c73..b40b74e7 100644 --- a/spec/external_spec.rb +++ b/spec/external_spec.rb @@ -2,11 +2,11 @@ requires_connection do - describe EventMachine::HttpRequest do + describe EventMachine::AblyHttpRequest::HttpRequest do it "should follow redirects on HEAD method (external)" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://www.google.com/').head :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://www.google.com/').head :redirects => 1 http.errback { failed(http) } http.callback { http.response_header.status.should == 200 @@ -17,7 +17,7 @@ it "should follow redirect to https and initiate the handshake" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://github.com/').get :redirects => 5 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://github.com/').get :redirects => 5 http.errback { failed(http) } http.callback { @@ -31,7 +31,7 @@ EventMachine.run { # digg.com uses chunked encoding - http = EventMachine::HttpRequest.new('http://www.httpwatch.com/httpgallery/chunked/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://www.httpwatch.com/httpgallery/chunked/').get http.errback { failed(http) } http.callback { @@ -68,7 +68,7 @@ # MUST send a final response after the request has been completed. url = 'http://ws.serviceobjects.com/lv/LeadValidation.asmx/ValidateLead_V2' - http = EventMachine::HttpRequest.new(url).post :body => {:name => :test} + http = EventMachine::AblyHttpRequest::HttpRequest.new(url).post :body => {:name => :test} http.errback { failed(http) } http.callback { @@ -83,7 +83,7 @@ EventMachine.run { options = {:head => {"accept-encoding" => "deflate"}, :redirects => 5} - http = EventMachine::HttpRequest.new('https://www.bing.com/').get options + http = EventMachine::AblyHttpRequest::HttpRequest.new('https://www.bing.com/').get options http.errback { failed(http) } http.callback { @@ -99,7 +99,7 @@ EventMachine.run { options = {:head => {"accept-encoding" => "gzip"}} # GitHub sends chunked gzip, time for a little Inception ;) - http = EventMachine::HttpRequest.new('https://github.com/igrigorik/em-http-request/commits/master').get options + http = EventMachine::AblyHttpRequest::HttpRequest.new('https://github.com/igrigorik/em-http-request/commits/master').get options http.errback { failed(http) } http.callback { @@ -121,7 +121,7 @@ it "should default to non-keepalive" do EventMachine.run { headers = {'If-Modified-Since' => 'Thu, 05 Aug 2010 22:54:44 GMT'} - http = EventMachine::HttpRequest.new('http://www.google.com/images/logos/ps_logo2.png').get :head => headers + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://www.google.com/images/logos/ps_logo2.png').get :head => headers http.errback { fail } start = Time.now.to_i @@ -134,7 +134,7 @@ it "should work with keep-alive servers" do EventMachine.run { - http = EventMachine::HttpRequest.new('https://github.com/igrigorik/em-http-request').get :keepalive => true + http = EventMachine::AblyHttpRequest::HttpRequest.new('https://github.com/igrigorik/em-http-request').get :keepalive => true http.errback { failed(http) } http.callback { diff --git a/spec/gzip_spec.rb b/spec/gzip_spec.rb index 88792daf..f7eb94b9 100644 --- a/spec/gzip_spec.rb +++ b/spec/gzip_spec.rb @@ -1,13 +1,13 @@ require 'helper' -describe EventMachine::HttpDecoders::GZip do +describe EventMachine::AblyHttpRequest::HttpDecoders::GZip do let(:compressed) { compressed = ["1f8b08089668a6500003686900cbc8e402007a7a6fed03000000"].pack("H*") } it "should extract the stream of a vanilla gzip" do - header = EventMachine::HttpDecoders::GZipHeader.new + header = EventMachine::AblyHttpRequest::HttpDecoders::GZipHeader.new stream = header.extract_stream(compressed) stream.unpack("H*")[0].should eq("cbc8e402007a7a6fed03000000") @@ -16,7 +16,7 @@ it "should decompress a vanilla gzip" do decompressed = "" - gz = EventMachine::HttpDecoders::GZip.new do |data| + gz = EventMachine::AblyHttpRequest::HttpDecoders::GZip.new do |data| decompressed << data end @@ -29,7 +29,7 @@ it "should decompress a vanilla gzip file byte by byte" do decompressed = "" - gz = EventMachine::HttpDecoders::GZip.new do |data| + gz = EventMachine::AblyHttpRequest::HttpDecoders::GZip.new do |data| decompressed << data end @@ -45,7 +45,7 @@ it "should decompress a large file" do decompressed = "" - gz = EventMachine::HttpDecoders::GZip.new do |data| + gz = EventMachine::AblyHttpRequest::HttpDecoders::GZip.new do |data| decompressed << data end @@ -65,7 +65,7 @@ examples.each do |example| decompressed = "" - gz = EventMachine::HttpDecoders::GZip.new do |data| + gz = EventMachine::AblyHttpRequest::HttpDecoders::GZip.new do |data| decompressed << data end @@ -81,11 +81,11 @@ it "should fail with a DecoderError if not a gzip file" do not_a_gzip = ["1f8c08089668a650000"].pack("H*") - header = EventMachine::HttpDecoders::GZipHeader.new + header = EventMachine::AblyHttpRequest::HttpDecoders::GZipHeader.new lambda { header.extract_stream(not_a_gzip) - }.should raise_exception(EventMachine::HttpDecoders::DecoderError) + }.should raise_exception(EventMachine::AblyHttpRequest::HttpDecoders::DecoderError) end end diff --git a/spec/http_proxy_spec.rb b/spec/http_proxy_spec.rb index 98c420f2..ce024640 100644 --- a/spec/http_proxy_spec.rb +++ b/spec/http_proxy_spec.rb @@ -3,7 +3,7 @@ shared_examples "*_PROXY var (through proxy)" do it "should use HTTP proxy" do EventMachine.run { - http = EventMachine::HttpRequest.new("#{proxy_test_scheme}://127.0.0.1:8090/?q=test").get + http = EventMachine::AblyHttpRequest::HttpRequest.new("#{proxy_test_scheme}://127.0.0.1:8090/?q=test").get http.errback { failed(http) } http.callback { @@ -17,7 +17,7 @@ end shared_examples "*_PROXY var (testing var)" do - subject { HttpConnectionOptions.new("#{proxy_test_scheme}://example.com", {}) } + subject { AblyHttpRequest::HttpConnectionOptions.new("#{proxy_test_scheme}://example.com", {}) } it { expect(subject.proxy_from_env).to eq({ :host => "127.0.0.1", :port => 8083, :type => :http }) } it { expect(subject.host).to eq "127.0.0.1" } it { expect(subject.port).to be 8083 } @@ -31,7 +31,7 @@ end end -describe EventMachine::HttpRequest do +describe EventMachine::AblyHttpRequest::HttpRequest do context "connections via" do context "without *_PROXY env" do @@ -40,7 +40,7 @@ it "should use HTTP proxy" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/?q=test', proxy).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/?q=test', proxy).get http.errback { failed(http) } http.callback { @@ -54,7 +54,7 @@ it "should use HTTP proxy with authentication" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/proxyauth?q=test', authenticated_proxy).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/proxyauth?q=test', authenticated_proxy).get http.errback { failed(http) } http.callback { @@ -69,7 +69,7 @@ it "should send absolute URIs to the proxy server" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/?q=test', proxy).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/?q=test', proxy).get http.errback { failed(http) } http.callback { @@ -87,7 +87,7 @@ it "should strip basic auth from before the host in URI sent to proxy" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://user:pass@127.0.0.1:8090/echo_authorization_header', proxy).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://user:pass@127.0.0.1:8090/echo_authorization_header', proxy).get http.errback { failed(http) } http.callback { @@ -103,7 +103,7 @@ it "should include query parameters specified in the options" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/', proxy).get :query => { 'q' => 'test' } + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/', proxy).get :query => { 'q' => 'test' } http.errback { failed(http) } http.callback { @@ -116,7 +116,7 @@ it "should use HTTP proxy while redirecting" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect', proxy).get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect', proxy).get :redirects => 1 http.errback { failed(http) } http.callback { @@ -202,7 +202,7 @@ PROXY_ENV_VARS.each {|k| ENV.delete k } end - subject { HttpConnectionOptions.new("http://example.com", {}) } + subject { AblyHttpRequest::HttpConnectionOptions.new("http://example.com", {}) } it { expect(subject.proxy_from_env).to be_nil } it { expect(subject.host).to eq "example.com" } it { expect(subject.port).to be 80 } diff --git a/spec/middleware/oauth2_spec.rb b/spec/middleware/oauth2_spec.rb index 750d8357..6b6acd40 100644 --- a/spec/middleware/oauth2_spec.rb +++ b/spec/middleware/oauth2_spec.rb @@ -1,13 +1,13 @@ -describe EventMachine::Middleware::OAuth2 do +describe EventMachine::AblyHttpRequest::Middleware::OAuth2 do it "should add an access token to a URI with no query parameters" do - middleware = EventMachine::Middleware::OAuth2.new(:access_token => "fedcba9876543210") + middleware = EventMachine::AblyHttpRequest::Middleware::OAuth2.new(:access_token => "fedcba9876543210") uri = Addressable::URI.parse("https://graph.facebook.com/me") middleware.update_uri! uri uri.to_s.should == "https://graph.facebook.com/me?access_token=fedcba9876543210" end it "should add an access token to a URI with query parameters" do - middleware = EventMachine::Middleware::OAuth2.new(:access_token => "fedcba9876543210") + middleware = EventMachine::AblyHttpRequest::Middleware::OAuth2.new(:access_token => "fedcba9876543210") uri = Addressable::URI.parse("https://graph.facebook.com/me?fields=photo") middleware.update_uri! uri uri.to_s.should == "https://graph.facebook.com/me?fields=photo&access_token=fedcba9876543210" diff --git a/spec/middleware_spec.rb b/spec/middleware_spec.rb index 9703d6d3..7a28d234 100644 --- a/spec/middleware_spec.rb +++ b/spec/middleware_spec.rb @@ -1,6 +1,6 @@ require 'helper' -describe EventMachine::HttpRequest do +describe EventMachine::AblyHttpRequest::HttpRequest do class EmptyMiddleware; end @@ -13,7 +13,7 @@ def response(resp) it "should accept middleware" do EventMachine.run { lambda { - conn = EM::HttpRequest.new('http://127.0.0.1:8090') + conn = EM::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090') conn.use ResponseMiddleware conn.use EmptyMiddleware @@ -37,7 +37,7 @@ def response(resp) it "should accept middleware initialization parameters" do EventMachine.run { - conn = EM::HttpRequest.new('http://127.0.0.1:8090') + conn = EM::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090') conn.use ConfigurableMiddleware, 'conf-value' do 'block-value' end @@ -62,7 +62,7 @@ def response(resp) it "should execute response middleware before user callbacks" do EventMachine.run { - conn = EM::HttpRequest.new('http://127.0.0.1:8090') + conn = EM::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090') conn.use ResponseMiddleware req = conn.get @@ -76,9 +76,9 @@ def response(resp) it "should execute global response middleware before user callbacks" do EventMachine.run { - EM::HttpRequest.use GlobalMiddleware + EM::AblyHttpRequest::HttpRequest.use GlobalMiddleware - conn = EM::HttpRequest.new('http://127.0.0.1:8090') + conn = EM::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090') req = conn.get req.callback { @@ -101,7 +101,7 @@ def request(client, head, body) it "should execute request middleware before dispatching request" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/') conn.use RequestMiddleware req = conn.post :body => "data" @@ -127,7 +127,7 @@ def response(resp) it "should use middleware to JSON encode and JSON decode the body" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/') conn.use JSONify req = conn.post :body => {:ruby => :hash} diff --git a/spec/multi_spec.rb b/spec/multi_spec.rb index cc52483a..de3b958f 100644 --- a/spec/multi_spec.rb +++ b/spec/multi_spec.rb @@ -1,18 +1,18 @@ require 'helper' require 'stallion' -describe EventMachine::MultiRequest do +describe EventMachine::AblyHttpRequest::MultiRequest do - let(:multi) { EventMachine::MultiRequest.new } + let(:multi) { EventMachine::AblyHttpRequest::MultiRequest.new } let(:url) { 'http://127.0.0.1:8090/' } it "should submit multiple requests in parallel and return once all of them are complete" do EventMachine.run { - multi.add :a, EventMachine::HttpRequest.new(url).get - multi.add :b, EventMachine::HttpRequest.new(url).post - multi.add :c, EventMachine::HttpRequest.new(url).head - multi.add :d, EventMachine::HttpRequest.new(url).delete - multi.add :e, EventMachine::HttpRequest.new(url).put + multi.add :a, EventMachine::AblyHttpRequest::HttpRequest.new(url).get + multi.add :b, EventMachine::AblyHttpRequest::HttpRequest.new(url).post + multi.add :c, EventMachine::AblyHttpRequest::HttpRequest.new(url).head + multi.add :d, EventMachine::AblyHttpRequest::HttpRequest.new(url).delete + multi.add :e, EventMachine::AblyHttpRequest::HttpRequest.new(url).put multi.callback { multi.responses[:callback].size.should == 5 @@ -60,8 +60,8 @@ it "should provide access to the requests by name" do EventMachine.run { - request1 = EventMachine::HttpRequest.new(url).get - request2 = EventMachine::HttpRequest.new(url).post + request1 = EventMachine::AblyHttpRequest::HttpRequest.new(url).get + request2 = EventMachine::AblyHttpRequest::HttpRequest.new(url).post multi.add :a, request1 multi.add :b, request2 @@ -82,7 +82,7 @@ it "should be false while the requests are not finished" do EventMachine.run { - multi.add :a, EventMachine::HttpRequest.new(url).get + multi.add :a, EventMachine::AblyHttpRequest::HttpRequest.new(url).get multi.should_not be_finished EventMachine.stop @@ -91,7 +91,7 @@ it "should be finished when all requests are finished" do EventMachine.run { - multi.add :a, EventMachine::HttpRequest.new(url).get + multi.add :a, EventMachine::AblyHttpRequest::HttpRequest.new(url).get multi.callback { multi.should be_finished diff --git a/spec/pipelining_spec.rb b/spec/pipelining_spec.rb index 35f9d056..d83f008b 100644 --- a/spec/pipelining_spec.rb +++ b/spec/pipelining_spec.rb @@ -2,13 +2,13 @@ requires_connection do - describe EventMachine::HttpRequest do + describe EventMachine::AblyHttpRequest::HttpRequest do it "should perform successful pipelined GETs" do EventMachine.run do # Mongrel doesn't support pipelined requests - bah! - conn = EventMachine::HttpRequest.new('http://www.bing.com/') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://www.bing.com/') pipe1 = conn.get :keepalive => true pipe2 = conn.get :path => '/news', :keepalive => true @@ -36,7 +36,7 @@ it "should perform successful pipelined HEAD requests" do EventMachine.run do - conn = EventMachine::HttpRequest.new('http://www.bing.com/') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://www.bing.com/') pipe1 = conn.head :keepalive => true pipe2 = conn.head :path => '/news', :keepalive => true diff --git a/spec/redirect_spec.rb b/spec/redirect_spec.rb index 5f18d493..2410ba2a 100644 --- a/spec/redirect_spec.rb +++ b/spec/redirect_spec.rb @@ -22,11 +22,11 @@ def response(r) end end -describe EventMachine::HttpRequest do +describe EventMachine::AblyHttpRequest::HttpRequest do it "should follow location redirects" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect').get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect').get :redirects => 1 http.errback { failed(http) } http.callback { http.response_header.status.should == 200 @@ -42,7 +42,7 @@ def response(r) it "should not follow redirects on created" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/created').get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/created').get :redirects => 1 http.errback { failed(http) } http.callback { http.response_header.status.should == 201 @@ -66,7 +66,7 @@ def response(r) @stub = StubServer.new(:host => '127.0.0.1', :port => 8070, :response => response) @echo = StubServer.new(:host => 'localhost', :port => 8071, :echo => true) - http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1 http.errback { failed(http) } http.callback do @@ -92,7 +92,7 @@ def response(r) @stub = StubServer.new(:host => '127.0.0.1', :port => 8070, :response => response) @echo = StubServer.new(:host => '127.0.0.1', :port => 8071, :echo => true) - http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1 http.errback { failed(http) } http.callback do @@ -119,7 +119,7 @@ def response(r) @stub = StubServer.new(:host => '127.0.0.1', :port => 8070, :response => response) @echo = StubServer.new(:host => '127.0.0.1', :port => 8071, :echo => true) - http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1 http.errback { failed(http) } http.callback do @@ -136,7 +136,7 @@ def response(r) response = "HTTP/1.0 301 MOVED PERMANENTLY\r\nlocation: http://127.0.0.1:8090/redirect\r\n\r\n" @stub = StubServer.new(:host => '127.0.0.1', :port => 8070, :response => response) - http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 3 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 3 http.errback { failed(http) } http.callback { @@ -154,7 +154,7 @@ def response(r) it "should follow redirects on HEAD method" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/head').head :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/head').head :redirects => 1 http.errback { failed(http) } http.callback { http.response_header.status.should == 200 @@ -166,7 +166,7 @@ def response(r) it "should report last_effective_url" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/').get http.errback { failed(http) } http.callback { http.response_header.status.should == 200 @@ -179,7 +179,7 @@ def response(r) it "should default to 0 redirects" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect').get http.errback { failed(http) } http.callback { http.response_header.status.should == 301 @@ -193,7 +193,7 @@ def response(r) it "should not invoke redirect logic on failed(http) connections" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/', :connect_timeout => 0.1).get :redirects => 5 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8070/', :connect_timeout => 0.1).get :redirects => 5 http.callback { failed(http) } http.errback { http.redirects.should == 0 @@ -204,7 +204,7 @@ def response(r) it "should normalize redirect urls" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/bad').get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/bad').get :redirects => 1 http.errback { failed(http) } http.callback { http.last_effective_url.to_s.should match('http://127.0.0.1:8090/') @@ -216,7 +216,7 @@ def response(r) it "should fail gracefully on a missing host in absolute Location header" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/nohost').get :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/nohost').get :redirects => 1 http.callback { failed(http) } http.errback { http.error.should == 'Location header format error' @@ -230,7 +230,7 @@ def response(r) t = Time.now.to_i EventMachine.heartbeat_interval = 0.1 - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/timeout', :inactivity_timeout => 0.1) + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/timeout', :inactivity_timeout => 0.1) http = conn.get :redirects => 1 http.callback { failed(http) } http.errback { @@ -242,7 +242,7 @@ def response(r) it "should capture and pass cookies on redirect and pass_cookies by default" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/multiple-with-cookie').get :redirects => 2, :head => {'cookie' => 'id=2;'} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/multiple-with-cookie').get :redirects => 2, :head => {'cookie' => 'id=2;'} http.errback { failed(http) } http.callback { http.response_header.status.should == 200 @@ -260,7 +260,7 @@ def response(r) it "should capture and not pass cookies on redirect if passing is disabled via pass_cookies" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/multiple-with-cookie').get :redirects => 2, :pass_cookies => false, :head => {'cookie' => 'id=2;'} + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/multiple-with-cookie').get :redirects => 2, :pass_cookies => false, :head => {'cookie' => 'id=2;'} http.errback { failed(http) } http.callback { http.response_header.status.should == 200 @@ -278,7 +278,7 @@ def response(r) it "should follow location redirects with path" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect').get :path => '/redirect', :redirects => 1 + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect').get :path => '/redirect', :redirects => 1 http.errback { failed(http) } http.callback { http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/gzip' @@ -292,7 +292,7 @@ def response(r) it "should call middleware each time it redirects" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/middleware_redirects_1') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/middleware_redirects_1') conn.use RedirectMiddleware http = conn.get :redirects => 3 http.errback { failed(http) } @@ -306,7 +306,7 @@ def response(r) it "should call middleware which may reject a redirection" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/middleware_redirects_1') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/middleware_redirects_1') conn.use PickyRedirectMiddleware http = conn.get :redirects => 3 http.errback { failed(http) } @@ -320,7 +320,7 @@ def response(r) it "should not add default http port to redirect url that don't include it" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/http_no_port') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/http_no_port') http = conn.get :redirects => 1 http.errback { http.last_effective_url.to_s.should == 'http://host/' @@ -331,7 +331,7 @@ def response(r) it "should not add default https port to redirect url that don't include it" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/https_no_port') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/https_no_port') http = conn.get :redirects => 1 http.errback { http.last_effective_url.to_s.should == 'https://host/' @@ -342,7 +342,7 @@ def response(r) it "should keep default http port in redirect url that include it" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/http_with_port') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/http_with_port') http = conn.get :redirects => 1 http.errback { http.last_effective_url.to_s.should == 'http://host:80/' @@ -353,7 +353,7 @@ def response(r) it "should keep default https port in redirect url that include it" do EventMachine.run { - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/https_with_port') + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/https_with_port') http = conn.get :redirects => 1 http.errback { http.last_effective_url.to_s.should == 'https://host:443/' @@ -364,7 +364,7 @@ def response(r) it "should ignore query option when redirecting" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/ignore_query_option').get :redirects => 1, :query => 'ignore=1' + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8090/redirect/ignore_query_option').get :redirects => 1, :query => 'ignore=1' http.errback { failed(http) } http.callback { http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/redirect/url' @@ -389,7 +389,7 @@ def response(r) HTTP stub_server = StubServer.new(:host => '127.0.0.1', :port => 8070, :keepalive => true, :response => response) - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8070/', :inactivity_timeout => 60) + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8070/', :inactivity_timeout => 60) http = conn.get :redirects => 1, :keepalive => true http.errback { failed(http) } http.callback { @@ -414,7 +414,7 @@ def response(r) HTTP stub_server = StubServer.new(:host => '127.0.0.1', :port => 8070, :keepalive => true, :response => response) - conn = EventMachine::HttpRequest.new('http://127.0.0.1:8070/', :inactivity_timeout => 60) + conn = EventMachine::AblyHttpRequest::HttpRequest.new('http://127.0.0.1:8070/', :inactivity_timeout => 60) http = conn.get :redirects => 1, :keepalive => true http.errback { failed(http) } http.callback { diff --git a/spec/socksify_proxy_spec.rb b/spec/socksify_proxy_spec.rb index 57cd46f4..ff93b8b4 100644 --- a/spec/socksify_proxy_spec.rb +++ b/spec/socksify_proxy_spec.rb @@ -3,14 +3,14 @@ requires_connection do requires_port(8080) do - describe EventMachine::HttpRequest do + describe EventMachine::AblyHttpRequest::HttpRequest do # ssh -D 8080 igvita let(:proxy) { {:proxy => { :host => '127.0.0.1', :port => 8080, :type => :socks5 }} } it "should use SOCKS5 proxy" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://jsonip.com/', proxy).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://jsonip.com/', proxy).get http.errback { failed(http) } http.callback { @@ -24,14 +24,14 @@ end requires_port(8081) do - describe EventMachine::HttpRequest do + describe EventMachine::AblyHttpRequest::HttpRequest do # brew install tinyproxy let(:http_proxy) { {:proxy => { :host => '127.0.0.1', :port => 8081 }} } it "should use HTTP proxy by default" do EventMachine.run { - http = EventMachine::HttpRequest.new('http://jsonip.com/', http_proxy).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('http://jsonip.com/', http_proxy).get http.errback { failed(http) } http.callback { @@ -44,7 +44,7 @@ it "should auto CONNECT via HTTP proxy for HTTPS requests" do EventMachine.run { - http = EventMachine::HttpRequest.new('https://ipjson.herokuapp.com/', http_proxy).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('https://ipjson.herokuapp.com/', http_proxy).get http.errback { failed(http) } http.callback { diff --git a/spec/ssl_spec.rb b/spec/ssl_spec.rb index 9806cd9f..82244d98 100644 --- a/spec/ssl_spec.rb +++ b/spec/ssl_spec.rb @@ -2,10 +2,10 @@ requires_connection do - describe EventMachine::HttpRequest do + describe EventMachine::AblyHttpRequest::HttpRequest do it "should initiate SSL/TLS on HTTPS connections" do EventMachine.run { - http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail/').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('https://mail.google.com:443/mail/').get http.errback { failed(http) } http.callback { @@ -29,7 +29,7 @@ it "should not warn if verify_peer is specified" do EventMachine.run { - http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail', {tls: {verify_peer: false}}).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('https://mail.google.com:443/mail', {tls: {verify_peer: false}}).get http.callback { $stderr.rewind @@ -42,7 +42,7 @@ it "should not warn if verify_peer is true" do EventMachine.run { - http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail', {tls: {verify_peer: true}}).get + http = EventMachine::AblyHttpRequest::HttpRequest.new('https://mail.google.com:443/mail', {tls: {verify_peer: true}}).get http.callback { $stderr.rewind @@ -55,7 +55,7 @@ it "should warn if verify_peer is unspecified" do EventMachine.run { - http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail').get + http = EventMachine::AblyHttpRequest::HttpRequest.new('https://mail.google.com:443/mail').get http.callback { $stderr.rewind