ethon 0.18.0 → 0.19.1

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +705 -2
  3. data/README.md +183 -40
  4. data/lib/ethon/curl.rb +22 -0
  5. data/lib/ethon/curls/classes.rb +284 -12
  6. data/lib/ethon/curls/codes.rb +28 -9
  7. data/lib/ethon/curls/constants.rb +61 -4
  8. data/lib/ethon/curls/form_options.rb +2 -1
  9. data/lib/ethon/curls/functions.rb +60 -11
  10. data/lib/ethon/curls/infos.rb +104 -5
  11. data/lib/ethon/curls/introspection.rb +139 -0
  12. data/lib/ethon/curls/introspection_constants.rb +42 -0
  13. data/lib/ethon/curls/mime.rb +187 -0
  14. data/lib/ethon/curls/optional.rb +14 -0
  15. data/lib/ethon/curls/options.rb +298 -71
  16. data/lib/ethon/curls/settings.rb +12 -3
  17. data/lib/ethon/curls/share.rb +165 -0
  18. data/lib/ethon/curls/share_constants.rb +47 -0
  19. data/lib/ethon/curls/url.rb +171 -0
  20. data/lib/ethon/curls/url_constants.rb +78 -0
  21. data/lib/ethon/easy/callbacks.rb +177 -51
  22. data/lib/ethon/easy/features.rb +4 -1
  23. data/lib/ethon/easy/files.rb +113 -0
  24. data/lib/ethon/easy/form.rb +65 -6
  25. data/lib/ethon/easy/header.rb +57 -15
  26. data/lib/ethon/easy/headers_api.rb +79 -0
  27. data/lib/ethon/easy/http/actionable.rb +25 -3
  28. data/lib/ethon/easy/http/get.rb +5 -1
  29. data/lib/ethon/easy/http/post.rb +1 -0
  30. data/lib/ethon/easy/http/postable.rb +9 -2
  31. data/lib/ethon/easy/http/putable.rb +19 -4
  32. data/lib/ethon/easy/http.rb +16 -4
  33. data/lib/ethon/easy/informations.rb +51 -2
  34. data/lib/ethon/easy/mime.rb +29 -0
  35. data/lib/ethon/easy/mirror.rb +17 -4
  36. data/lib/ethon/easy/operations.rb +264 -11
  37. data/lib/ethon/easy/options.rb +327 -8
  38. data/lib/ethon/easy/queryable.rb +59 -14
  39. data/lib/ethon/easy/response_callbacks.rb +33 -12
  40. data/lib/ethon/easy/share.rb +28 -0
  41. data/lib/ethon/easy/url.rb +32 -0
  42. data/lib/ethon/easy/util.rb +19 -0
  43. data/lib/ethon/easy.rb +185 -10
  44. data/lib/ethon/errors/fork_safety.rb +10 -0
  45. data/lib/ethon/errors/form_add.rb +12 -0
  46. data/lib/ethon/errors/global_init.rb +1 -1
  47. data/lib/ethon/errors/header.rb +12 -0
  48. data/lib/ethon/errors/invalid_option.rb +3 -1
  49. data/lib/ethon/errors/invalid_value.rb +3 -1
  50. data/lib/ethon/errors/mime.rb +17 -0
  51. data/lib/ethon/errors/multi_add.rb +1 -1
  52. data/lib/ethon/errors/multi_assign.rb +12 -0
  53. data/lib/ethon/errors/multi_fdset.rb +1 -1
  54. data/lib/ethon/errors/multi_perform.rb +12 -0
  55. data/lib/ethon/errors/multi_poll.rb +12 -0
  56. data/lib/ethon/errors/multi_remove.rb +1 -1
  57. data/lib/ethon/errors/multi_timeout.rb +2 -2
  58. data/lib/ethon/errors/multi_wait.rb +12 -0
  59. data/lib/ethon/errors/multi_wakeup.rb +12 -0
  60. data/lib/ethon/errors/select.rb +1 -1
  61. data/lib/ethon/errors/set_option.rb +27 -0
  62. data/lib/ethon/errors/share.rb +17 -0
  63. data/lib/ethon/errors/unsupported_option.rb +23 -0
  64. data/lib/ethon/errors/url.rb +17 -0
  65. data/lib/ethon/errors.rb +13 -0
  66. data/lib/ethon/libc.rb +30 -1
  67. data/lib/ethon/loggable.rb +1 -1
  68. data/lib/ethon/multi/operations.rb +269 -25
  69. data/lib/ethon/multi/options.rb +114 -12
  70. data/lib/ethon/multi/stack.rb +181 -10
  71. data/lib/ethon/multi.rb +51 -15
  72. data/lib/ethon/version.rb +1 -1
  73. data/lib/ethon.rb +8 -9
  74. metadata +35 -6
@@ -9,25 +9,89 @@ module Ethon
9
9
  # Sets max_total_connections option.
10
10
  #
11
11
  # @example Set max_total_connections option.
12
- # multi.max_total_conections = $value
12
+ # multi.max_total_connections = $value
13
13
  #
14
- # @param [ String ] value The value to set.
14
+ # @param [ Integer ] value The value to set.
15
15
  #
16
16
  # @return [ void ]
17
17
  def max_total_connections=(value)
18
- Curl.set_option(:max_total_connections, value_for(value, :int), handle, :multi)
18
+ Curl.set_option(:max_total_connections, value_for(value, :int, :max_total_connections), handle, :multi)
19
+ end
20
+
21
+ # Sets max_concurrent_streams option.
22
+ #
23
+ # @example Set max_concurrent_streams option.
24
+ # multi.max_concurrent_streams = $value
25
+ #
26
+ # @param [ Integer ] value The value to set.
27
+ #
28
+ # @return [ void ]
29
+ def max_concurrent_streams=(value)
30
+ Curl.set_option(:max_concurrent_streams, value_for(value, :int, :max_concurrent_streams), handle, :multi)
19
31
  end
20
32
 
21
33
  # Sets maxconnects option.
22
34
  #
35
+ # An explicit value also disables the automatic sizing described in
36
+ # #raise_default_maxconnects.
37
+ #
23
38
  # @example Set maxconnects option.
24
39
  # multi.maxconnects = $value
25
40
  #
26
- # @param [ String ] value The value to set.
41
+ # @param [ Integer ] value The value to set.
27
42
  #
28
43
  # @return [ void ]
29
44
  def maxconnects=(value)
30
- Curl.set_option(:maxconnects, value_for(value, :int), handle, :multi)
45
+ @maxconnects_configured = true unless value.nil?
46
+ set_maxconnects(value)
47
+ end
48
+
49
+ # Sets max_host_connections option.
50
+ #
51
+ # @example Set max_host_connections option.
52
+ # multi.max_host_connections = $value
53
+ #
54
+ # @param [ Integer ] value The value to set.
55
+ #
56
+ # @return [ void ]
57
+ def max_host_connections=(value)
58
+ Curl.set_option(:max_host_connections, value_for(value, :int, :max_host_connections), handle, :multi)
59
+ end
60
+
61
+ # Sets max_pipeline_length option.
62
+ #
63
+ # @example Set max_pipeline_length option.
64
+ # multi.max_pipeline_length = $value
65
+ #
66
+ # @param [ Integer ] value The value to set.
67
+ #
68
+ # @return [ void ]
69
+ def max_pipeline_length=(value)
70
+ Curl.set_option(:max_pipeline_length, value_for(value, :int, :max_pipeline_length), handle, :multi)
71
+ end
72
+
73
+ # Sets content_length_penalty_size option.
74
+ #
75
+ # @example Set content_length_penalty_size option.
76
+ # multi.content_length_penalty_size = $value
77
+ #
78
+ # @param [ Integer ] value The value to set.
79
+ #
80
+ # @return [ void ]
81
+ def content_length_penalty_size=(value)
82
+ Curl.set_option(:content_length_penalty_size, value, handle, :multi)
83
+ end
84
+
85
+ # Sets chunk_length_penalty_size option.
86
+ #
87
+ # @example Set chunk_length_penalty_size option.
88
+ # multi.chunk_length_penalty_size = $value
89
+ #
90
+ # @param [ Integer ] value The value to set.
91
+ #
92
+ # @return [ void ]
93
+ def chunk_length_penalty_size=(value)
94
+ Curl.set_option(:chunk_length_penalty_size, value, handle, :multi)
31
95
  end
32
96
 
33
97
  # Sets pipelining option.
@@ -35,11 +99,11 @@ module Ethon
35
99
  # @example Set pipelining option.
36
100
  # multi.pipelining = $value
37
101
  #
38
- # @param [ String ] value The value to set.
102
+ # @param [ Integer ] value The value to set.
39
103
  #
40
104
  # @return [ void ]
41
105
  def pipelining=(value)
42
- Curl.set_option(:pipelining, value_for(value, :int), handle, :multi)
106
+ Curl.set_option(:pipelining, value_for(value, :int, :pipelining), handle, :multi)
43
107
  end
44
108
 
45
109
  # Sets socketdata option.
@@ -47,7 +111,7 @@ module Ethon
47
111
  # @example Set socketdata option.
48
112
  # multi.socketdata = $value
49
113
  #
50
- # @param [ String ] value The value to set.
114
+ # @param [ FFI::Pointer, nil ] value The value to set.
51
115
  #
52
116
  # @return [ void ]
53
117
  def socketdata=(value)
@@ -59,11 +123,13 @@ module Ethon
59
123
  # @example Set socketfunction option.
60
124
  # multi.socketfunction = $value
61
125
  #
62
- # @param [ String ] value The value to set.
126
+ # @param [ Proc, nil ] value The value to set.
63
127
  #
64
128
  # @return [ void ]
65
129
  def socketfunction=(value)
66
130
  Curl.set_option(:socketfunction, value_for(value, :string), handle, :multi)
131
+ retain_callback(:socketfunction, value)
132
+ value
67
133
  end
68
134
 
69
135
  # Sets timerdata option.
@@ -71,7 +137,7 @@ module Ethon
71
137
  # @example Set timerdata option.
72
138
  # multi.timerdata = $value
73
139
  #
74
- # @param [ String ] value The value to set.
140
+ # @param [ FFI::Pointer, nil ] value The value to set.
75
141
  #
76
142
  # @return [ void ]
77
143
  def timerdata=(value)
@@ -83,15 +149,50 @@ module Ethon
83
149
  # @example Set timerfunction option.
84
150
  # multi.timerfunction = $value
85
151
  #
86
- # @param [ String ] value The value to set.
152
+ # @param [ Proc, nil ] value The value to set.
87
153
  #
88
154
  # @return [ void ]
89
155
  def timerfunction=(value)
90
156
  Curl.set_option(:timerfunction, value_for(value, :string), handle, :multi)
157
+ retain_callback(:timerfunction, value)
158
+ value
91
159
  end
92
160
 
93
161
  private
94
162
 
163
+ # Raise the default connection cache size to twice the largest number
164
+ # of easy handles ever attached at once.
165
+ #
166
+ # libcurl derives its default from the number of handles attached when
167
+ # a connection is returned. Ethon detaches each handle as soon as its
168
+ # transfer finishes, so that number falls during a run and libcurl
169
+ # closes connections that later requests could have reused.
170
+ #
171
+ # @param [ Integer ] count The number of attached handles after the
172
+ # pending add.
173
+ #
174
+ # @return [ void ]
175
+ def raise_default_maxconnects(count)
176
+ return if @maxconnects_configured
177
+ return if count <= (@maxconnects_high_water ||= 0)
178
+
179
+ @maxconnects_high_water = count
180
+ set_maxconnects(2 * count)
181
+ end
182
+
183
+ def set_maxconnects(value)
184
+ Curl.set_option(:maxconnects, value_for(value, :int, :maxconnects), handle, :multi)
185
+ end
186
+
187
+ def retain_callback(option, callback)
188
+ if callback.nil?
189
+ @procs.delete(option) if @procs
190
+ else
191
+ (@procs ||= {})[option] = callback
192
+ end
193
+ Curl::MultiHandle.retain_callback(handle, option, callback)
194
+ end
195
+
95
196
  # Return the value to set to multi handle. It is converted with the help
96
197
  # of bool_options, enum_options and int_options.
97
198
  #
@@ -105,13 +206,14 @@ module Ethon
105
206
  if type == :bool
106
207
  value ? 1 : 0
107
208
  elsif type == :int
108
- value.to_i
209
+ Ethon::Curls::Options.integer_value(option, value)
109
210
  elsif value.is_a?(String)
110
211
  Ethon::Easy::Util.escape_zero_byte(value)
111
212
  else
112
213
  value
113
214
  end
114
215
  end
216
+
115
217
  end
116
218
  end
117
219
  end
@@ -5,14 +5,18 @@ module Ethon
5
5
  # This module provides the multi stack behaviour.
6
6
  module Stack
7
7
 
8
- # Return easy handles.
8
+ # Return the easy handles currently added to this multi.
9
+ #
10
+ # The returned array is a snapshot. Adding or deleting entries there does
11
+ # not change the multi, because libcurl would still reference the handles.
12
+ # Use #add and #delete instead.
9
13
  #
10
14
  # @example Return easy handles.
11
15
  # multi.easy_handles
12
16
  #
13
- # @return [ Array ] The easy handles.
17
+ # @return [ Array<Easy> ] The easy handles.
14
18
  def easy_handles
15
- @easy_handles ||= []
19
+ stack.dup.freeze
16
20
  end
17
21
 
18
22
  # Add an easy to the stack.
@@ -22,13 +26,70 @@ module Ethon
22
26
  #
23
27
  # @param [ Easy ] easy The easy to add.
24
28
  #
29
+ # A failed add leaves the Easy untouched: its previous response and any
30
+ # streamed response file stay as they were.
31
+ #
32
+ # @raise [ TypeError ] If easy is not an Ethon::Easy.
25
33
  # @raise [ Ethon::Errors::MultiAdd ] If adding an easy failed.
26
34
  def add(easy)
27
- return nil if easy_handles.include?(easy)
35
+ return nil if stack.include?(easy)
36
+ unless easy.is_a?(Ethon::Easy)
37
+ raise TypeError, "expected an Ethon::Easy, got #{easy.class}"
38
+ end
39
+
40
+ # Another Multi may still own the handle natively. Reject before
41
+ # resetting its transfer state or touching its response stream.
42
+ if easy.instance_variable_get(:@multi)
43
+ raise Errors::EthonError, "Easy is still attached to a Multi; remove it first"
44
+ end
28
45
 
29
- code = Curl.multi_add_handle(handle, easy.handle)
30
- raise Errors::MultiAdd.new(code, easy) unless code == :ok
31
- easy_handles << easy
46
+ # Resolve every handle before touching the easy so a cleaned up or
47
+ # forked Multi or Easy cannot be changed while raising.
48
+ multi_handle = handle
49
+ easy_handle = easy.handle
50
+
51
+ # Pin libcurl's default connection cache size before the handle is
52
+ # attached. Finished handles are detached immediately, which would
53
+ # otherwise make the default shrink during a run and close reusable
54
+ # connections.
55
+ raise_default_maxconnects(stack.size + 1)
56
+
57
+ stack << easy
58
+ begin
59
+ code = Curl.multi_add_handle(multi_handle, easy_handle)
60
+ rescue Exception
61
+ # The native add may have completed before a socket or timer callback
62
+ # raised, so make sure libcurl does not keep an untracked handle.
63
+ detach_added_handle(easy, multi_handle, easy_handle)
64
+ raise
65
+ end
66
+
67
+ unless code == :ok
68
+ stack.delete(easy)
69
+ raise Errors::MultiAdd.new(code, easy)
70
+ end
71
+
72
+ # Cleanup must not free an easy that is still attached to this multi.
73
+ easy.instance_variable_set(:@multi, self)
74
+
75
+ # Only a successful add starts a new transfer. Resetting the previous
76
+ # response and opening its output stream before libcurl accepted the
77
+ # handle would make a failed add discard the response body and truncate
78
+ # a streamed response file.
79
+ begin
80
+ # A completion callback can add the Easy to this Multi before the
81
+ # previous transfer's destination is finished. Close or flush it
82
+ # first so prepare opens a fresh destination for this transfer.
83
+ easy.finish_response_body_io
84
+ easy.prepare_response_body_io
85
+ easy.reset_transfer_state
86
+ easy.clear_callback_error
87
+ rescue Exception
88
+ rollback_add(easy, multi_handle, easy_handle)
89
+ raise
90
+ end
91
+
92
+ easy_handles
32
93
  end
33
94
 
34
95
  # Delete an easy from stack.
@@ -39,11 +100,121 @@ module Ethon
39
100
  #
40
101
  # @raise [ Ethon::Errors::MultiRemove ] If removing an easy failed.
41
102
  def delete(easy)
42
- if easy_handles.delete(easy)
43
- code = Curl.multi_remove_handle(handle, easy.handle)
44
- raise Errors::MultiRemove.new(code, handle) unless code == :ok
103
+ if (index = stack.index(easy))
104
+ easy_handle = nil
105
+ stack.delete_at(index)
106
+ begin
107
+ easy_handle = easy.handle
108
+ code = Curl.multi_remove_handle(handle, easy_handle)
109
+ rescue Exception
110
+ # A socket callback can raise after libcurl has already detached
111
+ # the handle. Keep the stack in sync with the native multi instead
112
+ # of tracking a handle libcurl no longer owns.
113
+ if native_detached?(easy_handle)
114
+ begin
115
+ finish_removed_easy(easy)
116
+ rescue StandardError
117
+ # Keep the original remove error.
118
+ end
119
+ else
120
+ stack.insert(index, easy)
121
+ end
122
+ raise
123
+ end
124
+ unless code == :ok
125
+ stack.insert(index, easy)
126
+ raise Errors::MultiRemove.new(code, easy)
127
+ end
128
+
129
+ finish_removed_easy(easy)
45
130
  end
46
131
  end
132
+
133
+ private
134
+
135
+ # The mutable stack. Only this Multi may change it; every native change
136
+ # has to go through #add and #delete.
137
+ def stack
138
+ @easy_handles ||= []
139
+ end
140
+
141
+ # Finish an Easy that is no longer attached to this Multi.
142
+ def finish_removed_easy(easy)
143
+ if easy.instance_variable_get(:@multi).equal?(self)
144
+ easy.instance_variable_set(:@multi, nil)
145
+ end
146
+
147
+ begin
148
+ easy.finish_response_body_io
149
+ ensure
150
+ @running_count = nil if stack.empty?
151
+ end
152
+ end
153
+
154
+ # Whether libcurl detached the handle before a callback raised. Older
155
+ # libcurl cannot answer, so assume it is still attached and keep it
156
+ # tracked.
157
+ def native_detached?(easy_handle)
158
+ return false unless easy_handle
159
+ return false unless Curl.respond_to?(:multi_get_handles)
160
+
161
+ !native_handle?(easy_handle)
162
+ rescue StandardError
163
+ false
164
+ end
165
+
166
+ # Undo a native add whose Easy could not be prepared for the transfer.
167
+ def rollback_add(easy, multi_handle, easy_handle)
168
+ begin
169
+ easy.finish_response_body_io
170
+ rescue StandardError
171
+ # Best effort; the add is already being rolled back.
172
+ end
173
+
174
+ detach_added_handle(easy, multi_handle, easy_handle)
175
+ end
176
+
177
+ # Detach a handle that libcurl may have attached before an exception
178
+ # interrupted the add. The easy stays tracked when it may still be
179
+ # attached so that Multi#cleanup can retry instead of leaving libcurl
180
+ # with an untracked attached handle.
181
+ def detach_added_handle(easy, multi_handle, easy_handle)
182
+ result = begin
183
+ Curl.multi_remove_handle(multi_handle, easy_handle)
184
+ rescue Exception
185
+ nil
186
+ end
187
+
188
+ attached = case result
189
+ when :ok, :bad_easy_handle
190
+ false
191
+ when nil
192
+ # libcurl detaches the easy before it invokes a socket or
193
+ # timer callback, so a callback exception is not enough to
194
+ # assume it is still attached.
195
+ native_handle?(easy_handle)
196
+ else
197
+ true
198
+ end
199
+
200
+ if attached
201
+ easy.instance_variable_set(:@multi, self)
202
+ else
203
+ stack.delete(easy)
204
+ easy.instance_variable_set(:@multi, nil)
205
+ end
206
+ end
207
+
208
+ # Whether libcurl still knows the native easy handle. libcurl can only
209
+ # answer that question from 7.88.0 on; older versions assume the handle
210
+ # was detached, which matches the order inside Multi#delete.
211
+ def native_handle?(easy_handle)
212
+ return false unless Curl.respond_to?(:multi_get_handles)
213
+
214
+ native_handles.any? { |handle| handle == easy_handle }
215
+ rescue NotImplementedError
216
+ false
217
+ end
47
218
  end
48
219
  end
49
220
  end
data/lib/ethon/multi.rb CHANGED
@@ -12,6 +12,8 @@ module Ethon
12
12
  include Ethon::Multi::Operations
13
13
  include Ethon::Multi::Options
14
14
 
15
+ EXECUTION_MODES = [:perform, :socket_action].freeze
16
+
15
17
  # Create a new multi. Initialize curl in case
16
18
  # it didn't happen before.
17
19
  #
@@ -20,18 +22,19 @@ module Ethon
20
22
  #
21
23
  # @param [ Hash ] options The options.
22
24
  #
23
- # @option options :socketdata [String]
25
+ # @option options :socketdata [FFI::Pointer]
24
26
  # Pass a pointer to whatever you want passed to the
25
27
  # curl_socket_callback's forth argument, the userp pointer. This is not
26
28
  # used by libcurl but only passed-thru as-is. Set the callback pointer
27
- # with CURLMOPT_SOCKETFUNCTION.
28
- # @option options :pipelining [Boolean]
29
- # Pass a long set to 1 to enable or 0 to disable. Enabling pipelining
30
- # on a multi handle will make it attempt to perform HTTP Pipelining as
31
- # far as possible for transfers using this handle. This means that if
32
- # you add a second request that can use an already existing connection,
33
- # the second request will be "piped" on the same connection rather than
34
- # being executed in parallel. (Added in 7.16.0)
29
+ # with CURLMOPT_SOCKETFUNCTION. Keep the pointer alive for as long as the
30
+ # Multi may invoke the callback.
31
+ # @option options :pipelining [Integer]
32
+ # Pass a bitmask: 0 for CURLPIPE_NOTHING, 1 for CURLPIPE_HTTP1, or 2 for
33
+ # CURLPIPE_MULTIPLEX. CURLPIPE_HTTP1 is deprecated and has no effect
34
+ # since libcurl 7.62.0. CURLPIPE_MULTIPLEX allows libcurl to multiplex a
35
+ # new transfer over an existing HTTP/2 or HTTP/3 connection when
36
+ # possible, and is the default since libcurl 7.62.0. (Added in 7.16.0;
37
+ # multiplex support added in 7.43.0.)
35
38
  # @option options :timerfunction [Proc]
36
39
  # Pass a pointer to a function matching the curl_multi_timer_callback
37
40
  # prototype. This function will then be called when the timeout value
@@ -44,16 +47,21 @@ module Ethon
44
47
  # only when the fixed future timeout time actually changes. See also
45
48
  # CURLMOPT_TIMERDATA. This callback can be used instead of, or in
46
49
  # addition to, curl_multi_timeout(3). (Added in 7.16.0)
47
- # @option options :timerdata [String]
50
+ # @option options :timerdata [FFI::Pointer]
48
51
  # Pass a pointer to whatever you want passed to the
49
52
  # curl_multi_timer_callback's third argument, the userp pointer. This
50
53
  # is not used by libcurl but only passed-thru as-is. Set the callback
51
- # pointer with CURLMOPT_TIMERFUNCTION. (Added in 7.16.0)
54
+ # pointer with CURLMOPT_TIMERFUNCTION. Keep the pointer alive for as long
55
+ # as the Multi may invoke the callback. (Added in 7.16.0)
52
56
  # @option options :maxconnects [Integer]
53
57
  # Pass a long. The set number will be used as the maximum amount of
54
- # simultaneously open connections that libcurl may cache. Default is
55
- # 10, and libcurl will enlarge the size for each added easy handle to
56
- # make it fit 4 times the number of added easy handles.
58
+ # simultaneously open connections that libcurl may cache. When this is
59
+ # not set, Ethon sizes the cache to twice the largest number of easy
60
+ # handles attached to this Multi at once. libcurl derives its own default
61
+ # from the handles attached when a connection is returned, but Ethon
62
+ # detaches finished handles, which would otherwise shrink the cache during
63
+ # a run. Pass an explicit value to cap the cache yourself, or 0 to use
64
+ # libcurl's dynamic default.
57
65
  # By setting this option, you can prevent the cache size from growing
58
66
  # beyond the limit set by you.
59
67
  # When the cache is full, curl closes the oldest one in the cache to
@@ -61,6 +69,23 @@ module Ethon
61
69
  # This option is for the multi handle's use only, when using the easy
62
70
  # interface you should instead use the CURLOPT_MAXCONNECTS option.
63
71
  # (Added in 7.16.3)
72
+ # @option options :max_host_connections [Integer]
73
+ # Pass a long. The set number will be used as the maximum amount of
74
+ # simultaneously open connections to a single host.
75
+ # (Added in 7.30.0)
76
+ # @option options :max_pipeline_length [Integer]
77
+ # Pass a long. The set number will be used as the maximum amount of
78
+ # requests in a pipeline. This option is obsolete in libcurl 7.62.0 and
79
+ # later, where HTTP/1 pipelining has no effect.
80
+ # (Added in 7.16.0)
81
+ # @option options :content_length_penalty_size [Integer]
82
+ # Pass a curl_off_t. This option sets the size of the content length
83
+ # penalty in bytes. It is used by the pipelining code.
84
+ # (Added in 7.16.0)
85
+ # @option options :chunk_length_penalty_size [Integer]
86
+ # Pass a curl_off_t. This option sets the size of the chunk length penalty
87
+ # in bytes. It is used by the pipelining code.
88
+ # (Added in 7.16.0)
64
89
  # @option options :max_total_connections [Integer]
65
90
  # Pass a long. The set number will be used as the maximum amount of
66
91
  # simultaneously open connections in total. For each new session,
@@ -74,7 +99,7 @@ module Ethon
74
99
  # is 1 will not be treated as unlimited. Instead it will open only 1
75
100
  # connection and try to pipeline on it.
76
101
  # (Added in 7.30.0)
77
- # @option options :execution_mode [Boolean]
102
+ # @option options :execution_mode [Symbol]
78
103
  # Either :perform (default) or :socket_action, specifies the usage
79
104
  # method that will be used on this multi object. The default :perform
80
105
  # mode provides a #perform function that uses curl_multi_perform
@@ -88,7 +113,11 @@ module Ethon
88
113
  # @return [ Multi ] The new multi.
89
114
  def initialize(options = {})
90
115
  Curl.init
116
+ options = options.dup
91
117
  @execution_mode = options.delete(:execution_mode) || :perform
118
+ unless EXECUTION_MODES.include?(@execution_mode)
119
+ raise ArgumentError, "Unknown execution mode #{@execution_mode.inspect}"
120
+ end
92
121
  set_attributes(options)
93
122
  init_vars
94
123
  end
@@ -114,6 +143,13 @@ module Ethon
114
143
 
115
144
  private
116
145
 
146
+ # Ruby cannot duplicate the native multi handle or its Ruby-side
147
+ # bookkeeping safely. A shallow copy would share the easy-handle stack and
148
+ # the native handle, so cleaning up one copy would invalidate the other.
149
+ def initialize_copy(_other)
150
+ raise TypeError, "cannot duplicate an Ethon::Multi"
151
+ end
152
+
117
153
  # Internal function to gate functions to a specific execution mode
118
154
  #
119
155
  # @raise ArgumentError
data/lib/ethon/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  module Ethon
3
3
 
4
4
  # Ethon version.
5
- VERSION = '0.18.0'
5
+ VERSION = '0.19.1'
6
6
  end
data/lib/ethon.rb CHANGED
@@ -2,23 +2,22 @@
2
2
  require 'logger'
3
3
  require 'ffi'
4
4
  require 'thread'
5
- begin
6
- require 'mime/types/columnar'
7
- rescue LoadError
8
- begin
9
- require 'mime/types'
10
- rescue LoadError
11
- end
12
- end
13
5
  require 'tempfile'
14
6
 
15
7
  require 'ethon/libc'
16
8
  require 'ethon/curl'
9
+ require 'ethon/curls/mime'
17
10
  require 'ethon/easy'
11
+ require 'ethon/easy/url'
12
+ require 'ethon/easy/mime'
18
13
  require 'ethon/errors'
14
+ require 'ethon/curls/introspection'
15
+ require 'ethon/easy/headers_api'
19
16
  require 'ethon/loggable'
20
17
  require 'ethon/multi'
21
18
  require 'ethon/version'
19
+ require 'ethon/curls/share'
20
+ require 'ethon/easy/share'
22
21
 
23
22
  # Ethon is a very simple libcurl.
24
23
  # It provides direct access to libcurl functionality
@@ -31,6 +30,6 @@ require 'ethon/version'
31
30
  #
32
31
  # @note Please update to the latest libcurl version in order
33
32
  # to benefit from all features and bugfixes.
34
- # http://curl.haxx.se/download.html
33
+ # https://curl.se/download.html
35
34
  module Ethon
36
35
  end