http 0.1.0 → 0.2.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 http might be problematic. Click here for more details.
- data/.travis.yml +8 -2
 - data/CHANGES.md +6 -0
 - data/README.md +5 -10
 - data/http.gemspec +5 -3
 - data/lib/http.rb +19 -1
 - data/lib/http/chainable.rb +36 -12
 - data/lib/http/client.rb +37 -74
 - data/lib/http/compat/curb.rb +3 -2
 - data/lib/http/options.rb +109 -0
 - data/lib/http/request.rb +42 -0
 - data/lib/http/response.rb +73 -7
 - data/lib/http/uri_backport.rb +131 -0
 - data/lib/http/version.rb +1 -1
 - data/spec/http/compat/curb_spec.rb +1 -1
 - data/spec/http/options/callbacks_spec.rb +62 -0
 - data/spec/http/options/form_spec.rb +18 -0
 - data/spec/http/options/headers_spec.rb +29 -0
 - data/spec/http/options/merge_spec.rb +37 -0
 - data/spec/http/options/new_spec.rb +39 -0
 - data/spec/http/options/response_spec.rb +24 -0
 - data/spec/http/options_spec.rb +26 -0
 - data/spec/http/response_spec.rb +69 -0
 - data/spec/http_spec.rb +24 -2
 - data/spec/spec_helper.rb +1 -2
 - data/spec/support/{mock_server.rb → example_server.rb} +11 -14
 - metadata +80 -85
 - data/lib/http/parameters.rb +0 -17
 - data/parser/common.rl +0 -40
 - data/parser/http.rl +0 -220
 - data/parser/multipart.rl +0 -41
 
    
        data/lib/http/request.rb
    ADDED
    
    | 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Http
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Request
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Method is given as a lowercase symbol e.g. :get, :post
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_reader :method
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # "Request URI" as per RFC 2616
         
     | 
| 
      
 7 
     | 
    
         
            +
                # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :uri
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :headers, :body, :version
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # :nodoc:
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(method, uri, headers = {}, body = nil, version = "1.1")
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @method = method.to_s.downcase.to_sym
         
     | 
| 
      
 14 
     | 
    
         
            +
                  raise UnsupportedMethodError, "unknown method: #{method}" unless METHODS.include? @method
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  @uri = uri.is_a?(URI) ? uri : URI(uri.to_s)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  @headers = {}
         
     | 
| 
      
 19 
     | 
    
         
            +
                  headers.each do |name, value|
         
     | 
| 
      
 20 
     | 
    
         
            +
                    name = name.to_s
         
     | 
| 
      
 21 
     | 
    
         
            +
                    key = name[CANONICAL_HEADER]
         
     | 
| 
      
 22 
     | 
    
         
            +
                    key ||= Http.canonicalize_header(name)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @headers[key] = value
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  @body, @version = body, version
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                # Obtain the given header
         
     | 
| 
      
 30 
     | 
    
         
            +
                def [](header)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @headers[Http.canonicalize_header(header)]
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                # Create a Net::HTTP request from this request
         
     | 
| 
      
 35 
     | 
    
         
            +
                def to_net_http_request
         
     | 
| 
      
 36 
     | 
    
         
            +
                  request_class = Net::HTTP.const_get(@method.to_s.capitalize)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  request = request_class.new(@uri.request_uri, @headers)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  request.body = @body
         
     | 
| 
      
 39 
     | 
    
         
            +
                  request
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/http/response.rb
    CHANGED
    
    | 
         @@ -1,5 +1,62 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Http
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Response
         
     | 
| 
      
 3 
     | 
    
         
            +
                STATUS_CODES = {
         
     | 
| 
      
 4 
     | 
    
         
            +
                  100 => 'Continue',
         
     | 
| 
      
 5 
     | 
    
         
            +
                  101 => 'Switching Protocols',
         
     | 
| 
      
 6 
     | 
    
         
            +
                  102 => 'Processing',
         
     | 
| 
      
 7 
     | 
    
         
            +
                  200 => 'OK',
         
     | 
| 
      
 8 
     | 
    
         
            +
                  201 => 'Created',
         
     | 
| 
      
 9 
     | 
    
         
            +
                  202 => 'Accepted',
         
     | 
| 
      
 10 
     | 
    
         
            +
                  203 => 'Non-Authoritative Information',
         
     | 
| 
      
 11 
     | 
    
         
            +
                  204 => 'No Content',
         
     | 
| 
      
 12 
     | 
    
         
            +
                  205 => 'Reset Content',
         
     | 
| 
      
 13 
     | 
    
         
            +
                  206 => 'Partial Content',
         
     | 
| 
      
 14 
     | 
    
         
            +
                  207 => 'Multi-Status',
         
     | 
| 
      
 15 
     | 
    
         
            +
                  226 => 'IM Used',
         
     | 
| 
      
 16 
     | 
    
         
            +
                  300 => 'Multiple Choices',
         
     | 
| 
      
 17 
     | 
    
         
            +
                  301 => 'Moved Permanently',
         
     | 
| 
      
 18 
     | 
    
         
            +
                  302 => 'Found',
         
     | 
| 
      
 19 
     | 
    
         
            +
                  303 => 'See Other',
         
     | 
| 
      
 20 
     | 
    
         
            +
                  304 => 'Not Modified',
         
     | 
| 
      
 21 
     | 
    
         
            +
                  305 => 'Use Proxy',
         
     | 
| 
      
 22 
     | 
    
         
            +
                  306 => 'Reserved',
         
     | 
| 
      
 23 
     | 
    
         
            +
                  307 => 'Temporary Redirect',
         
     | 
| 
      
 24 
     | 
    
         
            +
                  400 => 'Bad Request',
         
     | 
| 
      
 25 
     | 
    
         
            +
                  401 => 'Unauthorized',
         
     | 
| 
      
 26 
     | 
    
         
            +
                  402 => 'Payment Required',
         
     | 
| 
      
 27 
     | 
    
         
            +
                  403 => 'Forbidden',
         
     | 
| 
      
 28 
     | 
    
         
            +
                  404 => 'Not Found',
         
     | 
| 
      
 29 
     | 
    
         
            +
                  405 => 'Method Not Allowed',
         
     | 
| 
      
 30 
     | 
    
         
            +
                  406 => 'Not Acceptable',
         
     | 
| 
      
 31 
     | 
    
         
            +
                  407 => 'Proxy Authentication Required',
         
     | 
| 
      
 32 
     | 
    
         
            +
                  408 => 'Request Timeout',
         
     | 
| 
      
 33 
     | 
    
         
            +
                  409 => 'Conflict',
         
     | 
| 
      
 34 
     | 
    
         
            +
                  410 => 'Gone',
         
     | 
| 
      
 35 
     | 
    
         
            +
                  411 => 'Length Required',
         
     | 
| 
      
 36 
     | 
    
         
            +
                  412 => 'Precondition Failed',
         
     | 
| 
      
 37 
     | 
    
         
            +
                  413 => 'Request Entity Too Large',
         
     | 
| 
      
 38 
     | 
    
         
            +
                  414 => 'Request-URI Too Long',
         
     | 
| 
      
 39 
     | 
    
         
            +
                  415 => 'Unsupported Media Type',
         
     | 
| 
      
 40 
     | 
    
         
            +
                  416 => 'Requested Range Not Satisfiable',
         
     | 
| 
      
 41 
     | 
    
         
            +
                  417 => 'Expectation Failed',
         
     | 
| 
      
 42 
     | 
    
         
            +
                  418 => "I'm a Teapot",
         
     | 
| 
      
 43 
     | 
    
         
            +
                  422 => 'Unprocessable Entity',
         
     | 
| 
      
 44 
     | 
    
         
            +
                  423 => 'Locked',
         
     | 
| 
      
 45 
     | 
    
         
            +
                  424 => 'Failed Dependency',
         
     | 
| 
      
 46 
     | 
    
         
            +
                  426 => 'Upgrade Required',
         
     | 
| 
      
 47 
     | 
    
         
            +
                  500 => 'Internal Server Error',
         
     | 
| 
      
 48 
     | 
    
         
            +
                  501 => 'Not Implemented',
         
     | 
| 
      
 49 
     | 
    
         
            +
                  502 => 'Bad Gateway',
         
     | 
| 
      
 50 
     | 
    
         
            +
                  503 => 'Service Unavailable',
         
     | 
| 
      
 51 
     | 
    
         
            +
                  504 => 'Gateway Timeout',
         
     | 
| 
      
 52 
     | 
    
         
            +
                  505 => 'HTTP Version Not Supported',
         
     | 
| 
      
 53 
     | 
    
         
            +
                  506 => 'Variant Also Negotiates',
         
     | 
| 
      
 54 
     | 
    
         
            +
                  507 => 'Insufficient Storage',
         
     | 
| 
      
 55 
     | 
    
         
            +
                  510 => 'Not Extended'
         
     | 
| 
      
 56 
     | 
    
         
            +
                }
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                SYMBOL_TO_STATUS_CODE = Hash[STATUS_CODES.map { |code, msg| [msg.downcase.gsub(/\s|-/, '_').to_sym, code] }]
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
       3 
60 
     | 
    
         
             
                attr_accessor :status
         
     | 
| 
       4 
61 
     | 
    
         
             
                attr_accessor :headers
         
     | 
| 
       5 
62 
     | 
    
         
             
                attr_accessor :body
         
     | 
| 
         @@ -15,9 +72,13 @@ module Http 
     | 
|
| 
       15 
72 
     | 
    
         
             
                  @headers = {}
         
     | 
| 
       16 
73 
     | 
    
         
             
                end
         
     | 
| 
       17 
74 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                # Set a header 
     | 
| 
       19 
     | 
    
         
            -
                def []=( 
     | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
      
 75 
     | 
    
         
            +
                # Set a header
         
     | 
| 
      
 76 
     | 
    
         
            +
                def []=(name, value)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  # If we have a canonical header, we're done
         
     | 
| 
      
 78 
     | 
    
         
            +
                  key = name[CANONICAL_HEADER]
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  # Convert to canonical capitalization
         
     | 
| 
      
 81 
     | 
    
         
            +
                  key ||= Http.canonicalize_header(name)
         
     | 
| 
       21 
82 
     | 
    
         | 
| 
       22 
83 
     | 
    
         
             
                  # Check if the header has already been set and group
         
     | 
| 
       23 
84 
     | 
    
         
             
                  old_value = @headers[key]
         
     | 
| 
         @@ -29,18 +90,23 @@ module Http 
     | 
|
| 
       29 
90 
     | 
    
         
             
                end
         
     | 
| 
       30 
91 
     | 
    
         | 
| 
       31 
92 
     | 
    
         
             
                # Get a header value
         
     | 
| 
       32 
     | 
    
         
            -
                def []( 
     | 
| 
       33 
     | 
    
         
            -
                  @headers[ 
     | 
| 
      
 93 
     | 
    
         
            +
                def [](name)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  @headers[name] || @headers[Http.canonicalize_header(name)]
         
     | 
| 
       34 
95 
     | 
    
         
             
                end
         
     | 
| 
       35 
96 
     | 
    
         | 
| 
       36 
97 
     | 
    
         
             
                # Parse the response body according to its content type
         
     | 
| 
       37 
98 
     | 
    
         
             
                def parse_body
         
     | 
| 
       38 
     | 
    
         
            -
                  if @headers[' 
     | 
| 
       39 
     | 
    
         
            -
                    mime_type = MimeType[@headers[' 
     | 
| 
      
 99 
     | 
    
         
            +
                  if @headers['Content-Type']
         
     | 
| 
      
 100 
     | 
    
         
            +
                    mime_type = MimeType[@headers['Content-Type'].split(/;\s*/).first]
         
     | 
| 
       40 
101 
     | 
    
         
             
                    return mime_type.parse(@body) if mime_type
         
     | 
| 
       41 
102 
     | 
    
         
             
                  end
         
     | 
| 
       42 
103 
     | 
    
         | 
| 
       43 
104 
     | 
    
         
             
                  @body
         
     | 
| 
       44 
105 
     | 
    
         
             
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                # Returns an Array ala Rack: `[status, headers, body]`
         
     | 
| 
      
 108 
     | 
    
         
            +
                def to_a
         
     | 
| 
      
 109 
     | 
    
         
            +
                  [status, headers, parse_body]
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
       45 
111 
     | 
    
         
             
              end
         
     | 
| 
       46 
112 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,131 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Taken from Ruby 1.9's uri/common.rb
         
     | 
| 
      
 2 
     | 
    
         
            +
            # By Akira Yamada <akira@ruby-lang.org>
         
     | 
| 
      
 3 
     | 
    
         
            +
            # License:
         
     | 
| 
      
 4 
     | 
    
         
            +
            #   You can redistribute it and/or modify it under the same term as Ruby.
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require 'uri'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            # Backport Ruby 1.9's form encoding/decoding functionality
         
     | 
| 
      
 9 
     | 
    
         
            +
            module URI
         
     | 
| 
      
 10 
     | 
    
         
            +
              TBLENCWWWCOMP_ = {} # :nodoc:
         
     | 
| 
      
 11 
     | 
    
         
            +
              256.times do |i|
         
     | 
| 
      
 12 
     | 
    
         
            +
                TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
              TBLENCWWWCOMP_[' '] = '+'
         
     | 
| 
      
 15 
     | 
    
         
            +
              TBLENCWWWCOMP_.freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
              TBLDECWWWCOMP_ = {} # :nodoc:
         
     | 
| 
      
 17 
     | 
    
         
            +
              256.times do |i|
         
     | 
| 
      
 18 
     | 
    
         
            +
                h, l = i>>4, i&15
         
     | 
| 
      
 19 
     | 
    
         
            +
                TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
         
     | 
| 
      
 20 
     | 
    
         
            +
                TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
         
     | 
| 
      
 21 
     | 
    
         
            +
                TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
         
     | 
| 
      
 22 
     | 
    
         
            +
                TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
              TBLDECWWWCOMP_['+'] = ' '
         
     | 
| 
      
 25 
     | 
    
         
            +
              TBLDECWWWCOMP_.freeze
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              # Encode given +str+ to URL-encoded form data.
         
     | 
| 
      
 28 
     | 
    
         
            +
              #
         
     | 
| 
      
 29 
     | 
    
         
            +
              # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
         
     | 
| 
      
 30 
     | 
    
         
            +
              # (ASCII space) to + and converts others to %XX.
         
     | 
| 
      
 31 
     | 
    
         
            +
              #
         
     | 
| 
      
 32 
     | 
    
         
            +
              # This is an implementation of
         
     | 
| 
      
 33 
     | 
    
         
            +
              # http://www.w3.org/TR/html5/association-of-controls-and-forms.html#url-encoded-form-data
         
     | 
| 
      
 34 
     | 
    
         
            +
              #
         
     | 
| 
      
 35 
     | 
    
         
            +
              # See URI.decode_www_form_component, URI.encode_www_form
         
     | 
| 
      
 36 
     | 
    
         
            +
              def self.encode_www_form_component(str)
         
     | 
| 
      
 37 
     | 
    
         
            +
                str.to_s.gsub(/[^*\-.0-9A-Z_a-z]/) { |chr| TBLENCWWWCOMP_[chr] }
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              # Decode given +str+ of URL-encoded form data.
         
     | 
| 
      
 41 
     | 
    
         
            +
              #
         
     | 
| 
      
 42 
     | 
    
         
            +
              # This decods + to SP.
         
     | 
| 
      
 43 
     | 
    
         
            +
              #
         
     | 
| 
      
 44 
     | 
    
         
            +
              # See URI.encode_www_form_component, URI.decode_www_form
         
     | 
| 
      
 45 
     | 
    
         
            +
              def self.decode_www_form_component(str)
         
     | 
| 
      
 46 
     | 
    
         
            +
                raise ArgumentError, "invalid %-encoding (#{str})" unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str
         
     | 
| 
      
 47 
     | 
    
         
            +
                str.gsub(/\+|%\h\h/) { |chr| TBLDECWWWCOMP_[chr] }
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              # Generate URL-encoded form data from given +enum+.
         
     | 
| 
      
 51 
     | 
    
         
            +
              #
         
     | 
| 
      
 52 
     | 
    
         
            +
              # This generates application/x-www-form-urlencoded data defined in HTML5
         
     | 
| 
      
 53 
     | 
    
         
            +
              # from given an Enumerable object.
         
     | 
| 
      
 54 
     | 
    
         
            +
              #
         
     | 
| 
      
 55 
     | 
    
         
            +
              # This internally uses URI.encode_www_form_component(str).
         
     | 
| 
      
 56 
     | 
    
         
            +
              #
         
     | 
| 
      
 57 
     | 
    
         
            +
              # This method doesn't convert the encoding of given items, so convert them
         
     | 
| 
      
 58 
     | 
    
         
            +
              # before call this method if you want to send data as other than original
         
     | 
| 
      
 59 
     | 
    
         
            +
              # encoding or mixed encoding data. (Strings which are encoded in an HTML5
         
     | 
| 
      
 60 
     | 
    
         
            +
              # ASCII incompatible encoding are converted to UTF-8.)
         
     | 
| 
      
 61 
     | 
    
         
            +
              #
         
     | 
| 
      
 62 
     | 
    
         
            +
              # This method doesn't handle files.  When you send a file, use
         
     | 
| 
      
 63 
     | 
    
         
            +
              # multipart/form-data.
         
     | 
| 
      
 64 
     | 
    
         
            +
              #
         
     | 
| 
      
 65 
     | 
    
         
            +
              # This is an implementation of
         
     | 
| 
      
 66 
     | 
    
         
            +
              # http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
         
     | 
| 
      
 67 
     | 
    
         
            +
              #
         
     | 
| 
      
 68 
     | 
    
         
            +
              #    URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
         
     | 
| 
      
 69 
     | 
    
         
            +
              #    #=> "q=ruby&lang=en"
         
     | 
| 
      
 70 
     | 
    
         
            +
              #    URI.encode_www_form("q" => "ruby", "lang" => "en")
         
     | 
| 
      
 71 
     | 
    
         
            +
              #    #=> "q=ruby&lang=en"
         
     | 
| 
      
 72 
     | 
    
         
            +
              #    URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
         
     | 
| 
      
 73 
     | 
    
         
            +
              #    #=> "q=ruby&q=perl&lang=en"
         
     | 
| 
      
 74 
     | 
    
         
            +
              #    URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
         
     | 
| 
      
 75 
     | 
    
         
            +
              #    #=> "q=ruby&q=perl&lang=en"
         
     | 
| 
      
 76 
     | 
    
         
            +
              #
         
     | 
| 
      
 77 
     | 
    
         
            +
              # See URI.encode_www_form_component, URI.decode_www_form
         
     | 
| 
      
 78 
     | 
    
         
            +
              def self.encode_www_form(enum)
         
     | 
| 
      
 79 
     | 
    
         
            +
                enum.map do |k,v|
         
     | 
| 
      
 80 
     | 
    
         
            +
                  if v.nil?
         
     | 
| 
      
 81 
     | 
    
         
            +
                    encode_www_form_component(k)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  elsif v.respond_to?(:to_ary)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    v.to_ary.map do |w|
         
     | 
| 
      
 84 
     | 
    
         
            +
                      str = encode_www_form_component(k)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      unless w.nil?
         
     | 
| 
      
 86 
     | 
    
         
            +
                        str << '='
         
     | 
| 
      
 87 
     | 
    
         
            +
                        str << encode_www_form_component(w)
         
     | 
| 
      
 88 
     | 
    
         
            +
                      end
         
     | 
| 
      
 89 
     | 
    
         
            +
                    end.join('&')
         
     | 
| 
      
 90 
     | 
    
         
            +
                  else
         
     | 
| 
      
 91 
     | 
    
         
            +
                    str = encode_www_form_component(k)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    str << '='
         
     | 
| 
      
 93 
     | 
    
         
            +
                    str << encode_www_form_component(v)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end.join('&')
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
              WFKV_ = '(?:[^%#=;&]*(?:%\h\h[^%#=;&]*)*)' # :nodoc:
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              # Decode URL-encoded form data from given +str+.
         
     | 
| 
      
 101 
     | 
    
         
            +
              #
         
     | 
| 
      
 102 
     | 
    
         
            +
              # This decodes application/x-www-form-urlencoded data
         
     | 
| 
      
 103 
     | 
    
         
            +
              # and returns array of key-value array.
         
     | 
| 
      
 104 
     | 
    
         
            +
              # This internally uses URI.decode_www_form_component.
         
     | 
| 
      
 105 
     | 
    
         
            +
              #
         
     | 
| 
      
 106 
     | 
    
         
            +
              # _charset_ hack is not supported now because the mapping from given charset
         
     | 
| 
      
 107 
     | 
    
         
            +
              # to Ruby's encoding is not clear yet.
         
     | 
| 
      
 108 
     | 
    
         
            +
              # see also http://www.w3.org/TR/html5/syntax.html#character-encodings-0
         
     | 
| 
      
 109 
     | 
    
         
            +
              #
         
     | 
| 
      
 110 
     | 
    
         
            +
              # This refers http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
         
     | 
| 
      
 111 
     | 
    
         
            +
              #
         
     | 
| 
      
 112 
     | 
    
         
            +
              # ary = URI.decode_www_form("a=1&a=2&b=3")
         
     | 
| 
      
 113 
     | 
    
         
            +
              # p ary                  #=> [['a', '1'], ['a', '2'], ['b', '3']]
         
     | 
| 
      
 114 
     | 
    
         
            +
              # p ary.assoc('a').last  #=> '1'
         
     | 
| 
      
 115 
     | 
    
         
            +
              # p ary.assoc('b').last  #=> '3'
         
     | 
| 
      
 116 
     | 
    
         
            +
              # p ary.rassoc('a').last #=> '2'
         
     | 
| 
      
 117 
     | 
    
         
            +
              # p Hash[ary]            # => {"a"=>"2", "b"=>"3"}
         
     | 
| 
      
 118 
     | 
    
         
            +
              #
         
     | 
| 
      
 119 
     | 
    
         
            +
              # See URI.decode_www_form_component, URI.encode_www_form
         
     | 
| 
      
 120 
     | 
    
         
            +
              def self.decode_www_form(str)
         
     | 
| 
      
 121 
     | 
    
         
            +
                return [] if str.empty?
         
     | 
| 
      
 122 
     | 
    
         
            +
                unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/o =~ str
         
     | 
| 
      
 123 
     | 
    
         
            +
                  raise ArgumentError, "invalid data of application/x-www-form-urlencoded (#{str})"
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
                ary = []
         
     | 
| 
      
 126 
     | 
    
         
            +
                $&.scan(/([^=;&]+)=([^;&]*)/) do
         
     | 
| 
      
 127 
     | 
    
         
            +
                  ary << [decode_www_form_component($1), decode_www_form_component($2)]
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
                ary
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/http/version.rb
    CHANGED
    
    
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Http::Options, "callbacks" do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              let(:opts){ Http::Options.new }
         
     | 
| 
      
 6 
     | 
    
         
            +
              let(:callback){ Proc.new{|r| nil } }
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              it 'recognizes invalid events' do
         
     | 
| 
      
 9 
     | 
    
         
            +
                lambda{
         
     | 
| 
      
 10 
     | 
    
         
            +
                  opts.with_callback(:notacallback, callback)
         
     | 
| 
      
 11 
     | 
    
         
            +
                }.should raise_error(ArgumentError, /notacallback/)
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              it 'recognizes invalid callbacks' do
         
     | 
| 
      
 15 
     | 
    
         
            +
                lambda{
         
     | 
| 
      
 16 
     | 
    
         
            +
                  opts.with_callback(:request, Object.new)
         
     | 
| 
      
 17 
     | 
    
         
            +
                }.should raise_error(ArgumentError, /invalid callback/)
         
     | 
| 
      
 18 
     | 
    
         
            +
                lambda{
         
     | 
| 
      
 19 
     | 
    
         
            +
                  opts.with_callback(:request, Proc.new{|a,b| nil})
         
     | 
| 
      
 20 
     | 
    
         
            +
                }.should raise_error(ArgumentError, /only one argument/)
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              describe "request" do
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                it 'defaults to []' do
         
     | 
| 
      
 26 
     | 
    
         
            +
                  opts.callbacks[:request].should eq([])
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                it 'may be specified with with_callback(:request, ...)' do
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  opts2 = opts.with_callback(:request, callback)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  opts.callbacks[:request].should eq([])
         
     | 
| 
      
 33 
     | 
    
         
            +
                  opts2.callbacks[:request].should eq([callback])
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  opts3 = opts2.with_callback(:request, callback)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  opts2.callbacks[:request].should eq([callback])
         
     | 
| 
      
 37 
     | 
    
         
            +
                  opts3.callbacks[:request].should eq([callback, callback])
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              describe "response" do
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                it 'defaults to []' do
         
     | 
| 
      
 45 
     | 
    
         
            +
                  opts.callbacks[:response].should eq([])
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                it 'may be specified with with_callback(:response, ...)' do
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  opts2 = opts.with_callback(:response, callback)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  opts.callbacks[:response].should eq([])
         
     | 
| 
      
 52 
     | 
    
         
            +
                  opts2.callbacks[:response].should eq([callback])
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  opts3 = opts2.with_callback(:response, callback)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  opts2.callbacks[:response].should eq([callback])
         
     | 
| 
      
 56 
     | 
    
         
            +
                  opts3.callbacks[:response].should eq([callback, callback])
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Http::Options, "form" do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              let(:opts){ Http::Options.new }
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              it 'defaults to nil' do
         
     | 
| 
      
 8 
     | 
    
         
            +
                opts.form.should be_nil
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              it 'may be specified with with_form_data' do
         
     | 
| 
      
 12 
     | 
    
         
            +
                opts2 = opts.with_form(:foo => 42)
         
     | 
| 
      
 13 
     | 
    
         
            +
                opts.form.should be_nil
         
     | 
| 
      
 14 
     | 
    
         
            +
                opts2.form.should eq(:foo => 42)
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Http::Options, "headers" do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              let(:opts){ Http::Options.new }
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              it 'defaults to {}' do
         
     | 
| 
      
 8 
     | 
    
         
            +
                opts.headers.should eq({})
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              it 'may be specified with with_headers' do
         
     | 
| 
      
 12 
     | 
    
         
            +
                opts2 = opts.with_headers("accept" => "json")
         
     | 
| 
      
 13 
     | 
    
         
            +
                opts.headers.should eq({})
         
     | 
| 
      
 14 
     | 
    
         
            +
                opts2.headers.should eq("accept" => "json")
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              it 'accepts any object that respond to :to_hash' do
         
     | 
| 
      
 18 
     | 
    
         
            +
                x = Struct.new(:to_hash).new("accept" => "json")
         
     | 
| 
      
 19 
     | 
    
         
            +
                opts.with_headers(x).headers["accept"].should eq("json")
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              it 'recognizes invalid headers' do
         
     | 
| 
      
 23 
     | 
    
         
            +
                lambda{
         
     | 
| 
      
 24 
     | 
    
         
            +
                  opts.with_headers(self)
         
     | 
| 
      
 25 
     | 
    
         
            +
                }.should raise_error(ArgumentError)
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe Http::Options, "merge" do
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              let(:opts){ Http::Options.new }
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              it 'supports a Hash' do
         
     | 
| 
      
 8 
     | 
    
         
            +
                old_response = opts.response
         
     | 
| 
      
 9 
     | 
    
         
            +
                opts.merge(:response => :body).response.should eq(:body)
         
     | 
| 
      
 10 
     | 
    
         
            +
                opts.response.should eq(old_response)
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              it 'supports another Options' do
         
     | 
| 
      
 14 
     | 
    
         
            +
                merged = opts.merge(Http::Options.new(:response => :body))
         
     | 
| 
      
 15 
     | 
    
         
            +
                merged.response.should eq(:body)
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              it 'merges as excepted in complex cases' do
         
     | 
| 
      
 19 
     | 
    
         
            +
                foo = Http::Options.new(
         
     | 
| 
      
 20 
     | 
    
         
            +
                  :response  => :body,
         
     | 
| 
      
 21 
     | 
    
         
            +
                  :form      => {:foo => 'foo'},
         
     | 
| 
      
 22 
     | 
    
         
            +
                  :headers   => {:accept  => "json",  :foo => 'foo'},
         
     | 
| 
      
 23 
     | 
    
         
            +
                  :callbacks => {:request => ["common"], :response => ["foo"]})
         
     | 
| 
      
 24 
     | 
    
         
            +
                bar = Http::Options.new(
         
     | 
| 
      
 25 
     | 
    
         
            +
                  :response  => :parsed_body,
         
     | 
| 
      
 26 
     | 
    
         
            +
                  :form      => {:bar => 'bar'},
         
     | 
| 
      
 27 
     | 
    
         
            +
                  :headers   => {:accept  => "xml", :bar => 'bar'},
         
     | 
| 
      
 28 
     | 
    
         
            +
                  :callbacks => {:request => ["common"], :response => ["bar"]})
         
     | 
| 
      
 29 
     | 
    
         
            +
                foo.merge(bar).to_hash.should eq(
         
     | 
| 
      
 30 
     | 
    
         
            +
                  :response  => :parsed_body,
         
     | 
| 
      
 31 
     | 
    
         
            +
                  :form      => {:bar => 'bar'},
         
     | 
| 
      
 32 
     | 
    
         
            +
                  :headers   => {:accept  => "xml", :foo => "foo", :bar => 'bar'},
         
     | 
| 
      
 33 
     | 
    
         
            +
                  :callbacks => {:request => ["common"], :response => ["foo", "bar"]}
         
     | 
| 
      
 34 
     | 
    
         
            +
                )
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     |