typhoeus 0.4.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +4 -0
  4. data/.travis.yml +26 -0
  5. data/CHANGELOG.md +341 -28
  6. data/CONTRIBUTING.md +20 -0
  7. data/Gemfile +31 -2
  8. data/Guardfile +9 -0
  9. data/LICENSE +1 -1
  10. data/README.md +486 -357
  11. data/Rakefile +21 -12
  12. data/UPGRADE.md +55 -0
  13. data/lib/rack/typhoeus/middleware/params_decoder/helper.rb +76 -0
  14. data/lib/rack/typhoeus/middleware/params_decoder.rb +57 -0
  15. data/lib/rack/typhoeus.rb +1 -0
  16. data/lib/typhoeus/adapters/faraday.rb +180 -0
  17. data/lib/typhoeus/cache/dalli.rb +28 -0
  18. data/lib/typhoeus/cache/rails.rb +28 -0
  19. data/lib/typhoeus/cache/redis.rb +35 -0
  20. data/lib/typhoeus/config.rb +69 -0
  21. data/lib/typhoeus/easy_factory.rb +180 -0
  22. data/lib/typhoeus/errors/no_stub.rb +12 -0
  23. data/lib/typhoeus/errors/typhoeus_error.rb +8 -0
  24. data/lib/typhoeus/errors.rb +9 -0
  25. data/lib/typhoeus/expectation.rb +217 -0
  26. data/lib/typhoeus/hydra/addable.rb +23 -0
  27. data/lib/typhoeus/hydra/before.rb +31 -0
  28. data/lib/typhoeus/hydra/block_connection.rb +35 -0
  29. data/lib/typhoeus/hydra/cacheable.rb +15 -0
  30. data/lib/typhoeus/hydra/memoizable.rb +56 -0
  31. data/lib/typhoeus/hydra/queueable.rb +83 -0
  32. data/lib/typhoeus/hydra/runnable.rb +19 -0
  33. data/lib/typhoeus/hydra/stubbable.rb +28 -0
  34. data/lib/typhoeus/hydra.rb +84 -236
  35. data/lib/typhoeus/pool.rb +70 -0
  36. data/lib/typhoeus/railtie.rb +12 -0
  37. data/lib/typhoeus/request/actions.rb +125 -0
  38. data/lib/typhoeus/request/before.rb +30 -0
  39. data/lib/typhoeus/request/block_connection.rb +52 -0
  40. data/lib/typhoeus/request/cacheable.rb +38 -0
  41. data/lib/typhoeus/request/callbacks.rb +151 -0
  42. data/lib/typhoeus/request/marshal.rb +22 -0
  43. data/lib/typhoeus/request/memoizable.rb +38 -0
  44. data/lib/typhoeus/request/operations.rb +40 -0
  45. data/lib/typhoeus/request/responseable.rb +29 -0
  46. data/lib/typhoeus/request/streamable.rb +34 -0
  47. data/lib/typhoeus/request/stubbable.rb +30 -0
  48. data/lib/typhoeus/request.rb +186 -231
  49. data/lib/typhoeus/response/cacheable.rb +14 -0
  50. data/lib/typhoeus/response/header.rb +105 -0
  51. data/lib/typhoeus/response/informations.rb +248 -0
  52. data/lib/typhoeus/response/status.rb +106 -0
  53. data/lib/typhoeus/response.rb +60 -115
  54. data/lib/typhoeus/version.rb +3 -1
  55. data/lib/typhoeus.rb +126 -39
  56. data/perf/profile.rb +14 -0
  57. data/perf/vs_nethttp.rb +64 -0
  58. data/spec/rack/typhoeus/middleware/params_decoder/helper_spec.rb +156 -0
  59. data/spec/rack/typhoeus/middleware/params_decoder_spec.rb +31 -0
  60. data/spec/spec_helper.rb +29 -0
  61. data/spec/support/localhost_server.rb +94 -0
  62. data/spec/support/memory_cache.rb +15 -0
  63. data/spec/support/server.rb +116 -0
  64. data/spec/typhoeus/adapters/faraday_spec.rb +339 -0
  65. data/spec/typhoeus/cache/dalli_spec.rb +41 -0
  66. data/spec/typhoeus/cache/redis_spec.rb +41 -0
  67. data/spec/typhoeus/config_spec.rb +15 -0
  68. data/spec/typhoeus/easy_factory_spec.rb +143 -0
  69. data/spec/typhoeus/errors/no_stub_spec.rb +13 -0
  70. data/spec/typhoeus/expectation_spec.rb +280 -0
  71. data/spec/typhoeus/hydra/addable_spec.rb +22 -0
  72. data/spec/typhoeus/hydra/before_spec.rb +98 -0
  73. data/spec/typhoeus/hydra/block_connection_spec.rb +18 -0
  74. data/spec/typhoeus/hydra/cacheable_spec.rb +88 -0
  75. data/spec/typhoeus/hydra/memoizable_spec.rb +53 -0
  76. data/spec/typhoeus/hydra/queueable_spec.rb +98 -0
  77. data/spec/typhoeus/hydra/runnable_spec.rb +137 -0
  78. data/spec/typhoeus/hydra/stubbable_spec.rb +48 -0
  79. data/spec/typhoeus/hydra_spec.rb +22 -0
  80. data/spec/typhoeus/pool_spec.rb +137 -0
  81. data/spec/typhoeus/request/actions_spec.rb +19 -0
  82. data/spec/typhoeus/request/before_spec.rb +93 -0
  83. data/spec/typhoeus/request/block_connection_spec.rb +75 -0
  84. data/spec/typhoeus/request/cacheable_spec.rb +94 -0
  85. data/spec/typhoeus/request/callbacks_spec.rb +91 -0
  86. data/spec/typhoeus/request/marshal_spec.rb +60 -0
  87. data/spec/typhoeus/request/memoizable_spec.rb +34 -0
  88. data/spec/typhoeus/request/operations_spec.rb +101 -0
  89. data/spec/typhoeus/request/responseable_spec.rb +13 -0
  90. data/spec/typhoeus/request/stubbable_spec.rb +45 -0
  91. data/spec/typhoeus/request_spec.rb +232 -0
  92. data/spec/typhoeus/response/header_spec.rb +147 -0
  93. data/spec/typhoeus/response/informations_spec.rb +283 -0
  94. data/spec/typhoeus/response/status_spec.rb +256 -0
  95. data/spec/typhoeus/response_spec.rb +100 -0
  96. data/spec/typhoeus_spec.rb +105 -0
  97. data/typhoeus.gemspec +25 -0
  98. metadata +146 -158
  99. data/lib/typhoeus/curl.rb +0 -453
  100. data/lib/typhoeus/easy/auth.rb +0 -14
  101. data/lib/typhoeus/easy/callbacks.rb +0 -33
  102. data/lib/typhoeus/easy/ffi_helper.rb +0 -61
  103. data/lib/typhoeus/easy/infos.rb +0 -90
  104. data/lib/typhoeus/easy/options.rb +0 -115
  105. data/lib/typhoeus/easy/proxy.rb +0 -20
  106. data/lib/typhoeus/easy/ssl.rb +0 -82
  107. data/lib/typhoeus/easy.rb +0 -115
  108. data/lib/typhoeus/filter.rb +0 -28
  109. data/lib/typhoeus/form.rb +0 -61
  110. data/lib/typhoeus/header.rb +0 -54
  111. data/lib/typhoeus/hydra/callbacks.rb +0 -24
  112. data/lib/typhoeus/hydra/connect_options.rb +0 -61
  113. data/lib/typhoeus/hydra/stubbing.rb +0 -68
  114. data/lib/typhoeus/hydra_mock.rb +0 -131
  115. data/lib/typhoeus/multi.rb +0 -146
  116. data/lib/typhoeus/param_processor.rb +0 -43
  117. data/lib/typhoeus/remote.rb +0 -306
  118. data/lib/typhoeus/remote_method.rb +0 -108
  119. data/lib/typhoeus/remote_proxy_object.rb +0 -50
  120. data/lib/typhoeus/utils.rb +0 -50
@@ -1,266 +1,221 @@
1
- require 'uri'
1
+ require 'zlib'
2
+ require 'digest/sha1'
3
+ require 'typhoeus/request/actions'
4
+ require 'typhoeus/request/before'
5
+ require 'typhoeus/request/block_connection'
6
+ require 'typhoeus/request/cacheable'
7
+ require 'typhoeus/request/callbacks'
8
+ require 'typhoeus/request/marshal'
9
+ require 'typhoeus/request/memoizable'
10
+ require 'typhoeus/request/operations'
11
+ require 'typhoeus/request/responseable'
12
+ require 'typhoeus/request/streamable'
13
+ require 'typhoeus/request/stubbable'
2
14
 
3
15
  module Typhoeus
16
+
17
+ # This class represents a request.
18
+ #
19
+ # @example (see #initialize)
20
+ #
21
+ # @example Make a request with the shortcut.
22
+ # response = Typhoeus.get("www.example.com")
23
+ #
24
+ # @see (see #initialize)
4
25
  class Request
5
- ACCESSOR_OPTIONS = [
6
- :method,
7
- :params,
8
- :body,
9
- :headers,
10
- :cache_key_basis,
11
- :connect_timeout,
12
- :timeout,
13
- :user_agent,
14
- :response,
15
- :cache_timeout,
16
- :follow_location,
17
- :max_redirects,
18
- :proxy,
19
- :proxy_username,
20
- :proxy_password,
21
- :disable_ssl_peer_verification,
22
- :disable_ssl_host_verification,
23
- :interface,
24
- :ssl_cert,
25
- :ssl_cert_type,
26
- :ssl_key,
27
- :ssl_key_type,
28
- :ssl_key_password,
29
- :ssl_cacert,
30
- :ssl_capath,
31
- :ssl_version,
32
- :verbose,
33
- :username,
34
- :password,
35
- :auth_method,
36
- :proxy_auth_method,
37
- :proxy_type
38
- ]
26
+ extend Request::Actions
27
+ include Request::Callbacks::Types
28
+ include Request::Callbacks
29
+ include Request::Streamable
30
+ include Request::Marshal
31
+ include Request::Operations
32
+ include Request::Responseable
33
+ include Request::Memoizable
34
+ include Request::Cacheable
35
+ include Request::BlockConnection
36
+ include Request::Stubbable
37
+ include Request::Before
38
+
39
+ # Returns the provided base url.
40
+ #
41
+ # @return [ String ]
42
+ attr_accessor :base_url
39
43
 
40
- attr_accessor *ACCESSOR_OPTIONS
44
+ # Returns options, which includes default parameters.
45
+ #
46
+ # @return [ Hash ]
47
+ attr_accessor :options
41
48
 
42
- # Initialize a new Request
49
+ # Returns the hydra in which the request ran, if any.
43
50
  #
44
- # Options:
45
- # * +url+ : Endpoint (URL) of the request
46
- # * +options+ : A hash containing options among :
47
- # ** +:method+ : :get (default) / :post / :put
48
- # ** +:params+ : params as a Hash
49
- # ** +:body+
50
- # ** +:timeout+ : timeout (ms)
51
- # ** +:interface+ : interface or ip address (string)
52
- # ** +:connect_timeout+ : connect timeout (ms)
53
- # ** +:headers+ : headers as Hash
54
- # ** +:cache_timeout+ : cache timeout (ms)
55
- # ** +:follow_location
56
- # ** +:max_redirects
57
- # ** +:proxy
58
- # ** +:disable_ssl_peer_verification
59
- # ** +:disable_ssl_host_verification
60
- # ** +:ssl_cert
61
- # ** +:ssl_cert_type
62
- # ** +:ssl_key
63
- # ** +:ssl_key_type
64
- # ** +:ssl_key_password
65
- # ** +:ssl_cacert
66
- # ** +:ssl_capath
67
- # ** +:verbose
68
- # ** +:username
69
- # ** +:password
70
- # ** +:auth_method
51
+ # @return [ Typhoeus::Hydra ]
71
52
  #
72
- def initialize(url, options = {})
73
- @url = url
74
- @method = options[:method] || :get
75
- @params = options[:params]
76
- @body = options[:body]
77
- @timeout = safe_to_i(options[:timeout])
78
- @connect_timeout = safe_to_i(options[:connect_timeout])
79
- @interface = options[:interface]
80
- @headers = options[:headers] || {}
53
+ # @api private
54
+ attr_accessor :hydra
81
55
 
82
- @cache_timeout = safe_to_i(options[:cache_timeout])
83
- @follow_location = options[:follow_location]
84
- @max_redirects = options[:max_redirects]
85
- @proxy = options[:proxy]
86
- @proxy_type = options[:proxy_type]
87
- @proxy_username = options[:proxy_username]
88
- @proxy_password = options[:proxy_password]
89
- @proxy_auth_method = options[:proxy_auth_method]
90
- @disable_ssl_peer_verification = options[:disable_ssl_peer_verification]
91
- @disable_ssl_host_verification = options[:disable_ssl_host_verification]
92
- @ssl_cert = options[:ssl_cert]
93
- @ssl_cert_type = options[:ssl_cert_type]
94
- @ssl_key = options[:ssl_key]
95
- @ssl_key_type = options[:ssl_key_type]
96
- @ssl_key_password = options[:ssl_key_password]
97
- @ssl_cacert = options[:ssl_cacert]
98
- @ssl_capath = options[:ssl_capath]
99
- @ssl_version = options[:ssl_version]
100
- @verbose = options[:verbose]
101
- @username = options[:username]
102
- @password = options[:password]
103
- @auth_method = options[:auth_method]
104
-
105
- @on_complete = nil
106
- @after_complete = nil
107
- @handled_response = nil
108
- end
56
+ # Returns the original options provided.
57
+ #
58
+ # @return [ Hash ]
59
+ #
60
+ # @api private
61
+ attr_accessor :original_options
109
62
 
110
- LOCALHOST_ALIASES = %w[ localhost 127.0.0.1 0.0.0.0 ]
63
+ # @return [ Boolean ]
64
+ #
65
+ # @api private
66
+ attr_accessor :block_connection
111
67
 
112
- def localhost?
113
- LOCALHOST_ALIASES.include?(parsed_uri.host)
114
- end
68
+ # Creates a new request.
69
+ #
70
+ # @example Simplest request.
71
+ # response = Typhoeus::Request.new("www.example.com").run
72
+ #
73
+ # @example Request with url parameters.
74
+ # response = Typhoeus::Request.new(
75
+ # "www.example.com",
76
+ # params: {a: 1}
77
+ # ).run
78
+ #
79
+ # @example Request with a body.
80
+ # response = Typhoeus::Request.new(
81
+ # "www.example.com",
82
+ # body: {b: 2}
83
+ # ).run
84
+ #
85
+ # @example Request with parameters and body.
86
+ # response = Typhoeus::Request.new(
87
+ # "www.example.com",
88
+ # params: {a: 1},
89
+ # body: {b: 2}
90
+ # ).run
91
+ #
92
+ # @example Create a request and allow follow redirections.
93
+ # response = Typhoeus::Request.new(
94
+ # "www.example.com",
95
+ # followlocation: true
96
+ # ).run
97
+ #
98
+ # @param [ String ] base_url The url to request.
99
+ # @param [ options ] options The options.
100
+ #
101
+ # @option options [ Hash ] :params Translated
102
+ # into url parameters.
103
+ # @option options [ Hash ] :body Translated
104
+ # into HTTP POST request body.
105
+ #
106
+ # @return [ Typhoeus::Request ] The request.
107
+ #
108
+ # @note See {http://rubydoc.info/github/typhoeus/ethon/Ethon/Easy/Options Ethon::Easy::Options} for more options.
109
+ #
110
+ # @see Typhoeus::Hydra
111
+ # @see Typhoeus::Response
112
+ # @see Typhoeus::Request::Actions
113
+ def initialize(base_url, options = {})
114
+ @base_url = base_url
115
+ @original_options = options
116
+ @options = options.dup
115
117
 
116
- def user_agent
117
- headers['User-Agent']
118
+ set_defaults
118
119
  end
119
120
 
121
+ # Return the url.
122
+ # In contrast to base_url which returns the value you specified, url returns
123
+ # the full url including the parameters.
124
+ #
125
+ # @example Get the url.
126
+ # request.url
127
+ #
128
+ # @since 0.5.5
120
129
  def url
121
- if [:post, :put].include?(@method)
122
- @url
123
- else
124
- url = "#{@url}?#{params_string}"
125
- url += "&#{URI.escape(@body)}" if @body
126
- url.gsub("?&", "?").gsub(/\?$/, '')
127
- end
128
- end
129
-
130
- def parsed_uri
131
- @parsed_uri ||= URI.parse(@url)
132
- end
133
-
134
- def host
135
- slash_location = @url.index('/', 8)
136
- if slash_location
137
- @url.slice(0, slash_location)
138
- else
139
- query_string_location = @url.index('?')
140
- return query_string_location ? @url.slice(0, query_string_location) : @url
141
- end
142
- end
143
-
144
- def host_domain
145
- parsed_uri.host
146
- end
147
-
148
- def params_string
149
- return nil unless params
150
- traversal = Typhoeus::Utils.traverse_params_hash(params)
151
- Typhoeus::Utils.traversal_to_param_string(traversal)
152
- end
153
-
154
- def on_complete(&block)
155
- @on_complete = block
130
+ easy = EasyFactory.new(self).get
131
+ url = easy.url
132
+ Typhoeus::Pool.release(easy)
133
+ url
156
134
  end
157
135
 
158
- def on_complete=(proc)
159
- @on_complete = proc
160
- end
161
-
162
- def after_complete(&block)
163
- @after_complete = block
164
- end
165
-
166
- def after_complete=(proc)
167
- @after_complete = proc
168
- end
169
-
170
- def call_handlers
171
- if @on_complete
172
- @handled_response = @on_complete.call(response)
173
- call_after_complete
174
- end
175
- end
176
-
177
- def call_after_complete
178
- @after_complete.call(@handled_response) if @after_complete
179
- end
180
-
181
- def handled_response=(val)
182
- @handled_response = val
183
- end
184
-
185
- def handled_response
186
- @handled_response || response
136
+ # Returns whether other is equal to self.
137
+ #
138
+ # @example Are request equal?
139
+ # request.eql?(other_request)
140
+ #
141
+ # @param [ Object ] other The object to check.
142
+ #
143
+ # @return [ Boolean ] Returns true if equal, else false.
144
+ #
145
+ # @api private
146
+ def eql?(other)
147
+ self.class == other.class &&
148
+ self.base_url == other.base_url &&
149
+ fuzzy_hash_eql?(self.options, other.options)
187
150
  end
188
151
 
189
- def inspect
190
- result = ":method => #{self.method.inspect},\n" <<
191
- "\t:url => #{URI.parse(self.url).to_s}"
192
- if self.body and !self.body.empty?
193
- result << ",\n\t:body => #{self.body.inspect}"
194
- end
195
-
196
- if self.params and !self.params.empty?
197
- result << ",\n\t:params => #{self.params.inspect}"
198
- end
199
-
200
- if self.headers and !self.headers.empty?
201
- result << ",\n\t:headers => #{self.headers.inspect}"
202
- end
203
-
204
- result
152
+ # Overrides Object#hash.
153
+ #
154
+ # @return [ Integer ] The integer representing the request.
155
+ #
156
+ # @api private
157
+ def hash
158
+ Zlib.crc32 cache_key
205
159
  end
206
160
 
161
+ # Returns a cache key for use with caching methods that required a string
162
+ # for a key. Will get used by ActiveSupport::Cache stores automatically.
163
+ #
164
+ # @return [ String ] The cache key.
207
165
  def cache_key
208
- Digest::SHA1.hexdigest(cache_key_basis || url)
209
- end
210
-
211
- def self.run(url, params)
212
- r = new(url, params)
213
- Typhoeus::Hydra.hydra.queue r
214
- Typhoeus::Hydra.hydra.run
215
- r.response
216
- end
217
-
218
- def self.get(url, params = {})
219
- run(url, params.merge(:method => :get))
166
+ Digest::SHA1.hexdigest "#{self.class.name}#{base_url}#{hashable_string_for(options)}"
220
167
  end
221
168
 
222
- def self.post(url, params = {})
223
- run(url, params.merge(:method => :post))
224
- end
225
-
226
- def self.put(url, params = {})
227
- run(url, params.merge(:method => :put))
228
- end
229
-
230
- def self.delete(url, params = {})
231
- run(url, params.merge(:method => :delete))
232
- end
233
-
234
- def self.head(url, params = {})
235
- run(url, params.merge(:method => :head))
169
+ # Mimics libcurls POST body generation. This is not accurate, but good
170
+ # enough for VCR.
171
+ #
172
+ # @return [ String ] The encoded body.
173
+ # otherwise.
174
+ #
175
+ # @api private
176
+ def encoded_body
177
+ Ethon::Easy::Form.new(nil, options[:body]).to_s
236
178
  end
237
179
 
238
- protected
180
+ private
239
181
 
240
- # Return the important data needed to serialize this Request, except the
241
- # `on_complete` and `after_complete` handlers, since they cannot be
242
- # marshalled.
243
- def marshal_dump
244
- (instance_variables - ['@on_complete', '@after_complete', :@on_complete, :@after_complete]).map do |name|
245
- [name, instance_variable_get(name)]
182
+ # Checks if two hashes are equal or not, discarding
183
+ # first-level hash order.
184
+ #
185
+ # @param [ Hash ] left
186
+ # @param [ Hash ] right hash to check for equality
187
+ #
188
+ # @return [ Boolean ] Returns true if hashes have
189
+ # same values for same keys and same length,
190
+ # even if the keys are given in a different order.
191
+ def fuzzy_hash_eql?(left, right)
192
+ return true if (left == right)
193
+
194
+ (left.count == right.count) && left.inject(true) do |res, kvp|
195
+ res && (kvp[1] == right[kvp[0]])
246
196
  end
247
197
  end
248
198
 
249
- def marshal_load(attributes)
250
- attributes.each { |name, value| instance_variable_set(name, value) }
251
- end
252
-
253
- def self.options
254
- ACCESSOR_OPTIONS
199
+ def hashable_string_for(obj)
200
+ case obj
201
+ when Hash
202
+ hashable_string_for(obj.sort_by {|sub_obj| sub_obj.first.to_s})
203
+ when Array
204
+ obj.map {|sub_obj| hashable_string_for(sub_obj)}.to_s
205
+ else
206
+ obj.to_s
207
+ end
255
208
  end
256
209
 
257
- private
210
+ # Sets default header and verbose when turned on.
211
+ def set_defaults
212
+ default_user_agent = Config.user_agent || Typhoeus::USER_AGENT
258
213
 
259
- def safe_to_i(value)
260
- return value if value.is_a?(Fixnum)
261
- return nil if value.nil?
262
- return nil if value.respond_to?(:empty?) && value.empty?
263
- value.to_i
214
+ options[:headers] = {'User-Agent' => default_user_agent}.merge(options[:headers] || {})
215
+ options[:headers]['Expect'] ||= ''
216
+ options[:verbose] = Typhoeus::Config.verbose if options[:verbose].nil? && !Typhoeus::Config.verbose.nil?
217
+ options[:maxredirs] ||= 50
218
+ options[:proxy] = Typhoeus::Config.proxy unless options.has_key?(:proxy) || Typhoeus::Config.proxy.nil?
264
219
  end
265
220
  end
266
221
  end
@@ -0,0 +1,14 @@
1
+ module Typhoeus
2
+ class Response
3
+ module Cacheable
4
+
5
+ # Set the cache status, if we got response from cache
6
+ # it will have cached? == true
7
+ attr_writer :cached
8
+
9
+ def cached?
10
+ defined?(@cached) ? !!@cached : false
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,105 @@
1
+ require 'delegate'
2
+
3
+ module Typhoeus
4
+ class Response
5
+
6
+ # This class represents the response header.
7
+ # It can be accessed like a hash.
8
+ # Values can be strings (normal case) or arrays of strings (for duplicates headers)
9
+ #
10
+ # @api private
11
+ class Header < DelegateClass(Hash)
12
+
13
+ # Create a new header.
14
+ #
15
+ # @example Create new header.
16
+ # Header.new(raw)
17
+ #
18
+ # @param [ String ] raw The raw header.
19
+ def initialize(raw)
20
+ super({})
21
+ @raw = raw
22
+ @sanitized = {}
23
+ parse
24
+ end
25
+
26
+ def [](key)
27
+ fetch(key) { @sanitized[key.to_s.downcase] }
28
+ end
29
+
30
+ # Parses the raw header.
31
+ #
32
+ # @example Parse header.
33
+ # header.parse
34
+ def parse
35
+ case @raw
36
+ when Hash
37
+ raw.each do |k, v|
38
+ process_pair(k, v)
39
+ end
40
+ when String
41
+ raw.split(/\r?\n(?!\s)/).each do |header|
42
+ header.strip!
43
+ next if header.empty? || header.start_with?( 'HTTP/' )
44
+ process_line(header)
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ # Processes line and saves the result.
52
+ #
53
+ # @return [ void ]
54
+ def process_line(header)
55
+ key, value = header.split(':', 2)
56
+ process_pair(key.strip, (value ? value.strip.gsub(/\r?\n\s*/, ' ') : ''))
57
+ end
58
+
59
+ # Sets key value pair for self and @sanitized.
60
+ #
61
+ # @return [ void ]
62
+ def process_pair(key, value)
63
+ set_value(key, value, self)
64
+ @sanitized[key.downcase] = self[key]
65
+ end
66
+
67
+ # Sets value for key in specified hash
68
+ #
69
+ # @return [ void ]
70
+ def set_value(key, value, hash)
71
+ current_value = hash[key]
72
+ if current_value
73
+ if current_value.is_a? Array
74
+ current_value << value
75
+ else
76
+ hash[key] = [current_value, value]
77
+ end
78
+ else
79
+ hash[key] = value
80
+ end
81
+ end
82
+
83
+ # Returns the raw header or empty string.
84
+ #
85
+ # @example Return raw header.
86
+ # header.raw
87
+ #
88
+ # @return [ String ] The raw header.
89
+ def raw
90
+ @raw || ''
91
+ end
92
+
93
+ # Sets the default proc for the specified hash independent of the Ruby version.
94
+ #
95
+ # @return [ void ]
96
+ def set_default_proc_on(hash, default_proc)
97
+ if hash.respond_to?(:default_proc=)
98
+ hash.default_proc = default_proc
99
+ else
100
+ hash.replace(Hash.new(&default_proc).merge(hash))
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end