patron 0.4.18 → 0.4.20
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.
- 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
|