/
curb.rb
169 lines (141 loc) · 5.14 KB
/
curb.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
require "httpi/adapter/base"
require "httpi/response"
module HTTPI
module Adapter
# = HTTPI::Adapter::Curb
#
# Adapter for the Curb client.
# http://rubygems.org/gems/curb
class Curb < Base
register :curb, :deps => %w(curb)
def initialize(request)
@request = request
@client = Curl::Easy.new
end
attr_reader :client
def request(method)
unless REQUEST_METHODS.include? method
raise NotSupportedError, "Curb does not support custom HTTP methods"
end
arguments = ["http_#{method}"]
if [:put, :post].include? method
arguments << (@request.body || "")
end
if @request.on_body
client.on_body do |data|
@request.on_body.call(data)
# curb requires you to return the length of the data read from the block.
# It allows you to abort the connection by returning a smaller value
data.length
end
end
do_request { |client| client.send(*arguments) }
rescue Curl::Err::SSLCACertificateError
raise SSLError
rescue Curl::Err::SSLPeerCertificateError
raise SSLError
rescue Curl::Err::ConnectionFailedError # connection refused
$!.extend ConnectionError
raise
end
private
def do_request
setup_client
yield @client
respond_with @client
end
def setup_client
basic_setup
if @request.auth.ntlm? && !Curl.ntlm?
raise NotSupportedError, "installed version of libcurl does not support NTLM authentication"
end
setup_ntlm_auth if @request.auth.ntlm?
setup_http_auth if @request.auth.http?
setup_gssnegotiate_auth if @request.auth.gssnegotiate?
setup_ssl_auth if @request.auth.ssl? || @request.ssl?
end
def basic_setup
@client.url = @request.url.to_s
@client.proxy_url = @request.proxy.to_s if @request.proxy
read_or_write_timeout = @request.read_timeout || @request.write_timeout
@client.timeout_ms = read_or_write_timeout * 1000 if read_or_write_timeout
@client.connect_timeout_ms = @request.open_timeout * 1000 if @request.open_timeout
@client.headers = @request.headers.to_hash
@client.verbose = false
# cURL workaround
# see: http://stackoverflow.com/a/10755612/102920
# https://github.com/typhoeus/typhoeus/issues/260
@client.set(:NOSIGNAL, true)
end
def setup_ntlm_auth
@client.http_auth_types = @request.auth.type
@client.username, @client.password = *@request.auth.credentials
end
def setup_http_auth
@client.http_auth_types = @request.auth.type
@client.username, @client.password = *@request.auth.credentials
end
def setup_gssnegotiate_auth
@client.http_auth_types = @request.auth.type
# The curl man page (http://curl.haxx.se/docs/manpage.html) says that
# you have to specify a fake username when using Negotiate auth, and
# they use ':' in their example.
@client.username = ':'
end
def setup_ssl_auth
ssl = @request.auth.ssl
if @request.auth.ssl?
if ssl.verify_mode == :none
@client.ssl_verify_host = 0
else
@client.cacert = ssl.ca_cert_file if ssl.ca_cert_file
@client.certtype = ssl.cert_type.to_s.upcase
end
# Send client-side certificate regardless of state of SSL verify mode
@client.cert_key = ssl.cert_key_file
@client.cert = ssl.cert_file
@client.certpassword = ssl.cert_key_password
@client.ssl_verify_peer = ssl.verify_mode == :peer
end
@client.ssl_version = case ssl.ssl_version
when :TLSv1_2 then 1
when :TLSv1_1 then 1
when :TLSv1 then 1
when :SSLv2 then 2
when :SSLv23 then 2
when :SSLv3 then 3
end
if ssl.min_version || ssl.max_version
raise NotImplementedError, "Method Not Implemented"
end
end
def respond_with(client)
headers = parse_header_string(client.header_str).last
Response.new client.response_code, headers, client.body_str
end
# Borrowed from Webmock's Curb adapter:
# http://github.com/bblimke/webmock/blob/master/lib/webmock/http_lib_adapters/curb.rb
def parse_header_string(header_string)
status, headers = nil, {}
return [status, headers] unless header_string
header_string.split(/\r\n/).each do |header|
if header =~ %r|^HTTP/1.[01] \d\d\d (.*)|
status = $1
else
parts = header.split(':', 2)
unless parts.empty?
parts[1].strip! unless parts[1].nil?
if headers.has_key?(parts[0])
headers[parts[0]] = [headers[parts[0]]] unless headers[parts[0]].kind_of? Array
headers[parts[0]] << parts[1]
else
headers[parts[0]] = parts[1]
end
end
end
end
[status, headers]
end
end
end
end