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