/
response.rb
125 lines (113 loc) · 3.83 KB
/
response.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
module Excon
class Response
attr_accessor :data
# backwards compatability reader/writers
def body=(new_body)
@data[:body] = new_body
end
def body
@data[:body]
end
def headers=(new_headers)
@data[:headers] = new_headers
end
def headers
@data[:headers]
end
def status=(new_status)
@data[:status] = new_status
end
def status
@data[:status]
end
def remote_ip=(new_remote_ip)
@data[:remote_ip] = new_remote_ip
end
def remote_ip
@data[:remote_ip]
end
def self.parse(socket, datum)
datum[:response] = {
:body => '',
:headers => {},
:status => socket.read(12)[9, 11].to_i,
:remote_ip => socket.respond_to?(:remote_ip) && socket.remote_ip
}
socket.readline # read the rest of the status line and CRLF
until ((data = socket.readline).chop!).empty?
key, value = data.split(/:\s*/, 2)
datum[:response][:headers][key] = ([*datum[:response][:headers][key]] << value).compact.join(', ')
if key.casecmp('Content-Length') == 0
content_length = value.to_i
elsif (key.casecmp('Transfer-Encoding') == 0) && (value.casecmp('chunked') == 0)
transfer_encoding_chunked = true
end
end
unless (['HEAD', 'CONNECT'].include?(datum[:method].to_s.upcase)) || NO_ENTITY.include?(datum[:response][:status])
# check to see if expects was set and matched
expected_status = !datum.has_key?(:expects) || [*datum[:expects]].include?(datum[:response][:status])
# if expects matched and there is a block, use it
if expected_status && datum.has_key?(:response_block)
if transfer_encoding_chunked
# 2 == "/r/n".length
while (chunk_size = socket.readline.chop!.to_i(16)) > 0
datum[:response_block].call(socket.read(chunk_size + 2).chop!, nil, nil)
end
socket.read(2)
elsif remaining = content_length
while remaining > 0
datum[:response_block].call(socket.read([datum[:chunk_size], remaining].min), [remaining - datum[:chunk_size], 0].max, content_length)
remaining -= datum[:chunk_size]
end
else
while remaining = socket.read(datum[:chunk_size])
datum[:response_block].call(remaining, remaining.length, content_length)
end
end
else # no block or unexpected status
if transfer_encoding_chunked
while (chunk_size = socket.readline.chop!.to_i(16)) > 0
datum[:response][:body] << socket.read(chunk_size + 2).chop! # 2 == "/r/n".length
end
socket.read(2) # 2 == "/r/n".length
elsif remaining = content_length
while remaining > 0
datum[:response][:body] << socket.read([datum[:chunk_size], remaining].min)
remaining -= datum[:chunk_size]
end
else
datum[:response][:body] << socket.read
end
end
end
datum
end
def initialize(params={})
@data = {
:body => '',
:headers => {}
}.merge(params)
@body = @data[:body]
@headers = @data[:headers]
@status = @data[:status]
@remote_ip = @data[:remote_ip]
end
def [](key)
@data[key]
end
def params
$stderr.puts("Excon::Response#params is deprecated use Excon::Response#data instead (#{caller.first})")
data
end
# Retrieve a specific header value. Header names are treated case-insensitively.
# @param [String] name Header name
def get_header(name)
headers.each do |key,value|
if key.casecmp(name) == 0
return value
end
end
nil
end
end # class Response
end # module Excon