patron 0.4.18 → 0.4.20
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/Gemfile.lock +2 -2
- data/{README.txt → README.md} +6 -6
- data/ext/patron/session_ext.c +56 -7
- data/lib/patron.rb +1 -0
- data/lib/patron/request.rb +44 -16
- data/lib/patron/response.rb +13 -5
- data/lib/patron/session.rb +46 -31
- data/lib/patron/version.rb +1 -1
- data/spec/certs/cacert.pem +36 -0
- data/spec/certs/privkey.pem +51 -0
- data/spec/request_spec.rb +18 -0
- data/spec/response_spec.rb +5 -1
- data/spec/session_spec.rb +28 -0
- data/spec/session_ssl_spec.rb +284 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/test_server.rb +20 -6
- metadata +35 -12
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
gemspec
|
data/Gemfile.lock
CHANGED
data/{README.txt → README.md}
RENAMED
@@ -1,13 +1,13 @@
|
|
1
|
-
|
1
|
+
# Ruby HTTP Client
|
2
2
|
|
3
|
-
|
3
|
+
## SYNOPSIS
|
4
4
|
|
5
5
|
Patron is a Ruby HTTP client library based on libcurl. It does not try to expose
|
6
6
|
the full "power" (read complexity) of libcurl but instead tries to provide a
|
7
7
|
sane API while taking advantage of libcurl under the hood.
|
8
8
|
|
9
9
|
|
10
|
-
|
10
|
+
## USAGE
|
11
11
|
|
12
12
|
Usage is very simple. First, you instantiate a Session object. You can set a few
|
13
13
|
default options on the Session instance that will be used by all subsequent
|
@@ -41,7 +41,7 @@ You can ship custom headers with a single request:
|
|
41
41
|
That is pretty much all there is to it.
|
42
42
|
|
43
43
|
|
44
|
-
|
44
|
+
## REQUIREMENTS
|
45
45
|
|
46
46
|
You need a recent version of libcurl in order to install this gem. On MacOS X
|
47
47
|
the provided libcurl is sufficient. You will have to install the libcurl
|
@@ -49,9 +49,9 @@ development packages on Debian or Ubuntu. Other Linux systems are probably
|
|
49
49
|
similar. Windows users are on your own. Good luck with that.
|
50
50
|
|
51
51
|
|
52
|
-
|
52
|
+
## INSTALL
|
53
53
|
|
54
|
-
|
54
|
+
sudo gem install patron
|
55
55
|
|
56
56
|
|
57
57
|
Copyright (c) 2008 The Hive
|
data/ext/patron/session_ext.c
CHANGED
@@ -260,10 +260,25 @@ static VALUE session_unescape(VALUE self, VALUE value) {
|
|
260
260
|
/* Callback used to iterate over the HTTP headers and store them in an slist. */
|
261
261
|
static int each_http_header(VALUE header_key, VALUE header_value, VALUE self) {
|
262
262
|
struct curl_state *state = get_curl_state(self);
|
263
|
+
CURL* curl = state->handle;
|
264
|
+
|
263
265
|
VALUE name = rb_obj_as_string(header_key);
|
264
266
|
VALUE value = rb_obj_as_string(header_value);
|
265
267
|
VALUE header_str = Qnil;
|
266
268
|
|
269
|
+
if (rb_str_cmp(name, rb_str_new2("Accept-Encoding")) == 0) {
|
270
|
+
if (rb_funcall(value, rb_intern("include?"), 1, rb_str_new2("gzip"))) {
|
271
|
+
#ifdef CURLOPT_ACCEPT_ENCODING
|
272
|
+
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip");
|
273
|
+
#elif defined CURLOPT_ENCODING
|
274
|
+
curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip");
|
275
|
+
#else
|
276
|
+
rb_raise(rb_eArgError,
|
277
|
+
"The libcurl version installed doesn't support 'gzip'.");
|
278
|
+
#endif
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
267
282
|
header_str = rb_str_plus(name, rb_str_new2(": "));
|
268
283
|
header_str = rb_str_plus(header_str, value);
|
269
284
|
|
@@ -325,7 +340,10 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
325
340
|
VALUE credentials = Qnil;
|
326
341
|
VALUE ignore_content_length = Qnil;
|
327
342
|
VALUE insecure = Qnil;
|
343
|
+
VALUE cacert = Qnil;
|
344
|
+
VALUE ssl_version = Qnil;
|
328
345
|
VALUE buffer_size = Qnil;
|
346
|
+
VALUE action_name = rb_iv_get(request, "@action");
|
329
347
|
|
330
348
|
headers = rb_iv_get(request, "@headers");
|
331
349
|
if (!NIL_P(headers)) {
|
@@ -336,7 +354,8 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
336
354
|
rb_hash_foreach(headers, each_http_header, self);
|
337
355
|
}
|
338
356
|
|
339
|
-
action = SYM2ID(
|
357
|
+
action = SYM2ID(action_name);
|
358
|
+
|
340
359
|
if (action == rb_intern("get")) {
|
341
360
|
VALUE data = rb_iv_get(request, "@upload_data");
|
342
361
|
VALUE download_file = rb_iv_get(request, "@file_name");
|
@@ -352,7 +371,7 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
352
371
|
curl_easy_setopt(curl, CURLOPT_INFILESIZE, len);
|
353
372
|
}
|
354
373
|
if (!NIL_P(download_file)) {
|
355
|
-
state->download_file = open_file(download_file, "
|
374
|
+
state->download_file = open_file(download_file, "wb");
|
356
375
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, state->download_file);
|
357
376
|
} else {
|
358
377
|
state->download_file = NULL;
|
@@ -386,7 +405,7 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
386
405
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
|
387
406
|
}
|
388
407
|
|
389
|
-
state->upload_file = open_file(filename, "
|
408
|
+
state->upload_file = open_file(filename, "rb");
|
390
409
|
curl_easy_setopt(curl, CURLOPT_READDATA, state->upload_file);
|
391
410
|
} else if (!NIL_P(multipart)) {
|
392
411
|
if (action == rb_intern("post")) {
|
@@ -405,6 +424,20 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
405
424
|
} else {
|
406
425
|
rb_raise(rb_eArgError, "Must provide either data or a filename when doing a PUT or POST");
|
407
426
|
}
|
427
|
+
|
428
|
+
// support for data passed with a DELETE request (e.g.: used by elasticsearch)
|
429
|
+
} else if (action == rb_intern("delete")) {
|
430
|
+
VALUE data = rb_iv_get(request, "@upload_data");
|
431
|
+
|
432
|
+
if (!NIL_P(data)) {
|
433
|
+
long len = RSTRING_LEN(data);
|
434
|
+
state->upload_buf = StringValuePtr(data);
|
435
|
+
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
436
|
+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, state->upload_buf);
|
437
|
+
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
|
438
|
+
}
|
439
|
+
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
|
440
|
+
|
408
441
|
} else if (action == rb_intern("head")) {
|
409
442
|
curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
|
410
443
|
} else {
|
@@ -440,7 +473,7 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
440
473
|
|
441
474
|
proxy = rb_iv_get(request, "@proxy");
|
442
475
|
if (!NIL_P(proxy)) {
|
443
|
-
|
476
|
+
curl_easy_setopt(curl, CURLOPT_PROXY, StringValuePtr(proxy));
|
444
477
|
}
|
445
478
|
|
446
479
|
proxy_type = rb_iv_get(request, "@proxy_type");
|
@@ -462,7 +495,24 @@ static void set_options_from_request(VALUE self, VALUE request) {
|
|
462
495
|
insecure = rb_iv_get(request, "@insecure");
|
463
496
|
if(!NIL_P(insecure)) {
|
464
497
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
465
|
-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST,
|
498
|
+
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
499
|
+
}
|
500
|
+
|
501
|
+
ssl_version = rb_iv_get(request, "@ssl_version");
|
502
|
+
if(!NIL_P(ssl_version)) {
|
503
|
+
char* version = StringValuePtr(ssl_version);
|
504
|
+
if(strcmp(version, "SSLv2") == 0) {
|
505
|
+
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv2);
|
506
|
+
} else if(strcmp(version, "SSLv3") == 0) {
|
507
|
+
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_SSLv3);
|
508
|
+
} else if(strcmp(version, "TLSv1") == 0) {
|
509
|
+
curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
|
510
|
+
}
|
511
|
+
}
|
512
|
+
|
513
|
+
cacert = rb_iv_get(request, "@cacert");
|
514
|
+
if(!NIL_P(cacert)) {
|
515
|
+
curl_easy_setopt(curl, CURLOPT_CAINFO, StringValuePtr(cacert));
|
466
516
|
}
|
467
517
|
|
468
518
|
buffer_size = rb_iv_get(request, "@buffer_size");
|
@@ -672,7 +722,7 @@ static VALUE set_debug_file(VALUE self, VALUE file) {
|
|
672
722
|
session_close_debug_file(state);
|
673
723
|
|
674
724
|
if(file_path != NULL && strlen(file_path) != 0) {
|
675
|
-
state->debug_file = open_file(file, "
|
725
|
+
state->debug_file = open_file(file, "wb");
|
676
726
|
} else {
|
677
727
|
state->debug_file = stderr;
|
678
728
|
}
|
@@ -730,4 +780,3 @@ void Init_session_ext() {
|
|
730
780
|
rb_define_const(mProxyType, "SOCKS4A", INT2FIX(CURLPROXY_SOCKS4A));
|
731
781
|
rb_define_const(mProxyType, "SOCKS5_HOSTNAME", INT2FIX(CURLPROXY_SOCKS5_HOSTNAME));
|
732
782
|
}
|
733
|
-
|
data/lib/patron.rb
CHANGED
@@ -28,6 +28,7 @@ cwd = Pathname(__FILE__).dirname
|
|
28
28
|
$:.unshift(cwd.to_s) unless $:.include?(cwd.to_s) || $:.include?(cwd.expand_path.to_s)
|
29
29
|
|
30
30
|
require 'patron/session'
|
31
|
+
require 'patron/version'
|
31
32
|
|
32
33
|
module Patron #:nodoc:
|
33
34
|
# Returns the version number of the Patron library as a string
|
data/lib/patron/request.rb
CHANGED
@@ -32,7 +32,7 @@ module Patron
|
|
32
32
|
# used in every request.
|
33
33
|
class Request
|
34
34
|
|
35
|
-
VALID_ACTIONS = [
|
35
|
+
VALID_ACTIONS = %w[GET PUT POST DELETE HEAD COPY]
|
36
36
|
|
37
37
|
def initialize
|
38
38
|
@action = :get
|
@@ -42,9 +42,20 @@ module Patron
|
|
42
42
|
@max_redirects = -1
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
READER_VARS = [
|
46
|
+
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure,
|
47
|
+
:ignore_content_length, :multipart, :action, :timeout, :connect_timeout,
|
48
|
+
:max_redirects, :headers, :auth_type, :upload_data, :buffer_size, :cacert,
|
49
|
+
:ssl_version
|
50
|
+
]
|
51
|
+
|
52
|
+
WRITER_VARS = [
|
53
|
+
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure,
|
54
|
+
:ignore_content_length, :multipart, :cacert, :ssl_version
|
55
|
+
]
|
56
|
+
|
57
|
+
attr_reader *READER_VARS
|
58
|
+
attr_writer *WRITER_VARS
|
48
59
|
|
49
60
|
# Set the type of authentication to use for this request.
|
50
61
|
#
|
@@ -71,22 +82,17 @@ module Patron
|
|
71
82
|
def upload_data=(data)
|
72
83
|
@upload_data = case data
|
73
84
|
when Hash
|
74
|
-
self.multipart ? data : Util.build_query_string_from_hash(data,
|
85
|
+
self.multipart ? data : Util.build_query_string_from_hash(data, action == :post)
|
75
86
|
else
|
76
87
|
data
|
77
88
|
end
|
78
89
|
end
|
79
90
|
|
80
|
-
def
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
def action=(new_action)
|
85
|
-
if !VALID_ACTIONS.include?(new_action)
|
91
|
+
def action=(action)
|
92
|
+
if !VALID_ACTIONS.include?(action.to_s.upcase)
|
86
93
|
raise ArgumentError, "Action must be one of #{VALID_ACTIONS.join(', ')}"
|
87
94
|
end
|
88
|
-
|
89
|
-
@action = new_action
|
95
|
+
@action = action.downcase.to_sym
|
90
96
|
end
|
91
97
|
|
92
98
|
def timeout=(new_timeout)
|
@@ -129,13 +135,35 @@ module Patron
|
|
129
135
|
@buffer_size = buffer_size != nil ? buffer_size.to_i : nil
|
130
136
|
end
|
131
137
|
|
138
|
+
def credentials
|
139
|
+
return nil if username.nil? || password.nil?
|
140
|
+
"#{username}:#{password}"
|
141
|
+
end
|
142
|
+
|
132
143
|
def action_name
|
133
144
|
@action.to_s.upcase
|
134
145
|
end
|
135
146
|
|
136
|
-
def
|
137
|
-
return
|
138
|
-
|
147
|
+
def eql?(request)
|
148
|
+
return false unless Request === request
|
149
|
+
|
150
|
+
READER_VARS.inject(true) do |memo, name|
|
151
|
+
memo && (self.send(name) == request.send(name))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
alias_method :==, :eql?
|
156
|
+
|
157
|
+
def marshal_dump
|
158
|
+
[ @url, @username, @password, @file_name, @proxy, @proxy_type, @insecure,
|
159
|
+
@ignore_content_length, @multipart, @action, @timeout, @connect_timeout,
|
160
|
+
@max_redirects, @headers, @auth_type, @upload_data, @buffer_size, @cacert ]
|
161
|
+
end
|
162
|
+
|
163
|
+
def marshal_load(data)
|
164
|
+
@url, @username, @password, @file_name, @proxy, @proxy_type, @insecure,
|
165
|
+
@ignore_content_length, @multipart, @action, @timeout, @connect_timeout,
|
166
|
+
@max_redirects, @headers, @auth_type, @upload_data, @buffer_size, @cacert = data
|
139
167
|
end
|
140
168
|
|
141
169
|
end
|
data/lib/patron/response.rb
CHANGED
@@ -35,13 +35,13 @@ module Patron
|
|
35
35
|
@status = status
|
36
36
|
@redirect_count = redirect_count
|
37
37
|
@body = body
|
38
|
-
|
38
|
+
|
39
39
|
@charset = determine_charset(header_data, body) || default_charset
|
40
|
-
|
40
|
+
|
41
41
|
[url, header_data].each do |attr|
|
42
42
|
convert_to_default_encoding!(attr)
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
parse_headers(header_data)
|
46
46
|
if @headers["Content-Type"] && @headers["Content-Type"][0, 5] == "text/"
|
47
47
|
convert_to_default_encoding!(@body)
|
@@ -55,14 +55,22 @@ module Patron
|
|
55
55
|
"#<Patron::Response @status_line='#{@status_line}'>"
|
56
56
|
end
|
57
57
|
|
58
|
+
def marshal_dump
|
59
|
+
[@url, @status, @status_line, @redirect_count, @body, @headers, @charset]
|
60
|
+
end
|
61
|
+
|
62
|
+
def marshal_load(data)
|
63
|
+
@url, @status, @status_line, @redirect_count, @body, @headers, @charset = data
|
64
|
+
end
|
65
|
+
|
58
66
|
private
|
59
67
|
|
60
68
|
def determine_charset(header_data, body)
|
61
69
|
header_data.match(charset_regex) || (body && body.match(charset_regex))
|
62
|
-
|
70
|
+
|
63
71
|
$1
|
64
72
|
end
|
65
|
-
|
73
|
+
|
66
74
|
def charset_regex
|
67
75
|
/(?:charset|encoding)="?([a-z0-9-]+)"?/i
|
68
76
|
end
|
data/lib/patron/session.rb
CHANGED
@@ -59,7 +59,7 @@ module Patron
|
|
59
59
|
attr_accessor :proxy_type
|
60
60
|
|
61
61
|
# Standard set of headers that are used in all requests.
|
62
|
-
|
62
|
+
attr_accessor :headers
|
63
63
|
|
64
64
|
# Set the authentication type for the request.
|
65
65
|
# @see Patron::Request#auth_type
|
@@ -68,6 +68,12 @@ module Patron
|
|
68
68
|
# Does this session stricly verify SSL certificates?
|
69
69
|
attr_accessor :insecure
|
70
70
|
|
71
|
+
# Specifies the ssl version
|
72
|
+
attr_accessor :ssl_version
|
73
|
+
|
74
|
+
# What cacert file should this session use to verify SSL certificates?
|
75
|
+
attr_accessor :cacert
|
76
|
+
|
71
77
|
# Does this session ignore Content-Size headers?
|
72
78
|
attr_accessor :ignore_content_length
|
73
79
|
|
@@ -138,6 +144,8 @@ module Patron
|
|
138
144
|
end
|
139
145
|
|
140
146
|
# As #get but sends an HTTP DELETE request.
|
147
|
+
# Notice: this method doesn't accept any +data+ argument: if you need to send data with
|
148
|
+
# a delete request, please, use the #request method.
|
141
149
|
def delete(url, headers = {})
|
142
150
|
request(:delete, url, headers)
|
143
151
|
end
|
@@ -189,39 +197,46 @@ module Patron
|
|
189
197
|
|
190
198
|
# Send an HTTP request to the specified +url+.
|
191
199
|
def request(action, url, headers, options = {})
|
200
|
+
req = build_request(action, url, headers, options)
|
201
|
+
handle_request(req)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Build a request object that can be used in +handle_request+
|
205
|
+
def build_request(action, url, headers, options = {})
|
192
206
|
# If the Expect header isn't set uploads are really slow
|
193
207
|
headers['Expect'] ||= ''
|
194
208
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
209
|
+
Request.new.tap do |req|
|
210
|
+
req.action = action
|
211
|
+
req.headers = self.headers.merge headers
|
212
|
+
req.timeout = options.fetch :timeout, self.timeout
|
213
|
+
req.connect_timeout = options.fetch :connect_timeout, self.connect_timeout
|
214
|
+
req.max_redirects = options.fetch :max_redirects, self.max_redirects
|
215
|
+
req.username = options.fetch :username, self.username
|
216
|
+
req.password = options.fetch :password, self.password
|
217
|
+
req.proxy = options.fetch :proxy, self.proxy
|
218
|
+
req.proxy_type = options.fetch :proxy_type, self.proxy_type
|
219
|
+
req.auth_type = options.fetch :auth_type, self.auth_type
|
220
|
+
req.insecure = options.fetch :insecure, self.insecure
|
221
|
+
req.ssl_version = options.fetch :ssl_version, self.ssl_version
|
222
|
+
req.cacert = options.fetch :cacert, self.cacert
|
223
|
+
req.ignore_content_length = options.fetch :ignore_content_length, self.ignore_content_length
|
224
|
+
req.buffer_size = options.fetch :buffer_size, self.buffer_size
|
225
|
+
req.multipart = options[:multipart]
|
226
|
+
req.upload_data = options[:data]
|
227
|
+
req.file_name = options[:file]
|
228
|
+
|
229
|
+
base_url = self.base_url.to_s
|
230
|
+
url = url.to_s
|
231
|
+
raise ArgumentError, "Empty URL" if base_url.empty? && url.empty?
|
232
|
+
uri = URI.join(base_url, url)
|
233
|
+
query = uri.query.to_s.split('&')
|
234
|
+
query += options[:query].is_a?(Hash) ? Util.build_query_pairs_from_hash(options[:query]) : options[:query].to_s.split('&')
|
235
|
+
uri.query = query.join('&')
|
236
|
+
uri.query = nil if uri.query.empty?
|
237
|
+
url = uri.to_s
|
238
|
+
req.url = url
|
239
|
+
end
|
224
240
|
end
|
225
|
-
|
226
241
|
end
|
227
242
|
end
|
data/lib/patron/version.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
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
|
35
|
+
djS6lZ9vTEbRM+GhU2PMKfvJbiQ/Y6Q=
|
36
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,51 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
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
|
51
|
+
-----END RSA PRIVATE KEY-----
|
data/spec/request_spec.rb
CHANGED
@@ -83,4 +83,22 @@ describe Patron::Request do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
end
|
86
|
+
|
87
|
+
describe :eql? do
|
88
|
+
|
89
|
+
it "should return true when two requests are equal" do
|
90
|
+
@request.should eql(Patron::Request.new)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should return false when two requests are not equal" do
|
94
|
+
req = Patron::Request.new
|
95
|
+
req.action = :post
|
96
|
+
@request.should_not eql(req)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should be able to serialize and deserialize itself" do
|
102
|
+
Marshal.load(Marshal.dump(@request)).should eql(@request)
|
103
|
+
end
|
86
104
|
end
|
data/spec/response_spec.rb
CHANGED
@@ -48,11 +48,15 @@ describe Patron::Response do
|
|
48
48
|
response.headers['Content-Type'].should == 'image/png'
|
49
49
|
response.body.encoding.should == Encoding::ASCII_8BIT
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
it "should not allow a default charset to be nil" do
|
53
53
|
Encoding.stub(:default_internal).and_return("UTF-8")
|
54
54
|
expect {
|
55
55
|
Patron::Response.new("url", "status", 0, "", "", nil)
|
56
56
|
}.to_not raise_error
|
57
57
|
end
|
58
|
+
|
59
|
+
it "should be able to serialize and deserialize itself" do
|
60
|
+
Marshal.load(Marshal.dump(@request)).should eql(@request)
|
61
|
+
end
|
58
62
|
end
|
data/spec/session_spec.rb
CHANGED
@@ -56,6 +56,11 @@ describe Patron::Session do
|
|
56
56
|
body.request_method.should == "GET"
|
57
57
|
end
|
58
58
|
|
59
|
+
it 'should ignore #base_url when a full URL is provided' do
|
60
|
+
@session.base_url = "http://example.com:123"
|
61
|
+
lambda { @session.get("http://localhost:9001/test") }.should_not raise_error(URI::InvalidURIError)
|
62
|
+
end
|
63
|
+
|
59
64
|
it "should download content with :get and a file path" do
|
60
65
|
tmpfile = "/tmp/patron_test.yaml"
|
61
66
|
response = @session.get_file "/test", tmpfile
|
@@ -65,12 +70,27 @@ describe Patron::Session do
|
|
65
70
|
FileUtils.rm tmpfile
|
66
71
|
end
|
67
72
|
|
73
|
+
it "should download correctly(md5 ok) with get_file" do
|
74
|
+
tmpfile = "/tmp/picture"
|
75
|
+
response = @session.get_file "/picture", tmpfile
|
76
|
+
response.body.should be_nil
|
77
|
+
File.size(File.join(File.dirname(__FILE__),"../pic.png")).should == File.size(tmpfile)
|
78
|
+
FileUtils.rm tmpfile
|
79
|
+
end
|
80
|
+
|
68
81
|
it "should include custom headers in a request" do
|
69
82
|
response = @session.get("/test", {"User-Agent" => "PatronTest"})
|
70
83
|
body = YAML::load(response.body)
|
71
84
|
body.header["user-agent"].should == ["PatronTest"]
|
72
85
|
end
|
73
86
|
|
87
|
+
it "should include default headers in a request, if they were defined" do
|
88
|
+
@session.headers = {"User-Agent" => "PatronTest"}
|
89
|
+
response = @session.get("/test")
|
90
|
+
body = YAML::load(response.body)
|
91
|
+
body.header["user-agent"].should == ["PatronTest"]
|
92
|
+
end
|
93
|
+
|
74
94
|
it "should merge custom headers with session headers" do
|
75
95
|
@session.headers["X-Test"] = "Testing"
|
76
96
|
response = @session.get("/test", {"User-Agent" => "PatronTest"})
|
@@ -146,6 +166,14 @@ describe Patron::Session do
|
|
146
166
|
body.header['content-length'].should == [data.size.to_s]
|
147
167
|
end
|
148
168
|
|
169
|
+
it "should upload data with :delete" do
|
170
|
+
data = "upload data"
|
171
|
+
response = @session.request(:delete, "/test", {}, :data => data)
|
172
|
+
body = YAML::load(response.body)
|
173
|
+
body.request_method.should == "DELETE"
|
174
|
+
body.header['content-length'].should == [data.size.to_s]
|
175
|
+
end
|
176
|
+
|
149
177
|
it "should raise when no data is provided to :put" do
|
150
178
|
lambda { @session.put("/test", nil) }.should raise_error(ArgumentError)
|
151
179
|
end
|
@@ -0,0 +1,284 @@
|
|
1
|
+
## -------------------------------------------------------------------
|
2
|
+
##
|
3
|
+
## Copyright (c) 2008 The Hive http://www.thehive.com/
|
4
|
+
##
|
5
|
+
## Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
## of this software and associated documentation files (the "Software"), to deal
|
7
|
+
## in the Software without restriction, including without limitation the rights
|
8
|
+
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
## copies of the Software, and to permit persons to whom the Software is
|
10
|
+
## furnished to do so, subject to the following conditions:
|
11
|
+
##
|
12
|
+
## The above copyright notice and this permission notice shall be included in
|
13
|
+
## all copies or substantial portions of the Software.
|
14
|
+
##
|
15
|
+
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
## THE SOFTWARE.
|
22
|
+
##
|
23
|
+
## -------------------------------------------------------------------
|
24
|
+
require File.expand_path("./spec") + '/spec_helper.rb'
|
25
|
+
require 'webrick'
|
26
|
+
require 'yaml'
|
27
|
+
require 'base64'
|
28
|
+
require 'fileutils'
|
29
|
+
|
30
|
+
describe Patron::Session do
|
31
|
+
|
32
|
+
before(:each) do
|
33
|
+
@session = Patron::Session.new
|
34
|
+
@session.base_url = "https://localhost:9043"
|
35
|
+
@session.insecure = true
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should retrieve a url with :get" do
|
39
|
+
response = @session.get("/test")
|
40
|
+
body = YAML::load(response.body)
|
41
|
+
body.request_method.should == "GET"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should download content with :get and a file path" do
|
45
|
+
tmpfile = "/tmp/patron_test.yaml"
|
46
|
+
response = @session.get_file "/test", tmpfile
|
47
|
+
response.body.should be_nil
|
48
|
+
body = YAML::load_file(tmpfile)
|
49
|
+
body.request_method.should == "GET"
|
50
|
+
FileUtils.rm tmpfile
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should download correctly(md5 ok) with get_file" do
|
54
|
+
tmpfile = "/tmp/picture"
|
55
|
+
response = @session.get_file "/picture", tmpfile
|
56
|
+
response.body.should be_nil
|
57
|
+
File.size(File.join(File.dirname(__FILE__),"../pic.png")).should == File.size(tmpfile)
|
58
|
+
FileUtils.rm tmpfile
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should include custom headers in a request" do
|
62
|
+
response = @session.get("/test", {"User-Agent" => "PatronTest"})
|
63
|
+
body = YAML::load(response.body)
|
64
|
+
body.header["user-agent"].should == ["PatronTest"]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should merge custom headers with session headers" do
|
68
|
+
@session.headers["X-Test"] = "Testing"
|
69
|
+
response = @session.get("/test", {"User-Agent" => "PatronTest"})
|
70
|
+
body = YAML::load(response.body)
|
71
|
+
body.header["user-agent"].should == ["PatronTest"]
|
72
|
+
body.header["x-test"].should == ["Testing"]
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should raise an exception on timeout" do
|
76
|
+
@session.timeout = 1
|
77
|
+
lambda {@session.get("/timeout")}.should raise_error(Patron::TimeoutError)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should follow redirects by default" do
|
81
|
+
@session.max_redirects = 1
|
82
|
+
response = @session.get("/redirect")
|
83
|
+
body = YAML::load(response.body)
|
84
|
+
response.status.should == 200
|
85
|
+
body.path.should == "/test"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should include redirect count in response" do
|
89
|
+
@session.max_redirects = 1
|
90
|
+
response = @session.get("/redirect")
|
91
|
+
response.redirect_count.should == 1
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not follow redirects when configured to do so" do
|
95
|
+
@session.max_redirects = 0
|
96
|
+
response = @session.get("/redirect")
|
97
|
+
response.status.should == 301
|
98
|
+
response.body.should be_empty
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should retrieve URL metadata with :head" do
|
102
|
+
response = @session.head("/test")
|
103
|
+
response.status.should == 200
|
104
|
+
response.body.should be_empty
|
105
|
+
response.headers.should_not be_empty
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should send a delete request with :delete" do
|
109
|
+
response = @session.delete("/test")
|
110
|
+
body = YAML::load(response.body)
|
111
|
+
body.request_method.should == "DELETE"
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should send a COPY request with :copy" do
|
115
|
+
response = @session.copy("/test", "/test2")
|
116
|
+
body = YAML::load(response.body)
|
117
|
+
body.request_method.should == "COPY"
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should include a Destination header in COPY requests" do
|
121
|
+
response = @session.copy("/test", "/test2")
|
122
|
+
body = YAML::load(response.body)
|
123
|
+
body.header['destination'].first.should == "/test2"
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should upload data with :get" do
|
127
|
+
data = "upload data"
|
128
|
+
response = @session.request(:get, "/test", {}, :data => data)
|
129
|
+
body = YAML::load(response.body)
|
130
|
+
body.request_method.should == "GET"
|
131
|
+
body.header['content-length'].should == [data.size.to_s]
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should upload data with :put" do
|
135
|
+
data = "upload data"
|
136
|
+
response = @session.put("/test", data)
|
137
|
+
body = YAML::load(response.body)
|
138
|
+
body.request_method.should == "PUT"
|
139
|
+
body.header['content-length'].should == [data.size.to_s]
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should raise when no data is provided to :put" do
|
143
|
+
lambda { @session.put("/test", nil) }.should raise_error(ArgumentError)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should upload a file with :put" do
|
147
|
+
response = @session.put_file("/test", "LICENSE")
|
148
|
+
body = YAML::load(response.body)
|
149
|
+
body.request_method.should == "PUT"
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should raise when no file is provided to :put" do
|
153
|
+
lambda { @session.put_file("/test", nil) }.should raise_error(ArgumentError)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should use chunked encoding when uploading a file with :put" do
|
157
|
+
response = @session.put_file("/test", "LICENSE")
|
158
|
+
body = YAML::load(response.body)
|
159
|
+
body.header['transfer-encoding'].first.should == "chunked"
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should upload data with :post" do
|
163
|
+
data = "upload data"
|
164
|
+
response = @session.post("/test", data)
|
165
|
+
body = YAML::load(response.body)
|
166
|
+
body.request_method.should == "POST"
|
167
|
+
body.header['content-length'].should == [data.size.to_s]
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should post a hash of arguments as a urlencoded form" do
|
171
|
+
data = {:foo => 123, 'baz' => '++hello world++'}
|
172
|
+
response = @session.post("/testpost", data)
|
173
|
+
body = YAML::load(response.body)
|
174
|
+
body['content_type'].should == "application/x-www-form-urlencoded"
|
175
|
+
body['body'].should match(/baz=%2B%2Bhello%20world%2B%2B/)
|
176
|
+
body['body'].should match(/foo=123/)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should raise when no data is provided to :post" do
|
180
|
+
lambda { @session.post("/test", nil) }.should raise_error(ArgumentError)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should upload a file with :post" do
|
184
|
+
response = @session.post_file("/test", "LICENSE")
|
185
|
+
body = YAML::load(response.body)
|
186
|
+
body.request_method.should == "POST"
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should upload a multipart with :post" do
|
190
|
+
response = @session.post_multipart("/test", { :test_data => "123" }, { :test_file => "LICENSE" } )
|
191
|
+
body = YAML::load(response.body)
|
192
|
+
body.request_method.should == "POST"
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should raise when no file is provided to :post" do
|
196
|
+
lambda { @session.post_file("/test", nil) }.should raise_error(ArgumentError)
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should use chunked encoding when uploading a file with :post" do
|
200
|
+
response = @session.post_file("/test", "LICENSE")
|
201
|
+
body = YAML::load(response.body)
|
202
|
+
body.header['transfer-encoding'].first.should == "chunked"
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should handle cookies if set" do
|
206
|
+
@session.handle_cookies
|
207
|
+
response = @session.get("/setcookie").body
|
208
|
+
YAML::load(response).header['cookie'].first.should == "session_id=foo123"
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should not handle cookies by default" do
|
212
|
+
response = @session.get("/setcookie").body
|
213
|
+
YAML::load(response).header.should_not include('cookie')
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should ignore a wrong Content-Length when asked to" do
|
217
|
+
lambda {
|
218
|
+
@session.ignore_content_length = true
|
219
|
+
@session.get("/wrongcontentlength")
|
220
|
+
}.should_not raise_error
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should fail by default with a Content-Length too high" do
|
224
|
+
lambda {
|
225
|
+
@session.ignore_content_length = nil
|
226
|
+
@session.get("/wrongcontentlength")
|
227
|
+
}.should raise_error(Patron::PartialFileError)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should raise exception if cookie store is not writable or readable" do
|
231
|
+
lambda { @session.handle_cookies("/trash/clash/foo") }.should raise_error(ArgumentError)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should work with multiple threads" do
|
235
|
+
threads = []
|
236
|
+
3.times do
|
237
|
+
threads << Thread.new do
|
238
|
+
session = Patron::Session.new
|
239
|
+
session.base_url = "https://localhost:9043"
|
240
|
+
session.insecure = true
|
241
|
+
session.post_file("/test", "LICENSE")
|
242
|
+
end
|
243
|
+
end
|
244
|
+
threads.each {|t| t.join }
|
245
|
+
end
|
246
|
+
|
247
|
+
xit "should fail when insecure mode is off" do
|
248
|
+
# This spec fails, but I suspect that it is a setup problem.
|
249
|
+
lambda {
|
250
|
+
@session.insecure = nil
|
251
|
+
response = @session.get("/test")
|
252
|
+
}.should raise_error(Patron::Error)
|
253
|
+
end
|
254
|
+
|
255
|
+
it "should work when insecure mode is off but certificate is supplied" do
|
256
|
+
@session.insecure = nil
|
257
|
+
@session.cacert = 'spec/certs/cacert.pem'
|
258
|
+
response = @session.get("/test")
|
259
|
+
body = YAML::load(response.body)
|
260
|
+
body.request_method.should == "GET"
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should work with different SSL versions" do
|
264
|
+
['SSLv3', 'TLSv1'].each do |version|
|
265
|
+
@session.ssl_version = version
|
266
|
+
response = @session.get("/test")
|
267
|
+
response.status.should == 200
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# ------------------------------------------------------------------------
|
272
|
+
describe 'when debug is enabled' do
|
273
|
+
it 'it should not clobber stderr' do
|
274
|
+
rdev = STDERR.stat.rdev
|
275
|
+
|
276
|
+
@session.enable_debug
|
277
|
+
STDERR.stat.rdev.should be == rdev
|
278
|
+
|
279
|
+
@session.enable_debug
|
280
|
+
STDERR.stat.rdev.should be == rdev
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -29,4 +29,5 @@ require 'patron'
|
|
29
29
|
|
30
30
|
Dir['./spec/support/**/*.rb'].each { |fn| require fn }
|
31
31
|
|
32
|
-
PatronTestServer.start if RUBY_VERSION >= '1.9'
|
32
|
+
PatronTestServer.start(nil, false, 9001) if RUBY_VERSION >= '1.9'
|
33
|
+
PatronTestServer.start(nil, true, 9043) if RUBY_VERSION >= '1.9'
|
data/spec/support/test_server.rb
CHANGED
@@ -24,6 +24,9 @@
|
|
24
24
|
## -------------------------------------------------------------------
|
25
25
|
require 'yaml'
|
26
26
|
require 'webrick'
|
27
|
+
require 'webrick/https'
|
28
|
+
require 'openssl'
|
29
|
+
|
27
30
|
include WEBrick
|
28
31
|
|
29
32
|
# This ugly little hack is necessary to make the specs pass when running
|
@@ -125,22 +128,32 @@ end
|
|
125
128
|
|
126
129
|
class PatronTestServer
|
127
130
|
|
128
|
-
def self.start( log_file = nil )
|
129
|
-
new(log_file).start
|
131
|
+
def self.start( log_file = nil, ssl = false, port = 9001 )
|
132
|
+
new(log_file, ssl, port).start
|
130
133
|
end
|
131
134
|
|
132
|
-
def initialize( log_file = nil )
|
135
|
+
def initialize( log_file = nil, ssl = false, port = 9001 )
|
133
136
|
log_file ||= StringIO.new
|
134
137
|
log = WEBrick::Log.new(log_file)
|
135
138
|
|
136
|
-
|
137
|
-
:Port =>
|
139
|
+
options = {
|
140
|
+
:Port => port,
|
138
141
|
:Logger => log,
|
139
142
|
:AccessLog => [
|
140
143
|
[ log, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
|
141
144
|
[ log, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
|
142
145
|
]
|
143
|
-
|
146
|
+
}
|
147
|
+
|
148
|
+
if ssl
|
149
|
+
options[:SSLEnable] = true
|
150
|
+
options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.open("spec/certs/cacert.pem").read)
|
151
|
+
options[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.open("spec/certs/privkey.pem").read)
|
152
|
+
options[:SSLCertName] = [ ["CN", WEBrick::Utils::getservername ] ]
|
153
|
+
end
|
154
|
+
|
155
|
+
@server = WEBrick::HTTPServer.new(options)
|
156
|
+
|
144
157
|
@server.mount("/test", TestServlet)
|
145
158
|
@server.mount("/testpost", TestPostBodyServlet)
|
146
159
|
@server.mount("/timeout", TimeoutServlet)
|
@@ -149,6 +162,7 @@ class PatronTestServer
|
|
149
162
|
@server.mount("/setcookie", SetCookieServlet)
|
150
163
|
@server.mount("/repetitiveheader", RepetitiveHeaderServlet)
|
151
164
|
@server.mount("/wrongcontentlength", WrongContentLengthServlet)
|
165
|
+
|
152
166
|
end
|
153
167
|
|
154
168
|
def start
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: patron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.20
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 1.0.0
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: rake-compiler
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: 0.7.5
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.7.5
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rspec
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: 2.3.0
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.3.0
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: rcov
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,7 +69,12 @@ dependencies:
|
|
54
69
|
version: 0.9.9
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.9.9
|
58
78
|
description: Ruby HTTP client library based on libcurl
|
59
79
|
email:
|
60
80
|
- phil.toland@gmail.com
|
@@ -69,7 +89,7 @@ files:
|
|
69
89
|
- Gemfile
|
70
90
|
- Gemfile.lock
|
71
91
|
- LICENSE
|
72
|
-
- README.
|
92
|
+
- README.md
|
73
93
|
- Rakefile
|
74
94
|
- ext/patron/.gitignore
|
75
95
|
- ext/patron/extconf.rb
|
@@ -89,10 +109,13 @@ files:
|
|
89
109
|
- pic.png
|
90
110
|
- script/console
|
91
111
|
- script/test_server
|
112
|
+
- spec/certs/cacert.pem
|
113
|
+
- spec/certs/privkey.pem
|
92
114
|
- spec/patron_spec.rb
|
93
115
|
- spec/request_spec.rb
|
94
116
|
- spec/response_spec.rb
|
95
117
|
- spec/session_spec.rb
|
118
|
+
- spec/session_ssl_spec.rb
|
96
119
|
- spec/spec_helper.rb
|
97
120
|
- spec/support/test_server.rb
|
98
121
|
- spec/util_spec.rb
|
@@ -117,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
140
|
version: 1.2.0
|
118
141
|
requirements: []
|
119
142
|
rubyforge_project: patron
|
120
|
-
rubygems_version: 1.8.
|
143
|
+
rubygems_version: 1.8.23
|
121
144
|
signing_key:
|
122
145
|
specification_version: 3
|
123
146
|
summary: Patron HTTP Client
|