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