typhoeus 0.4.2 → 0.5.0.rc

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.
Files changed (57) hide show
  1. data/CHANGELOG.md +86 -28
  2. data/Gemfile +17 -1
  3. data/README.md +20 -422
  4. data/Rakefile +21 -12
  5. data/lib/typhoeus/config.rb +47 -0
  6. data/lib/typhoeus/errors/no_stub.rb +12 -0
  7. data/lib/typhoeus/errors/typhoeus_error.rb +8 -0
  8. data/lib/typhoeus/errors.rb +9 -0
  9. data/lib/typhoeus/expectation.rb +174 -0
  10. data/lib/typhoeus/hydra/before.rb +30 -0
  11. data/lib/typhoeus/hydra/block_connection.rb +35 -0
  12. data/lib/typhoeus/hydra/easy_factory.rb +79 -0
  13. data/lib/typhoeus/hydra/easy_pool.rb +42 -0
  14. data/lib/typhoeus/hydra/memoizable.rb +55 -0
  15. data/lib/typhoeus/hydra/queueable.rb +48 -0
  16. data/lib/typhoeus/hydra/runnable.rb +21 -0
  17. data/lib/typhoeus/hydra/stubbable.rb +26 -0
  18. data/lib/typhoeus/hydra.rb +83 -236
  19. data/lib/typhoeus/request/actions.rb +125 -0
  20. data/lib/typhoeus/request/before.rb +30 -0
  21. data/lib/typhoeus/request/block_connection.rb +52 -0
  22. data/lib/typhoeus/request/callbacks.rb +98 -0
  23. data/lib/typhoeus/request/marshal.rb +21 -0
  24. data/lib/typhoeus/request/memoizable.rb +38 -0
  25. data/lib/typhoeus/request/operations.rb +71 -0
  26. data/lib/typhoeus/request/responseable.rb +29 -0
  27. data/lib/typhoeus/request/stubbable.rb +28 -0
  28. data/lib/typhoeus/request.rb +144 -241
  29. data/lib/typhoeus/response/header.rb +54 -0
  30. data/lib/typhoeus/response/informations.rb +205 -0
  31. data/lib/typhoeus/response/status.rb +80 -0
  32. data/lib/typhoeus/response.rb +45 -118
  33. data/lib/typhoeus/version.rb +3 -1
  34. data/lib/typhoeus.rb +87 -39
  35. metadata +37 -143
  36. data/lib/typhoeus/curl.rb +0 -453
  37. data/lib/typhoeus/easy/auth.rb +0 -14
  38. data/lib/typhoeus/easy/callbacks.rb +0 -33
  39. data/lib/typhoeus/easy/ffi_helper.rb +0 -61
  40. data/lib/typhoeus/easy/infos.rb +0 -90
  41. data/lib/typhoeus/easy/options.rb +0 -115
  42. data/lib/typhoeus/easy/proxy.rb +0 -20
  43. data/lib/typhoeus/easy/ssl.rb +0 -82
  44. data/lib/typhoeus/easy.rb +0 -115
  45. data/lib/typhoeus/filter.rb +0 -28
  46. data/lib/typhoeus/form.rb +0 -61
  47. data/lib/typhoeus/header.rb +0 -54
  48. data/lib/typhoeus/hydra/callbacks.rb +0 -24
  49. data/lib/typhoeus/hydra/connect_options.rb +0 -61
  50. data/lib/typhoeus/hydra/stubbing.rb +0 -68
  51. data/lib/typhoeus/hydra_mock.rb +0 -131
  52. data/lib/typhoeus/multi.rb +0 -146
  53. data/lib/typhoeus/param_processor.rb +0 -43
  54. data/lib/typhoeus/remote.rb +0 -306
  55. data/lib/typhoeus/remote_method.rb +0 -108
  56. data/lib/typhoeus/remote_proxy_object.rb +0 -50
  57. data/lib/typhoeus/utils.rb +0 -50
@@ -0,0 +1,205 @@
1
+ module Typhoeus
2
+ class Response
3
+
4
+ # This module contains logic about informations
5
+ # on a response.
6
+ module Informations
7
+
8
+ # Return libcurls return value.
9
+ #
10
+ # @example Get return_code.
11
+ # response.return_code
12
+ #
13
+ # @return [ Symbol ] The return_code.
14
+ def return_code
15
+ options[:return_code]
16
+ end
17
+
18
+ # Return the http response body.
19
+ #
20
+ # @example Get response_body.
21
+ # response.response_body
22
+ #
23
+ # @return [ String ] The response_body.
24
+ def response_body
25
+ options[:response_body] || options[:body]
26
+ end
27
+ alias :body :response_body
28
+
29
+ # Return the http response headers.
30
+ #
31
+ # @example Get response_headers.
32
+ # response.response_headers
33
+ #
34
+ # @return [ String ] The response_headers.
35
+ def response_headers
36
+ options[:response_headers]
37
+ end
38
+
39
+ # Return the last received HTTP, FTP or SMTP response code.
40
+ # The value will be zero if no server response code has
41
+ # been received. Note that a proxy's CONNECT response should
42
+ # be read with http_connect_code and not this.
43
+ #
44
+ # @example Get response_code.
45
+ # response.response_code
46
+ #
47
+ # @return [ Integer ] The response_code.
48
+ def response_code
49
+ options[:response_code] || options[:code]
50
+ end
51
+ alias :code :response_code
52
+
53
+ # Return the available http auth methods.
54
+ # Bitmask indicating the authentication method(s)
55
+ # available.
56
+ #
57
+ # @example Get httpauth_avail.
58
+ # response.httpauth_avail
59
+ #
60
+ # @return [ Integer ] The bitmask.
61
+ def httpauth_avail
62
+ options[:httpauth_avail]
63
+ end
64
+
65
+
66
+ # Return the total time in seconds for the previous
67
+ # transfer, including name resolving, TCP connect etc.
68
+ #
69
+ # @example Get total_time.
70
+ # response.total_time
71
+ #
72
+ # @return [ Float ] The total_time.
73
+ def total_time
74
+ options[:total_time] || options[:time]
75
+ end
76
+ alias :time :total_time
77
+
78
+ # Return the time, in seconds, it took from the start
79
+ # until the first byte is received by libcurl. This
80
+ # includes pretransfer time and also the time the
81
+ # server needs to calculate the result.
82
+ #
83
+ # @example Get starttransfer_time.
84
+ # response.starttransfer_time
85
+ #
86
+ # @return [ Float ] The starttransfer_time.
87
+ def starttransfer_time
88
+ options[:starttransfer_time] || options[:start_transfer_time]
89
+ end
90
+ alias :start_transfer_time :starttransfer_time
91
+
92
+ # Return the time, in seconds, it took from the start
93
+ # until the SSL/SSH connect/handshake to the remote
94
+ # host was completed. This time is most often very near
95
+ # to the pre transfer time, except for cases such as HTTP
96
+ # pippelining where the pretransfer time can be delayed
97
+ # due to waits in line for the pipeline and more.
98
+ #
99
+ # @example Get appconnect_time.
100
+ # response.appconnect_time
101
+ #
102
+ # @return [ Float ] The appconnect_time.
103
+ def appconnect_time
104
+ options[:appconnect_time] || options[:app_connect_time]
105
+ end
106
+ alias :app_connect_time :appconnect_time
107
+
108
+ # Return the time, in seconds, it took from the start
109
+ # until the file transfer is just about to begin. This
110
+ # includes all pre-transfer commands and negotiations
111
+ # that are specific to the particular protocol(s) involved.
112
+ # It does not involve the sending of the protocol-
113
+ # specific request that triggers a transfer.
114
+ #
115
+ # @example Get pretransfer_time.
116
+ # response.pretransfer_time
117
+ #
118
+ # @return [ Float ] The pretransfer_time.
119
+ def pretransfer_time
120
+ options[:pretransfer_time]
121
+ end
122
+
123
+ # Return the time, in seconds, it took from the start
124
+ # until the connect to the remote host (or proxy) was completed.
125
+ #
126
+ # @example Get connect_time.
127
+ # response.connect_time
128
+ #
129
+ # @return [ Float ] The connect_time.
130
+ def connect_time
131
+ options[:connect_time]
132
+ end
133
+
134
+ # Return the time, in seconds, it took from the
135
+ # start until the name resolving was completed.
136
+ #
137
+ # @example Get namelookup_time.
138
+ # response.namelookup_time
139
+ #
140
+ # @return [ Float ] The namelookup_time.
141
+ def namelookup_time
142
+ options[:namelookup_time] || options[:name_lookup_time]
143
+ end
144
+ alias :name_lookup_time :namelookup_time
145
+
146
+ # Return the last used effective url.
147
+ #
148
+ # @example Get effective_url.
149
+ # response.effective_url
150
+ #
151
+ # @return [ String ] The effective_url.
152
+ def effective_url
153
+ options[:effective_url]
154
+ end
155
+
156
+ # Return the string holding the IP address of the most recent
157
+ # connection done with this curl handle. This string
158
+ # may be IPv6 if that's enabled.
159
+ #
160
+ # @example Get primary_ip.
161
+ # response.primary_ip
162
+ #
163
+ # @return [ String ] The primary_ip.
164
+ def primary_ip
165
+ options[:primary_ip]
166
+ end
167
+
168
+ # Return the total number of redirections that were
169
+ # actually followed
170
+ #
171
+ # @example Get redirect_count.
172
+ # response.redirect_count
173
+ #
174
+ # @return [ Integer ] The redirect_count.
175
+ def redirect_count
176
+ options[:redirect_count]
177
+ end
178
+
179
+ # Returns the response header.
180
+ #
181
+ # @example Return headers.
182
+ # response.headers
183
+ #
184
+ # @return [ Typhoeus::Header ] The response header.
185
+ def headers
186
+ return nil if response_headers.nil? && @headers.nil?
187
+ @headers ||= Response::Header.new(response_headers.split("\r\n\r\n").last)
188
+ end
189
+ alias :headers_hash :headers
190
+
191
+ # Return all redirections in between as multiple
192
+ # responses with header.
193
+ #
194
+ # @example Return redirections.
195
+ # response.redirections
196
+ #
197
+ # @return [ Array<Typhoeus::Response> ] The redirections
198
+ def redirections
199
+ return [] unless response_headers
200
+ response_headers.split("\r\n\r\n")[0..-2].map{ |h| Response.new(:response_headers => h) }
201
+ end
202
+ end
203
+ end
204
+ end
205
+
@@ -0,0 +1,80 @@
1
+ module Typhoeus
2
+ class Response
3
+
4
+ # This module contains logic about the http
5
+ # status.
6
+ module Status
7
+
8
+ # Return the status message if present.
9
+ #
10
+ # @example Return status message.
11
+ # reesponse.status_message
12
+ #
13
+ # @return [ String ] The message.
14
+ def status_message
15
+ return @status_message if defined?(@status_message) && @status_message
16
+ return options[:status_message] unless options[:status_message].nil?
17
+
18
+ # HTTP servers can choose not to include the explanation to HTTP codes. The RFC
19
+ # states this (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4):
20
+ # Except when responding to a HEAD request, the server SHOULD include an entity containing
21
+ # an explanation of the error situation [...]
22
+ # This means 'HTTP/1.1 404' is as valid as 'HTTP/1.1 404 Not Found' and we have to handle it.
23
+
24
+ # Regexp doc: http://rubular.com/r/eAr1oVYsVa
25
+ if first_header_line != nil and first_header_line[/\d{3} (.*)$/, 1] != nil
26
+ @status_message = first_header_line[/\d{3} (.*)$/, 1].chomp
27
+ else
28
+ @status_message = nil
29
+ end
30
+ end
31
+
32
+ # Return the http version.
33
+ #
34
+ # @example Return http version.
35
+ # response.http_version
36
+ #
37
+ # @return [ String ] The http version.
38
+ def http_version
39
+ @http_version ||= first_header_line ? first_header_line[/HTTP\/(\S+)/, 1] : nil
40
+ end
41
+
42
+ # Return wether the response is a success.
43
+ #
44
+ # @example Return if the response was successful.
45
+ # response.success?
46
+ #
47
+ # @return [ Boolean ] Return true if successful, false else.
48
+ def success?
49
+ return_code == :ok && response_code >= 200 && response_code < 300
50
+ end
51
+
52
+ # Return wether the response is modified.
53
+ #
54
+ # @example Return if the response was modified.
55
+ # response.modified?
56
+ #
57
+ # @return [ Boolean ] Return true if modified, false else.
58
+ def modified?
59
+ return_code == :ok && response_code != 304
60
+ end
61
+
62
+ # Return wether the response is timed out.
63
+ #
64
+ # @example Return if the response timed out.
65
+ # response.timed_out?
66
+ #
67
+ # @return [ Boolean ] Return true if timed out, false else.
68
+ def timed_out?
69
+ [:operation_timedout, :couldnt_connect].include?(return_code)
70
+ end
71
+
72
+ private
73
+
74
+ # :nodoc:
75
+ def first_header_line
76
+ @first_header_line ||= response_headers.to_s.split("\n").first
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,123 +1,50 @@
1
- module Typhoeus
2
- class Response
3
- attr_accessor :request, :mock
4
- attr_reader :code, :headers, :body, :time,
5
- :requested_url, :requested_remote_method,
6
- :requested_http_method, :start_time,
7
- :effective_url, :start_transfer_time,
8
- :app_connect_time, :pretransfer_time,
9
- :connect_time, :name_lookup_time,
10
- :curl_return_code, :curl_error_message,
11
- :primary_ip, :redirect_count
12
-
13
- attr_writer :headers_hash
14
-
15
- def initialize(params = {})
16
- @code = params[:code]
17
- @curl_return_code = params[:curl_return_code]
18
- @curl_error_message = params[:curl_error_message]
19
- @status_message = params[:status_message]
20
- @http_version = params[:http_version]
21
- @headers = params[:headers]
22
- @body = params[:body]
23
- @time = params[:time]
24
- @requested_url = params[:requested_url]
25
- @requested_http_method = params[:requested_http_method]
26
- @start_time = params[:start_time]
27
- @start_transfer_time = params[:start_transfer_time]
28
- @app_connect_time = params[:app_connect_time]
29
- @pretransfer_time = params[:pretransfer_time]
30
- @connect_time = params[:connect_time]
31
- @name_lookup_time = params[:name_lookup_time]
32
- @request = params[:request]
33
- @effective_url = params[:effective_url]
34
- @primary_ip = params[:primary_ip]
35
- @redirect_count = params[:redirect_count]
36
- @mock = params[:mock] || false # default
37
- @headers_hash = Header.new(params[:headers_hash]) if params[:headers_hash]
38
- end
39
-
40
- # Returns true if this is a mock response.
41
- def mock?
42
- @mock
43
- end
44
-
45
- def headers
46
- @headers ||= @headers_hash ? construct_header_string : ''
47
- end
48
-
49
- def headers_hash
50
- @headers_hash ||= begin
51
- headers.split("\n").map {|o| o.strip}.inject(Typhoeus::Header.new) do |hash, o|
52
- if o.empty? || o =~ /^HTTP\/[\d\.]+/
53
- hash
54
- else
55
- i = o.index(":") || o.size
56
- key = o.slice(0, i)
57
- value = o.slice(i + 1, o.size)
58
- value = value.strip unless value.nil?
59
- if hash.key? key
60
- hash[key] = [hash[key], value].flatten
61
- else
62
- hash[key] = value
63
- end
64
-
65
- hash
66
- end
67
- end
68
- end
69
- end
70
-
71
- def status_message
72
- return @status_message if @status_message != nil
73
-
74
- # HTTP servers can choose not to include the explanation to HTTP codes. The RFC
75
- # states this (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4):
76
- # Except when responding to a HEAD request, the server SHOULD include an entity containing
77
- # an explanation of the error situation [...]
78
- # This means 'HTTP/1.1 404' is as valid as 'HTTP/1.1 404 Not Found' and we have to handle it.
1
+ require 'typhoeus/response/header'
2
+ require 'typhoeus/response/informations'
3
+ require 'typhoeus/response/status'
79
4
 
80
- # Regexp doc: http://rubular.com/r/eAr1oVYsVa
81
- if first_header_line != nil and first_header_line[/\d{3} (.*)$/, 1] != nil
82
- @status_message = first_header_line[/\d{3} (.*)$/, 1].chomp
83
- else
84
- @status_message = nil
85
- end
86
- end
87
-
88
- def http_version
89
- @http_version ||= first_header_line ? first_header_line[/HTTP\/(\S+)/, 1] : nil
90
- end
91
-
92
- def success?
93
- @code >= 200 && @code < 300
94
- end
95
-
96
- def modified?
97
- @code != 304
98
- end
5
+ module Typhoeus
99
6
 
100
- def timed_out?
101
- curl_return_code == 28
7
+ # This class respresents the response.
8
+ class Response
9
+ include Response::Informations
10
+ include Response::Status
11
+
12
+ # Remembers the corresponding request.
13
+ #
14
+ # @example Get request.
15
+ # request = Typhoeus::Request.get("www.example.com")
16
+ # response = request.run
17
+ # response == request.response
18
+ # #=> true
19
+ #
20
+ # @return [ Typhoeus::Request ]
21
+ attr_accessor :request
22
+
23
+ # The options provided, contains all the
24
+ # informations about the request.
25
+ #
26
+ # @return [ Hash ]
27
+ attr_accessor :options
28
+
29
+ # @api private
30
+ attr_writer :mock
31
+
32
+ # Create a new response.
33
+ #
34
+ # @example Create a response.
35
+ # Response.new
36
+ #
37
+ # @param [ Hash ] options The options hash.
38
+ #
39
+ # @return [ Response ] The new response.
40
+ def initialize(options = {})
41
+ @options = options
42
+ @headers = options[:headers]
43
+ end
44
+
45
+ # @api private
46
+ def mock
47
+ defined?(@mock) ? @mock : options[:mock]
102
48
  end
103
-
104
- private
105
-
106
- def first_header_line
107
- @first_header_line ||= @headers.to_s.split("\n").first
108
- end
109
-
110
- def construct_header_string
111
- lines = ["HTTP/#{http_version} #{code} #{status_message}"]
112
-
113
- @headers_hash.each do |key, values|
114
- [values].flatten.each do |value|
115
- lines << "#{key}: #{value}"
116
- end
117
- end
118
-
119
- lines << '' << ''
120
- lines.join("\r\n")
121
- end
122
49
  end
123
50
  end
@@ -1,3 +1,5 @@
1
1
  module Typhoeus
2
- VERSION = '0.4.2'
2
+
3
+ # The current Typhoeus version.
4
+ VERSION = '0.5.0.rc'
3
5
  end
data/lib/typhoeus.rb CHANGED
@@ -1,56 +1,104 @@
1
1
  require 'digest/sha2'
2
+ require 'ethon'
2
3
 
3
- require 'typhoeus/utils'
4
- require 'typhoeus/header'
5
- require 'typhoeus/curl'
6
- require 'typhoeus/easy'
7
- require 'typhoeus/form'
8
- require 'typhoeus/multi'
9
- require 'typhoeus/filter'
10
- require 'typhoeus/param_processor'
11
- require 'typhoeus/remote'
12
- require 'typhoeus/remote_proxy_object'
13
- require 'typhoeus/response'
14
- require 'typhoeus/request'
4
+ require 'typhoeus/config'
5
+ require 'typhoeus/errors'
6
+ require 'typhoeus/expectation'
15
7
  require 'typhoeus/hydra'
16
- require 'typhoeus/hydra_mock'
8
+ require 'typhoeus/request'
9
+ require 'typhoeus/response'
17
10
  require 'typhoeus/version'
18
11
 
12
+ # Typhoeus is a http client library based on Ethon which
13
+ # wraps libcurl.
14
+ #
15
+ # @example (see Typhoeus::Request)
16
+ # @example (see Typhoeus::Hydra)
17
+ #
18
+ # @see Typhoeus::Request
19
+ # @see Typhoeus::Hydra
20
+ #
21
+ # @since 0.5.0
19
22
  module Typhoeus
20
- def self.easy_object_pool
21
- @easy_objects ||= []
22
- end
23
+ extend self
24
+ extend Hydra::EasyPool
25
+ extend Request::Actions
26
+ extend Request::Callbacks::Types
23
27
 
24
- def self.init_easy_object_pool
25
- 20.times do
26
- easy_object_pool << Typhoeus::Easy.new
27
- end
28
- end
28
+ # The default typhoeus user agent.
29
+ USER_AGENT = "Typhoeus - https://github.com/typhoeus/typhoeus"
29
30
 
30
- def self.release_easy_object(easy)
31
- easy.reset
32
- easy_object_pool << easy
31
+ # Set the Typhoeus configuration options by passing a block.
32
+ #
33
+ # @example (see Typhoeus::Config)
34
+ #
35
+ # @yield [ Typhoeus::Config ]
36
+ #
37
+ # @return [ Typhoeus::Config ] The configuration.
38
+ #
39
+ # @see Typhoeus::Config
40
+ def configure
41
+ yield Config
33
42
  end
34
43
 
35
- def self.get_easy_object
36
- if easy_object_pool.empty?
37
- Typhoeus::Easy.new
38
- else
39
- easy_object_pool.pop
44
+ # Stub out specific request.
45
+ #
46
+ # @example (see Typhoeus::Expectation)
47
+ #
48
+ # @param [ String ] url The url to stub out.
49
+ # @param [ Hash ] options The options to stub out.
50
+ #
51
+ # @return [ Typhoeus::Expectation ] The expecatation.
52
+ #
53
+ # @see Typhoeus::Expectation
54
+ def stub(url, options = {})
55
+ expectation = Expectation.all.find{ |e| e.url == url && e.options == options }
56
+ return expectation if expectation
57
+
58
+ Expectation.new(url, options).tap do |new_expectation|
59
+ Expectation.all << new_expectation
40
60
  end
41
61
  end
42
62
 
43
- def self.add_easy_request(easy_object)
44
- Thread.current[:curl_multi] ||= Typhoeus::Multi.new
45
- Thread.current[:curl_multi].add(easy_object)
63
+ # Add before callbacks.
64
+ #
65
+ # @example Add before callback.
66
+ # Typhoeus.before { |request| p request.url }
67
+ #
68
+ # @param [ Block ] block The callback.
69
+ #
70
+ # @yield [ Typhoeus::Request ]
71
+ #
72
+ # @return [ Array<Block> ] All before blocks.
73
+ def before(&block)
74
+ @before ||= []
75
+ @before << block if block_given?
76
+ @before
46
77
  end
47
78
 
48
- def self.perform_easy_requests
49
- multi = Thread.current[:curl_multi]
50
- start_time = Time.now
51
- multi.easy_handles.each do |easy|
52
- easy.start_time = start_time
53
- end
54
- multi.perform
79
+ # Execute given block as if block connection is turned off.
80
+ # The old block connection state is restored afterwards.
81
+ #
82
+ # @example Make a real request, no matter if its blocked.
83
+ # Typhoeus::Config.block_connection = true
84
+ # Typhoeus.get("www.example.com").code
85
+ # #=> raise Typhoeus::Errors::NoStub
86
+ #
87
+ # Typhoeus.with_connection do
88
+ # Typhoeus.get("www.example.com").code
89
+ # #=> :ok
90
+ # end
91
+ #
92
+ # @param [ Block ] block The block to execute.
93
+ #
94
+ # @return [ Object ] Returns the return value of block.
95
+ #
96
+ # @see Typhoeus::Config#block_connection
97
+ def with_connection
98
+ old = Config.block_connection
99
+ Config.block_connection = false
100
+ result = yield if block_given?
101
+ Config.block_connection = old
102
+ result
55
103
  end
56
104
  end