patron 0.7.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/lib/patron/error.rb +17 -8
- data/lib/patron/response.rb +59 -37
- data/lib/patron/response_decoding.rb +128 -0
- data/lib/patron/session.rb +4 -3
- data/lib/patron/version.rb +1 -1
- data/spec/response_spec.rb +59 -33
- data/spec/session_spec.rb +4 -3
- data/spec/spec_helper.rb +1 -0
- data/spec/support/test_server.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e89c04473c18143c350dcbbbf6f98b1c66b2e7e
|
4
|
+
data.tar.gz: 5a019a6f724c14056f949eb4fafb538454b959c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6468281adbd09ad4cbc02eaaa64c36926e108ecd6c9b7a38ffcf8223e5c9930147197c858dac1d30b0f728ece4065f97f32604e07f4016036363b5e110f30ce6
|
7
|
+
data.tar.gz: 897c874cf04b2f3d8fa5e327a5cc78bd86d3ba4a6b27828812c67bc3433cdc0d4c4e6bad266ef655a9349d8f7d711cd95d60bd8c166ede3c8456c788fcc21197
|
data/.travis.yml
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
### 0.8.0
|
2
|
+
|
3
|
+
* Add `Response#inspectable_body`, `Response#decoded_body`. `decoded_body` will atempt to decode
|
4
|
+
the HTTP response into your internal encoding, using the charset header that the server has
|
5
|
+
provided. Note that this operation may fail - if the server said that the body is in a certain
|
6
|
+
encoding, but this is then overridden with, say, `meta` elements in the HTML Patron is _not_
|
7
|
+
going to parse the HTML to figure out how to decode.
|
8
|
+
|
1
9
|
### 0.7.0
|
2
10
|
|
3
11
|
* Allow Ruby File objects to be passed as `data` to `Session#put`, `Sesion#post` etc.
|
data/Gemfile.lock
CHANGED
data/lib/patron/error.rb
CHANGED
@@ -31,29 +31,38 @@ module Patron
|
|
31
31
|
|
32
32
|
# Gets raised when the URL passed to Patron used a protocol that it does not support.
|
33
33
|
# This most likely the result of a misspelled protocol string.
|
34
|
-
class UnsupportedProtocol
|
34
|
+
class UnsupportedProtocol < Error; end
|
35
35
|
|
36
36
|
# Gets raised when a request is attempted with an unsupported SSL version.
|
37
|
-
class UnsupportedSSLVersion
|
37
|
+
class UnsupportedSSLVersion < Error; end
|
38
38
|
|
39
39
|
# Gets raised when the URL was not properly formatted.
|
40
|
-
class URLFormatError
|
40
|
+
class URLFormatError < Error; end
|
41
41
|
|
42
42
|
# Gets raised when the remote host name could not be resolved.
|
43
|
-
class HostResolutionError
|
43
|
+
class HostResolutionError < Error; end
|
44
44
|
|
45
45
|
# Gets raised when failing to connect to the remote host.
|
46
|
-
class ConnectionFailed
|
46
|
+
class ConnectionFailed < Error; end
|
47
47
|
|
48
48
|
# Gets raised when the response was shorter or larger than expected.
|
49
49
|
# This happens when the server first reports an expected transfer size,
|
50
50
|
# and then delivers data that doesn't match the previously given size.
|
51
|
-
class PartialFileError
|
51
|
+
class PartialFileError < Error; end
|
52
52
|
|
53
53
|
# Gets raised on an operation timeout. The specified time-out period was reached.
|
54
|
-
class TimeoutError
|
54
|
+
class TimeoutError < Error; end
|
55
55
|
|
56
56
|
# Gets raised on too many redirects. When following redirects, Patron hit the maximum amount.
|
57
|
-
class TooManyRedirects
|
57
|
+
class TooManyRedirects < Error; end
|
58
58
|
|
59
|
+
# Gets raised when the server specifies an encoding that could not be found, or has an invalid name,
|
60
|
+
# or when the server "lies" about the encoding of the response body (such as can be the case
|
61
|
+
# when the server specifies an encoding in `Content-Type`) which the HTML generator then overrides
|
62
|
+
# with a `meta` element.
|
63
|
+
class HeaderCharsetInvalid < Error; end
|
64
|
+
|
65
|
+
# Gets raised when you try to use `decoded_body` but it can't
|
66
|
+
# be represented by your Ruby process's current internal encoding
|
67
|
+
class NonRepresentableBody < HeaderCharsetInvalid; end
|
59
68
|
end
|
data/lib/patron/response.rb
CHANGED
@@ -28,6 +28,8 @@ module Patron
|
|
28
28
|
|
29
29
|
# Represents the response from the HTTP server.
|
30
30
|
class Response
|
31
|
+
include ResponseDecoding
|
32
|
+
|
31
33
|
# @return [String] the original URL used to perform the request (contains the final URL after redirects)
|
32
34
|
attr_reader :url
|
33
35
|
|
@@ -40,14 +42,16 @@ module Patron
|
|
40
42
|
# @return [Fixnum] how many redirects were followed when fulfilling this request
|
41
43
|
attr_reader :redirect_count
|
42
44
|
|
43
|
-
# @return [String, nil] the response body
|
45
|
+
# @return [String, nil] the response body as a String encoded as `Encoding::BINARY` or
|
46
|
+
# or `nil` if the response was written directly to a file
|
44
47
|
attr_reader :body
|
45
|
-
|
48
|
+
|
46
49
|
# @return [Hash] the response headers. If there were multiple headers received for the same value
|
47
50
|
# (like "Cookie"), the header values will be within an Array under the key for the header, in order.
|
48
51
|
attr_reader :headers
|
49
52
|
|
50
|
-
# @return [String] the recognized name of the charset for the response
|
53
|
+
# @return [String] the recognized name of the charset for the response. The name is not checked
|
54
|
+
# to be a valid charset name, just stored. To check the charset for validity, use #body_decodable?
|
51
55
|
attr_reader :charset
|
52
56
|
|
53
57
|
# Overridden so that the output is shorter and there is no response body printed
|
@@ -56,24 +60,15 @@ module Patron
|
|
56
60
|
"#<Patron::Response @status_line='#{@status_line}'>"
|
57
61
|
end
|
58
62
|
|
59
|
-
def initialize(url, status, redirect_count,
|
60
|
-
|
61
|
-
default_charset = "ASCII-8BIT" unless default_charset
|
62
|
-
@url = url
|
63
|
+
def initialize(url, status, redirect_count, raw_header_data, body, default_charset = nil)
|
64
|
+
@url = url.force_encoding(Encoding::ASCII) # the URL is always an ASCII subset, _always_.
|
63
65
|
@status = status
|
64
66
|
@redirect_count = redirect_count
|
65
|
-
@body = body
|
66
|
-
|
67
|
-
@charset = determine_charset(header_data, body) || default_charset
|
68
|
-
|
69
|
-
[url, header_data].each do |attr|
|
70
|
-
convert_to_default_encoding!(attr)
|
71
|
-
end
|
67
|
+
@body = body.force_encoding(Encoding::BINARY) if body
|
72
68
|
|
69
|
+
header_data = decode_header_data(raw_header_data)
|
73
70
|
parse_headers(header_data)
|
74
|
-
|
75
|
-
convert_to_default_encoding!(@body)
|
76
|
-
end
|
71
|
+
@charset = charset_from_content_type
|
77
72
|
end
|
78
73
|
|
79
74
|
# Tells whether the HTTP response code is less than 400
|
@@ -90,30 +85,58 @@ module Patron
|
|
90
85
|
status >= 400
|
91
86
|
end
|
92
87
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
88
|
+
# Tells whether the response body can be decoded losslessly into the curren internal encoding
|
89
|
+
#
|
90
|
+
# @return [Boolean] true if the body is decodable, false if otherwise
|
91
|
+
def body_decodable?
|
92
|
+
return true if @body.nil?
|
93
|
+
return true if decoded_body
|
94
|
+
rescue HeaderCharsetInvalid, NonRepresentableBody
|
95
|
+
false
|
100
96
|
end
|
101
97
|
|
102
|
-
|
103
|
-
|
98
|
+
# Returns the response body converted into the Ruby process internal encoding (the one set as `Encoding.default_internal`).
|
99
|
+
# As the response gets returned, the response body is not assumed to be in any encoding whatsoever - it will be explicitly
|
100
|
+
# set to `Encoding::BINARY` (as if you were reading a file in binary mode).
|
101
|
+
#
|
102
|
+
# When you call `decoded_body`, the method will
|
103
|
+
# look at the `Content-Type` response header, and check if that header specified a charset. If it did, the method will then
|
104
|
+
# check whether the specified charset is valid (whether it is possible to find a matching `Encoding` class in the VM).
|
105
|
+
# Once that succeeds, the method will check whether the response body _is_ in the encoding that the server said it is.
|
106
|
+
#
|
107
|
+
# This might not be the case - you can, for instance, easily serve an HTML document with a UTF-8 header (with the header
|
108
|
+
# being configured somewhere on the webserver level) and then have the actual HTML document override it with a
|
109
|
+
# `meta` element or `charset` containing an overriding charset. However, parsing the response body is outside of scope for
|
110
|
+
# Patron, so if this situation happens (the server sets a charset in the header but this header does not match what the server
|
111
|
+
# actually sends in the body) you will get an exception stating this is a problem.
|
112
|
+
#
|
113
|
+
# The next step is actually converting the body to the internal Ruby encoding. That stage may raise an exception as well, if
|
114
|
+
# you are using an internal encoding which can't represent the response body faithfully. For example, if you run Ruby with
|
115
|
+
# a CJK internal encoding, and the response you are trying to decode uses Greek characters and is UTF-8, you are going to
|
116
|
+
# get an exception since it is impossible to coerce those characters to your internal encoding.
|
117
|
+
#
|
118
|
+
# @raise {Patron::HeaderCharsetInvalid} when the server supplied a wrong or incorrect charset, {Patron::NonRepresentableBody}
|
119
|
+
# when unable to decode the body into the current process encoding.
|
120
|
+
# @return [String, nil]
|
121
|
+
def decoded_body
|
122
|
+
return unless @body
|
123
|
+
@decoded_body ||= decode_body(true)
|
104
124
|
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
125
|
+
|
126
|
+
# Works the same as `decoded_body`, with one substantial difference: characters which can't be represented
|
127
|
+
# in your process' default encoding are going to be replaced with question marks. This can be used for raising
|
128
|
+
# errors when you receive responses which indicate errors on the server you are calling. For example, if you expect
|
129
|
+
# a binary download, and the server sends you an error message and you don't really want to bother figuring out
|
130
|
+
# the encoding it has - but you need to append this response to an error log or similar.
|
131
|
+
#
|
132
|
+
# @see Patron::Response#decoded_body
|
133
|
+
# @return [String, nil]
|
134
|
+
def inspectable_body
|
135
|
+
return unless @body
|
136
|
+
@inspectable_body ||= decode_body(false)
|
110
137
|
end
|
111
138
|
|
112
|
-
|
113
|
-
if str.respond_to?(:encode) && Encoding.default_internal
|
114
|
-
str.force_encoding(charset).encode!(Encoding.default_internal)
|
115
|
-
end
|
116
|
-
end
|
139
|
+
private
|
117
140
|
|
118
141
|
# Called by the C code to parse and set the headers
|
119
142
|
def parse_headers(header_data)
|
@@ -138,6 +161,5 @@ module Patron
|
|
138
161
|
end
|
139
162
|
end
|
140
163
|
end
|
141
|
-
|
142
164
|
end
|
143
165
|
end
|
@@ -0,0 +1,128 @@
|
|
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
|
+
|
27
|
+
module Patron
|
28
|
+
# Contains methods used for decoding the HTTP response body. These are only ever used internally
|
29
|
+
# by the Response class.
|
30
|
+
module ResponseDecoding
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
CHARSET_CONTENT_TYPE_RE = /(?:charset|encoding)="?([a-z0-9-]+)"?/i.freeze
|
35
|
+
|
36
|
+
MISREPORTED_ENCODING_ERROR = <<-EOF
|
37
|
+
The server stated that the response has the charset matching %{declared}, but the actual
|
38
|
+
response body failed to decode as such (not flagged as `valid_encoding?')
|
39
|
+
Maybe the response body has a different encoding than suggested by the
|
40
|
+
server, or a binary response has been tagged by the server as text by mistake.
|
41
|
+
If you are performing requests against servers that are known to report wrong or invalid charsets, use
|
42
|
+
`Response#body' instead and handle the character set coercion externally. For instance, you may elect to parse
|
43
|
+
the resulting HTML/XML for charset declarations.
|
44
|
+
EOF
|
45
|
+
|
46
|
+
INVALID_CHARSET_NAME_ERROR = <<-EOF
|
47
|
+
The server specified an invalid charset in the Content-Type header (%{content_type}), \
|
48
|
+
or Ruby does not support this charset. If you are performing requests against servers \
|
49
|
+
that are known to report wrong or invalid charsets, use 'Response#body` instead \
|
50
|
+
and handle the character set coercion at call site.
|
51
|
+
EOF
|
52
|
+
|
53
|
+
INTERNAL_CHARSET_MISMATCH_ERROR = <<-EOF
|
54
|
+
The response body is %{source_encoding}, but the current \
|
55
|
+
`Encoding.default_internal' (or the encoding for a new empty string if you never \
|
56
|
+
set `Encoding.default_internal') - %{target_encoding} - cannot be used to represent the response body in \
|
57
|
+
a lossless way. Your options are:
|
58
|
+
a) using `Response#body' instead
|
59
|
+
b) switching your Ruby process to an encoding that supports the needed repertoire
|
60
|
+
c) using `Response#inspectable_body' to convert the body in a lossy way
|
61
|
+
EOF
|
62
|
+
|
63
|
+
def decode_body(strict)
|
64
|
+
# Try to detect the body encoding from headers
|
65
|
+
body_encoding = encoding_from_headers_or_binary
|
66
|
+
|
67
|
+
# See if the body actually _is_ in this encoding.
|
68
|
+
encoding_matched = @body.force_encoding(body_encoding).valid_encoding?
|
69
|
+
if !encoding_matched
|
70
|
+
raise HeaderCharsetInvalid, MISREPORTED_ENCODING_ERROR % {declared: body_encoding}
|
71
|
+
end
|
72
|
+
|
73
|
+
if strict
|
74
|
+
convert_encoding_and_raise(@body)
|
75
|
+
else
|
76
|
+
@body.encode(internal_encoding, :undefined => :replace, :replace => '?')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def convert_encoding_and_raise(str)
|
81
|
+
internal = internal_encoding
|
82
|
+
str.encode(internal)
|
83
|
+
rescue Encoding::UndefinedConversionError => e
|
84
|
+
enc = str.encoding == Encoding::BINARY ? 'binary' : str.encoding.to_s
|
85
|
+
raise NonRepresentableBody,
|
86
|
+
INTERNAL_CHARSET_MISMATCH_ERROR % {source_encoding: enc, target_encoding: internal}
|
87
|
+
end
|
88
|
+
|
89
|
+
def charset_from_content_type
|
90
|
+
return $1 if @headers["Content-Type"].to_s =~ CHARSET_CONTENT_TYPE_RE
|
91
|
+
end
|
92
|
+
|
93
|
+
def encoding_from_headers_or_binary
|
94
|
+
return Encoding::BINARY unless charset_name = charset_from_content_type
|
95
|
+
Encoding.find(charset_name)
|
96
|
+
rescue ArgumentError => e # invalid charset name
|
97
|
+
raise HeaderCharsetInvalid,
|
98
|
+
INVALID_CHARSET_NAME_ERROR % {content_type: @headers['Content-Type'].inspect}
|
99
|
+
end
|
100
|
+
|
101
|
+
def internal_encoding
|
102
|
+
# Use a trick here - instead of using `default_internal` we will create
|
103
|
+
# an empty string, and then get it's encoding instead. For example, this holds
|
104
|
+
# true on 2.1+ on OSX:
|
105
|
+
#
|
106
|
+
# Encoding.default_internal #=> nil
|
107
|
+
# ''.encoding #=> #<Encoding:UTF-8>
|
108
|
+
Encoding.default_internal || ''.encoding
|
109
|
+
end
|
110
|
+
|
111
|
+
def decode_header_data(str)
|
112
|
+
# Header data is tricky. Strictly speaking, it _must_ be ISO-encoded. However, Content-Disposition
|
113
|
+
# sometimes gets sent as raw UTF8 - and most browsers (except for localized IE versions on Windows)
|
114
|
+
# treat it as such. So a fallback chain of 8859-1->UTF8->binary seems the most sane.
|
115
|
+
tries = [Encoding::ISO8859_1, Encoding::UTF_8, Encoding::BINARY]
|
116
|
+
tries.each do |possible_enc|
|
117
|
+
begin
|
118
|
+
return str.encode(possible_enc)
|
119
|
+
rescue ::Encoding::UndefinedConversionError
|
120
|
+
next
|
121
|
+
end
|
122
|
+
end
|
123
|
+
str # if it doesn't encode, just give back what we got
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private_constant :ResponseDecoding if respond_to?(:private_constant)
|
128
|
+
end
|
data/lib/patron/session.rb
CHANGED
@@ -27,6 +27,7 @@
|
|
27
27
|
require 'uri'
|
28
28
|
require 'patron/error'
|
29
29
|
require 'patron/request'
|
30
|
+
require 'patron/response_decoding'
|
30
31
|
require 'patron/response'
|
31
32
|
require 'patron/session_ext'
|
32
33
|
require 'patron/util'
|
@@ -347,9 +348,9 @@ module Patron
|
|
347
348
|
# the Request object, and not it's public methods.
|
348
349
|
#
|
349
350
|
# @param action[String] the HTTP verb
|
350
|
-
# @
|
351
|
-
# @
|
352
|
-
# @
|
351
|
+
# @param url[#to_s] the addition to the base url component, or a complete URL
|
352
|
+
# @param headers[Hash] a hash of headers, "Accept" will be automatically set to an empty string if not provided
|
353
|
+
# @param options[Hash] any overriding options (will shadow the options from the Session object)
|
353
354
|
# @return [Patron::Request] the request that will be passed to ++handle_request++
|
354
355
|
def build_request(action, url, headers, options = {})
|
355
356
|
# If the Expect header isn't set uploads are really slow
|
data/lib/patron/version.rb
CHANGED
data/spec/response_spec.rb
CHANGED
@@ -32,6 +32,12 @@ require 'base64'
|
|
32
32
|
require 'fileutils'
|
33
33
|
|
34
34
|
describe Patron::Response do
|
35
|
+
around(:each) do |example|
|
36
|
+
previous_internal = Encoding.default_internal
|
37
|
+
example.run
|
38
|
+
Encoding.default_internal = previous_internal
|
39
|
+
end
|
40
|
+
|
35
41
|
before(:each) do
|
36
42
|
@session = Patron::Session.new
|
37
43
|
@session.base_url = "http://localhost:9001"
|
@@ -75,47 +81,67 @@ describe Patron::Response do
|
|
75
81
|
response = @session.get("/repetitiveheader")
|
76
82
|
expect(response.headers['Set-Cookie']).to be == ["a=1","b=2"]
|
77
83
|
end
|
84
|
+
|
85
|
+
describe '#decoded_body and #inspectable_body' do
|
86
|
+
it "should raise with explicitly binary response bodies but allow an inspectable body" do
|
87
|
+
Encoding.default_internal = Encoding::UTF_8
|
88
|
+
response = @session.get("/picture")
|
89
|
+
expect(response.headers['Content-Type']).to be == 'image/png'
|
90
|
+
expect(response.body.encoding).to be == Encoding::BINARY
|
91
|
+
expect(response).not_to be_body_decodable
|
92
|
+
expect {
|
93
|
+
response.decoded_body
|
94
|
+
}.to raise_error(Patron::NonRepresentableBody)
|
95
|
+
|
96
|
+
inspectable = response.inspectable_body
|
97
|
+
expect(inspectable.encoding).to eq(Encoding::UTF_8)
|
98
|
+
expect(inspectable).to be_valid_encoding
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should encode body in the internal charset" do
|
102
|
+
allow(Encoding).to receive(:default_internal).and_return("UTF-8")
|
78
103
|
|
79
|
-
|
80
|
-
|
81
|
-
expect(response.headers['Content-Type']).to be == 'image/png'
|
82
|
-
expect(response.body.encoding).to be == Encoding::ASCII_8BIT
|
83
|
-
end
|
84
|
-
|
85
|
-
it "should not allow a default charset to be nil" do
|
86
|
-
allow(Encoding).to receive(:default_internal).and_return("UTF-8")
|
87
|
-
expect {
|
88
|
-
Patron::Response.new("url", "status", 0, "", "", nil)
|
89
|
-
}.to_not raise_error
|
90
|
-
end
|
91
|
-
|
92
|
-
it "should be able to serialize and deserialize itself" do
|
93
|
-
expect(Marshal.load(Marshal.dump(@request))).to eql(@request)
|
94
|
-
end
|
95
|
-
|
96
|
-
it "should encode body in the internal charset" do
|
97
|
-
allow(Encoding).to receive(:default_internal).and_return("UTF-8")
|
98
|
-
|
99
|
-
greek_encoding = Encoding.find("ISO-8859-7")
|
100
|
-
utf_encoding = Encoding.find("UTF-8")
|
104
|
+
greek_encoding = Encoding.find("ISO-8859-7")
|
105
|
+
utf_encoding = Encoding.find("UTF-8")
|
101
106
|
|
102
|
-
|
103
|
-
|
107
|
+
headers = "HTTP/1.1 200 OK \r\nContent-Type: text/css;charset=ISO-8859-7\r\n"
|
108
|
+
body = "Ππ".encode(greek_encoding) # Greek alphabet
|
104
109
|
|
105
|
-
|
110
|
+
response = Patron::Response.new("url", "status", 0, headers, body, nil)
|
106
111
|
|
107
|
-
|
112
|
+
expect(response).to be_body_decodable
|
113
|
+
expect(response.decoded_body.encoding).to eql(utf_encoding)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should fallback to default charset when header or body charset is not valid" do
|
117
|
+
allow(Encoding).to receive(:default_internal).and_return("UTF-8")
|
118
|
+
|
119
|
+
encoding = Encoding.find("UTF-8")
|
120
|
+
headers = "HTTP/1.1 200 OK \r\nContent-Type: text/css; charset=invalid\r\n"
|
121
|
+
body = "who knows which encoding this CSS is in?"
|
122
|
+
|
123
|
+
response = Patron::Response.new("url", "status", 0, headers, body, "UTF-8")
|
124
|
+
expect(response.charset).to eq('invalid')
|
125
|
+
|
126
|
+
expect(response).not_to be_body_decodable
|
127
|
+
expect {
|
128
|
+
response.decoded_body
|
129
|
+
}.to raise_error(Patron::HeaderCharsetInvalid)
|
130
|
+
end
|
108
131
|
end
|
109
132
|
|
110
|
-
it "
|
111
|
-
|
112
|
-
|
133
|
+
it "decodes a header that contains UTF-8 even though internal encoding is ASCII" do
|
134
|
+
Encoding.default_internal = Encoding::ASCII
|
113
135
|
encoding = Encoding.find("UTF-8")
|
114
|
-
headers = "HTTP/1.1 200 OK \r\nContent-
|
115
|
-
body = "
|
136
|
+
headers = "HTTP/1.1 200 OK \r\nContent-Disposition: attachment,filename=\"žфайлец.txt\"\r\n"
|
137
|
+
body = "this is a file with a Russian filename set in content-disposition"
|
116
138
|
|
117
139
|
response = Patron::Response.new("url", "status", 0, headers, body, "UTF-8")
|
118
|
-
|
119
|
-
expect(
|
140
|
+
dispo = response.headers['Content-Disposition']
|
141
|
+
expect(dispo.encoding).to eq(Encoding::UTF_8)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should be able to serialize and deserialize itself" do
|
145
|
+
expect(Marshal.load(Marshal.dump(@request))).to eql(@request)
|
120
146
|
end
|
121
147
|
end
|
data/spec/session_spec.rb
CHANGED
@@ -28,6 +28,7 @@ require 'webrick'
|
|
28
28
|
require 'yaml'
|
29
29
|
require 'base64'
|
30
30
|
require 'fileutils'
|
31
|
+
require 'securerandom'
|
31
32
|
|
32
33
|
describe Patron::Session do
|
33
34
|
|
@@ -233,7 +234,7 @@ describe Patron::Session do
|
|
233
234
|
# but it does get used nevertheless - for instance, it is a usual
|
234
235
|
# practice when interacting with an ElasticSearch cluster where
|
235
236
|
# you can have very deeply going queries, which are still technically GETs
|
236
|
-
data =
|
237
|
+
data = Random.new.bytes(1024 * 24)
|
237
238
|
response = @session.request(:get, "/test", {}, :data => data)
|
238
239
|
body = YAML::load(response.body)
|
239
240
|
expect(body.request_method).to be == "GET"
|
@@ -241,7 +242,7 @@ describe Patron::Session do
|
|
241
242
|
end
|
242
243
|
|
243
244
|
it "should upload data with :put" do
|
244
|
-
data =
|
245
|
+
data = Random.new.bytes(1024 * 24)
|
245
246
|
response = @session.put("/test", data)
|
246
247
|
body = YAML::load(response.body)
|
247
248
|
expect(body.request_method).to be == "PUT"
|
@@ -518,7 +519,7 @@ describe Patron::Session do
|
|
518
519
|
session = Patron::Session.new do |patron|
|
519
520
|
patron.timeout = args[:timeout]
|
520
521
|
patron.base_url = args[:base_url]
|
521
|
-
patron.headers =
|
522
|
+
patron.headers = args[:headers]
|
522
523
|
end
|
523
524
|
|
524
525
|
it 'sets the base_url' do
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/test_server.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: patron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phillip Toland
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -92,6 +92,7 @@ files:
|
|
92
92
|
- ".gitignore"
|
93
93
|
- ".rspec"
|
94
94
|
- ".travis.yml"
|
95
|
+
- ".yardopts"
|
95
96
|
- CHANGELOG.md
|
96
97
|
- Gemfile
|
97
98
|
- Gemfile.lock
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- lib/patron/proxy_type.rb
|
110
111
|
- lib/patron/request.rb
|
111
112
|
- lib/patron/response.rb
|
113
|
+
- lib/patron/response_decoding.rb
|
112
114
|
- lib/patron/session.rb
|
113
115
|
- lib/patron/util.rb
|
114
116
|
- lib/patron/version.rb
|