patron 0.8.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|