patron 0.10.0 → 0.11.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 +6 -0
- data/ext/patron/session_ext.c +67 -15
- data/lib/patron/header_parser.rb +32 -0
- data/lib/patron/request.rb +2 -2
- data/lib/patron/response.rb +5 -7
- data/lib/patron/session.rb +7 -0
- data/lib/patron/version.rb +1 -1
- data/spec/header_parser_spec.rb +73 -0
- data/spec/sample_response_headers/headers_wetransfer.txt +18 -0
- data/spec/sample_response_headers/headers_wetransfer_with_proxy_status.txt +20 -0
- data/spec/sample_response_headers/headers_wetransfer_with_redirect.txt +24 -0
- data/spec/sample_response_headers/headers_with_set_cookie.txt +9 -0
- data/spec/session_spec.rb +59 -0
- data/spec/support/test_server.rb +5 -4
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03bffb3257b8f683269cebfec72cc4ea4fd86075
|
4
|
+
data.tar.gz: 2ece06c37c56204f850e138bdf3c481856953dd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f4a926542616faefd9a2a431d802d8941f99f5eb8c6c592c1ddb1119b30a4000743be8778f62148db4678300265f0283b6b1b927bfa2a8b8dacffb49adc3d2f
|
7
|
+
data.tar.gz: 8f8b71f17282c674149ba146ac32c3e7fb4f954ce44cfa162405b713fe5add7648b1c3d261afb6a5bc58317dbb333049afda93cce6dc8c9023a9256478c0bd4e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
### 0.11.0
|
2
|
+
|
3
|
+
* Added `Session#progress_callback` which accepts a callable object, which can be used to report session progress during request
|
4
|
+
execution.
|
5
|
+
* Fixed parsing of response headers when multiple responses are involved (redirect chains and HTTP proxies)
|
6
|
+
|
1
7
|
### 0.10.0
|
2
8
|
|
3
9
|
* Added `Session#low_speed_time` and `Session#low_speed_limit`. When used, they will force libCURL to raise
|
data/ext/patron/session_ext.c
CHANGED
@@ -40,7 +40,12 @@ struct patron_curl_state {
|
|
40
40
|
membuffer header_buffer;
|
41
41
|
membuffer body_buffer;
|
42
42
|
size_t download_byte_limit;
|
43
|
+
VALUE user_progress_blk;
|
43
44
|
int interrupt;
|
45
|
+
size_t dltotal;
|
46
|
+
size_t dlnow;
|
47
|
+
size_t ultotal;
|
48
|
+
size_t ulnow;
|
44
49
|
};
|
45
50
|
|
46
51
|
|
@@ -69,16 +74,45 @@ static size_t file_write_handler(void* stream, size_t size, size_t nmemb, FILE*
|
|
69
74
|
}
|
70
75
|
}
|
71
76
|
|
77
|
+
static int call_user_rb_progress_blk(void* vd_curl_state) {
|
78
|
+
struct patron_curl_state* state = (struct patron_curl_state*)vd_curl_state;
|
79
|
+
// Invoke the block with the array
|
80
|
+
VALUE blk_result = rb_funcall(state->user_progress_blk,
|
81
|
+
rb_intern("call"), 4,
|
82
|
+
LONG2NUM(state->dltotal),
|
83
|
+
LONG2NUM(state->dlnow),
|
84
|
+
LONG2NUM(state->ultotal),
|
85
|
+
LONG2NUM(state->ulnow));
|
86
|
+
return 0;
|
87
|
+
}
|
88
|
+
|
89
|
+
|
72
90
|
/* A non-zero return value from the progress handler will terminate the current
|
73
91
|
* request. We use this fact in order to interrupt any request when either the
|
74
92
|
* user calls the "interrupt" method on the session or when the Ruby interpreter
|
75
93
|
* is attempting to exit.
|
76
94
|
*/
|
77
|
-
static int session_progress_handler(void
|
95
|
+
static int session_progress_handler(void* clientp, size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) {
|
78
96
|
struct patron_curl_state* state = (struct patron_curl_state*) clientp;
|
79
|
-
|
80
|
-
|
81
|
-
|
97
|
+
state->dltotal = dltotal;
|
98
|
+
state->dlnow = dlnow;
|
99
|
+
state->ultotal = ultotal;
|
100
|
+
state->ulnow = ulnow;
|
101
|
+
|
102
|
+
// If a progress proc has been set, re-acquire the GIL and call it using
|
103
|
+
// `call_user_rb_progress_blk`. TODO: use the retval of that proc
|
104
|
+
// to permit premature abort
|
105
|
+
if(RTEST(state->user_progress_blk)) {
|
106
|
+
// Even though it is not documented, rb_thread_call_with_gvl is available even when
|
107
|
+
// rb_thread_call_without_gvl is not. See https://bugs.ruby-lang.org/issues/5543#note-4
|
108
|
+
// > rb_thread_call_with_gvl() is globally-visible (but not in headers)
|
109
|
+
// > for 1.9.3: https://bugs.ruby-lang.org/issues/4328
|
110
|
+
#if (defined(HAVE_TBR) || defined(HAVE_TCWOGVL)) && defined(USE_TBR)
|
111
|
+
rb_thread_call_with_gvl((void *(*)(void *)) call_user_rb_progress_blk, (void*)state);
|
112
|
+
#else
|
113
|
+
call_user_rb_progress_blk((void*)state);
|
114
|
+
#endif
|
115
|
+
}
|
82
116
|
|
83
117
|
// Set the interrupt if the download byte limit has been reached
|
84
118
|
if(state->download_byte_limit != 0 && (dltotal > state->download_byte_limit)) {
|
@@ -410,6 +444,7 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
410
444
|
VALUE action_name = rb_funcall(request, rb_intern("action"), 0);
|
411
445
|
VALUE a_c_encoding = rb_funcall(request, rb_intern("automatic_content_encoding"), 0);
|
412
446
|
VALUE download_byte_limit = rb_funcall(request, rb_intern("download_byte_limit"), 0);
|
447
|
+
VALUE maybe_progress_proc = rb_funcall(request, rb_intern("progress_callback"), 0);
|
413
448
|
|
414
449
|
if (RTEST(download_byte_limit)) {
|
415
450
|
state->download_byte_limit = FIX2INT(download_byte_limit);
|
@@ -417,6 +452,12 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
417
452
|
state->download_byte_limit = 0;
|
418
453
|
}
|
419
454
|
|
455
|
+
if (rb_obj_is_proc(maybe_progress_proc)) {
|
456
|
+
state->user_progress_blk = maybe_progress_proc;
|
457
|
+
} else {
|
458
|
+
state->user_progress_blk = Qnil;
|
459
|
+
}
|
460
|
+
|
420
461
|
headers = rb_funcall(request, rb_intern("headers"), 0);
|
421
462
|
if (RTEST(headers)) {
|
422
463
|
if (rb_type(headers) != T_HASH) {
|
@@ -718,6 +759,17 @@ static VALUE select_error(CURLcode code) {
|
|
718
759
|
return error;
|
719
760
|
}
|
720
761
|
|
762
|
+
|
763
|
+
/* Uses as the unblocking function when the thread running Patron gets
|
764
|
+
signaled. The important difference with session_interrupt is that we
|
765
|
+
are not allowed to touch any Ruby structures while outside the GIL,
|
766
|
+
but we _are_ permitted to touch our internal curl state struct
|
767
|
+
*/
|
768
|
+
void session_ubf_abort(void* patron_state) {
|
769
|
+
struct patron_curl_state* state = (struct patron_curl_state*) patron_state;
|
770
|
+
state->interrupt = INTERRUPT_ABORT;
|
771
|
+
}
|
772
|
+
|
721
773
|
/* Perform the actual HTTP request by calling libcurl. */
|
722
774
|
static VALUE perform_request(VALUE self) {
|
723
775
|
struct patron_curl_state *state = get_patron_curl_state(self);
|
@@ -745,17 +797,17 @@ static VALUE perform_request(VALUE self) {
|
|
745
797
|
}
|
746
798
|
|
747
799
|
#if (defined(HAVE_TBR) || defined(HAVE_TCWOGVL)) && defined(USE_TBR)
|
748
|
-
#if defined(HAVE_TCWOGVL)
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
#else
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
#endif
|
800
|
+
#if defined(HAVE_TCWOGVL)
|
801
|
+
ret = (CURLcode) rb_thread_call_without_gvl(
|
802
|
+
(void *(*)(void *)) curl_easy_perform, curl,
|
803
|
+
session_ubf_abort, (void*)state
|
804
|
+
);
|
805
|
+
#else
|
806
|
+
ret = (CURLcode) rb_thread_blocking_region(
|
807
|
+
(rb_blocking_function_t*) curl_easy_perform, curl,
|
808
|
+
session_ubf_abort, (void*)state
|
809
|
+
);
|
810
|
+
#endif
|
759
811
|
#else
|
760
812
|
ret = curl_easy_perform(curl);
|
761
813
|
#endif
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Patron::HeaderParser
|
2
|
+
CRLF = /#{Regexp.escape("\r\n")}/
|
3
|
+
|
4
|
+
# Returned for each response parsed out
|
5
|
+
class SingleResponseHeaders < Struct.new(:status_line, :headers)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Parses a string with lines delimited with CRLF into
|
9
|
+
# an Array of SingleResponseHeaders objects. libCURL supplies
|
10
|
+
# us multiple responses in sequence, so if we encounter multiple redirect
|
11
|
+
# or operate through a proxy - that adds ConnectionEstablished status at
|
12
|
+
# the beginning of the response - we need to account for parsing
|
13
|
+
# multiple response headres and potentially preserving them.
|
14
|
+
#
|
15
|
+
# @param [String] the string of headers, with responses delimited by empty lines. All lines must end with CRLF
|
16
|
+
# @return Array<SingleResponseHeaders>
|
17
|
+
def self.parse(headers_from_multiple_responses_in_sequence)
|
18
|
+
s = StringScanner.new(headers_from_multiple_responses_in_sequence)
|
19
|
+
responses = []
|
20
|
+
until s.eos?
|
21
|
+
return unless scanned = s.scan_until(CRLF)
|
22
|
+
matched_line = scanned[0..-3]
|
23
|
+
if matched_line =~ /^HTTP\/\d\.\d \d+/
|
24
|
+
responses << SingleResponseHeaders.new(matched_line.strip, [])
|
25
|
+
elsif matched_line =~ /^[^:]+\:/
|
26
|
+
raise "Header should follow an HTTP status line" unless responses.any?
|
27
|
+
responses[-1].headers << matched_line
|
28
|
+
end # else it is the end of the headers for the request
|
29
|
+
end
|
30
|
+
responses
|
31
|
+
end
|
32
|
+
end
|
data/lib/patron/request.rb
CHANGED
@@ -25,13 +25,13 @@ module Patron
|
|
25
25
|
:ignore_content_length, :multipart, :action, :timeout, :connect_timeout,
|
26
26
|
:max_redirects, :headers, :auth_type, :upload_data, :buffer_size, :cacert,
|
27
27
|
:ssl_version, :http_version, :automatic_content_encoding, :force_ipv4, :download_byte_limit,
|
28
|
-
:low_speed_time, :low_speed_limit
|
28
|
+
:low_speed_time, :low_speed_limit, :progress_callback
|
29
29
|
]
|
30
30
|
|
31
31
|
WRITER_VARS = [
|
32
32
|
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure,
|
33
33
|
:ignore_content_length, :multipart, :cacert, :ssl_version, :http_version, :automatic_content_encoding, :force_ipv4, :download_byte_limit,
|
34
|
-
:low_speed_time, :low_speed_limit
|
34
|
+
:low_speed_time, :low_speed_limit, :progress_callback
|
35
35
|
]
|
36
36
|
|
37
37
|
attr_reader *READER_VARS
|
data/lib/patron/response.rb
CHANGED
@@ -113,16 +113,14 @@ module Patron
|
|
113
113
|
private
|
114
114
|
|
115
115
|
# Called by the C code to parse and set the headers
|
116
|
-
def parse_headers(
|
116
|
+
def parse_headers(header_data_for_multiple_responses)
|
117
117
|
@headers = {}
|
118
118
|
|
119
|
-
|
120
|
-
|
121
|
-
@status_line = lines.shift
|
122
|
-
|
123
|
-
lines.each do |line|
|
124
|
-
break if line.empty?
|
119
|
+
responses = Patron::HeaderParser.parse(header_data_for_multiple_responses)
|
120
|
+
last_response = responses[-1] # Only use the last response (for proxies and redirects)
|
125
121
|
|
122
|
+
@status_line = last_response.status_line
|
123
|
+
last_response.headers.each do |line|
|
126
124
|
hdr, val = line.split(":", 2)
|
127
125
|
|
128
126
|
val.strip! unless val.nil?
|
data/lib/patron/session.rb
CHANGED
@@ -5,6 +5,7 @@ require 'patron/response_decoding'
|
|
5
5
|
require 'patron/response'
|
6
6
|
require 'patron/session_ext'
|
7
7
|
require 'patron/util'
|
8
|
+
require 'patron/header_parser'
|
8
9
|
|
9
10
|
module Patron
|
10
11
|
|
@@ -101,6 +102,11 @@ module Patron
|
|
101
102
|
|
102
103
|
private :handle_request, :add_cookie_file, :set_debug_file
|
103
104
|
|
105
|
+
# @return [#call, nil] callable object that will be called with 4 arguments
|
106
|
+
# during request/response execution - `dltotal`, `dlnow`, `ultotal`, `ulnow`.
|
107
|
+
# All these arguments are in bytes.
|
108
|
+
attr_accessor :progress_callback
|
109
|
+
|
104
110
|
# Create a new Session object for performing requests.
|
105
111
|
#
|
106
112
|
# @param args[Hash] options for the Session (same names as the writable attributes of the Session)
|
@@ -371,6 +377,7 @@ module Patron
|
|
371
377
|
req.ignore_content_length = options.fetch :ignore_content_length, self.ignore_content_length
|
372
378
|
req.buffer_size = options.fetch :buffer_size, self.buffer_size
|
373
379
|
req.download_byte_limit = options.fetch :download_byte_limit, self.download_byte_limit
|
380
|
+
req.progress_callback = options.fetch :progress_callback, self.progress_callback
|
374
381
|
req.multipart = options[:multipart]
|
375
382
|
req.upload_data = options[:data]
|
376
383
|
req.file_name = options[:file]
|
data/lib/patron/version.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Patron::HeaderParser do
|
4
|
+
it 'parses a standard header' do
|
5
|
+
simple_headers_path = File.dirname(__FILE__) + '/sample_response_headers/headers_wetransfer.txt'
|
6
|
+
responses = Patron::HeaderParser.parse(File.read(simple_headers_path))
|
7
|
+
|
8
|
+
expect(responses.length).to eq(1)
|
9
|
+
first_response = responses[0]
|
10
|
+
expect(first_response.status_line).to eq("HTTP/1.1 200 OK")
|
11
|
+
expect(first_response.headers).to be_kind_of(Array)
|
12
|
+
expect(first_response.headers[0]).to eq("Date: Mon, 29 Jan 2018 00:09:09 GMT")
|
13
|
+
expect(first_response.headers[1]).to eq("Content-Type: text/html; charset=utf-8")
|
14
|
+
expect(first_response.headers[2]).to eq("Transfer-Encoding: chunked")
|
15
|
+
expect(first_response.headers[3]).to eq("Connection: keep-alive")
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'parses a sequence of responses resulting from a redirect' do
|
19
|
+
simple_headers_path = File.dirname(__FILE__) + '/sample_response_headers/headers_wetransfer_with_redirect.txt'
|
20
|
+
responses = Patron::HeaderParser.parse(File.read(simple_headers_path))
|
21
|
+
|
22
|
+
expect(responses.length).to eq(2)
|
23
|
+
first_response = responses[0]
|
24
|
+
|
25
|
+
expect(first_response.status_line).to eq("HTTP/1.1 301 Moved Permanently")
|
26
|
+
expect(first_response.headers).to be_kind_of(Array)
|
27
|
+
expect(first_response.headers[0]).to eq("Date: Mon, 29 Jan 2018 00:42:27 GMT")
|
28
|
+
expect(first_response.headers[2]).to eq("Connection: keep-alive")
|
29
|
+
expect(first_response.headers[3]).to eq("Location: https://wetransfer.com/")
|
30
|
+
expect(first_response.headers[4]).to be_nil
|
31
|
+
|
32
|
+
second_response = responses[1]
|
33
|
+
expect(second_response.status_line).to eq("HTTP/1.1 200 OK")
|
34
|
+
expect(second_response.headers).to be_kind_of(Array)
|
35
|
+
expect(second_response.headers[0]).to eq("Date: Mon, 29 Jan 2018 00:42:27 GMT")
|
36
|
+
expect(second_response.headers[1]).to eq("Content-Type: text/html; charset=utf-8")
|
37
|
+
expect(second_response.headers[2]).to eq("Transfer-Encoding: chunked")
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'parses response headers that set cookies' do
|
41
|
+
simple_headers_path = File.dirname(__FILE__) + '/sample_response_headers/headers_with_set_cookie.txt'
|
42
|
+
responses = Patron::HeaderParser.parse(File.read(simple_headers_path))
|
43
|
+
|
44
|
+
expect(responses.length).to eq(1)
|
45
|
+
first_response = responses[0]
|
46
|
+
|
47
|
+
expect(first_response.status_line).to eq("HTTP/1.1 200 OK")
|
48
|
+
expect(first_response.headers).to be_kind_of(Array)
|
49
|
+
expect(first_response.headers[0]).to eq("Content-Type: text/plain")
|
50
|
+
expect(first_response.headers[1]).to start_with('Server:')
|
51
|
+
expect(first_response.headers[2]).to eq("Date: Mon, 29 Jan 2018 01:50:54 GMT")
|
52
|
+
expect(first_response.headers[3]).to eq("Content-Length: 3")
|
53
|
+
expect(first_response.headers[4]).to eq("Connection: Keep-Alive")
|
54
|
+
expect(first_response.headers[5]).to eq("Set-Cookie: a=1")
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'parses response headers with an extra status line from a proxy' do
|
58
|
+
simple_headers_path = File.dirname(__FILE__) + '/sample_response_headers/headers_wetransfer_with_proxy_status.txt'
|
59
|
+
responses = Patron::HeaderParser.parse(File.read(simple_headers_path))
|
60
|
+
|
61
|
+
expect(responses.length).to eq(2)
|
62
|
+
first_response = responses[0]
|
63
|
+
|
64
|
+
expect(first_response.status_line).to eq("HTTP/1.1 200 Connection established")
|
65
|
+
expect(first_response.headers).to be_kind_of(Array)
|
66
|
+
expect(first_response.headers).to be_empty
|
67
|
+
|
68
|
+
second_response = responses[1]
|
69
|
+
expect(second_response.status_line).to eq("HTTP/1.1 200 OK")
|
70
|
+
expect(second_response.headers).to be_kind_of(Array)
|
71
|
+
expect(second_response.headers[0]).to eq("Date: Mon, 29 Jan 2018 00:09:09 GMT")
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Mon, 29 Jan 2018 00:09:09 GMT
|
3
|
+
Content-Type: text/html; charset=utf-8
|
4
|
+
Transfer-Encoding: chunked
|
5
|
+
Connection: keep-alive
|
6
|
+
X-Frame-Options: SAMEORIGIN
|
7
|
+
X-XSS-Protection: 1; mode=block
|
8
|
+
X-Content-Type-Options: nosniff
|
9
|
+
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
|
10
|
+
Pragma: no-cache
|
11
|
+
Expires: Fri, 01 Jan 1990 00:00:00 GMT
|
12
|
+
Vary: Accept-Encoding, Origin
|
13
|
+
Set-Cookie: _wt_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJTllOGNkZDg3N2Y2Mzc2M2E1NWY2NTQwMjYzNzljODJhBjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMXk4YWtnZDVNYlRYUmJJWkFFaWkzVXFWM3IzbE1aU2xSbFl3SDQ3aHArbU09BjsARg%3D%3D--fe13900e81f693acb4a80d372d864c78704fa776; domain=wetransfer.com; path=/; secure; HttpOnly
|
14
|
+
X-Request-Id: 62b0d737-b0f8-400b-ac3a-0f84acf241af
|
15
|
+
X-Opaque: dev-1.wt-19171
|
16
|
+
X-Runtime: 0.116529
|
17
|
+
Strict-Transport-Security: max-age=15552000; includeSubDomains;
|
18
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
HTTP/1.1 200 Connection established
|
2
|
+
|
3
|
+
HTTP/1.1 200 OK
|
4
|
+
Date: Mon, 29 Jan 2018 00:09:09 GMT
|
5
|
+
Content-Type: text/html; charset=utf-8
|
6
|
+
Transfer-Encoding: chunked
|
7
|
+
Connection: keep-alive
|
8
|
+
X-Frame-Options: SAMEORIGIN
|
9
|
+
X-XSS-Protection: 1; mode=block
|
10
|
+
X-Content-Type-Options: nosniff
|
11
|
+
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
|
12
|
+
Pragma: no-cache
|
13
|
+
Expires: Fri, 01 Jan 1990 00:00:00 GMT
|
14
|
+
Vary: Accept-Encoding, Origin
|
15
|
+
Set-Cookie: _wt_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJTllOGNkZDg3N2Y2Mzc2M2E1NWY2NTQwMjYzNzljODJhBjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMXk4YWtnZDVNYlRYUmJJWkFFaWkzVXFWM3IzbE1aU2xSbFl3SDQ3aHArbU09BjsARg%3D%3D--fe13900e81f693acb4a80d372d864c78704fa776; domain=wetransfer.com; path=/; secure; HttpOnly
|
16
|
+
X-Request-Id: 62b0d737-b0f8-400b-ac3a-0f84acf241af
|
17
|
+
X-Opaque: dev-1.wt-19171
|
18
|
+
X-Runtime: 0.116529
|
19
|
+
Strict-Transport-Security: max-age=15552000; includeSubDomains;
|
20
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
HTTP/1.1 301 Moved Permanently
|
2
|
+
Date: Mon, 29 Jan 2018 00:42:27 GMT
|
3
|
+
Content-Length: 0
|
4
|
+
Connection: keep-alive
|
5
|
+
Location: https://wetransfer.com/
|
6
|
+
|
7
|
+
HTTP/1.1 200 OK
|
8
|
+
Date: Mon, 29 Jan 2018 00:42:27 GMT
|
9
|
+
Content-Type: text/html; charset=utf-8
|
10
|
+
Transfer-Encoding: chunked
|
11
|
+
Connection: keep-alive
|
12
|
+
X-Frame-Options: SAMEORIGIN
|
13
|
+
X-XSS-Protection: 1; mode=block
|
14
|
+
X-Content-Type-Options: nosniff
|
15
|
+
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
|
16
|
+
Pragma: no-cache
|
17
|
+
Expires: Fri, 01 Jan 1990 00:00:00 GMT
|
18
|
+
Vary: Accept-Encoding, Origin
|
19
|
+
Set-Cookie: _wt_session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiJWYyMGFmZGRlY2QyNDJkNTA3YTc2YmJmYzg2MWNhMGZlBjsAVEkiEF9jc3JmX3Rva2VuBjsARkkiMUkvMUJqR0tCelZxQytKQU1kbDV4ZW5xNDNSaUlDNnVrODhDTEY4Q0dxbXM9BjsARg%3D%3D--b46297a54dd838e0c26b5559314b10deb1f59312; domain=wetransfer.com; path=/; secure; HttpOnly
|
20
|
+
X-Request-Id: 09e7a0ef-e9a1-4519-a580-8185e6d64c1c
|
21
|
+
X-Opaque: dev-1.wt-24185
|
22
|
+
X-Runtime: 0.102958
|
23
|
+
Strict-Transport-Security: max-age=15552000; includeSubDomains;
|
24
|
+
|
data/spec/session_spec.rb
CHANGED
@@ -178,6 +178,59 @@ describe Patron::Session do
|
|
178
178
|
expect {@session.get("/slow")}.to raise_error(Patron::TimeoutError)
|
179
179
|
end
|
180
180
|
|
181
|
+
it "is able to terminate the thread that is running a slow request using Thread#kill (uses the custom unblock)" do
|
182
|
+
t = Thread.new do
|
183
|
+
session = Patron::Session.new
|
184
|
+
session.timeout = 40
|
185
|
+
session.base_url = "http://localhost:9001"
|
186
|
+
session.get("/slow")
|
187
|
+
end
|
188
|
+
|
189
|
+
# Our test server starts sending the body only after 20 seconds. We should be able to abort
|
190
|
+
# using a signal during that time.
|
191
|
+
started = Time.now.to_i
|
192
|
+
sleep 5 # Less than what it takes for the server to respond
|
193
|
+
t.kill # Kill the thread forcibly
|
194
|
+
t.join # wrap up the thread. If Patron is still busy there, this join call will still take 15s.
|
195
|
+
|
196
|
+
delta_s = Time.now.to_i - started
|
197
|
+
expect(delta_s).to be_within(2).of(5)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "is able to terminate the thread that is running a slow request" do
|
201
|
+
t = Thread.new do
|
202
|
+
trap('SIGINT') do
|
203
|
+
exit # exit the thread
|
204
|
+
end
|
205
|
+
session = Patron::Session.new
|
206
|
+
session.timeout = 40
|
207
|
+
session.base_url = "http://localhost:9001"
|
208
|
+
session.get("/slow")
|
209
|
+
end
|
210
|
+
|
211
|
+
# Our test server starts sending the body only after 20 seconds. We should be able to abort
|
212
|
+
# using a signal during that time.
|
213
|
+
started = Time.now.to_i
|
214
|
+
sleep 5 # Less than what it takes for the server to respond
|
215
|
+
Process.kill("INT", Process.pid) # Signal ourselves...
|
216
|
+
t.join # wrap up the thread. If Patron is still busy there, this join call will still take 15s.
|
217
|
+
delta_s = Time.now.to_i - started
|
218
|
+
expect(delta_s).to be_within(2).of(5)
|
219
|
+
end
|
220
|
+
|
221
|
+
it "receives progress callbacks" do
|
222
|
+
session = Patron::Session.new
|
223
|
+
session.timeout = 40
|
224
|
+
session.base_url = "http://localhost:9001"
|
225
|
+
callback_args = []
|
226
|
+
session.progress_callback = Proc.new {|dltotal, dlnow, ultotal, ulnow|
|
227
|
+
callback_args << [dltotal, dlnow, ultotal, ulnow]
|
228
|
+
}
|
229
|
+
session.get("/slow")
|
230
|
+
|
231
|
+
expect(callback_args).not_to be_empty
|
232
|
+
end
|
233
|
+
|
181
234
|
it "should follow redirects by default" do
|
182
235
|
@session.max_redirects = 1
|
183
236
|
response = @session.get("/redirect")
|
@@ -186,6 +239,12 @@ describe Patron::Session do
|
|
186
239
|
expect(body.path).to be == "/test"
|
187
240
|
end
|
188
241
|
|
242
|
+
it "should not keep the Location header from the redirecting response" do
|
243
|
+
@session.max_redirects = 1
|
244
|
+
response = @session.get("/redirect")
|
245
|
+
expect(response.headers['Location']).to be_nil
|
246
|
+
end
|
247
|
+
|
189
248
|
it "should include redirect count in response" do
|
190
249
|
@session.max_redirects = 1
|
191
250
|
response = @session.get("/redirect")
|
data/spec/support/test_server.rb
CHANGED
@@ -66,8 +66,8 @@ class GzipServlet < HTTPServlet::AbstractServlet
|
|
66
66
|
|
67
67
|
content_length = out.size
|
68
68
|
# Content-Length gets set automatically by WEBrick, and if we do it manually
|
69
|
-
# here then two headers will be set.
|
70
|
-
#
|
69
|
+
# here then two headers will be set. This is also against the content encoding
|
70
|
+
# description in the HTTP 1.1 RFC - but hey, Webrick!
|
71
71
|
res.header['Content-Encoding'] = 'deflate'
|
72
72
|
res.header['Vary'] = 'Accept-Encoding'
|
73
73
|
res.body = out.string
|
@@ -82,9 +82,10 @@ end
|
|
82
82
|
|
83
83
|
class SlowServlet < HTTPServlet::AbstractServlet
|
84
84
|
def do_GET(req,res)
|
85
|
-
sleep 3
|
86
85
|
res.header['Content-Type'] = 'text/plain'
|
87
|
-
res.body
|
86
|
+
res.body << 'x'
|
87
|
+
sleep 20
|
88
|
+
res.body << 'rest of body'
|
88
89
|
end
|
89
90
|
end
|
90
91
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: patron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phillip Toland
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- ext/patron/sglib.h
|
121
121
|
- lib/patron.rb
|
122
122
|
- lib/patron/error.rb
|
123
|
+
- lib/patron/header_parser.rb
|
123
124
|
- lib/patron/proxy_type.rb
|
124
125
|
- lib/patron/request.rb
|
125
126
|
- lib/patron/response.rb
|
@@ -133,9 +134,14 @@ files:
|
|
133
134
|
- script/test_server
|
134
135
|
- spec/certs/cacert.pem
|
135
136
|
- spec/certs/privkey.pem
|
137
|
+
- spec/header_parser_spec.rb
|
136
138
|
- spec/patron_spec.rb
|
137
139
|
- spec/request_spec.rb
|
138
140
|
- spec/response_spec.rb
|
141
|
+
- spec/sample_response_headers/headers_wetransfer.txt
|
142
|
+
- spec/sample_response_headers/headers_wetransfer_with_proxy_status.txt
|
143
|
+
- spec/sample_response_headers/headers_wetransfer_with_redirect.txt
|
144
|
+
- spec/sample_response_headers/headers_with_set_cookie.txt
|
139
145
|
- spec/session_spec.rb
|
140
146
|
- spec/session_ssl_spec.rb
|
141
147
|
- spec/spec_helper.rb
|