em-http-request 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of em-http-request might be problematic. Click here for more details.
- data/Changelog.md +4 -0
 - data/README.md +2 -2
 - data/em-http-request.gemspec +4 -4
 - data/examples/digest_auth/client.rb +25 -0
 - data/examples/digest_auth/server.rb +28 -0
 - data/examples/fibered-http.rb +1 -1
 - data/lib/em-http/client.rb +13 -6
 - data/lib/em-http/decoders.rb +138 -29
 - data/lib/em-http/http_client_options.rb +6 -5
 - data/lib/em-http/http_connection.rb +9 -4
 - data/lib/em-http/http_connection_options.rb +13 -2
 - data/lib/em-http/http_header.rb +21 -1
 - data/lib/em-http/middleware/digest_auth.rb +112 -0
 - data/lib/em-http/middleware/oauth2.rb +28 -0
 - data/lib/em-http/request.rb +1 -0
 - data/lib/em-http/version.rb +1 -1
 - data/spec/client_spec.rb +62 -7
 - data/spec/digest_auth_spec.rb +48 -0
 - data/spec/external_spec.rb +2 -2
 - data/spec/fixtures/gzip-sample.gz +0 -0
 - data/spec/gzip_spec.rb +68 -0
 - data/spec/helper.rb +1 -0
 - data/spec/middleware/oauth2_spec.rb +15 -0
 - data/spec/pipelining_spec.rb +2 -2
 - data/spec/redirect_spec.rb +66 -12
 - data/spec/socksify_proxy_spec.rb +36 -0
 - data/spec/stallion.rb +7 -0
 - metadata +20 -10
 
| 
         @@ -18,7 +18,8 @@ class HttpConnectionOptions 
     | 
|
| 
       18 
18 
     | 
    
         
             
                end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                uri = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI::parse(uri.to_s)
         
     | 
| 
       21 
     | 
    
         
            -
                 
     | 
| 
      
 21 
     | 
    
         
            +
                @https = uri.scheme == "https"
         
     | 
| 
      
 22 
     | 
    
         
            +
                uri.port ||= (@https ? 443 : 80)
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
       23 
24 
     | 
    
         
             
                if proxy = options[:proxy]
         
     | 
| 
       24 
25 
     | 
    
         
             
                  @host = proxy[:host]
         
     | 
| 
         @@ -29,5 +30,15 @@ class HttpConnectionOptions 
     | 
|
| 
       29 
30 
     | 
    
         
             
                end
         
     | 
| 
       30 
31 
     | 
    
         
             
              end
         
     | 
| 
       31 
32 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
              def http_proxy 
     | 
| 
      
 33 
     | 
    
         
            +
              def http_proxy?
         
     | 
| 
      
 34 
     | 
    
         
            +
                @proxy && (@proxy[:type] == :http || @proxy[:type].nil?) && !@https
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              def connect_proxy?
         
     | 
| 
      
 38 
     | 
    
         
            +
                @proxy && (@proxy[:type] == :http || @proxy[:type].nil?) && @https
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              def socks_proxy?
         
     | 
| 
      
 42 
     | 
    
         
            +
                @proxy && (@proxy[:type] == :socks5)
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
       33 
44 
     | 
    
         
             
            end
         
     | 
    
        data/lib/em-http/http_header.rb
    CHANGED
    
    | 
         @@ -25,7 +25,7 @@ module EventMachine 
     | 
|
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                # HTTP response status as an integer
         
     | 
| 
       27 
27 
     | 
    
         
             
                def status
         
     | 
| 
       28 
     | 
    
         
            -
                  Integer(http_status) rescue 0
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @status ||= Integer(http_status) rescue 0
         
     | 
| 
       29 
29 
     | 
    
         
             
                end
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                # Length of content as an integer, or nil if chunked/unspecified
         
     | 
| 
         @@ -59,5 +59,25 @@ module EventMachine 
     | 
|
| 
       59 
59 
     | 
    
         
             
                def [](key)
         
     | 
| 
       60 
60 
     | 
    
         
             
                  super(key) || super(key.upcase.gsub('-','_'))
         
     | 
| 
       61 
61 
     | 
    
         
             
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def informational?
         
     | 
| 
      
 64 
     | 
    
         
            +
                  100 <= status && 200 > status
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                def successful?
         
     | 
| 
      
 68 
     | 
    
         
            +
                  200 <= status && 300 > status
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def redirection?
         
     | 
| 
      
 72 
     | 
    
         
            +
                  300 <= status && 400 > status
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def client_error?
         
     | 
| 
      
 76 
     | 
    
         
            +
                  400 <= status && 500 > status
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def server_error?
         
     | 
| 
      
 80 
     | 
    
         
            +
                  500 <= status && 600 > status
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
       62 
82 
     | 
    
         
             
              end
         
     | 
| 
       63 
83 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,112 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module EventMachine
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Middleware
         
     | 
| 
      
 3 
     | 
    
         
            +
                require 'digest'
         
     | 
| 
      
 4 
     | 
    
         
            +
                require 'securerandom'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                class DigestAuth
         
     | 
| 
      
 7 
     | 
    
         
            +
                  include EventMachine::HttpEncoding
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_accessor :auth_digest, :is_digest_auth
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def initialize(www_authenticate, opts = {})
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @nonce_count = -1
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @opts = opts
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @digest_params = {
         
     | 
| 
      
 15 
     | 
    
         
            +
                        algorithm: 'MD5' # MD5 is the default hashing algorithm
         
     | 
| 
      
 16 
     | 
    
         
            +
                    }
         
     | 
| 
      
 17 
     | 
    
         
            +
                    if (@is_digest_auth = www_authenticate =~ /^Digest/)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      get_params(www_authenticate)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def request(client, head, body)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    # Allow HTTP basic auth fallback
         
     | 
| 
      
 24 
     | 
    
         
            +
                    if @is_digest_auth
         
     | 
| 
      
 25 
     | 
    
         
            +
                      head['Authorization'] = build_auth_digest(client.req.method, client.req.uri.path, @opts.merge(@digest_params))
         
     | 
| 
      
 26 
     | 
    
         
            +
                    else
         
     | 
| 
      
 27 
     | 
    
         
            +
                      head['Authorization'] = [@opts[:username], @opts[:password]]
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                    [head, body]
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def response(resp)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    # If the server responds with the Authentication-Info header, set the nonce to the new value
         
     | 
| 
      
 34 
     | 
    
         
            +
                    if @is_digest_auth && (authentication_info = resp.response_header['Authentication-Info'])
         
     | 
| 
      
 35 
     | 
    
         
            +
                      authentication_info =~ /nextnonce="?(.*?)"?(,|\z)/
         
     | 
| 
      
 36 
     | 
    
         
            +
                      @digest_params[:nonce] = $1
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def build_auth_digest(method, uri, params = nil)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    params = @opts.merge(@digest_params) if !params
         
     | 
| 
      
 42 
     | 
    
         
            +
                    nonce_count = next_nonce
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    user = unescape params[:username]
         
     | 
| 
      
 45 
     | 
    
         
            +
                    password = unescape params[:password]
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    splitted_algorithm = params[:algorithm].split('-')
         
     | 
| 
      
 48 
     | 
    
         
            +
                    sess = "-sess" if splitted_algorithm[1]
         
     | 
| 
      
 49 
     | 
    
         
            +
                    raw_algorithm = splitted_algorithm[0]
         
     | 
| 
      
 50 
     | 
    
         
            +
                    if %w(MD5 SHA1 SHA2 SHA256 SHA384 SHA512 RMD160).include? raw_algorithm
         
     | 
| 
      
 51 
     | 
    
         
            +
                      algorithm = eval("Digest::#{raw_algorithm}")
         
     | 
| 
      
 52 
     | 
    
         
            +
                    else
         
     | 
| 
      
 53 
     | 
    
         
            +
                      raise "Unknown algorithm: #{raw_algorithm}"
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
                    qop = params[:qop]
         
     | 
| 
      
 56 
     | 
    
         
            +
                    cnonce = make_cnonce if qop or sess
         
     | 
| 
      
 57 
     | 
    
         
            +
                    a1 = if sess
         
     | 
| 
      
 58 
     | 
    
         
            +
                      [
         
     | 
| 
      
 59 
     | 
    
         
            +
                        algorithm.hexdigest("#{params[:username]}:#{params[:realm]}:#{params[:password]}"),
         
     | 
| 
      
 60 
     | 
    
         
            +
                        params[:nonce],
         
     | 
| 
      
 61 
     | 
    
         
            +
                        cnonce,
         
     | 
| 
      
 62 
     | 
    
         
            +
                        ].join ':'
         
     | 
| 
      
 63 
     | 
    
         
            +
                    else
         
     | 
| 
      
 64 
     | 
    
         
            +
                      "#{params[:username]}:#{params[:realm]}:#{params[:password]}"
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                    ha1 = algorithm.hexdigest a1
         
     | 
| 
      
 67 
     | 
    
         
            +
                    ha2 = algorithm.hexdigest "#{method}:#{uri}"
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    request_digest = [ha1, params[:nonce]]
         
     | 
| 
      
 70 
     | 
    
         
            +
                    request_digest.push(('%08x' % @nonce_count), cnonce, qop) if qop
         
     | 
| 
      
 71 
     | 
    
         
            +
                    request_digest << ha2
         
     | 
| 
      
 72 
     | 
    
         
            +
                    request_digest = request_digest.join ':'
         
     | 
| 
      
 73 
     | 
    
         
            +
                    header = [
         
     | 
| 
      
 74 
     | 
    
         
            +
                      "Digest username=\"#{params[:username]}\"",
         
     | 
| 
      
 75 
     | 
    
         
            +
                      "realm=\"#{params[:realm]}\"",
         
     | 
| 
      
 76 
     | 
    
         
            +
                      "algorithm=#{raw_algorithm}#{sess}",
         
     | 
| 
      
 77 
     | 
    
         
            +
                      "uri=\"#{uri}\"",
         
     | 
| 
      
 78 
     | 
    
         
            +
                      "nonce=\"#{params[:nonce]}\"",
         
     | 
| 
      
 79 
     | 
    
         
            +
                      "response=\"#{algorithm.hexdigest(request_digest)[0, 32]}\"",
         
     | 
| 
      
 80 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 81 
     | 
    
         
            +
                    if params[:qop]
         
     | 
| 
      
 82 
     | 
    
         
            +
                      header << "qop=#{qop}"
         
     | 
| 
      
 83 
     | 
    
         
            +
                      header << "nc=#{'%08x' % @nonce_count}"
         
     | 
| 
      
 84 
     | 
    
         
            +
                      header << "cnonce=\"#{cnonce}\""
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
                    header << "opaque=\"#{params[:opaque]}\"" if params.key? :opaque
         
     | 
| 
      
 87 
     | 
    
         
            +
                    header.join(', ')
         
     | 
| 
      
 88 
     | 
    
         
            +
                  end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  # Process the WWW_AUTHENTICATE header to get the authentication parameters
         
     | 
| 
      
 91 
     | 
    
         
            +
                  def get_params(www_authenticate)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    www_authenticate.scan(/(\w+)="?(.*?)"?(,|\z)/).each do |match|
         
     | 
| 
      
 93 
     | 
    
         
            +
                      @digest_params[match[0].to_sym] = match[1]
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  # Generate a client nonce
         
     | 
| 
      
 98 
     | 
    
         
            +
                  def make_cnonce
         
     | 
| 
      
 99 
     | 
    
         
            +
                    Digest::MD5.hexdigest [
         
     | 
| 
      
 100 
     | 
    
         
            +
                      Time.now.to_i,
         
     | 
| 
      
 101 
     | 
    
         
            +
                      $$,
         
     | 
| 
      
 102 
     | 
    
         
            +
                      SecureRandom.random_number(2**32),
         
     | 
| 
      
 103 
     | 
    
         
            +
                    ].join ':'
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  # Keep track of the nounce count
         
     | 
| 
      
 107 
     | 
    
         
            +
                  def next_nonce
         
     | 
| 
      
 108 
     | 
    
         
            +
                    @nonce_count += 1
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
              end
         
     | 
| 
      
 112 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module EventMachine
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Middleware
         
     | 
| 
      
 3 
     | 
    
         
            +
                class OAuth2
         
     | 
| 
      
 4 
     | 
    
         
            +
                  include EM::HttpEncoding
         
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_accessor :access_token
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(opts={})
         
     | 
| 
      
 8 
     | 
    
         
            +
                    self.access_token = opts[:access_token] or raise "No :access_token provided"
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def request(client, head, body)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    uri = client.req.uri.dup
         
     | 
| 
      
 13 
     | 
    
         
            +
                    update_uri! uri
         
     | 
| 
      
 14 
     | 
    
         
            +
                    client.req.set_uri uri
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    [head, body]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def update_uri!(uri)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    if uri.query.nil?
         
     | 
| 
      
 21 
     | 
    
         
            +
                      uri.query = encode_param(:access_token, access_token)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    else
         
     | 
| 
      
 23 
     | 
    
         
            +
                      uri.query += "&#{encode_param(:access_token, access_token)}"
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/em-http/request.rb
    CHANGED
    
    
    
        data/lib/em-http/version.rb
    CHANGED
    
    
    
        data/spec/client_spec.rb
    CHANGED
    
    | 
         @@ -453,6 +453,7 @@ describe EventMachine::HttpRequest do 
     | 
|
| 
       453 
453 
     | 
    
         
             
                  http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/timeout', :inactivity_timeout => 0.1).get
         
     | 
| 
       454 
454 
     | 
    
         | 
| 
       455 
455 
     | 
    
         
             
                  http.errback {
         
     | 
| 
      
 456 
     | 
    
         
            +
                    http.error.should == Errno::ETIMEDOUT
         
     | 
| 
       456 
457 
     | 
    
         
             
                    (Time.now.to_i - t).should <= 1
         
     | 
| 
       457 
458 
     | 
    
         
             
                    EventMachine.stop
         
     | 
| 
       458 
459 
     | 
    
         
             
                  }
         
     | 
| 
         @@ -682,9 +683,6 @@ describe EventMachine::HttpRequest do 
     | 
|
| 
       682 
683 
     | 
    
         
             
              end
         
     | 
| 
       683 
684 
     | 
    
         | 
| 
       684 
685 
     | 
    
         
             
              it "should get the body without Content-Length" do
         
     | 
| 
       685 
     | 
    
         
            -
                pending "blocked on new http_parser.rb release"
         
     | 
| 
       686 
     | 
    
         
            -
                # https://github.com/igrigorik/em-http-request/issues/168
         
     | 
| 
       687 
     | 
    
         
            -
             
     | 
| 
       688 
686 
     | 
    
         
             
                EventMachine.run {
         
     | 
| 
       689 
687 
     | 
    
         
             
                  @s = StubServer.new("HTTP/1.1 200 OK\r\n\r\nFoo")
         
     | 
| 
       690 
688 
     | 
    
         | 
| 
         @@ -763,15 +761,17 @@ describe EventMachine::HttpRequest do 
     | 
|
| 
       763 
761 
     | 
    
         | 
| 
       764 
762 
     | 
    
         
             
              it "should reconnect if connection was closed between requests" do
         
     | 
| 
       765 
763 
     | 
    
         
             
                EventMachine.run {
         
     | 
| 
       766 
     | 
    
         
            -
                  conn = EM::HttpRequest.new('http://127.0.0.1:8090/' 
     | 
| 
       767 
     | 
    
         
            -
                  req = conn.get 
     | 
| 
      
 764 
     | 
    
         
            +
                  conn = EM::HttpRequest.new('http://127.0.0.1:8090/')
         
     | 
| 
      
 765 
     | 
    
         
            +
                  req = conn.get
         
     | 
| 
       768 
766 
     | 
    
         | 
| 
       769 
767 
     | 
    
         
             
                  req.callback do
         
     | 
| 
       770 
     | 
    
         
            -
                     
     | 
| 
       771 
     | 
    
         
            -
                      req = conn.get
         
     | 
| 
      
 768 
     | 
    
         
            +
                    conn.close('client closing connection')
         
     | 
| 
       772 
769 
     | 
    
         | 
| 
      
 770 
     | 
    
         
            +
                    EM.next_tick do
         
     | 
| 
      
 771 
     | 
    
         
            +
                      req = conn.get :path => "/gzip"
         
     | 
| 
       773 
772 
     | 
    
         
             
                      req.callback do
         
     | 
| 
       774 
773 
     | 
    
         
             
                        req.response_header.status.should == 200
         
     | 
| 
      
 774 
     | 
    
         
            +
                        req.response.should match('compressed')
         
     | 
| 
       775 
775 
     | 
    
         
             
                        EventMachine.stop
         
     | 
| 
       776 
776 
     | 
    
         
             
                      end
         
     | 
| 
       777 
777 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -779,6 +779,23 @@ describe EventMachine::HttpRequest do 
     | 
|
| 
       779 
779 
     | 
    
         
             
                }
         
     | 
| 
       780 
780 
     | 
    
         
             
              end
         
     | 
| 
       781 
781 
     | 
    
         | 
| 
      
 782 
     | 
    
         
            +
              it "should report error if connection was closed by server on client keepalive requests" do
         
     | 
| 
      
 783 
     | 
    
         
            +
                EventMachine.run {
         
     | 
| 
      
 784 
     | 
    
         
            +
                  conn = EM::HttpRequest.new('http://127.0.0.1:8090/')
         
     | 
| 
      
 785 
     | 
    
         
            +
                  req = conn.get :keepalive => true
         
     | 
| 
      
 786 
     | 
    
         
            +
             
     | 
| 
      
 787 
     | 
    
         
            +
                  req.callback do
         
     | 
| 
      
 788 
     | 
    
         
            +
                    req = conn.get
         
     | 
| 
      
 789 
     | 
    
         
            +
             
     | 
| 
      
 790 
     | 
    
         
            +
                    req.callback { failed(http) }
         
     | 
| 
      
 791 
     | 
    
         
            +
                    req.errback do
         
     | 
| 
      
 792 
     | 
    
         
            +
                      req.error.should match('connection closed by server')
         
     | 
| 
      
 793 
     | 
    
         
            +
                      EventMachine.stop
         
     | 
| 
      
 794 
     | 
    
         
            +
                    end
         
     | 
| 
      
 795 
     | 
    
         
            +
                  end
         
     | 
| 
      
 796 
     | 
    
         
            +
                }
         
     | 
| 
      
 797 
     | 
    
         
            +
              end
         
     | 
| 
      
 798 
     | 
    
         
            +
             
     | 
| 
       782 
799 
     | 
    
         
             
              it 'should handle malformed Content-Type header repetitions' do
         
     | 
| 
       783 
800 
     | 
    
         
             
                EventMachine.run {
         
     | 
| 
       784 
801 
     | 
    
         
             
                  response =<<-HTTP.gsub(/^ +/, '').strip
         
     | 
| 
         @@ -830,4 +847,42 @@ describe EventMachine::HttpRequest do 
     | 
|
| 
       830 
847 
     | 
    
         
             
                  }
         
     | 
| 
       831 
848 
     | 
    
         
             
                }
         
     | 
| 
       832 
849 
     | 
    
         
             
              end
         
     | 
| 
      
 850 
     | 
    
         
            +
             
     | 
| 
      
 851 
     | 
    
         
            +
              context "User-Agent" do
         
     | 
| 
      
 852 
     | 
    
         
            +
                it 'should default to "EventMachine HttpClient"' do
         
     | 
| 
      
 853 
     | 
    
         
            +
                  EventMachine.run {
         
     | 
| 
      
 854 
     | 
    
         
            +
                    http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get
         
     | 
| 
      
 855 
     | 
    
         
            +
             
     | 
| 
      
 856 
     | 
    
         
            +
                    http.errback { failed(http) }
         
     | 
| 
      
 857 
     | 
    
         
            +
                    http.callback {
         
     | 
| 
      
 858 
     | 
    
         
            +
                      http.response.should == '"EventMachine HttpClient"'
         
     | 
| 
      
 859 
     | 
    
         
            +
                      EventMachine.stop
         
     | 
| 
      
 860 
     | 
    
         
            +
                    }
         
     | 
| 
      
 861 
     | 
    
         
            +
                  }
         
     | 
| 
      
 862 
     | 
    
         
            +
                end
         
     | 
| 
      
 863 
     | 
    
         
            +
             
     | 
| 
      
 864 
     | 
    
         
            +
                it 'should keep header if given empty string' do
         
     | 
| 
      
 865 
     | 
    
         
            +
                  EventMachine.run {
         
     | 
| 
      
 866 
     | 
    
         
            +
                    http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get(:head => { 'user-agent'=>'' })
         
     | 
| 
      
 867 
     | 
    
         
            +
             
     | 
| 
      
 868 
     | 
    
         
            +
                    http.errback { failed(http) }
         
     | 
| 
      
 869 
     | 
    
         
            +
                    http.callback {
         
     | 
| 
      
 870 
     | 
    
         
            +
                      http.response.should == '""'
         
     | 
| 
      
 871 
     | 
    
         
            +
                      EventMachine.stop
         
     | 
| 
      
 872 
     | 
    
         
            +
                    }
         
     | 
| 
      
 873 
     | 
    
         
            +
                  }
         
     | 
| 
      
 874 
     | 
    
         
            +
                end
         
     | 
| 
      
 875 
     | 
    
         
            +
             
     | 
| 
      
 876 
     | 
    
         
            +
                it 'should ommit header if given nil' do
         
     | 
| 
      
 877 
     | 
    
         
            +
                  EventMachine.run {
         
     | 
| 
      
 878 
     | 
    
         
            +
                    http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo-user-agent').get(:head => { 'user-agent'=>nil })
         
     | 
| 
      
 879 
     | 
    
         
            +
             
     | 
| 
      
 880 
     | 
    
         
            +
                    http.errback { failed(http) }
         
     | 
| 
      
 881 
     | 
    
         
            +
                    http.callback {
         
     | 
| 
      
 882 
     | 
    
         
            +
                      http.response.should == 'nil'
         
     | 
| 
      
 883 
     | 
    
         
            +
                      EventMachine.stop
         
     | 
| 
      
 884 
     | 
    
         
            +
                    }
         
     | 
| 
      
 885 
     | 
    
         
            +
                  }
         
     | 
| 
      
 886 
     | 
    
         
            +
                end
         
     | 
| 
      
 887 
     | 
    
         
            +
              end
         
     | 
| 
       833 
888 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            $: << 'lib' << '../lib'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'em-http/middleware/digest_auth'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            describe 'Digest Auth Authentication header generation' do
         
     | 
| 
      
 8 
     | 
    
         
            +
              before :each do
         
     | 
| 
      
 9 
     | 
    
         
            +
                @reference_header = 'Digest username="digest_username", realm="DigestAuth_REALM", algorithm=MD5, uri="/", nonce="MDAxMzQzNzQwNjA2OmRjZjAyZDY3YWMyMWVkZGQ4OWE2Nzg3ZTY3YTNlMjg5", response="96829962ffc31fa2852f86dc7f9f609b", opaque="BzdNK3gsJ2ixTrBJ"'
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              it 'should generate the correct header'  do
         
     | 
| 
      
 13 
     | 
    
         
            +
                www_authenticate = 'Digest realm="DigestAuth_REALM", nonce="MDAxMzQzNzQwNjA2OmRjZjAyZDY3YWMyMWVkZGQ4OWE2Nzg3ZTY3YTNlMjg5", opaque="BzdNK3gsJ2ixTrBJ", stale=false, algorithm=MD5'
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                params = {
         
     | 
| 
      
 16 
     | 
    
         
            +
                  username: 'digest_username',
         
     | 
| 
      
 17 
     | 
    
         
            +
                  password: 'digest_password'
         
     | 
| 
      
 18 
     | 
    
         
            +
                }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                middleware = EM::Middleware::DigestAuth.new(www_authenticate, params)
         
     | 
| 
      
 21 
     | 
    
         
            +
                middleware.build_auth_digest('GET', '/').should == @reference_header
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              it 'should not generate the same header for a different user' do
         
     | 
| 
      
 25 
     | 
    
         
            +
                www_authenticate = 'Digest realm="DigestAuth_REALM", nonce="MDAxMzQzNzQwNjA2OmRjZjAyZDY3YWMyMWVkZGQ4OWE2Nzg3ZTY3YTNlMjg5", opaque="BzdNK3gsJ2ixTrBJ", stale=false, algorithm=MD5'
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                params = {
         
     | 
| 
      
 28 
     | 
    
         
            +
                  username: 'digest_username_2',
         
     | 
| 
      
 29 
     | 
    
         
            +
                  password: 'digest_password'
         
     | 
| 
      
 30 
     | 
    
         
            +
                }
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                middleware = EM::Middleware::DigestAuth.new(www_authenticate, params)
         
     | 
| 
      
 33 
     | 
    
         
            +
                middleware.build_auth_digest('GET', '/').should_not == @reference_header
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              it 'should not generate the same header if the nounce changes' do
         
     | 
| 
      
 37 
     | 
    
         
            +
                www_authenticate = 'Digest realm="DigestAuth_REALM", nonce="MDAxMzQzNzQwNjA2OmRjZjAyZDY3YWMyMWVkZGQ4OWE2Nzg3ZTY3YTNlMjg6", opaque="BzdNK3gsJ2ixTrBJ", stale=false, algorithm=MD5'
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                params = {
         
     | 
| 
      
 40 
     | 
    
         
            +
                  username: 'digest_username_2',
         
     | 
| 
      
 41 
     | 
    
         
            +
                  password: 'digest_password'
         
     | 
| 
      
 42 
     | 
    
         
            +
                }
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                middleware = EM::Middleware::DigestAuth.new(www_authenticate, params)
         
     | 
| 
      
 45 
     | 
    
         
            +
                middleware.build_auth_digest('GET', '/').should_not == @reference_header
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/external_spec.rb
    CHANGED
    
    | 
         @@ -17,7 +17,7 @@ requires_connection do 
     | 
|
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
                it "should follow redirect to https and initiate the handshake" do
         
     | 
| 
       19 
19 
     | 
    
         
             
                  EventMachine.run {
         
     | 
| 
       20 
     | 
    
         
            -
                    http = EventMachine::HttpRequest.new('http:// 
     | 
| 
      
 20 
     | 
    
         
            +
                    http = EventMachine::HttpRequest.new('http://github.com/').get :redirects => 5
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                    http.errback { failed(http) }
         
     | 
| 
       23 
23 
     | 
    
         
             
                    http.callback {
         
     | 
| 
         @@ -31,7 +31,7 @@ requires_connection do 
     | 
|
| 
       31 
31 
     | 
    
         
             
                  EventMachine.run {
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
33 
     | 
    
         
             
                    # digg.com uses chunked encoding
         
     | 
| 
       34 
     | 
    
         
            -
                    http = EventMachine::HttpRequest.new('http:// 
     | 
| 
      
 34 
     | 
    
         
            +
                    http = EventMachine::HttpRequest.new('http://www.httpwatch.com/httpgallery/chunked/').get
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
                    http.errback { failed(http) }
         
     | 
| 
       37 
37 
     | 
    
         
             
                    http.callback {
         
     | 
| 
         Binary file 
     | 
    
        data/spec/gzip_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe EventMachine::HttpDecoders::GZip do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              let(:compressed) {
         
     | 
| 
      
 6 
     | 
    
         
            +
                compressed = ["1f8b08089668a6500003686900cbc8e402007a7a6fed03000000"].pack("H*")
         
     | 
| 
      
 7 
     | 
    
         
            +
              }
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              it "should extract the stream of a vanilla gzip" do
         
     | 
| 
      
 10 
     | 
    
         
            +
                header = EventMachine::HttpDecoders::GZipHeader.new
         
     | 
| 
      
 11 
     | 
    
         
            +
                stream = header.extract_stream(compressed)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                stream.unpack("H*")[0].should eq("cbc8e402007a7a6fed03000000")
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              it "should decompress a vanilla gzip" do
         
     | 
| 
      
 17 
     | 
    
         
            +
                decompressed = ""
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                gz = EventMachine::HttpDecoders::GZip.new do |data|
         
     | 
| 
      
 20 
     | 
    
         
            +
                  decompressed << data
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                gz << compressed
         
     | 
| 
      
 24 
     | 
    
         
            +
                gz.finalize!
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                decompressed.should eq("hi\n")
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              it "should decompress a vanilla gzip file byte by byte" do
         
     | 
| 
      
 30 
     | 
    
         
            +
                decompressed = ""
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                gz = EventMachine::HttpDecoders::GZip.new do |data|
         
     | 
| 
      
 33 
     | 
    
         
            +
                  decompressed << data
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                compressed.each_char do |byte|
         
     | 
| 
      
 37 
     | 
    
         
            +
                  gz << byte
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                gz.finalize!
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                decompressed.should eq("hi\n")
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              it "should decompress a large file" do
         
     | 
| 
      
 46 
     | 
    
         
            +
                decompressed = ""
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                gz = EventMachine::HttpDecoders::GZip.new do |data|
         
     | 
| 
      
 49 
     | 
    
         
            +
                  decompressed << data
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                gz << File.read(File.dirname(__FILE__) + "/fixtures/gzip-sample.gz")
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                gz.finalize!
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                decompressed.size.should eq(32907)
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              it "should fail with a DecoderError if not a gzip file" do
         
     | 
| 
      
 60 
     | 
    
         
            +
                not_a_gzip = ["1f8c08089668a650000"].pack("H*")
         
     | 
| 
      
 61 
     | 
    
         
            +
                header = EventMachine::HttpDecoders::GZipHeader.new
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                lambda {
         
     | 
| 
      
 64 
     | 
    
         
            +
                  header.extract_stream(not_a_gzip)
         
     | 
| 
      
 65 
     | 
    
         
            +
                }.should raise_exception(EventMachine::HttpDecoders::DecoderError)
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     |