patron 0.8.0 → 0.9.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/.travis.yml +2 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +10 -9
- data/Rakefile +5 -12
- data/ext/patron/session_ext.c +163 -51
- data/lib/patron/error.rb +6 -0
- data/lib/patron/request.rb +2 -2
- data/lib/patron/response.rb +2 -2
- data/lib/patron/session.rb +12 -1
- data/lib/patron/version.rb +1 -1
- data/patron.gemspec +35 -24
- data/spec/patron_spec.rb +8 -1
- data/spec/response_spec.rb +1 -1
- data/spec/session_spec.rb +28 -0
- data/spec/session_ssl_spec.rb +19 -2
- data/spec/support/test_server.rb +9 -0
- metadata +33 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6d71dc0eaded4e48a770c409f82a571a4f77151
|
4
|
+
data.tar.gz: cdf33d99ce93e86f79d7889860be83f2cb87ba94
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7d7a4a3832c5b9774cc8171b51e69c15fba9d9d05a050b0ecda76c437ee3db9bb87aad218dfd3f34b54bb34e99612f410e56a0ab21714f5010b3fdb381bbd01
|
7
|
+
data.tar.gz: 72dddcb493902c90deb114655c61a247a7927f2be526c212a8294be5c3a0bb144fd92fb666450a84cd83c249a91f2b105d9508caf87210b432acec5b85ae496f
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
### 0.9.1
|
2
|
+
|
3
|
+
* Added ssl_version options `TLSv1_1`, `TLSv1_2`, `TLSv1_3` for explicitly forcing the SSL version
|
4
|
+
* requires the appropriate versions of libCURL and OpenSSL installed to support these new options
|
5
|
+
* reference: https://curl.haxx.se/libcurl/c/CURLOPT_SSLVERSION.html
|
6
|
+
* Added a new `:http_version` option with `HTTPv1_1` and `HTTPv2_0` values to explicitly set the HTTP version of HTTP/1.1 or HTTP/2.0
|
7
|
+
* requires the appropriate versions of libCURL and OpenSSL installed to support these new options
|
8
|
+
* reference: https://curl.haxx.se/libcurl/c/CURLOPT_HTTP_VERSION.html
|
9
|
+
* Updates the gem release procedure for more convenience, using the updated Rubygems.org tasks
|
10
|
+
* Update a few minor dependencies and documentation to be Ruby 2.4.1-compatible, add 2.4.1. to Travis CI matrix
|
11
|
+
* Add `Session#download_byte_limit` for limiting the permitted download size.
|
12
|
+
This can be very useful in dealing with untrusted download sources, which might attempt
|
13
|
+
to send very large responses that would overwhelm the receiving client.
|
14
|
+
* Add `Patron.libcurl_version_exact` which returns a triplet of major, minor and patch libCURL version numbers. This can be used
|
15
|
+
for more fine-grained matching when using some more esoteric Curl features which might not necessarily be available on libCURL
|
16
|
+
Patron has been linked against.
|
17
|
+
|
1
18
|
### 0.8.0
|
2
19
|
|
3
20
|
* Add `Response#inspectable_body`, `Response#decoded_body`. `decoded_body` will atempt to decode
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
patron (0.
|
4
|
+
patron (0.9.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
diff-lcs (1.2.5)
|
10
10
|
docile (1.1.5)
|
11
|
-
json (1.
|
11
|
+
json (2.1.0)
|
12
12
|
rake (10.4.2)
|
13
13
|
rake-compiler (0.9.5)
|
14
14
|
rake
|
@@ -25,23 +25,24 @@ GEM
|
|
25
25
|
diff-lcs (>= 1.2.0, < 2.0)
|
26
26
|
rspec-support (~> 3.3.0)
|
27
27
|
rspec-support (3.3.0)
|
28
|
-
simplecov (0.
|
28
|
+
simplecov (0.14.1)
|
29
29
|
docile (~> 1.1.0)
|
30
|
-
json (
|
30
|
+
json (>= 1.8, < 3)
|
31
31
|
simplecov-html (~> 0.10.0)
|
32
|
-
simplecov-html (0.10.
|
32
|
+
simplecov-html (0.10.1)
|
33
33
|
yard (0.8.7.6)
|
34
34
|
|
35
35
|
PLATFORMS
|
36
36
|
ruby
|
37
37
|
|
38
38
|
DEPENDENCIES
|
39
|
-
bundler (>= 1
|
39
|
+
bundler (>= 1)
|
40
40
|
patron!
|
41
|
-
rake
|
41
|
+
rake (~> 10)
|
42
|
+
rake-compiler
|
42
43
|
rspec (>= 2.3.0)
|
43
|
-
simplecov (
|
44
|
+
simplecov (~> 0.10)
|
44
45
|
yard (~> 0.8)
|
45
46
|
|
46
47
|
BUNDLED WITH
|
47
|
-
1.
|
48
|
+
1.14.6
|
data/Rakefile
CHANGED
@@ -24,17 +24,17 @@
|
|
24
24
|
require 'rake/clean'
|
25
25
|
require 'rake/extensiontask'
|
26
26
|
require 'rspec/core/rake_task'
|
27
|
-
require
|
27
|
+
require "bundler/gem_tasks"
|
28
28
|
require 'yard'
|
29
29
|
|
30
|
+
RSpec::Core::RakeTask.new(:spec)
|
31
|
+
|
30
32
|
Rake::ExtensionTask.new do |ext|
|
31
33
|
ext.name = 'session_ext' # indicate the name of the extension.
|
32
34
|
ext.ext_dir = 'ext/patron' # search for 'hello_world' inside it.
|
33
35
|
ext.lib_dir = 'lib/patron' # put binaries into this folder.
|
34
36
|
end
|
35
37
|
|
36
|
-
Bundler::GemHelper.install_tasks
|
37
|
-
|
38
38
|
CLEAN.include FileList["ext/patron/*"].exclude(/^.*\.(rb|c|h)$/)
|
39
39
|
CLOBBER.include %w( doc coverage pkg )
|
40
40
|
|
@@ -50,12 +50,5 @@ YARD::Rake::YardocTask.new do |t|
|
|
50
50
|
t.stats_options = ['--list-undoc']
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
t.rspec_opts = %w( --colour --format progress )
|
56
|
-
t.pattern = 'spec/**/*_spec.rb'
|
57
|
-
end
|
58
|
-
|
59
|
-
task :spec => [:compile]
|
60
|
-
|
61
|
-
task :default => :spec
|
53
|
+
task :default => [:clobber, :compile, :spec]
|
54
|
+
task :build => [:clobber] # Make sure no binaries end up in the gem
|
data/ext/patron/session_ext.c
CHANGED
@@ -32,6 +32,8 @@
|
|
32
32
|
#include "sglib.h" /* Simple Generic Library -> http://sglib.sourceforge.net */
|
33
33
|
|
34
34
|
#define UNUSED_ARGUMENT(x) (void)x
|
35
|
+
#define INTERRUPT_ABORT 1
|
36
|
+
#define INTERRUPT_DOWNLOAD_OVERFLOW 2
|
35
37
|
|
36
38
|
static VALUE mPatron = Qnil;
|
37
39
|
static VALUE mProxyType = Qnil;
|
@@ -40,15 +42,16 @@ static VALUE cRequest = Qnil;
|
|
40
42
|
static VALUE ePatronError = Qnil;
|
41
43
|
static VALUE eUnsupportedProtocol = Qnil;
|
42
44
|
static VALUE eUnsupportedSSLVersion = Qnil;
|
45
|
+
static VALUE eUnsupportedHTTPVersion = Qnil;
|
43
46
|
static VALUE eURLFormatError = Qnil;
|
44
47
|
static VALUE eHostResolutionError = Qnil;
|
45
48
|
static VALUE eConnectionFailed = Qnil;
|
46
49
|
static VALUE ePartialFileError = Qnil;
|
47
50
|
static VALUE eTimeoutError = Qnil;
|
48
51
|
static VALUE eTooManyRedirects = Qnil;
|
52
|
+
static VALUE eAborted = Qnil;
|
49
53
|
|
50
|
-
|
51
|
-
struct curl_state {
|
54
|
+
struct patron_curl_state {
|
52
55
|
CURL* handle;
|
53
56
|
char* upload_buf;
|
54
57
|
FILE* download_file;
|
@@ -60,6 +63,7 @@ struct curl_state {
|
|
60
63
|
struct curl_httppost* last;
|
61
64
|
membuffer header_buffer;
|
62
65
|
membuffer body_buffer;
|
66
|
+
size_t download_byte_limit;
|
63
67
|
int interrupt;
|
64
68
|
};
|
65
69
|
|
@@ -78,17 +82,39 @@ static size_t session_write_handler(char* stream, size_t size, size_t nmemb, mem
|
|
78
82
|
return size * nmemb;
|
79
83
|
}
|
80
84
|
|
85
|
+
/* Used as WRITEFUNCTION for file downloads (required on Windows) */
|
86
|
+
static size_t file_write_handler(void* stream, size_t size, size_t nmemb, FILE* fp) {
|
87
|
+
fwrite(stream, size, nmemb, fp);
|
88
|
+
if (ferror(fp)) {
|
89
|
+
return 0;
|
90
|
+
} else {
|
91
|
+
/* Just always OK the write */
|
92
|
+
return size * nmemb;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
81
96
|
/* A non-zero return value from the progress handler will terminate the current
|
82
97
|
* request. We use this fact in order to interrupt any request when either the
|
83
98
|
* user calls the "interrupt" method on the session or when the Ruby interpreter
|
84
99
|
* is attempting to exit.
|
85
100
|
*/
|
86
|
-
static int session_progress_handler(void *clientp,
|
87
|
-
struct
|
88
|
-
UNUSED_ARGUMENT(dltotal);
|
101
|
+
static int session_progress_handler(void *clientp, size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) {
|
102
|
+
struct patron_curl_state* state = (struct patron_curl_state*) clientp;
|
89
103
|
UNUSED_ARGUMENT(dlnow);
|
90
104
|
UNUSED_ARGUMENT(ultotal);
|
91
105
|
UNUSED_ARGUMENT(ulnow);
|
106
|
+
|
107
|
+
// Set the interrupt if the download byte limit has been reached
|
108
|
+
if(state->download_byte_limit != 0 && (dltotal > state->download_byte_limit)) {
|
109
|
+
state->interrupt = INTERRUPT_DOWNLOAD_OVERFLOW;
|
110
|
+
}
|
111
|
+
|
112
|
+
// If the interrupt value is anything except 0, the perform() call
|
113
|
+
// will be aborted by libCURL.
|
114
|
+
// Note however that some older versions of libcurl have a bug which
|
115
|
+
// makes the return value of this callback be ignored, and the request
|
116
|
+
// will actually proceed undeterred.
|
117
|
+
// See https://sourceforge.net/p/curl/bugs/1318/
|
92
118
|
return state->interrupt;
|
93
119
|
}
|
94
120
|
|
@@ -96,32 +122,32 @@ static int session_progress_handler(void *clientp, double dltotal, double dlnow,
|
|
96
122
|
/*----------------------------------------------------------------------------*/
|
97
123
|
/* List of active curl sessions */
|
98
124
|
|
99
|
-
struct
|
100
|
-
struct
|
101
|
-
struct
|
125
|
+
struct patron_curl_state_list {
|
126
|
+
struct patron_curl_state *state;
|
127
|
+
struct patron_curl_state_list *next;
|
102
128
|
};
|
103
129
|
|
104
130
|
#define CS_LIST_COMPARATOR(p, _state_) (p->state - _state_)
|
105
131
|
|
106
|
-
static struct
|
132
|
+
static struct patron_curl_state_list *cs_list = NULL;
|
107
133
|
|
108
|
-
static void cs_list_append( struct
|
109
|
-
struct
|
134
|
+
static void cs_list_append( struct patron_curl_state *state ) {
|
135
|
+
struct patron_curl_state_list *item = NULL;
|
110
136
|
|
111
137
|
assert(state != NULL);
|
112
|
-
item = ruby_xmalloc(sizeof(struct
|
138
|
+
item = ruby_xmalloc(sizeof(struct patron_curl_state_list));
|
113
139
|
item->state = state;
|
114
140
|
item->next = NULL;
|
115
141
|
|
116
|
-
SGLIB_LIST_ADD(struct
|
142
|
+
SGLIB_LIST_ADD(struct patron_curl_state_list, cs_list, item, next);
|
117
143
|
}
|
118
144
|
|
119
|
-
static void cs_list_remove(struct
|
120
|
-
struct
|
145
|
+
static void cs_list_remove(struct patron_curl_state *state) {
|
146
|
+
struct patron_curl_state_list *item = NULL;
|
121
147
|
|
122
148
|
assert(state != NULL);
|
123
149
|
|
124
|
-
SGLIB_LIST_DELETE_IF_MEMBER(struct
|
150
|
+
SGLIB_LIST_DELETE_IF_MEMBER(struct patron_curl_state_list, cs_list, state, CS_LIST_COMPARATOR, next, item);
|
125
151
|
if (item) {
|
126
152
|
ruby_xfree(item);
|
127
153
|
}
|
@@ -130,8 +156,8 @@ static void cs_list_remove(struct curl_state *state) {
|
|
130
156
|
static void cs_list_interrupt(VALUE data) {
|
131
157
|
UNUSED_ARGUMENT(data);
|
132
158
|
|
133
|
-
SGLIB_LIST_MAP_ON_ELEMENTS(struct
|
134
|
-
item->state->interrupt =
|
159
|
+
SGLIB_LIST_MAP_ON_ELEMENTS(struct patron_curl_state_list, cs_list, item, next, {
|
160
|
+
item->state->interrupt = INTERRUPT_ABORT;
|
135
161
|
});
|
136
162
|
}
|
137
163
|
|
@@ -139,7 +165,7 @@ static void cs_list_interrupt(VALUE data) {
|
|
139
165
|
/*----------------------------------------------------------------------------*/
|
140
166
|
/* Object allocation */
|
141
167
|
|
142
|
-
static void session_close_debug_file(struct
|
168
|
+
static void session_close_debug_file(struct patron_curl_state *curl) {
|
143
169
|
if (curl->debug_file && stderr != curl->debug_file) {
|
144
170
|
fclose(curl->debug_file);
|
145
171
|
}
|
@@ -147,7 +173,7 @@ static void session_close_debug_file(struct curl_state *curl) {
|
|
147
173
|
}
|
148
174
|
|
149
175
|
/* Cleans up the Curl handle when the Session object is garbage collected. */
|
150
|
-
void session_free(struct
|
176
|
+
void session_free(struct patron_curl_state *curl) {
|
151
177
|
if (curl->handle) {
|
152
178
|
curl_easy_cleanup(curl->handle);
|
153
179
|
curl->handle = NULL;
|
@@ -163,10 +189,10 @@ void session_free(struct curl_state *curl) {
|
|
163
189
|
free(curl);
|
164
190
|
}
|
165
191
|
|
166
|
-
/* Allocates
|
192
|
+
/* Allocates patron_curl_state data needed for a new Session object. */
|
167
193
|
VALUE session_alloc(VALUE klass) {
|
168
|
-
struct
|
169
|
-
VALUE obj = Data_Make_Struct(klass, struct
|
194
|
+
struct patron_curl_state* curl;
|
195
|
+
VALUE obj = Data_Make_Struct(klass, struct patron_curl_state, NULL, session_free, curl);
|
170
196
|
|
171
197
|
membuffer_init( &curl->header_buffer );
|
172
198
|
membuffer_init( &curl->body_buffer );
|
@@ -175,16 +201,22 @@ VALUE session_alloc(VALUE klass) {
|
|
175
201
|
return obj;
|
176
202
|
}
|
177
203
|
|
178
|
-
/* Return the
|
179
|
-
static struct
|
180
|
-
struct
|
181
|
-
Data_Get_Struct(self, struct
|
204
|
+
/* Return the patron_curl_state from the ruby VALUE which is the Session instance. */
|
205
|
+
static struct patron_curl_state* get_patron_curl_state(VALUE self) {
|
206
|
+
struct patron_curl_state* state;
|
207
|
+
Data_Get_Struct(self, struct patron_curl_state, state);
|
182
208
|
|
183
209
|
if (NULL == state->handle) {
|
184
210
|
state->handle = curl_easy_init();
|
185
211
|
curl_easy_setopt(state->handle, CURLOPT_NOSIGNAL, 1);
|
186
212
|
curl_easy_setopt(state->handle, CURLOPT_NOPROGRESS, 0);
|
187
|
-
|
213
|
+
#if LIBCURL_VERSION_NUM >= 0x072000
|
214
|
+
/* this is libCURLv7.32.0 or later, supports CURLOPT_XFERINFOFUNCTION */
|
215
|
+
curl_easy_setopt(state->handle, CURLOPT_XFERINFOFUNCTION, &session_progress_handler);
|
216
|
+
#else
|
217
|
+
curl_easy_setopt(state->handle, CURLOPT_PROGRESSFUNCTION, &session_progress_handler);
|
218
|
+
#endif
|
219
|
+
|
188
220
|
curl_easy_setopt(state->handle, CURLOPT_PROGRESSDATA, state);
|
189
221
|
}
|
190
222
|
|
@@ -206,6 +238,20 @@ static VALUE libcurl_version(VALUE klass) {
|
|
206
238
|
return rb_str_new2(value);
|
207
239
|
}
|
208
240
|
|
241
|
+
/*
|
242
|
+
* Returns the version of the embedded libcurl.
|
243
|
+
*
|
244
|
+
* @return [Array] an array of MAJOR, MINOR, PATCH integers
|
245
|
+
*/
|
246
|
+
static VALUE libcurl_version_exact(VALUE klass) {
|
247
|
+
UNUSED_ARGUMENT(klass);
|
248
|
+
VALUE cv_major = INT2FIX(LIBCURL_VERSION_MAJOR);
|
249
|
+
VALUE cv_minor = INT2FIX(LIBCURL_VERSION_MINOR);
|
250
|
+
VALUE cv_patch = INT2FIX(LIBCURL_VERSION_PATCH);
|
251
|
+
VALUE version_arr = rb_ary_new3(3, cv_major, cv_minor, cv_patch);
|
252
|
+
return version_arr;
|
253
|
+
}
|
254
|
+
|
209
255
|
/*
|
210
256
|
* Escapes the provided string using libCURL URL escaping functions.
|
211
257
|
*
|
@@ -218,7 +264,7 @@ static VALUE session_escape(VALUE self, VALUE value) {
|
|
218
264
|
char* escaped = NULL;
|
219
265
|
VALUE retval = Qnil;
|
220
266
|
|
221
|
-
struct
|
267
|
+
struct patron_curl_state* state = curl_easy_init();
|
222
268
|
escaped = curl_easy_escape(state->handle,
|
223
269
|
RSTRING_PTR(string),
|
224
270
|
(int) RSTRING_LEN(string));
|
@@ -241,7 +287,7 @@ static VALUE session_unescape(VALUE self, VALUE value) {
|
|
241
287
|
char* unescaped = NULL;
|
242
288
|
VALUE retval = Qnil;
|
243
289
|
|
244
|
-
struct
|
290
|
+
struct patron_curl_state* state = curl_easy_init();
|
245
291
|
unescaped = curl_easy_unescape(state->handle,
|
246
292
|
RSTRING_PTR(string),
|
247
293
|
(int) RSTRING_LEN(string),
|
@@ -256,7 +302,7 @@ static VALUE session_unescape(VALUE self, VALUE value) {
|
|
256
302
|
|
257
303
|
/* Callback used to iterate over the HTTP headers and store them in an slist. */
|
258
304
|
static int each_http_header(VALUE header_key, VALUE header_value, VALUE self) {
|
259
|
-
struct
|
305
|
+
struct patron_curl_state *state = get_patron_curl_state(self);
|
260
306
|
CURL* curl = state->handle;
|
261
307
|
|
262
308
|
VALUE name = rb_obj_as_string(header_key);
|
@@ -286,7 +332,7 @@ static int each_http_header(VALUE header_key, VALUE header_value, VALUE self) {
|
|
286
332
|
}
|
287
333
|
|
288
334
|
static int formadd_values(VALUE data_key, VALUE data_value, VALUE self) {
|
289
|
-
struct
|
335
|
+
struct patron_curl_state *state = get_patron_curl_state(self);
|
290
336
|
VALUE name = rb_obj_as_string(data_key);
|
291
337
|
VALUE value = rb_obj_as_string(data_value);
|
292
338
|
|
@@ -297,7 +343,7 @@ static int formadd_values(VALUE data_key, VALUE data_value, VALUE self) {
|
|
297
343
|
}
|
298
344
|
|
299
345
|
static int formadd_files(VALUE data_key, VALUE data_value, VALUE self) {
|
300
|
-
struct
|
346
|
+
struct patron_curl_state *state = get_patron_curl_state(self);
|
301
347
|
VALUE name = rb_obj_as_string(data_key);
|
302
348
|
VALUE value = rb_obj_as_string(data_value);
|
303
349
|
|
@@ -317,7 +363,7 @@ static void set_curl_request_body(CURL* curl, char* buf, curl_off_t len) {
|
|
317
363
|
#endif
|
318
364
|
}
|
319
365
|
|
320
|
-
static void set_chunked_encoding(struct
|
366
|
+
static void set_chunked_encoding(struct patron_curl_state *state) {
|
321
367
|
state->headers = curl_slist_append(state->headers, "Transfer-Encoding: chunked");
|
322
368
|
}
|
323
369
|
|
@@ -330,7 +376,7 @@ static FILE* open_file(VALUE filename, const char* perms) {
|
|
330
376
|
return handle;
|
331
377
|
}
|
332
378
|
|
333
|
-
static void set_request_body_file(struct
|
379
|
+
static void set_request_body_file(struct patron_curl_state* state, VALUE r_path_str) {
|
334
380
|
CURL* curl = state->handle;
|
335
381
|
|
336
382
|
state->request_body_file = open_file(r_path_str, "rb");
|
@@ -345,7 +391,7 @@ static void set_request_body_file(struct curl_state* state, VALUE r_path_str) {
|
|
345
391
|
#endif
|
346
392
|
}
|
347
393
|
|
348
|
-
static void set_request_body(struct
|
394
|
+
static void set_request_body(struct patron_curl_state* state, VALUE stringable_or_file) {
|
349
395
|
CURL* curl = state->handle;
|
350
396
|
if(rb_respond_to(stringable_or_file, rb_intern("to_path"))) {
|
351
397
|
// Set up a file read callback (read the entire request body from a file).
|
@@ -368,7 +414,7 @@ static void set_request_body(struct curl_state* state, VALUE stringable_or_file)
|
|
368
414
|
* handle.
|
369
415
|
*/
|
370
416
|
static void set_options_from_request(VALUE self, VALUE request) {
|
371
|
-
struct
|
417
|
+
struct patron_curl_state* state = get_patron_curl_state(self);
|
372
418
|
CURL* curl = state->handle;
|
373
419
|
|
374
420
|
ID action = Qnil;
|
@@ -383,16 +429,23 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
383
429
|
VALUE insecure = Qnil;
|
384
430
|
VALUE cacert = Qnil;
|
385
431
|
VALUE ssl_version = Qnil;
|
432
|
+
VALUE http_version = Qnil;
|
386
433
|
VALUE buffer_size = Qnil;
|
387
434
|
VALUE action_name = rb_funcall(request, rb_intern("action"), 0);
|
388
435
|
VALUE a_c_encoding = rb_funcall(request, rb_intern("automatic_content_encoding"), 0);
|
436
|
+
VALUE download_byte_limit = rb_funcall(request, rb_intern("download_byte_limit"), 0);
|
437
|
+
|
438
|
+
if (RTEST(download_byte_limit)) {
|
439
|
+
state->download_byte_limit = FIX2INT(download_byte_limit);
|
440
|
+
} else {
|
441
|
+
state->download_byte_limit = 0;
|
442
|
+
}
|
389
443
|
|
390
444
|
headers = rb_funcall(request, rb_intern("headers"), 0);
|
391
445
|
if (RTEST(headers)) {
|
392
446
|
if (rb_type(headers) != T_HASH) {
|
393
447
|
rb_raise(rb_eArgError, "Headers must be passed in a hash.");
|
394
448
|
}
|
395
|
-
|
396
449
|
rb_hash_foreach(headers, each_http_header, self);
|
397
450
|
}
|
398
451
|
|
@@ -410,8 +463,12 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
410
463
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
|
411
464
|
}
|
412
465
|
if (RTEST(download_file)) {
|
466
|
+
// we need the WRITEDATA option for the file destination
|
413
467
|
state->download_file = open_file(download_file, "wb");
|
414
468
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, state->download_file);
|
469
|
+
// curl docs say that CURLOPT_WRITEFUNCTION must be set too
|
470
|
+
// to avoid issues on Windows
|
471
|
+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &file_write_handler);
|
415
472
|
} else {
|
416
473
|
state->download_file = NULL;
|
417
474
|
}
|
@@ -553,17 +610,69 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
553
610
|
if(RTEST(ssl_version)) {
|
554
611
|
VALUE ssl_version_str = rb_funcall(ssl_version, rb_intern("to_s"), 0);
|
555
612
|
char* version = StringValuePtr(ssl_version_str);
|
613
|
+
|
556
614
|
if(strcmp(version, "SSLv2") == 0) {
|
557
615
|
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv2);
|
558
616
|
} else if(strcmp(version, "SSLv3") == 0) {
|
559
617
|
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3);
|
560
618
|
} else if(strcmp(version, "TLSv1") == 0) {
|
561
|
-
|
562
|
-
}
|
619
|
+
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
|
620
|
+
}
|
621
|
+
#if LIBCURL_VERSION_NUM >= 0x072200
|
622
|
+
/* this is libCURLv7.34.0 or later */
|
623
|
+
else if(strcmp(version, "TLSv1_0") == 0) {
|
624
|
+
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0); //libcurl v7.34.0+
|
625
|
+
} else if(strcmp(version, "TLSv1_1") == 0) {
|
626
|
+
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1); //libcurl v7.34.0+
|
627
|
+
} else if(strcmp(version, "TLSv1_2") == 0) {
|
628
|
+
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); //libcurl v7.34.0+
|
629
|
+
}
|
630
|
+
#endif
|
631
|
+
#if LIBCURL_VERSION_NUM >= 0x073400
|
632
|
+
/* this is libCURLv7.52.0 or later */
|
633
|
+
else if(strcmp(version, "TLSv1_3") == 0) {
|
634
|
+
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_3); //libcurl v7.52.0+
|
635
|
+
}
|
636
|
+
#endif
|
637
|
+
else {
|
563
638
|
rb_raise(eUnsupportedSSLVersion, "Unsupported SSL version: %s", version);
|
564
639
|
}
|
565
640
|
}
|
566
641
|
|
642
|
+
http_version = rb_funcall(request, rb_intern("http_version"), 0);
|
643
|
+
if(RTEST(http_version)) {
|
644
|
+
VALUE http_version_str = rb_funcall(http_version, rb_intern("to_s"), 0);
|
645
|
+
char* version = StringValuePtr(http_version_str);
|
646
|
+
if(strcmp(version, "None") == 0) {
|
647
|
+
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
|
648
|
+
} else if(strcmp(version, "HTTPv1_0") == 0) {
|
649
|
+
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
|
650
|
+
} else if(strcmp(version, "HTTPv1_1") == 0) {
|
651
|
+
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
652
|
+
}
|
653
|
+
#if LIBCURL_VERSION_NUM >= 0x072100
|
654
|
+
/* this is libCURLv7.33.0 or later */
|
655
|
+
else if(strcmp(version, "HTTPv2_0") == 0) {
|
656
|
+
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); //libcurl v7.33.0+
|
657
|
+
}
|
658
|
+
#endif
|
659
|
+
#if LIBCURL_VERSION_NUM >= 0x072F00
|
660
|
+
/* this is libCURLv7.47.0 or later */
|
661
|
+
else if(strcmp(version, "HTTPv2_TLS") == 0) {
|
662
|
+
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); //libcurl v7.47.0+
|
663
|
+
}
|
664
|
+
#endif
|
665
|
+
#if LIBCURL_VERSION_NUM >= 0x073100
|
666
|
+
/* this is libCURLv7.49.0 or later */
|
667
|
+
else if(strcmp(version, "HTTPv2_PRIOR") == 0) {
|
668
|
+
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); //libcurl v7.49.0+
|
669
|
+
}
|
670
|
+
#endif
|
671
|
+
else {
|
672
|
+
rb_raise(eUnsupportedHTTPVersion, "Unsupported HTTP version: %s", version);
|
673
|
+
}
|
674
|
+
}
|
675
|
+
|
567
676
|
cacert = rb_funcall(request, rb_intern("cacert"), 0);
|
568
677
|
if(RTEST(cacert)) {
|
569
678
|
curl_easy_setopt(curl, CURLOPT_CAINFO, StringValuePtr(cacert));
|
@@ -616,7 +725,7 @@ static VALUE select_error(CURLcode code) {
|
|
616
725
|
case CURLE_PARTIAL_FILE: error = ePartialFileError; break;
|
617
726
|
case CURLE_OPERATION_TIMEDOUT: error = eTimeoutError; break;
|
618
727
|
case CURLE_TOO_MANY_REDIRECTS: error = eTooManyRedirects; break;
|
619
|
-
|
728
|
+
case CURLE_ABORTED_BY_CALLBACK: error = eAborted; break;
|
620
729
|
default: error = ePatronError;
|
621
730
|
}
|
622
731
|
|
@@ -625,13 +734,13 @@ static VALUE select_error(CURLcode code) {
|
|
625
734
|
|
626
735
|
/* Perform the actual HTTP request by calling libcurl. */
|
627
736
|
static VALUE perform_request(VALUE self) {
|
628
|
-
struct
|
737
|
+
struct patron_curl_state *state = get_patron_curl_state(self);
|
629
738
|
CURL* curl = state->handle;
|
630
739
|
membuffer* header_buffer = NULL;
|
631
740
|
membuffer* body_buffer = NULL;
|
632
741
|
CURLcode ret = 0;
|
633
742
|
|
634
|
-
state->interrupt = 0;
|
743
|
+
state->interrupt = 0; /* clear the interrupt flag */
|
635
744
|
|
636
745
|
header_buffer = &state->header_buffer;
|
637
746
|
body_buffer = &state->body_buffer;
|
@@ -682,7 +791,7 @@ static VALUE perform_request(VALUE self) {
|
|
682
791
|
* all request related objects such as the header slist.
|
683
792
|
*/
|
684
793
|
static VALUE cleanup(VALUE self) {
|
685
|
-
struct
|
794
|
+
struct patron_curl_state *state = get_patron_curl_state(self);
|
686
795
|
curl_easy_reset(state->handle);
|
687
796
|
|
688
797
|
if (state->headers) {
|
@@ -741,8 +850,8 @@ static VALUE session_handle_request(VALUE self, VALUE request) {
|
|
741
850
|
* @return self
|
742
851
|
*/
|
743
852
|
static VALUE session_reset(VALUE self) {
|
744
|
-
struct
|
745
|
-
Data_Get_Struct(self, struct
|
853
|
+
struct patron_curl_state *state;
|
854
|
+
Data_Get_Struct(self, struct patron_curl_state, state);
|
746
855
|
|
747
856
|
if (NULL != state->handle) {
|
748
857
|
cleanup(self);
|
@@ -760,8 +869,8 @@ static VALUE session_reset(VALUE self) {
|
|
760
869
|
* @return [void] This method always raises
|
761
870
|
*/
|
762
871
|
static VALUE session_interrupt(VALUE self) {
|
763
|
-
struct
|
764
|
-
state->interrupt =
|
872
|
+
struct patron_curl_state *state = get_patron_curl_state(self);
|
873
|
+
state->interrupt = INTERRUPT_ABORT;
|
765
874
|
return self;
|
766
875
|
}
|
767
876
|
|
@@ -775,7 +884,7 @@ static VALUE session_interrupt(VALUE self) {
|
|
775
884
|
* @return self
|
776
885
|
*/
|
777
886
|
static VALUE add_cookie_file(VALUE self, VALUE file) {
|
778
|
-
struct
|
887
|
+
struct patron_curl_state *state = get_patron_curl_state(self);
|
779
888
|
CURL* curl = state->handle;
|
780
889
|
char* file_path = NULL;
|
781
890
|
|
@@ -796,7 +905,7 @@ static VALUE add_cookie_file(VALUE self, VALUE file) {
|
|
796
905
|
* @return self
|
797
906
|
*/
|
798
907
|
static VALUE set_debug_file(VALUE self, VALUE file) {
|
799
|
-
struct
|
908
|
+
struct patron_curl_state *state = get_patron_curl_state(self);
|
800
909
|
char* file_path = RSTRING_PTR(file);
|
801
910
|
|
802
911
|
session_close_debug_file(state);
|
@@ -826,14 +935,17 @@ void Init_session_ext() {
|
|
826
935
|
|
827
936
|
eUnsupportedProtocol = rb_const_get(mPatron, rb_intern("UnsupportedProtocol"));
|
828
937
|
eUnsupportedSSLVersion = rb_const_get(mPatron, rb_intern("UnsupportedSSLVersion"));
|
938
|
+
eUnsupportedHTTPVersion = rb_const_get(mPatron, rb_intern("UnsupportedHTTPVersion"));
|
829
939
|
eURLFormatError = rb_const_get(mPatron, rb_intern("URLFormatError"));
|
830
940
|
eHostResolutionError = rb_const_get(mPatron, rb_intern("HostResolutionError"));
|
831
941
|
eConnectionFailed = rb_const_get(mPatron, rb_intern("ConnectionFailed"));
|
832
942
|
ePartialFileError = rb_const_get(mPatron, rb_intern("PartialFileError"));
|
833
943
|
eTimeoutError = rb_const_get(mPatron, rb_intern("TimeoutError"));
|
834
944
|
eTooManyRedirects = rb_const_get(mPatron, rb_intern("TooManyRedirects"));
|
945
|
+
eAborted = rb_const_get(mPatron, rb_intern("Aborted"));
|
835
946
|
|
836
|
-
rb_define_module_function(mPatron, "libcurl_version",
|
947
|
+
rb_define_module_function(mPatron, "libcurl_version", libcurl_version, 0);
|
948
|
+
rb_define_module_function(mPatron, "libcurl_version_exact", libcurl_version_exact, 0);
|
837
949
|
|
838
950
|
cSession = rb_define_class_under(mPatron, "Session", rb_cObject);
|
839
951
|
cRequest = rb_define_class_under(mPatron, "Request", rb_cObject);
|
data/lib/patron/error.rb
CHANGED
@@ -36,6 +36,9 @@ module Patron
|
|
36
36
|
# Gets raised when a request is attempted with an unsupported SSL version.
|
37
37
|
class UnsupportedSSLVersion < Error; end
|
38
38
|
|
39
|
+
# Gets raised when a request is attempted with an unsupported HTTP version.
|
40
|
+
class UnsupportedHTTPVersion < Error; end
|
41
|
+
|
39
42
|
# Gets raised when the URL was not properly formatted.
|
40
43
|
class URLFormatError < Error; end
|
41
44
|
|
@@ -56,6 +59,9 @@ module Patron
|
|
56
59
|
# Gets raised on too many redirects. When following redirects, Patron hit the maximum amount.
|
57
60
|
class TooManyRedirects < Error; end
|
58
61
|
|
62
|
+
# Gets raised if the progress callback, or an interrupt, aborts the Curl perform() call
|
63
|
+
class Aborted < Error; end
|
64
|
+
|
59
65
|
# Gets raised when the server specifies an encoding that could not be found, or has an invalid name,
|
60
66
|
# or when the server "lies" about the encoding of the response body (such as can be the case
|
61
67
|
# when the server specifies an encoding in `Content-Type`) which the HTML generator then overrides
|
data/lib/patron/request.rb
CHANGED
@@ -50,12 +50,12 @@ module Patron
|
|
50
50
|
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure,
|
51
51
|
:ignore_content_length, :multipart, :action, :timeout, :connect_timeout,
|
52
52
|
:max_redirects, :headers, :auth_type, :upload_data, :buffer_size, :cacert,
|
53
|
-
:ssl_version, :automatic_content_encoding, :force_ipv4
|
53
|
+
:ssl_version, :http_version, :automatic_content_encoding, :force_ipv4, :download_byte_limit
|
54
54
|
]
|
55
55
|
|
56
56
|
WRITER_VARS = [
|
57
57
|
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure,
|
58
|
-
:ignore_content_length, :multipart, :cacert, :ssl_version, :automatic_content_encoding, :force_ipv4
|
58
|
+
:ignore_content_length, :multipart, :cacert, :ssl_version, :http_version, :automatic_content_encoding, :force_ipv4, :download_byte_limit
|
59
59
|
]
|
60
60
|
|
61
61
|
attr_reader *READER_VARS
|
data/lib/patron/response.rb
CHANGED
@@ -33,13 +33,13 @@ module Patron
|
|
33
33
|
# @return [String] the original URL used to perform the request (contains the final URL after redirects)
|
34
34
|
attr_reader :url
|
35
35
|
|
36
|
-
# @return [
|
36
|
+
# @return [Integer] the HTTP status code of the final response after all the redirects
|
37
37
|
attr_reader :status
|
38
38
|
|
39
39
|
# @return [String] the complete status line (code and message)
|
40
40
|
attr_reader :status_line
|
41
41
|
|
42
|
-
# @return [
|
42
|
+
# @return [Integer] how many redirects were followed when fulfilling this request
|
43
43
|
attr_reader :redirect_count
|
44
44
|
|
45
45
|
# @return [String, nil] the response body as a String encoded as `Encoding::BINARY` or
|
data/lib/patron/session.rb
CHANGED
@@ -82,6 +82,10 @@ module Patron
|
|
82
82
|
# The supported values are nil, "SSLv2", "SSLv3", and "TLSv1".
|
83
83
|
attr_accessor :ssl_version
|
84
84
|
|
85
|
+
# @return [String] the HTTP version for the requests, or "None" if libcurl is to choose permitted versions
|
86
|
+
# The supported values are "None", "HTTPv1_0", "HTTPv1_1", "HTTPv2_0", "HTTPv2_TLS", and "HTTPv2_PRIOR".
|
87
|
+
attr_accessor :http_version
|
88
|
+
|
85
89
|
# @return [String] path to the CA file used for certificate verification, or `nil` if CURL default is used
|
86
90
|
attr_accessor :cacert
|
87
91
|
|
@@ -105,7 +109,12 @@ module Patron
|
|
105
109
|
|
106
110
|
# @return [Boolean] Support automatic Content-Encoding decompression and set liberal Accept-Encoding headers
|
107
111
|
attr_accessor :automatic_content_encoding
|
108
|
-
|
112
|
+
|
113
|
+
# @return [Fixnum, nil] Limit the amount of bytes downloaded. If it is set to nil
|
114
|
+
# (default) no limit will be applied.
|
115
|
+
# **Note that this only works on libCURL 7.34 and newer**
|
116
|
+
attr_accessor :download_byte_limit
|
117
|
+
|
109
118
|
private :handle_request, :add_cookie_file, :set_debug_file
|
110
119
|
|
111
120
|
# Create a new Session object for performing requests.
|
@@ -371,9 +380,11 @@ module Patron
|
|
371
380
|
req.auth_type = options.fetch :auth_type, self.auth_type
|
372
381
|
req.insecure = options.fetch :insecure, self.insecure
|
373
382
|
req.ssl_version = options.fetch :ssl_version, self.ssl_version
|
383
|
+
req.http_version = options.fetch :http_version, self.http_version
|
374
384
|
req.cacert = options.fetch :cacert, self.cacert
|
375
385
|
req.ignore_content_length = options.fetch :ignore_content_length, self.ignore_content_length
|
376
386
|
req.buffer_size = options.fetch :buffer_size, self.buffer_size
|
387
|
+
req.download_byte_limit = options.fetch :download_byte_limit, self.download_byte_limit
|
377
388
|
req.multipart = options[:multipart]
|
378
389
|
req.upload_data = options[:data]
|
379
390
|
req.file_name = options[:file]
|
data/lib/patron/version.rb
CHANGED
data/patron.gemspec
CHANGED
@@ -1,33 +1,44 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'patron/version'
|
3
5
|
|
4
|
-
Gem::Specification.new do |
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "patron"
|
8
|
+
spec.version = Patron::VERSION
|
9
|
+
spec.platform = Gem::Platform::RUBY
|
10
|
+
spec.authors = ["Phillip Toland"]
|
11
|
+
spec.email = ["phil.toland@gmail.com"]
|
12
|
+
spec.homepage = "https://github.com/toland/patron"
|
13
|
+
spec.summary = "Patron HTTP Client"
|
14
|
+
spec.description = "Ruby HTTP client library based on libcurl"
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
+
# Prevent pushing this gem to RubyGemspec.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushespec."
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.required_rubygems_version = ">= 1.2.0"
|
25
|
+
spec.rubyforge_project = "patron"
|
16
26
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
s.files = `git ls-files`.split("\n")
|
24
|
-
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
25
|
-
s.require_paths = ["lib", "ext"]
|
26
|
-
s.extensions = ["ext/patron/extconf.rb"]
|
27
|
-
s.post_install_message = %q{
|
27
|
+
spec.files = `git ls-files -z`.split("\x0")
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib", "ext"]
|
31
|
+
spec.extensions = ["ext/patron/extconf.rb"]
|
32
|
+
spec.post_install_message = %q{
|
28
33
|
Thank you for installing Patron. On OSX, make sure you are using libCURL with OpenSSL.
|
29
|
-
SecureTransport-based builds might cause crashes in forking
|
34
|
+
SecureTransport-based builds might cause crashes in forking environment.
|
30
35
|
|
31
36
|
For more info see https://github.com/curl/curl/issues/788
|
32
37
|
}
|
38
|
+
spec.add_development_dependency "rake", "~> 10"
|
39
|
+
spec.add_development_dependency "bundler", ">= 1"
|
40
|
+
spec.add_development_dependency "rspec", ">= 2.3.0"
|
41
|
+
spec.add_development_dependency "simplecov", "~> 0.10"
|
42
|
+
spec.add_development_dependency "yard", "~> 0.8"
|
43
|
+
spec.add_development_dependency "rake-compiler"
|
33
44
|
end
|
data/spec/patron_spec.rb
CHANGED
@@ -38,9 +38,16 @@ describe Patron do
|
|
38
38
|
expect(version).to match(%r|^\d+.\d+.\d+$|)
|
39
39
|
end
|
40
40
|
|
41
|
-
it "should return the version
|
41
|
+
it "should return the version string of the libcurl library" do
|
42
42
|
version = Patron.libcurl_version
|
43
43
|
expect(version).to match(%r|^libcurl/\d+.\d+.\d+|)
|
44
44
|
end
|
45
45
|
|
46
|
+
it "should return the version numbers of the libcurl library" do
|
47
|
+
version = Patron.libcurl_version_exact
|
48
|
+
expect(version.length).to eq(3)
|
49
|
+
expect(version[0]).to be > 0
|
50
|
+
expect(version[1]).to be >= 0
|
51
|
+
expect(version[2]).to be >= 0
|
52
|
+
end
|
46
53
|
end
|
data/spec/response_spec.rb
CHANGED
@@ -45,7 +45,7 @@ describe Patron::Response do
|
|
45
45
|
|
46
46
|
it 'recovers the status code' do
|
47
47
|
response = @session.get("/repetitiveheader")
|
48
|
-
expect(response.status).to be_kind_of(
|
48
|
+
expect(response.status).to be_kind_of(Integer)
|
49
49
|
expect(response.status).to eq(200)
|
50
50
|
end
|
51
51
|
|
data/spec/session_spec.rb
CHANGED
@@ -129,6 +129,34 @@ describe Patron::Session do
|
|
129
129
|
FileUtils.rm tmpfile
|
130
130
|
end
|
131
131
|
|
132
|
+
it "downloads a very large file" do
|
133
|
+
tmpfile = "/tmp/large-succeeded"
|
134
|
+
@session.get_file "/very-large", tmpfile
|
135
|
+
expect(File.size(tmpfile)).to eq(15 * 1024 * 1024)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "raises an exception if the download body limit is exceeded when using direct-to-file download" do
|
139
|
+
tmpfile = "/tmp/large-aborted"
|
140
|
+
@session.download_byte_limit = 1024
|
141
|
+
@session.buffer_size = 1024
|
142
|
+
expect {
|
143
|
+
@session.get_file "/very-large", tmpfile
|
144
|
+
}.to raise_error(Patron::Error)
|
145
|
+
# On Ruby 2.x Patron::Aborted takes precedence, but
|
146
|
+
# on 1.9 it will be Patron::PartialFileError
|
147
|
+
expect(File.size(tmpfile)).to be < (1024 * 3)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "raises an exception if the download body limit is exceeded when using download-to-memory" do
|
151
|
+
@session.download_byte_limit = 1024
|
152
|
+
@session.buffer_size = 1024
|
153
|
+
expect {
|
154
|
+
@session.get "/very-large"
|
155
|
+
}.to raise_error(Patron::Error)
|
156
|
+
# On Ruby 2.x Patron::Aborted takes precedence, but
|
157
|
+
# on 1.9 it will be Patron::PartialFileError
|
158
|
+
end
|
159
|
+
|
132
160
|
it "should not send the user-agent if it has been deleted from headers" do
|
133
161
|
@session.headers.delete 'User-Agent'
|
134
162
|
response = @session.get("/test")
|
data/spec/session_ssl_spec.rb
CHANGED
@@ -284,7 +284,7 @@ describe Patron::Session do
|
|
284
284
|
end
|
285
285
|
|
286
286
|
it "should work with different SSL versions" do
|
287
|
-
['SSLv3',
|
287
|
+
['SSLv3','TLSv1'].each do |version|
|
288
288
|
@session.ssl_version = version
|
289
289
|
response = @session.get("/test")
|
290
290
|
expect(response.status).to be == 200
|
@@ -299,7 +299,24 @@ describe Patron::Session do
|
|
299
299
|
}.to raise_error(Patron::UnsupportedSSLVersion)
|
300
300
|
end
|
301
301
|
end
|
302
|
-
|
302
|
+
|
303
|
+
it "should work with different HTTP versions" do
|
304
|
+
['HTTPv1_0','HTTPv1_1'].each do |version|
|
305
|
+
@session.http_version = version
|
306
|
+
response = @session.get("/test")
|
307
|
+
expect(response.status).to be == 200
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should raise when an unsupported or unknown HTTP version is requested" do
|
312
|
+
['something', 1].each do |version|
|
313
|
+
@session.http_version = version
|
314
|
+
expect {
|
315
|
+
@session.get("/test")
|
316
|
+
}.to raise_error(Patron::UnsupportedHTTPVersion)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
303
320
|
# ------------------------------------------------------------------------
|
304
321
|
describe 'when debug is enabled' do
|
305
322
|
it 'it should not clobber stderr' do
|
data/spec/support/test_server.rb
CHANGED
@@ -163,6 +163,14 @@ class WrongContentLengthServlet < HTTPServlet::AbstractServlet
|
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
166
|
+
# Serves a substantial amount of data
|
167
|
+
class LargeServlet < HTTPServlet::AbstractServlet
|
168
|
+
def do_GET(req, res)
|
169
|
+
res.content_length = 15 * 1024 * 1024
|
170
|
+
res.body = Random.new.bytes(15 * 1024 * 1024)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
166
174
|
class PatronTestServer
|
167
175
|
|
168
176
|
def self.start( log_file = nil, ssl = false, port = 9001 )
|
@@ -198,6 +206,7 @@ class PatronTestServer
|
|
198
206
|
@server.mount("/redirect", RedirectServlet)
|
199
207
|
@server.mount("/evil-redirect", EvilRedirectServlet)
|
200
208
|
@server.mount("/picture", PictureServlet)
|
209
|
+
@server.mount("/very-large", LargeServlet)
|
201
210
|
@server.mount("/setcookie", SetCookieServlet)
|
202
211
|
@server.mount("/repetitiveheader", RepetitiveHeaderServlet)
|
203
212
|
@server.mount("/wrongcontentlength", WrongContentLengthServlet)
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: patron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phillip Toland
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-07-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '10'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '10'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '1'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -56,16 +56,16 @@ dependencies:
|
|
56
56
|
name: simplecov
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.10
|
61
|
+
version: '0.10'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.10
|
68
|
+
version: '0.10'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: yard
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.8'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake-compiler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
description: Ruby HTTP client library based on libcurl
|
84
98
|
email:
|
85
99
|
- phil.toland@gmail.com
|
@@ -130,11 +144,12 @@ files:
|
|
130
144
|
- spec/util_spec.rb
|
131
145
|
homepage: https://github.com/toland/patron
|
132
146
|
licenses: []
|
133
|
-
metadata:
|
147
|
+
metadata:
|
148
|
+
allowed_push_host: https://rubygems.org
|
134
149
|
post_install_message: |2
|
135
150
|
|
136
151
|
Thank you for installing Patron. On OSX, make sure you are using libCURL with OpenSSL.
|
137
|
-
SecureTransport-based builds might cause crashes in forking
|
152
|
+
SecureTransport-based builds might cause crashes in forking environment.
|
138
153
|
|
139
154
|
For more info see https://github.com/curl/curl/issues/788
|
140
155
|
rdoc_options: []
|
@@ -153,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
168
|
version: 1.2.0
|
154
169
|
requirements: []
|
155
170
|
rubyforge_project: patron
|
156
|
-
rubygems_version: 2.5.
|
171
|
+
rubygems_version: 2.5.2
|
157
172
|
signing_key:
|
158
173
|
specification_version: 4
|
159
174
|
summary: Patron HTTP Client
|