ethon 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +698 -2
- data/README.md +183 -40
- data/ethon.gemspec +9 -1
- 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 +178 -46
- 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 +259 -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 -2
- data/lib/ethon/multi/operations.rb +269 -25
- data/lib/ethon/multi/options.rb +86 -12
- data/lib/ethon/multi/stack.rb +175 -10
- data/lib/ethon/multi.rb +44 -12
- data/lib/ethon/version.rb +1 -1
- data/lib/ethon.rb +8 -9
- metadata +52 -4
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Ethon
|
|
3
|
+
module Curl
|
|
4
|
+
|
|
5
|
+
# CURLOPT_MIMEPOST is the modern replacement for the deprecated form API.
|
|
6
|
+
Ethon::Curls::Options.option(:easy, :mimepost, :ffipointer, 269)
|
|
7
|
+
|
|
8
|
+
# Owns a curl_mime structure.
|
|
9
|
+
class Mime < OwnedPointer
|
|
10
|
+
def self.native_release(pointer)
|
|
11
|
+
Curl.mime_free(pointer) if Curl.respond_to?(:mime_free)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @param [ Ethon::Easy, Curl::EasyHandle, FFI::Pointer ] easy The easy
|
|
15
|
+
# handle used to create the MIME structure.
|
|
16
|
+
def initialize(easy)
|
|
17
|
+
Curl.init
|
|
18
|
+
unless Curl.respond_to?(:mime_init)
|
|
19
|
+
raise NotImplementedError, "curl_mime_init is not supported by this libcurl"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
easy_handle = mime_easy_handle(easy)
|
|
23
|
+
pointer = Curl.mime_init(easy_handle)
|
|
24
|
+
raise NoMemoryError, "curl_mime_init failed" if pointer.null?
|
|
25
|
+
|
|
26
|
+
@easy = easy
|
|
27
|
+
super(pointer)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Add a part to the MIME structure.
|
|
31
|
+
#
|
|
32
|
+
# @return [ MimePart ] The new part.
|
|
33
|
+
def addpart
|
|
34
|
+
check_pid!
|
|
35
|
+
pointer = Curl.mime_addpart(self)
|
|
36
|
+
raise NoMemoryError, "curl_mime_addpart failed" if pointer.null?
|
|
37
|
+
|
|
38
|
+
part = MimePart.new(pointer, self)
|
|
39
|
+
(@parts ||= []) << part
|
|
40
|
+
part
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# libcurl dereferences the easy handle while creating a MIME structure,
|
|
46
|
+
# so a Multi or another owned pointer would corrupt memory.
|
|
47
|
+
def mime_easy_handle(easy)
|
|
48
|
+
case easy
|
|
49
|
+
when Ethon::Easy
|
|
50
|
+
easy.handle
|
|
51
|
+
when Ethon::Curl::EasyHandle
|
|
52
|
+
easy.check_pid!
|
|
53
|
+
easy
|
|
54
|
+
when Ethon::Curl::OwnedPointer
|
|
55
|
+
raise TypeError, "expected an Ethon::Easy or easy handle, got #{easy.class}"
|
|
56
|
+
when ::FFI::Pointer
|
|
57
|
+
raise ArgumentError, "cannot create a MIME structure for a null easy handle" if easy.null?
|
|
58
|
+
|
|
59
|
+
easy
|
|
60
|
+
else
|
|
61
|
+
raise TypeError, "expected an Ethon::Easy or easy handle, got #{easy.class}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# A part inside a MIME structure. The part is owned by its Mime.
|
|
67
|
+
class MimePart
|
|
68
|
+
attr_reader :mime, :handle
|
|
69
|
+
|
|
70
|
+
def initialize(pointer, mime)
|
|
71
|
+
@handle = pointer
|
|
72
|
+
@mime = mime
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def name=(value)
|
|
76
|
+
call(:mime_name, header_line(:name, value))
|
|
77
|
+
value
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def filename=(value)
|
|
81
|
+
call(:mime_filename, header_line(:filename, value))
|
|
82
|
+
value
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def type=(value)
|
|
86
|
+
call(:mime_type, header_line(:type, value))
|
|
87
|
+
value
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def encoder=(value)
|
|
91
|
+
call(:mime_encoder, header_line(:encoder, value))
|
|
92
|
+
value
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def file=(value)
|
|
96
|
+
call(:mime_filedata, value.nil? ? nil : native_string(:file, value))
|
|
97
|
+
value
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def data=(value)
|
|
101
|
+
if value.nil?
|
|
102
|
+
call(:mime_data, nil, 0)
|
|
103
|
+
else
|
|
104
|
+
data = value.to_s
|
|
105
|
+
call(:mime_data, data, data.bytesize)
|
|
106
|
+
end
|
|
107
|
+
value
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Set extra headers for this part.
|
|
111
|
+
#
|
|
112
|
+
# @param [ String, Array<String>, Hash, Curl::Slist, nil ] value The
|
|
113
|
+
# headers to add. Strings, arrays and hashes are converted into a
|
|
114
|
+
# curl_slist owned by libcurl.
|
|
115
|
+
#
|
|
116
|
+
# @return [ Object ] The value.
|
|
117
|
+
def headers=(value)
|
|
118
|
+
@mime.check_pid!
|
|
119
|
+
list = nil
|
|
120
|
+
retained = nil
|
|
121
|
+
owned = false
|
|
122
|
+
|
|
123
|
+
if value.nil?
|
|
124
|
+
# Clear the headers.
|
|
125
|
+
elsif value.is_a?(Curl::Slist)
|
|
126
|
+
value.check_pid!
|
|
127
|
+
list = value
|
|
128
|
+
retained = value
|
|
129
|
+
else
|
|
130
|
+
lines = value.is_a?(Hash) ? value.map { |key, item| "#{key}: #{item}" } : Array(value)
|
|
131
|
+
lines = header_lines(lines)
|
|
132
|
+
list = Curl::Slist.build(lines)
|
|
133
|
+
owned = !list.nil?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
code = Curl.mime_headers(@handle, list, owned ? 1 : 0)
|
|
137
|
+
unless code == :ok
|
|
138
|
+
Curl.slist_free_all(list) if owned
|
|
139
|
+
raise Errors::Mime.new(code)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# The previous list must stay reachable until libcurl replaces it.
|
|
143
|
+
@headers_list = retained
|
|
144
|
+
value
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
# Convert a value to the C string libcurl receives, rejecting embedded
|
|
150
|
+
# NUL bytes with Ethon's error type instead of FFI's ArgumentError.
|
|
151
|
+
def native_string(option, value)
|
|
152
|
+
string = value.to_s
|
|
153
|
+
if string.include?("\0")
|
|
154
|
+
raise Errors::InvalidValue.new(option, value)
|
|
155
|
+
end
|
|
156
|
+
string
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# The content type and encoder are written into the part headers
|
|
160
|
+
# verbatim, so CR or LF would add headers to the part or the request.
|
|
161
|
+
# A nil value clears the field instead of setting an empty string.
|
|
162
|
+
def header_line(option, value)
|
|
163
|
+
return if value.nil?
|
|
164
|
+
|
|
165
|
+
Ethon::Easy::Util.validate_header_line(option, native_string(option, value))
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Normalize header lines and reject values libcurl would send verbatim
|
|
169
|
+
# as additional part headers.
|
|
170
|
+
def header_lines(lines)
|
|
171
|
+
# An empty line describes no header, so drop it. That also makes a
|
|
172
|
+
# single empty string clear the part headers.
|
|
173
|
+
lines.map(&:to_s).reject(&:empty?).map do |line|
|
|
174
|
+
Ethon::Easy::Util.validate_header_line(:headers, line)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def call(function, *args)
|
|
179
|
+
@mime.check_pid!
|
|
180
|
+
code = Curl.public_send(function, @handle, *args)
|
|
181
|
+
raise Errors::Mime.new(code) unless code == :ok
|
|
182
|
+
|
|
183
|
+
code
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Ethon
|
|
3
|
+
module Curls
|
|
4
|
+
# Attach libcurl functions that may not exist in the loaded library.
|
|
5
|
+
module Optional
|
|
6
|
+
# Attach a function, ignoring builds that do not export it.
|
|
7
|
+
def self.attach(base, name, func, args, returns, options = {})
|
|
8
|
+
base.attach_function(name, func, args, returns, options)
|
|
9
|
+
rescue ::FFI::NotFoundError
|
|
10
|
+
nil
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|