patron-new 0.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.autotest +15 -0
- data/.gitignore +8 -0
- data/.rspec +0 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +31 -0
- data/LICENSE +19 -0
- data/README.txt +57 -0
- data/Rakefile +69 -0
- data/ext/patron/.gitignore +4 -0
- data/ext/patron/extconf.rb +49 -0
- data/ext/patron/membuffer.c +85 -0
- data/ext/patron/membuffer.h +78 -0
- data/ext/patron/session_ext.c +769 -0
- data/ext/patron/sglib.h +1952 -0
- data/lib/patron.rb +38 -0
- data/lib/patron/error.rb +55 -0
- data/lib/patron/proxy_type.rb +10 -0
- data/lib/patron/request.rb +168 -0
- data/lib/patron/response.rb +107 -0
- data/lib/patron/session.rb +234 -0
- data/lib/patron/util.rb +50 -0
- data/lib/patron/version.rb +3 -0
- data/patron.gemspec +26 -0
- data/pic.png +0 -0
- data/script/console +11 -0
- data/script/test_server +30 -0
- data/spec/certs/cacert.pem +36 -0
- data/spec/certs/privkey.pem +51 -0
- data/spec/patron_spec.rb +38 -0
- data/spec/request_spec.rb +104 -0
- data/spec/response_spec.rb +62 -0
- data/spec/session_spec.rb +338 -0
- data/spec/session_ssl_spec.rb +275 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support/test_server.rb +189 -0
- data/spec/util_spec.rb +92 -0
- metadata +138 -0
data/lib/patron.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
## -------------------------------------------------------------------
|
2
|
+
##
|
3
|
+
## Patron HTTP Client: Bootstrap script
|
4
|
+
## Copyright (c) 2008 The Hive http://www.thehive.com/
|
5
|
+
##
|
6
|
+
## Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
## of this software and associated documentation files (the "Software"), to deal
|
8
|
+
## in the Software without restriction, including without limitation the rights
|
9
|
+
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
## copies of the Software, and to permit persons to whom the Software is
|
11
|
+
## furnished to do so, subject to the following conditions:
|
12
|
+
##
|
13
|
+
## The above copyright notice and this permission notice shall be included in
|
14
|
+
## all copies or substantial portions of the Software.
|
15
|
+
##
|
16
|
+
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
## THE SOFTWARE.
|
23
|
+
##
|
24
|
+
## -------------------------------------------------------------------
|
25
|
+
require 'pathname'
|
26
|
+
|
27
|
+
cwd = Pathname(__FILE__).dirname
|
28
|
+
$:.unshift(cwd.to_s) unless $:.include?(cwd.to_s) || $:.include?(cwd.expand_path.to_s)
|
29
|
+
|
30
|
+
require 'patron/session'
|
31
|
+
require 'patron/version'
|
32
|
+
|
33
|
+
module Patron #:nodoc:
|
34
|
+
# Returns the version number of the Patron library as a string
|
35
|
+
def self.version
|
36
|
+
VERSION
|
37
|
+
end
|
38
|
+
end
|
data/lib/patron/error.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
## -------------------------------------------------------------------
|
2
|
+
##
|
3
|
+
## Patron HTTP Client: Error definitions
|
4
|
+
## Copyright (c) 2008 The Hive http://www.thehive.com/
|
5
|
+
##
|
6
|
+
## Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
## of this software and associated documentation files (the "Software"), to deal
|
8
|
+
## in the Software without restriction, including without limitation the rights
|
9
|
+
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
## copies of the Software, and to permit persons to whom the Software is
|
11
|
+
## furnished to do so, subject to the following conditions:
|
12
|
+
##
|
13
|
+
## The above copyright notice and this permission notice shall be included in
|
14
|
+
## all copies or substantial portions of the Software.
|
15
|
+
##
|
16
|
+
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
## THE SOFTWARE.
|
23
|
+
##
|
24
|
+
## -------------------------------------------------------------------
|
25
|
+
|
26
|
+
module Patron
|
27
|
+
|
28
|
+
# Base class for Patron exceptions.
|
29
|
+
class Error < StandardError; end
|
30
|
+
|
31
|
+
# The URL you passed to Patron used a protocol that it does not support.
|
32
|
+
# This most likely the result of a misspelled protocol string.
|
33
|
+
class UnsupportedProtocol < Error; end
|
34
|
+
|
35
|
+
# The URL was not properly formatted.
|
36
|
+
class URLFormatError < Error; end
|
37
|
+
|
38
|
+
# Could not resolve the remote host name.
|
39
|
+
class HostResolutionError < Error; end
|
40
|
+
|
41
|
+
# Failed to connect to the remote host.
|
42
|
+
class ConnectionFailed < Error; end
|
43
|
+
|
44
|
+
# A file transfer was shorter or larger than expected.
|
45
|
+
# This happens when the server first reports an expected transfer size,
|
46
|
+
# and then delivers data that doesn't match the previously given size.
|
47
|
+
class PartialFileError < Error; end
|
48
|
+
|
49
|
+
# Operation timeout. The specified time-out period was reached.
|
50
|
+
class TimeoutError < Error; end
|
51
|
+
|
52
|
+
# Too many redirects. When following redirects, Patron hit the maximum amount.
|
53
|
+
class TooManyRedirects < Error; end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
## -------------------------------------------------------------------
|
2
|
+
##
|
3
|
+
## Patron HTTP Client: Request class
|
4
|
+
## Copyright (c) 2008 The Hive http://www.thehive.com/
|
5
|
+
##
|
6
|
+
## Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
## of this software and associated documentation files (the "Software"), to deal
|
8
|
+
## in the Software without restriction, including without limitation the rights
|
9
|
+
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
## copies of the Software, and to permit persons to whom the Software is
|
11
|
+
## furnished to do so, subject to the following conditions:
|
12
|
+
##
|
13
|
+
## The above copyright notice and this permission notice shall be included in
|
14
|
+
## all copies or substantial portions of the Software.
|
15
|
+
##
|
16
|
+
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
## THE SOFTWARE.
|
23
|
+
##
|
24
|
+
## -------------------------------------------------------------------
|
25
|
+
|
26
|
+
require 'patron/util'
|
27
|
+
|
28
|
+
module Patron
|
29
|
+
|
30
|
+
# Represents the information necessary for an HTTP request.
|
31
|
+
# This is basically a data object with validation. Not all fields will be
|
32
|
+
# used in every request.
|
33
|
+
class Request
|
34
|
+
|
35
|
+
VALID_ACTIONS = %w[GET PUT POST DELETE HEAD COPY]
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@action = 'GET'
|
39
|
+
@headers = {}
|
40
|
+
@timeout = 0
|
41
|
+
@connect_timeout = 0
|
42
|
+
@max_redirects = -1
|
43
|
+
end
|
44
|
+
|
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
|
+
]
|
50
|
+
|
51
|
+
WRITER_VARS = [
|
52
|
+
:url, :username, :password, :file_name, :proxy, :proxy_type, :insecure,
|
53
|
+
:ignore_content_length, :multipart, :cacert
|
54
|
+
]
|
55
|
+
|
56
|
+
attr_reader *READER_VARS
|
57
|
+
attr_writer *WRITER_VARS
|
58
|
+
|
59
|
+
# Set the type of authentication to use for this request.
|
60
|
+
#
|
61
|
+
# @param [String, Symbol] type - The type of authentication to use for this request, can be one of
|
62
|
+
# :basic, :digest, or :any
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# sess.username = "foo"
|
66
|
+
# sess.password = "sekrit"
|
67
|
+
# sess.auth_type = :digest
|
68
|
+
def auth_type=(type=:basic)
|
69
|
+
@auth_type = case type
|
70
|
+
when :basic, "basic"
|
71
|
+
Request::AuthBasic
|
72
|
+
when :digest, "digest"
|
73
|
+
Request::AuthDigest
|
74
|
+
when :any, "any"
|
75
|
+
Request::AuthAny
|
76
|
+
else
|
77
|
+
raise "#{type.inspect} is an unknown authentication type"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def upload_data=(data)
|
82
|
+
@upload_data = case data
|
83
|
+
when Hash
|
84
|
+
self.multipart ? data : Util.build_query_string_from_hash(data, action == 'POST')
|
85
|
+
else
|
86
|
+
data
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def action=(new_action)
|
91
|
+
action = new_action.to_s.upcase
|
92
|
+
|
93
|
+
if !VALID_ACTIONS.include?(action)
|
94
|
+
raise ArgumentError, "Action must be one of #{VALID_ACTIONS.join(', ')}"
|
95
|
+
end
|
96
|
+
|
97
|
+
@action = action
|
98
|
+
end
|
99
|
+
|
100
|
+
def timeout=(new_timeout)
|
101
|
+
if new_timeout && new_timeout.to_i < 1
|
102
|
+
raise ArgumentError, "Timeout must be a positive integer greater than 0"
|
103
|
+
end
|
104
|
+
|
105
|
+
@timeout = new_timeout.to_i
|
106
|
+
end
|
107
|
+
|
108
|
+
def connect_timeout=(new_timeout)
|
109
|
+
if new_timeout && new_timeout.to_i < 1
|
110
|
+
raise ArgumentError, "Timeout must be a positive integer greater than 0"
|
111
|
+
end
|
112
|
+
|
113
|
+
@connect_timeout = new_timeout.to_i
|
114
|
+
end
|
115
|
+
|
116
|
+
def max_redirects=(new_max_redirects)
|
117
|
+
if new_max_redirects.to_i < -1
|
118
|
+
raise ArgumentError, "Max redirects must be a positive integer, 0 or -1"
|
119
|
+
end
|
120
|
+
|
121
|
+
@max_redirects = new_max_redirects.to_i
|
122
|
+
end
|
123
|
+
|
124
|
+
def headers=(new_headers)
|
125
|
+
if !new_headers.kind_of?(Hash)
|
126
|
+
raise ArgumentError, "Headers must be a hash"
|
127
|
+
end
|
128
|
+
|
129
|
+
@headers = new_headers
|
130
|
+
end
|
131
|
+
|
132
|
+
def buffer_size=(buffer_size)
|
133
|
+
if buffer_size != nil && buffer_size.to_i < 1
|
134
|
+
raise ArgumentError, "Buffer size must be a positive integer greater than 0 or nil"
|
135
|
+
end
|
136
|
+
|
137
|
+
@buffer_size = buffer_size != nil ? buffer_size.to_i : nil
|
138
|
+
end
|
139
|
+
|
140
|
+
def credentials
|
141
|
+
return nil if username.nil? || password.nil?
|
142
|
+
"#{username}:#{password}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def eql?(request)
|
146
|
+
return false unless Request === request
|
147
|
+
|
148
|
+
READER_VARS.inject(true) do |memo, name|
|
149
|
+
memo && (self.send(name) == request.send(name))
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
alias_method :==, :eql?
|
154
|
+
|
155
|
+
def marshal_dump
|
156
|
+
[ @url, @username, @password, @file_name, @proxy, @proxy_type, @insecure,
|
157
|
+
@ignore_content_length, @multipart, @action, @timeout, @connect_timeout,
|
158
|
+
@max_redirects, @headers, @auth_type, @upload_data, @buffer_size, @cacert ]
|
159
|
+
end
|
160
|
+
|
161
|
+
def marshal_load(data)
|
162
|
+
@url, @username, @password, @file_name, @proxy, @proxy_type, @insecure,
|
163
|
+
@ignore_content_length, @multipart, @action, @timeout, @connect_timeout,
|
164
|
+
@max_redirects, @headers, @auth_type, @upload_data, @buffer_size, @cacert = data
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
## -------------------------------------------------------------------
|
2
|
+
##
|
3
|
+
## Patron HTTP Client: Response class
|
4
|
+
## Copyright (c) 2008 The Hive http://www.thehive.com/
|
5
|
+
##
|
6
|
+
## Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
## of this software and associated documentation files (the "Software"), to deal
|
8
|
+
## in the Software without restriction, including without limitation the rights
|
9
|
+
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
## copies of the Software, and to permit persons to whom the Software is
|
11
|
+
## furnished to do so, subject to the following conditions:
|
12
|
+
##
|
13
|
+
## The above copyright notice and this permission notice shall be included in
|
14
|
+
## all copies or substantial portions of the Software.
|
15
|
+
##
|
16
|
+
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
## THE SOFTWARE.
|
23
|
+
##
|
24
|
+
## -------------------------------------------------------------------
|
25
|
+
|
26
|
+
module Patron
|
27
|
+
|
28
|
+
# Represents the response from the HTTP server.
|
29
|
+
class Response
|
30
|
+
|
31
|
+
def initialize(url, status, redirect_count, header_data, body, default_charset = nil)
|
32
|
+
# Don't let a response clear out the default charset, which would cause encoding to fail
|
33
|
+
default_charset = "ASCII-8BIT" unless default_charset
|
34
|
+
@url = url
|
35
|
+
@status = status
|
36
|
+
@redirect_count = redirect_count
|
37
|
+
@body = body
|
38
|
+
|
39
|
+
@charset = determine_charset(header_data, body) || default_charset
|
40
|
+
|
41
|
+
[url, header_data].each do |attr|
|
42
|
+
convert_to_default_encoding!(attr)
|
43
|
+
end
|
44
|
+
|
45
|
+
parse_headers(header_data)
|
46
|
+
if @headers["Content-Type"] && @headers["Content-Type"][0, 5] == "text/"
|
47
|
+
convert_to_default_encoding!(@body)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :url, :status, :status_line, :redirect_count, :body, :headers, :charset
|
52
|
+
|
53
|
+
def inspect
|
54
|
+
# Avoid spamming the console with the header and body data
|
55
|
+
"#<Patron::Response @status_line='#{@status_line}'>"
|
56
|
+
end
|
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
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def determine_charset(header_data, body)
|
69
|
+
header_data.match(charset_regex) || (body && body.match(charset_regex))
|
70
|
+
|
71
|
+
$1
|
72
|
+
end
|
73
|
+
|
74
|
+
def charset_regex
|
75
|
+
/(?:charset|encoding)="?([a-z0-9-]+)"?/i
|
76
|
+
end
|
77
|
+
|
78
|
+
def convert_to_default_encoding!(str)
|
79
|
+
if str.respond_to?(:encode) && Encoding.default_internal
|
80
|
+
str.force_encoding(charset).encode!(Encoding.default_internal)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Called by the C code to parse and set the headers
|
85
|
+
def parse_headers(header_data)
|
86
|
+
@headers = {}
|
87
|
+
|
88
|
+
header_data.split(/\r\n/).each do |header|
|
89
|
+
if header =~ %r|^HTTP/1.[01]|
|
90
|
+
@status_line = header.strip
|
91
|
+
else
|
92
|
+
parts = header.split(':', 2)
|
93
|
+
unless parts.empty?
|
94
|
+
parts[1].strip! unless parts[1].nil?
|
95
|
+
if @headers.has_key?(parts[0])
|
96
|
+
@headers[parts[0]] = [@headers[parts[0]]] unless @headers[parts[0]].kind_of? Array
|
97
|
+
@headers[parts[0]] << parts[1]
|
98
|
+
else
|
99
|
+
@headers[parts[0]] = parts[1]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
## -------------------------------------------------------------------
|
2
|
+
##
|
3
|
+
## Patron HTTP Client: Session class
|
4
|
+
## Copyright (c) 2008 The Hive http://www.thehive.com/
|
5
|
+
##
|
6
|
+
## Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
## of this software and associated documentation files (the "Software"), to deal
|
8
|
+
## in the Software without restriction, including without limitation the rights
|
9
|
+
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
## copies of the Software, and to permit persons to whom the Software is
|
11
|
+
## furnished to do so, subject to the following conditions:
|
12
|
+
##
|
13
|
+
## The above copyright notice and this permission notice shall be included in
|
14
|
+
## all copies or substantial portions of the Software.
|
15
|
+
##
|
16
|
+
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
## THE SOFTWARE.
|
23
|
+
##
|
24
|
+
## -------------------------------------------------------------------
|
25
|
+
|
26
|
+
require 'uri'
|
27
|
+
require 'patron/error'
|
28
|
+
require 'patron/request'
|
29
|
+
require 'patron/response'
|
30
|
+
require 'patron/session_ext'
|
31
|
+
require 'patron/util'
|
32
|
+
|
33
|
+
module Patron
|
34
|
+
|
35
|
+
# This class represents multiple request/response transactions with an HTTP
|
36
|
+
# server. This is the primary API for Patron.
|
37
|
+
class Session
|
38
|
+
|
39
|
+
# HTTP connection timeout in seconds. Defaults to 1 second.
|
40
|
+
attr_accessor :connect_timeout
|
41
|
+
|
42
|
+
# HTTP transaction timeout in seconds. Defaults to 5 seconds.
|
43
|
+
attr_accessor :timeout
|
44
|
+
|
45
|
+
# Maximum number of times to follow redirects.
|
46
|
+
# Set to 0 to disable and -1 to follow all redirects. Defaults to 5.
|
47
|
+
attr_accessor :max_redirects
|
48
|
+
|
49
|
+
# Prepended to the URL in all requests.
|
50
|
+
attr_accessor :base_url
|
51
|
+
|
52
|
+
# Username and password for http authentication
|
53
|
+
attr_accessor :username, :password
|
54
|
+
|
55
|
+
# Proxy URL in cURL format ('hostname:8080')
|
56
|
+
attr_accessor :proxy
|
57
|
+
|
58
|
+
# Proxy type (default is HTTP), see constants under ProxyType for supported types.
|
59
|
+
attr_accessor :proxy_type
|
60
|
+
|
61
|
+
# Standard set of headers that are used in all requests.
|
62
|
+
attr_accessor :headers
|
63
|
+
|
64
|
+
# Set the authentication type for the request.
|
65
|
+
# @see Patron::Request#auth_type
|
66
|
+
attr_accessor :auth_type
|
67
|
+
|
68
|
+
# Does this session stricly verify SSL certificates?
|
69
|
+
attr_accessor :insecure
|
70
|
+
|
71
|
+
# What cacert file should this session use to verify SSL certificates?
|
72
|
+
attr_accessor :cacert
|
73
|
+
|
74
|
+
# Does this session ignore Content-Size headers?
|
75
|
+
attr_accessor :ignore_content_length
|
76
|
+
|
77
|
+
# Set the buffer size for this request. This option will
|
78
|
+
# only be set if buffer_size is non-nil
|
79
|
+
attr_accessor :buffer_size
|
80
|
+
|
81
|
+
# Default encoding of responses. Used if no charset is provided by the host.
|
82
|
+
attr_accessor :default_response_charset
|
83
|
+
|
84
|
+
private :handle_request, :enable_cookie_session, :set_debug_file
|
85
|
+
|
86
|
+
# Create a new Session object.
|
87
|
+
def initialize
|
88
|
+
@headers = {}
|
89
|
+
@timeout = 5
|
90
|
+
@connect_timeout = 1
|
91
|
+
@max_redirects = 5
|
92
|
+
@auth_type = :basic
|
93
|
+
end
|
94
|
+
|
95
|
+
# Turn on cookie handling for this session, storing them in memory by
|
96
|
+
# default or in +file+ if specified. The +file+ must be readable and
|
97
|
+
# writable. Calling multiple times will add more files.
|
98
|
+
def handle_cookies(file = nil)
|
99
|
+
if file
|
100
|
+
path = Pathname(file).expand_path
|
101
|
+
unless File.exists?(file) and File.writable?(path.dirname)
|
102
|
+
raise ArgumentError, "Can't create file #{path} (permission error)"
|
103
|
+
end
|
104
|
+
unless File.readable?(file) or File.writable?(path)
|
105
|
+
raise ArgumentError, "Can't read or write file #{path} (permission error)"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
enable_cookie_session(path.to_s)
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
# Enable debug output to stderr or to specified +file+.
|
113
|
+
def enable_debug(file = nil)
|
114
|
+
set_debug_file(file.to_s)
|
115
|
+
end
|
116
|
+
|
117
|
+
###################################################################
|
118
|
+
### Standard HTTP methods
|
119
|
+
###
|
120
|
+
|
121
|
+
# Retrieve the contents of the specified +url+ optionally sending the
|
122
|
+
# specified headers. If the +base_url+ varaible is set then it is prepended
|
123
|
+
# to the +url+ parameter. Any custom headers are merged with the contents
|
124
|
+
# of the +headers+ instance variable. The results are returned in a
|
125
|
+
# Response object.
|
126
|
+
# Notice: this method doesn't accept any +data+ argument: if you need to send data with
|
127
|
+
# a get request, please, use the #request method.
|
128
|
+
def get(url, headers = {})
|
129
|
+
request(:get, url, headers)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Retrieve the contents of the specified +url+ as with #get, but the
|
133
|
+
# content at the URL is downloaded directly into the specified file.
|
134
|
+
def get_file(url, filename, headers = {})
|
135
|
+
request(:get, url, headers, :file => filename)
|
136
|
+
end
|
137
|
+
|
138
|
+
# As #get but sends an HTTP HEAD request.
|
139
|
+
def head(url, headers = {})
|
140
|
+
request(:head, url, headers)
|
141
|
+
end
|
142
|
+
|
143
|
+
# As #get but sends an HTTP DELETE request.
|
144
|
+
# Notice: this method doesn't accept any +data+ argument: if you need to send data with
|
145
|
+
# a delete request, please, use the #request method.
|
146
|
+
def delete(url, headers = {})
|
147
|
+
request(:delete, url, headers)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Uploads the passed +data+ to the specified +url+ using HTTP PUT. +data+
|
151
|
+
# must be a string.
|
152
|
+
def put(url, data, headers = {})
|
153
|
+
request(:put, url, headers, :data => data)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Uploads the contents of a file to the specified +url+ using HTTP PUT.
|
157
|
+
def put_file(url, filename, headers = {})
|
158
|
+
request(:put, url, headers, :file => filename)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Uploads the passed +data+ to the specified +url+ using HTTP POST. +data+
|
162
|
+
# can be a string or a hash.
|
163
|
+
def post(url, data, headers = {})
|
164
|
+
if data.is_a?(Hash)
|
165
|
+
data = data.map {|k,v| urlencode(k.to_s) + '=' + urlencode(v.to_s) }.join('&')
|
166
|
+
headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
167
|
+
end
|
168
|
+
request(:post, url, headers, :data => data)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Uploads the contents of a file to the specified +url+ using HTTP POST.
|
172
|
+
def post_file(url, filename, headers = {})
|
173
|
+
request(:post, url, headers, :file => filename)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Uploads the contents of a file and data to the specified +url+ using HTTP POST.
|
177
|
+
def post_multipart(url, data, filename, headers = {})
|
178
|
+
request(:post, url, headers, {:data => data, :file => filename, :multipart => true})
|
179
|
+
end
|
180
|
+
|
181
|
+
###################################################################
|
182
|
+
### WebDAV methods
|
183
|
+
###
|
184
|
+
|
185
|
+
# Sends a WebDAV COPY request to the specified +url+.
|
186
|
+
def copy(url, dest, headers = {})
|
187
|
+
headers['Destination'] = dest
|
188
|
+
request(:copy, url, headers)
|
189
|
+
end
|
190
|
+
|
191
|
+
###################################################################
|
192
|
+
### Basic API methods
|
193
|
+
###
|
194
|
+
|
195
|
+
# Send an HTTP request to the specified +url+.
|
196
|
+
def request(action, url, headers, options = {})
|
197
|
+
# If the Expect header isn't set uploads are really slow
|
198
|
+
headers['Expect'] ||= ''
|
199
|
+
|
200
|
+
req = Request.new
|
201
|
+
req.action = action
|
202
|
+
req.headers = self.headers.merge headers
|
203
|
+
req.timeout = options.fetch :timeout, self.timeout
|
204
|
+
req.connect_timeout = options.fetch :connect_timeout, self.connect_timeout
|
205
|
+
req.max_redirects = options.fetch :max_redirects, self.max_redirects
|
206
|
+
req.username = options.fetch :username, self.username
|
207
|
+
req.password = options.fetch :password, self.password
|
208
|
+
req.proxy = options.fetch :proxy, self.proxy
|
209
|
+
req.proxy_type = options.fetch :proxy_type, self.proxy_type
|
210
|
+
req.auth_type = options.fetch :auth_type, self.auth_type
|
211
|
+
req.insecure = options.fetch :insecure, self.insecure
|
212
|
+
req.cacert = options.fetch :cacert, self.cacert
|
213
|
+
req.ignore_content_length = options.fetch :ignore_content_length, self.ignore_content_length
|
214
|
+
req.buffer_size = options.fetch :buffer_size, self.buffer_size
|
215
|
+
req.multipart = options[:multipart]
|
216
|
+
req.upload_data = options[:data]
|
217
|
+
req.file_name = options[:file]
|
218
|
+
|
219
|
+
base_url = self.base_url.to_s
|
220
|
+
url = url.to_s
|
221
|
+
raise ArgumentError, "Empty URL" if base_url.empty? && url.empty?
|
222
|
+
uri = URI.join(base_url, url)
|
223
|
+
query = uri.query.to_s.split('&')
|
224
|
+
query += options[:query].is_a?(Hash) ? Util.build_query_pairs_from_hash(options[:query]) : options[:query].to_s.split('&')
|
225
|
+
uri.query = query.join('&')
|
226
|
+
uri.query = nil if uri.query.empty?
|
227
|
+
url = uri.to_s
|
228
|
+
req.url = url
|
229
|
+
|
230
|
+
handle_request(req)
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
end
|