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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +705 -2
- data/README.md +183 -40
- data/lib/ethon/curl.rb +22 -0
- data/lib/ethon/curls/classes.rb +284 -12
- data/lib/ethon/curls/codes.rb +28 -9
- data/lib/ethon/curls/constants.rb +61 -4
- data/lib/ethon/curls/form_options.rb +2 -1
- data/lib/ethon/curls/functions.rb +60 -11
- data/lib/ethon/curls/infos.rb +104 -5
- data/lib/ethon/curls/introspection.rb +139 -0
- data/lib/ethon/curls/introspection_constants.rb +42 -0
- data/lib/ethon/curls/mime.rb +187 -0
- data/lib/ethon/curls/optional.rb +14 -0
- data/lib/ethon/curls/options.rb +298 -71
- data/lib/ethon/curls/settings.rb +12 -3
- data/lib/ethon/curls/share.rb +165 -0
- data/lib/ethon/curls/share_constants.rb +47 -0
- data/lib/ethon/curls/url.rb +171 -0
- data/lib/ethon/curls/url_constants.rb +78 -0
- data/lib/ethon/easy/callbacks.rb +177 -51
- data/lib/ethon/easy/features.rb +4 -1
- data/lib/ethon/easy/files.rb +113 -0
- data/lib/ethon/easy/form.rb +65 -6
- data/lib/ethon/easy/header.rb +57 -15
- data/lib/ethon/easy/headers_api.rb +79 -0
- data/lib/ethon/easy/http/actionable.rb +25 -3
- data/lib/ethon/easy/http/get.rb +5 -1
- data/lib/ethon/easy/http/post.rb +1 -0
- data/lib/ethon/easy/http/postable.rb +9 -2
- data/lib/ethon/easy/http/putable.rb +19 -4
- data/lib/ethon/easy/http.rb +16 -4
- data/lib/ethon/easy/informations.rb +51 -2
- data/lib/ethon/easy/mime.rb +29 -0
- data/lib/ethon/easy/mirror.rb +17 -4
- data/lib/ethon/easy/operations.rb +264 -11
- data/lib/ethon/easy/options.rb +327 -8
- data/lib/ethon/easy/queryable.rb +59 -14
- data/lib/ethon/easy/response_callbacks.rb +33 -12
- data/lib/ethon/easy/share.rb +28 -0
- data/lib/ethon/easy/url.rb +32 -0
- data/lib/ethon/easy/util.rb +19 -0
- data/lib/ethon/easy.rb +185 -10
- data/lib/ethon/errors/fork_safety.rb +10 -0
- data/lib/ethon/errors/form_add.rb +12 -0
- data/lib/ethon/errors/global_init.rb +1 -1
- data/lib/ethon/errors/header.rb +12 -0
- data/lib/ethon/errors/invalid_option.rb +3 -1
- data/lib/ethon/errors/invalid_value.rb +3 -1
- data/lib/ethon/errors/mime.rb +17 -0
- data/lib/ethon/errors/multi_add.rb +1 -1
- data/lib/ethon/errors/multi_assign.rb +12 -0
- data/lib/ethon/errors/multi_fdset.rb +1 -1
- data/lib/ethon/errors/multi_perform.rb +12 -0
- data/lib/ethon/errors/multi_poll.rb +12 -0
- data/lib/ethon/errors/multi_remove.rb +1 -1
- data/lib/ethon/errors/multi_timeout.rb +2 -2
- data/lib/ethon/errors/multi_wait.rb +12 -0
- data/lib/ethon/errors/multi_wakeup.rb +12 -0
- data/lib/ethon/errors/select.rb +1 -1
- data/lib/ethon/errors/set_option.rb +27 -0
- data/lib/ethon/errors/share.rb +17 -0
- data/lib/ethon/errors/unsupported_option.rb +23 -0
- data/lib/ethon/errors/url.rb +17 -0
- data/lib/ethon/errors.rb +13 -0
- data/lib/ethon/libc.rb +30 -1
- data/lib/ethon/loggable.rb +1 -1
- data/lib/ethon/multi/operations.rb +269 -25
- data/lib/ethon/multi/options.rb +114 -12
- data/lib/ethon/multi/stack.rb +181 -10
- data/lib/ethon/multi.rb +51 -15
- data/lib/ethon/version.rb +1 -1
- data/lib/ethon.rb +8 -9
- metadata +35 -6
data/lib/ethon/multi/options.rb
CHANGED
|
@@ -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.
|
|
12
|
+
# multi.max_total_connections = $value
|
|
13
13
|
#
|
|
14
|
-
# @param [
|
|
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 [
|
|
41
|
+
# @param [ Integer ] value The value to set.
|
|
27
42
|
#
|
|
28
43
|
# @return [ void ]
|
|
29
44
|
def maxconnects=(value)
|
|
30
|
-
|
|
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 [
|
|
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 [
|
|
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 [
|
|
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 [
|
|
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 [
|
|
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
|
|
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
|
data/lib/ethon/multi/stack.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
43
|
-
|
|
44
|
-
|
|
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 [
|
|
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
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
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 [
|
|
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.
|
|
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.
|
|
55
|
-
#
|
|
56
|
-
#
|
|
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 [
|
|
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
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
|
-
#
|
|
33
|
+
# https://curl.se/download.html
|
|
35
34
|
module Ethon
|
|
36
35
|
end
|