patron 0.11.1 → 0.12.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/.travis.yml +1 -0
- data/CHANGELOG.md +6 -0
- data/ext/patron/session_ext.c +5 -0
- data/lib/patron/header_parser.rb +7 -11
- data/lib/patron/request.rb +2 -2
- data/lib/patron/session.rb +4 -0
- data/lib/patron/version.rb +1 -1
- data/patron.gemspec +2 -0
- data/script/test_server +1 -1
- data/spec/header_parser_spec.rb +14 -0
- data/spec/sample_response_headers/webmock_headers_without_trailing_crlf.txt +15 -0
- data/spec/session_spec.rb +11 -1
- data/spec/spec_helper.rb +2 -2
- data/spec/support/config.ru +128 -0
- data/spec/support/test_server.rb +13 -218
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7eb76f695d888463be44f05ca6bc3f8acb345953
|
4
|
+
data.tar.gz: 6e53654564c133b451743d14126b9833c6d2f20a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4954733879999e8a0012059ce2972c664fec4f47be970f9bb1ae58cd52da2d040e95ef722421c56e85a268110e2096e957cb0a92a98e258e80f111f5407224d5
|
7
|
+
data.tar.gz: ad4c2bf766cac5916a35d4612d06a096f2a5b21fa89c4c8b6b933a13c25b6e01a7fe0501c2a7244c57a389b6f210a90c527d4c56062d4c5ed7c6a4a60dff6dfd
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
### 0.12.0
|
2
|
+
|
3
|
+
* Replace StringScanner in HeaderParser with StringIO, fix Webmock regression when the headers string would
|
4
|
+
not have an empty CRLF-terminated line at the end - which would cause the parser to return a nil.
|
5
|
+
* Added `Session#dns_cache_timeout` as a config option for CURLOPT_DNS_CACHE_TIMEOUT
|
6
|
+
|
1
7
|
### 0.11.1
|
2
8
|
|
3
9
|
* Make sure StringScanner is available to HeaderParser.
|
data/ext/patron/session_ext.c
CHANGED
@@ -588,6 +588,11 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
588
588
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FIX2INT(timeout));
|
589
589
|
}
|
590
590
|
|
591
|
+
timeout = rb_funcall(request, rb_intern("dns_cache_timeout"), 0);
|
592
|
+
if (RTEST(timeout)) {
|
593
|
+
curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, FIX2INT(timeout));
|
594
|
+
}
|
595
|
+
|
591
596
|
VALUE low_speed_time = rb_funcall(request, rb_intern("low_speed_time"), 0);
|
592
597
|
if(RTEST(low_speed_time)) {
|
593
598
|
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, FIX2LONG(low_speed_time));
|
data/lib/patron/header_parser.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
require 'strscan'
|
2
|
-
|
3
1
|
module Patron::HeaderParser
|
4
|
-
|
2
|
+
HTTP_STATUS_LINE_START_RE = /^HTTP\/\d\.\d \d+/
|
3
|
+
HEADER_LINE_START_RE = /^[^:]+\:/
|
5
4
|
|
6
5
|
# Returned for each response parsed out
|
7
6
|
class SingleResponseHeaders < Struct.new(:status_line, :headers)
|
@@ -16,17 +15,14 @@ module Patron::HeaderParser
|
|
16
15
|
#
|
17
16
|
# @param [String] the string of headers, with responses delimited by empty lines. All lines must end with CRLF
|
18
17
|
# @return Array<SingleResponseHeaders>
|
19
|
-
def self.parse(
|
20
|
-
s = StringScanner.new(headers_from_multiple_responses_in_sequence)
|
18
|
+
def self.parse(string_of_headers_from_multiple_responses_in_sequence)
|
21
19
|
responses = []
|
22
|
-
|
23
|
-
|
24
|
-
matched_line = scanned[0..-3]
|
25
|
-
if matched_line =~ /^HTTP\/\d\.\d \d+/
|
20
|
+
string_of_headers_from_multiple_responses_in_sequence.each_line do |matched_line|
|
21
|
+
if matched_line =~ HTTP_STATUS_LINE_START_RE
|
26
22
|
responses << SingleResponseHeaders.new(matched_line.strip, [])
|
27
|
-
elsif matched_line =~
|
23
|
+
elsif matched_line =~ HEADER_LINE_START_RE
|
28
24
|
raise "Header should follow an HTTP status line" unless responses.any?
|
29
|
-
responses[-1].headers << matched_line
|
25
|
+
responses[-1].headers << matched_line.strip
|
30
26
|
end # else it is the end of the headers for the request
|
31
27
|
end
|
32
28
|
responses
|
data/lib/patron/request.rb
CHANGED
@@ -22,14 +22,14 @@ module Patron
|
|
22
22
|
|
23
23
|
READER_VARS = [
|
24
24
|
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure,
|
25
|
-
:ignore_content_length, :multipart, :action, :timeout, :connect_timeout,
|
25
|
+
:ignore_content_length, :multipart, :action, :timeout, :connect_timeout, :dns_cache_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
28
|
:low_speed_time, :low_speed_limit, :progress_callback
|
29
29
|
]
|
30
30
|
|
31
31
|
WRITER_VARS = [
|
32
|
-
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure,
|
32
|
+
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure, :dns_cache_timeout,
|
33
33
|
:ignore_content_length, :multipart, :cacert, :ssl_version, :http_version, :automatic_content_encoding, :force_ipv4, :download_byte_limit,
|
34
34
|
:low_speed_time, :low_speed_limit, :progress_callback
|
35
35
|
]
|
data/lib/patron/session.rb
CHANGED
@@ -19,6 +19,9 @@ module Patron
|
|
19
19
|
# @return [Integer] HTTP transaction timeout in seconds. Defaults to 5 seconds.
|
20
20
|
attr_accessor :timeout
|
21
21
|
|
22
|
+
# @return [Integer] DNS cache timeout in seconds. Defaults to 60 seconds. Set to 0 for no cache, or to -1 for indefinite caching.
|
23
|
+
attr_accessor :dns_cache_timeout
|
24
|
+
|
22
25
|
# Maximum number of redirects to follow
|
23
26
|
# Set to 0 to disable and -1 to follow all redirects. Defaults to 5.
|
24
27
|
# @return [Integer]
|
@@ -361,6 +364,7 @@ module Patron
|
|
361
364
|
req.automatic_content_encoding = options.fetch :automatic_content_encoding, self.automatic_content_encoding
|
362
365
|
req.timeout = options.fetch :timeout, self.timeout
|
363
366
|
req.connect_timeout = options.fetch :connect_timeout, self.connect_timeout
|
367
|
+
req.dns_cache_timeout = options.fetch :dns_cache_timeout, self.dns_cache_timeout
|
364
368
|
req.low_speed_time = options.fetch :low_speed_time, self.low_speed_time
|
365
369
|
req.low_speed_limit = options.fetch :low_speed_limit, self.low_speed_limit
|
366
370
|
req.force_ipv4 = options.fetch :force_ipv4, self.force_ipv4
|
data/lib/patron/version.rb
CHANGED
data/patron.gemspec
CHANGED
@@ -40,5 +40,7 @@ For more info see https://github.com/curl/curl/issues/788
|
|
40
40
|
spec.add_development_dependency "rspec", ">= 2.3.0"
|
41
41
|
spec.add_development_dependency "simplecov", "~> 0.10"
|
42
42
|
spec.add_development_dependency "yard", "~> 0.8"
|
43
|
+
spec.add_development_dependency "rack", "~> 1"
|
44
|
+
spec.add_development_dependency "puma", '~> 3.11'
|
43
45
|
spec.add_development_dependency "rake-compiler"
|
44
46
|
end
|
data/script/test_server
CHANGED
data/spec/header_parser_spec.rb
CHANGED
@@ -70,4 +70,18 @@ describe Patron::HeaderParser do
|
|
70
70
|
expect(second_response.headers).to be_kind_of(Array)
|
71
71
|
expect(second_response.headers[0]).to eq("Date: Mon, 29 Jan 2018 00:09:09 GMT")
|
72
72
|
end
|
73
|
+
|
74
|
+
it 'parses headers without the trailing CRLF from Webmock' do
|
75
|
+
path = File.dirname(__FILE__) + '/sample_response_headers/webmock_headers_without_trailing_crlf.txt'
|
76
|
+
responses = Patron::HeaderParser.parse(File.read(path))
|
77
|
+
|
78
|
+
expect(responses.length).to eq(1)
|
79
|
+
first_response = responses[0]
|
80
|
+
|
81
|
+
expect(first_response.status_line).to eq("HTTP/1.1 200 OK")
|
82
|
+
expect(first_response.headers).to be_kind_of(Array)
|
83
|
+
expect(first_response.headers).not_to be_empty
|
84
|
+
last_header = first_response.headers[-1]
|
85
|
+
expect(last_header).to eq('Connection: Close')
|
86
|
+
end
|
73
87
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Cache-Control: max-age=0, private, must-revalidate
|
3
|
+
Content-Type: application/json; charset=utf-8
|
4
|
+
Date: Mon, 31 Mar 2014 09:06:55 GMT
|
5
|
+
Etag: "99914b932bd37a50b983c5e7c90ae93b"
|
6
|
+
Status: 200 OK
|
7
|
+
Strict-Transport-Security: max-age=31536000
|
8
|
+
X-Content-Type-Options: nosniff
|
9
|
+
X-Frame-Options: SAMEORIGIN
|
10
|
+
X-Request-Id: 58f4c05f-0f35-4957-ad73-1c6eb25ff1cc
|
11
|
+
X-Runtime: 0.014435
|
12
|
+
X-Ua-Compatible: chrome=1
|
13
|
+
X-Xss-Protection: 1; mode=block
|
14
|
+
Transfer-Encoding: chunked
|
15
|
+
Connection: Close
|
data/spec/session_spec.rb
CHANGED
@@ -166,6 +166,11 @@ describe Patron::Session do
|
|
166
166
|
expect(body.header["x-test"]).to be == ["Testing"]
|
167
167
|
end
|
168
168
|
|
169
|
+
it "accepts the DNS cache timeout option" do
|
170
|
+
@session.dns_cache_timeout = 60
|
171
|
+
@session.get("/")
|
172
|
+
end
|
173
|
+
|
169
174
|
it "should raise an exception on timeout" do
|
170
175
|
@session.timeout = 1
|
171
176
|
expect {@session.get("/timeout")}.to raise_error(Patron::TimeoutError)
|
@@ -333,7 +338,7 @@ describe Patron::Session do
|
|
333
338
|
data = "upload data"
|
334
339
|
response = @session.patch("/testpatch", data)
|
335
340
|
body = YAML::load(response.body)
|
336
|
-
expect(body
|
341
|
+
expect(body.body).to eq("upload data")
|
337
342
|
end
|
338
343
|
|
339
344
|
it "should upload data with :delete" do
|
@@ -551,6 +556,7 @@ describe Patron::Session do
|
|
551
556
|
|
552
557
|
let(:args) { {
|
553
558
|
:timeout => 10,
|
559
|
+
:dns_cache_timeout => 10,
|
554
560
|
:base_url => 'http://localhost:9001',
|
555
561
|
:headers => {'User-Agent' => 'myapp/1.0'}
|
556
562
|
} }
|
@@ -565,6 +571,10 @@ describe Patron::Session do
|
|
565
571
|
expect(session.timeout).to be == args[:timeout]
|
566
572
|
end
|
567
573
|
|
574
|
+
it 'sets DNS cache timeout' do
|
575
|
+
expect(session.dns_cache_timeout).to be == args[:dns_cache_timeout]
|
576
|
+
end
|
577
|
+
|
568
578
|
it 'sets headers' do
|
569
579
|
expect(session.headers).to be == args[:headers]
|
570
580
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -18,5 +18,5 @@ $stderr.puts "Build against #{Patron.libcurl_version}"
|
|
18
18
|
|
19
19
|
Dir['./spec/support/**/*.rb'].each { |fn| require fn }
|
20
20
|
|
21
|
-
PatronTestServer.start(
|
22
|
-
PatronTestServer.start(
|
21
|
+
Thread.new { PatronTestServer.start(false, 9001) }
|
22
|
+
Thread.new { PatronTestServer.start(true, 9043) }
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
## HTTP test server for integration tests
|
5
|
+
|
6
|
+
Readback = Proc.new {|env|
|
7
|
+
# The Patron test suite is originally written to enable the following
|
8
|
+
# testing pattern:
|
9
|
+
# * Patron does a request
|
10
|
+
# * The request gets captured by Webrick
|
11
|
+
# * Webrick then takes it's Request object and YAMLs it wholesale (body, headers and all)
|
12
|
+
# * ...and sends it back to the client
|
13
|
+
# * The test suite then de-YAMLs the request object from the response body and does assertions on it
|
14
|
+
#
|
15
|
+
# The easiest way to fake it, preserving the entire test suite intact, is to simulate a Webrick
|
16
|
+
# request object using an ostruct. Of note is the following:
|
17
|
+
#
|
18
|
+
# 1) Webrick returns headers in arrays by default, accounting for repeats like with Cookie
|
19
|
+
# 2) Webrick does not convert headers to ENV_VARS_FOR_FCI it only downcases them. We have to match that.
|
20
|
+
req = Rack::Request.new(env)
|
21
|
+
req_headers = env.to_a.select do |(k,v)|
|
22
|
+
k.is_a?(String) && v.is_a?(String)
|
23
|
+
end.map do |(k,v)|
|
24
|
+
[k.downcase.gsub(/^http_/, '').gsub(/_/o, '-'), [v]]
|
25
|
+
end
|
26
|
+
|
27
|
+
fake_webrick_request_object = OpenStruct.new({
|
28
|
+
:path => req.fullpath,
|
29
|
+
:request_method => req.request_method.to_s,
|
30
|
+
:header => Hash[req_headers],
|
31
|
+
:body => env['rack.input'].read,
|
32
|
+
})
|
33
|
+
|
34
|
+
body_str = fake_webrick_request_object.to_yaml
|
35
|
+
[200, {'Content-Type' => 'text/plain', 'Content-Length' => body_str.bytesize.to_s}, [body_str]]
|
36
|
+
}
|
37
|
+
|
38
|
+
GzipServlet = Proc.new {|env|
|
39
|
+
raise "Need to have the right Accept-Encoding: header" unless env['HTTP_ACCEPT_ENCODING']
|
40
|
+
|
41
|
+
body = Enumerator.new do |y|
|
42
|
+
z = Zlib::Deflate.new(Zlib::DEFAULT_COMPRESSION)
|
43
|
+
1024.times {
|
44
|
+
y.yield(z.deflate('Some highly compressible data'))
|
45
|
+
}
|
46
|
+
y.yield(z.finish.to_s)
|
47
|
+
y.yield(z.close.to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
[200, {'Content-Encoding' => 'deflate', 'Vary' => 'Accept-Encoding'}, body]
|
51
|
+
}
|
52
|
+
|
53
|
+
TimeoutServlet = Proc.new {|env|
|
54
|
+
sleep 1.1
|
55
|
+
[200, {'Content-Type' => 'text/plain'}, ['That took a while']]
|
56
|
+
}
|
57
|
+
|
58
|
+
SlowServlet = Proc.new {|env|
|
59
|
+
body = Enumerator.new do |y|
|
60
|
+
y.yield 'x'
|
61
|
+
sleep 20
|
62
|
+
y.yield 'rest of body'
|
63
|
+
end
|
64
|
+
[200, {'Content-Type' => 'text/plain'}, body]
|
65
|
+
}
|
66
|
+
|
67
|
+
RedirectServlet = Proc.new {|env|
|
68
|
+
port = env.fetch('SERVER_PORT')
|
69
|
+
[301, {'Location' => "http://localhost:#{port}/test"}, []]
|
70
|
+
}
|
71
|
+
|
72
|
+
EvilRedirectServlet = Proc.new {|env|
|
73
|
+
[301, {'Location' => "smtp://mailbox:secret@localhost"}, []]
|
74
|
+
}
|
75
|
+
|
76
|
+
BodyReadback = Proc.new {|env|
|
77
|
+
readback = {'method' => env['REQUEST_METHOD'], 'body' => env['rack.input'].read, 'content_type' => env.fetch('HTTP_CONTENT_TYPE')}
|
78
|
+
[200, {'Content-Type' => 'text/plain'}, [readback]]
|
79
|
+
}
|
80
|
+
|
81
|
+
TestPatchBodyServlet = BodyReadback
|
82
|
+
|
83
|
+
SetCookieServlet = Proc.new {|env|
|
84
|
+
[301, {'Set-Cookie' => 'session_id=foo123', 'Location' => 'http://localhost:9001/test'}, []]
|
85
|
+
}
|
86
|
+
|
87
|
+
RepetitiveHeaderServlet = Proc.new {|env|
|
88
|
+
# The values of the header must be Strings,
|
89
|
+
# consisting of lines (for multiple header values, e.g. multiple
|
90
|
+
# <tt>Set-Cookie</tt> values) separated by "\\n".
|
91
|
+
[200, {'Set-Cookie' => "a=1\nb=2", 'Content-Type' => 'text/plain'}, ['Hi.']]
|
92
|
+
}
|
93
|
+
|
94
|
+
PictureServlet = Proc.new {|env|
|
95
|
+
[200, {'Content-Type' => 'image/png'}, [File.read('./pic.png')]]
|
96
|
+
}
|
97
|
+
|
98
|
+
WrongContentLengthServlet = Proc.new {|env|
|
99
|
+
[200, {'Content-Length' => '1024', 'Content-Type' => 'text/plain'}, ['Hello.']]
|
100
|
+
}
|
101
|
+
|
102
|
+
# Serves a substantial amount of data
|
103
|
+
LargeServlet = Proc.new {|env|
|
104
|
+
len = 15 * 1024 * 1024
|
105
|
+
body = Enumerator.new do |y|
|
106
|
+
15.times do
|
107
|
+
y.yield(Random.new.bytes(1024 * 1024))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
[200, {'Content-Type' => 'binary/octet-stream', 'Content-Length' => len.to_s}, body]
|
111
|
+
}
|
112
|
+
|
113
|
+
run Rack::URLMap.new({
|
114
|
+
"/" => Proc.new {|env| [200, {'Content-Length' => '2'}, ['Welcome']]},
|
115
|
+
"/test" => Readback,
|
116
|
+
"/testpost" => BodyReadback,
|
117
|
+
"/testpatch" => BodyReadback,
|
118
|
+
"/timeout" => TimeoutServlet,
|
119
|
+
"/slow" => SlowServlet,
|
120
|
+
"/redirect" => RedirectServlet,
|
121
|
+
"/evil-redirect" => EvilRedirectServlet,
|
122
|
+
"/picture" => PictureServlet,
|
123
|
+
"/very-large" => LargeServlet,
|
124
|
+
"/setcookie" => SetCookieServlet,
|
125
|
+
"/repetitiveheader" => RepetitiveHeaderServlet,
|
126
|
+
"/wrongcontentlength" => WrongContentLengthServlet,
|
127
|
+
"/gzip-compressed" => GzipServlet,
|
128
|
+
})
|
data/spec/support/test_server.rb
CHANGED
@@ -1,225 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
require 'yaml'
|
6
|
-
require 'webrick'
|
7
|
-
require 'webrick/https'
|
8
|
-
require 'openssl'
|
9
|
-
require 'zlib'
|
10
|
-
|
11
|
-
include WEBrick
|
12
|
-
|
13
|
-
# This ugly little hack is necessary to make the specs pass when running
|
14
|
-
# the test_server script under Ruby 1.9. URI::Parser#to_yaml generates
|
15
|
-
# regexp representations that YAML.parse cannot parse.
|
16
|
-
class URI::Parser
|
17
|
-
def to_yaml(opts = {})
|
18
|
-
{}.to_yaml(opts)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module RespondWith
|
23
|
-
def respond_with(method, req, res)
|
24
|
-
res.body = req.to_yaml
|
25
|
-
res['Content-Type'] = "text/plain"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class TestServlet < HTTPServlet::AbstractServlet
|
30
|
-
|
31
|
-
include RespondWith
|
32
|
-
|
33
|
-
def do_GET(req,res)
|
34
|
-
respond_with(:GET, req, res)
|
35
|
-
end
|
36
|
-
|
37
|
-
def do_POST(req,res)
|
38
|
-
respond_with(:POST, req, res)
|
39
|
-
end
|
40
|
-
|
41
|
-
def do_PUT(req,res)
|
42
|
-
respond_with(:PUT, req, res)
|
43
|
-
end
|
44
|
-
|
45
|
-
def do_DELETE(req,res)
|
46
|
-
respond_with(:DELETE, req, res)
|
47
|
-
end
|
48
|
-
|
49
|
-
def do_COPY(req,res)
|
50
|
-
respond_with(:COPY, req, res)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class GzipServlet < HTTPServlet::AbstractServlet
|
55
|
-
|
56
|
-
def do_GET(req,res)
|
57
|
-
raise "Need to have the right Accept-Encoding: header" unless req.header['Accept-Encoding']
|
58
|
-
|
59
|
-
out = StringIO.new
|
60
|
-
z = Zlib::Deflate.new(Zlib::DEFAULT_COMPRESSION)
|
61
|
-
1024.times {
|
62
|
-
out << z.deflate('Some highly compressible data')
|
63
|
-
}
|
64
|
-
out << z.finish
|
65
|
-
z.close
|
66
|
-
|
67
|
-
content_length = out.size
|
68
|
-
# Content-Length gets set automatically by WEBrick, and if we do it manually
|
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
|
-
res.header['Content-Encoding'] = 'deflate'
|
72
|
-
res.header['Vary'] = 'Accept-Encoding'
|
73
|
-
res.body = out.string
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
class TimeoutServlet < HTTPServlet::AbstractServlet
|
78
|
-
def do_GET(req,res)
|
79
|
-
sleep(1.1)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
class SlowServlet < HTTPServlet::AbstractServlet
|
84
|
-
def do_GET(req,res)
|
85
|
-
res.header['Content-Type'] = 'text/plain'
|
86
|
-
res.body << 'x'
|
87
|
-
sleep 20
|
88
|
-
res.body << 'rest of body'
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
class RedirectServlet < HTTPServlet::AbstractServlet
|
93
|
-
def do_GET(req,res)
|
94
|
-
res['Location'] = "http://localhost:9001/test"
|
95
|
-
res.status = 301
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
class EvilRedirectServlet < HTTPServlet::AbstractServlet
|
100
|
-
def do_GET(req,res)
|
101
|
-
res['Location'] = "smtp://mailbox:secret@localhost"
|
102
|
-
res.status = 301
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
class TestPostBodyServlet < HTTPServlet::AbstractServlet
|
107
|
-
include RespondWith
|
108
|
-
def do_POST(req, res)
|
109
|
-
respond_with(:POST, {'body' => req.body, 'content_type' => req.content_type}, res)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
class TestPatchBodyServlet < HTTPServlet::AbstractServlet
|
114
|
-
include RespondWith
|
115
|
-
def do_PATCH(req, res)
|
116
|
-
respond_with(:PATCH, {'body' => req.body, 'content_type' => req.content_type}, res)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
class SetCookieServlet < HTTPServlet::AbstractServlet
|
121
|
-
def do_GET(req, res)
|
122
|
-
res['Set-Cookie'] = "session_id=foo123"
|
123
|
-
res['Location'] = "http://localhost:9001/test"
|
124
|
-
res.status = 301
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
class RepetitiveHeaderServlet < HTTPServlet::AbstractServlet
|
129
|
-
def do_GET(req, res)
|
130
|
-
# the only way to get webrick to output two headers with the same name is using cookies, so that's what we'll do:
|
131
|
-
res.cookies << Cookie.new('a', '1')
|
132
|
-
res.cookies << Cookie.new('b', '2')
|
133
|
-
res['Content-Type'] = "text/plain"
|
134
|
-
res.body = "Hi."
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
class PictureServlet < HTTPServlet::AbstractServlet
|
139
|
-
def do_GET(req, res)
|
140
|
-
res['Content-Type'] = "image/png"
|
141
|
-
res.body = File.read("./pic.png")
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
class WrongContentLengthServlet < HTTPServlet::AbstractServlet
|
146
|
-
def do_GET(req, res)
|
147
|
-
res.keep_alive = false
|
148
|
-
res.content_length = 1024
|
149
|
-
res.body = "Hello."
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
# Serves a substantial amount of data
|
154
|
-
class LargeServlet < HTTPServlet::AbstractServlet
|
155
|
-
def do_GET(req, res)
|
156
|
-
res.content_length = 15 * 1024 * 1024
|
157
|
-
res.body = Random.new.bytes(15 * 1024 * 1024)
|
158
|
-
end
|
159
|
-
end
|
1
|
+
require 'rack'
|
2
|
+
require 'puma'
|
3
|
+
require 'rack/handler/puma'
|
160
4
|
|
161
5
|
class PatronTestServer
|
6
|
+
APP = Rack::Builder.new { eval(File.read(File.dirname(__FILE__) + '/config.ru')) }
|
162
7
|
|
163
|
-
def self.start(
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
def initialize( log_file = nil, ssl = false, port = 9001 )
|
168
|
-
log_file ||= StringIO.new
|
169
|
-
log = WEBrick::Log.new(log_file)
|
8
|
+
def self.start(ssl = false, port = 9001 )
|
9
|
+
@ssl = ssl
|
10
|
+
keypath = File.expand_path(File.read(File.dirname(__FILE__) + '/../certs/privkey.pem'))
|
11
|
+
certpath = File.expand_path(File.read(File.dirname(__FILE__) + '/../certs/cacert.pem'))
|
170
12
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
[ log, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
|
176
|
-
[ log, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
|
177
|
-
]
|
178
|
-
}
|
179
|
-
|
180
|
-
if ssl
|
181
|
-
options[:SSLEnable] = true
|
182
|
-
options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.open("spec/certs/cacert.pem").read)
|
183
|
-
options[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.open("spec/certs/privkey.pem").read)
|
184
|
-
options[:SSLCertName] = [ ["CN", WEBrick::Utils::getservername ] ]
|
13
|
+
host = if ssl
|
14
|
+
'ssl://127.0.0.1:%d?key=%s&cert=%s' % [port, keypath, certpath]
|
15
|
+
else
|
16
|
+
'tcp://127.0.0.1:%d' % port
|
185
17
|
end
|
186
|
-
|
187
|
-
@server = WEBrick::HTTPServer.new(options)
|
188
|
-
|
189
|
-
@server.mount("/test", TestServlet)
|
190
|
-
@server.mount("/testpost", TestPostBodyServlet)
|
191
|
-
@server.mount("/testpatch", TestPatchBodyServlet)
|
192
|
-
@server.mount("/timeout", TimeoutServlet)
|
193
|
-
@server.mount("/slow", SlowServlet)
|
194
|
-
@server.mount("/redirect", RedirectServlet)
|
195
|
-
@server.mount("/evil-redirect", EvilRedirectServlet)
|
196
|
-
@server.mount("/picture", PictureServlet)
|
197
|
-
@server.mount("/very-large", LargeServlet)
|
198
|
-
@server.mount("/setcookie", SetCookieServlet)
|
199
|
-
@server.mount("/repetitiveheader", RepetitiveHeaderServlet)
|
200
|
-
@server.mount("/wrongcontentlength", WrongContentLengthServlet)
|
201
|
-
@server.mount("/gzip-compressed", GzipServlet)
|
202
|
-
end
|
203
|
-
|
204
|
-
def start
|
205
|
-
trap('INT') {
|
206
|
-
begin
|
207
|
-
@server.shutdown unless @server.nil?
|
208
|
-
rescue Object => e
|
209
|
-
$stderr.puts "Error #{__FILE__}:#{__LINE__}\n#{e.message}"
|
210
|
-
end
|
211
|
-
}
|
212
|
-
|
213
|
-
@thread = Thread.new { @server.start }
|
214
|
-
Thread.pass
|
215
|
-
self
|
216
|
-
end
|
217
|
-
|
218
|
-
def join
|
219
|
-
if defined? @thread and @thread
|
220
|
-
@thread.join
|
221
|
-
end
|
222
|
-
self
|
18
|
+
Rack::Handler::Puma.run(APP, {:Port => port.to_i, :Verbose => true, :Host => '0.0.0.0'})
|
223
19
|
end
|
224
20
|
end
|
225
|
-
|
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.12.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: 2018-
|
11
|
+
date: 2018-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -80,6 +80,34 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.8'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rack
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: puma
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.11'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.11'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: rake-compiler
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,9 +170,11 @@ files:
|
|
142
170
|
- spec/sample_response_headers/headers_wetransfer_with_proxy_status.txt
|
143
171
|
- spec/sample_response_headers/headers_wetransfer_with_redirect.txt
|
144
172
|
- spec/sample_response_headers/headers_with_set_cookie.txt
|
173
|
+
- spec/sample_response_headers/webmock_headers_without_trailing_crlf.txt
|
145
174
|
- spec/session_spec.rb
|
146
175
|
- spec/session_ssl_spec.rb
|
147
176
|
- spec/spec_helper.rb
|
177
|
+
- spec/support/config.ru
|
148
178
|
- spec/support/test_server.rb
|
149
179
|
- spec/util_spec.rb
|
150
180
|
homepage: https://github.com/toland/patron
|