C0 code coverage information
Generated on Fri Jul 11 15:55:29 -0700 2008 with rcov 0.7.0
Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
1 require 'net/http'
2 require 'openid'
3 require 'openid/util'
4
5 begin
6 require 'net/https'
7 rescue LoadError
8 OpenID::Util.log('WARNING: no SSL support found. Will not be able ' +
9 'to fetch HTTPS URLs!')
10 require 'net/http'
11 end
12
13 MAX_RESPONSE_KB = 1024
14
15 module Net
16 class HTTP
17 def post_connection_check(hostname)
18 check_common_name = true
19 cert = @socket.io.peer_cert
20 cert.extensions.each { |ext|
21 next if ext.oid != "subjectAltName"
22 ext.value.split(/,\s+/).each{ |general_name|
23 if /\ADNS:(.*)/ =~ general_name
24 check_common_name = false
25 reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
26 return true if /\A#{reg}\z/i =~ hostname
27 elsif /\AIP Address:(.*)/ =~ general_name
28 check_common_name = false
29 return true if $1 == hostname
30 end
31 }
32 }
33 if check_common_name
34 cert.subject.to_a.each{ |oid, value|
35 if oid == "CN"
36 reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
37 return true if /\A#{reg}\z/i =~ hostname
38 end
39 }
40 end
41 raise OpenSSL::SSL::SSLError, "hostname does not match"
42 end
43 end
44 end
45
46 module OpenID
47 # Our HTTPResponse class extends Net::HTTPResponse with an additional
48 # method, final_url.
49 class HTTPResponse
50 attr_accessor :final_url
51
52 attr_accessor :_response
53
54 def self._from_net_response(response, final_url, headers=nil)
55 me = self.new
56 me._response = response
57 me.final_url = final_url
58 return me
59 end
60
61 def method_missing(method, *args)
62 @_response.send(method, *args)
63 end
64
65 def body=(s)
66 @_response.instance_variable_set('@body', s)
67 # XXX Hack to work around ruby's HTTP library behavior. @body
68 # is only returned if it has been read from the response
69 # object's socket, but since we're not using a socket in this
70 # case, we need to set the @read flag to true to avoid a bug in
71 # Net::HTTPResponse.stream_check when @socket is nil.
72 @_response.instance_variable_set('@read', true)
73 end
74 end
75
76 class FetchingError < OpenIDError
77 end
78
79 class HTTPRedirectLimitReached < FetchingError
80 end
81
82 class SSLFetchingError < FetchingError
83 end
84
85 @fetcher = nil
86
87 def self.fetch(url, body=nil, headers=nil,
88 redirect_limit=StandardFetcher::REDIRECT_LIMIT)
89 return fetcher.fetch(url, body, headers, redirect_limit)
90 end
91
92 def self.fetcher
93 if @fetcher.nil?
94 @fetcher = StandardFetcher.new
95 end
96
97 return @fetcher
98 end
99
100 def self.fetcher=(fetcher)
101 @fetcher = fetcher
102 end
103
104 # Set the default fetcher to use the HTTP proxy defined in the environment
105 # variable 'http_proxy'.
106 def self.fetcher_use_env_http_proxy
107 proxy_string = ENV['http_proxy']
108 return unless proxy_string
109
110 proxy_uri = URI.parse(proxy_string)
111 @fetcher = StandardFetcher.new(proxy_uri.host, proxy_uri.port,
112 proxy_uri.user, proxy_uri.password)
113 end
114
115 class StandardFetcher
116
117 USER_AGENT = "ruby-openid/#{OpenID::VERSION} (#{RUBY_PLATFORM})"
118
119 REDIRECT_LIMIT = 5
120 TIMEOUT = 60
121
122 attr_accessor :ca_file
123 attr_accessor :timeout
124
125 # I can fetch through a HTTP proxy; arguments are as for Net::HTTP::Proxy.
126 def initialize(proxy_addr=nil, proxy_port=nil,
127 proxy_user=nil, proxy_pass=nil)
128 @ca_file = nil
129 @proxy = Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user, proxy_pass)
130 @timeout = TIMEOUT
131 end
132
133 def supports_ssl?(conn)
134 return conn.respond_to?(:use_ssl=)
135 end
136
137 def make_http(uri)
138 http = @proxy.new(uri.host, uri.port)
139 http.read_timeout = @timeout
140 http.open_timeout = @timeout
141 return http
142 end
143
144 def set_verified(conn, verify)
145 if verify
146 conn.verify_mode = OpenSSL::SSL::VERIFY_PEER
147 else
148 conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
149 end
150 end
151
152 def make_connection(uri)
153 conn = make_http(uri)
154
155 if !conn.is_a?(Net::HTTP)
156 raise RuntimeError, sprintf("Expected Net::HTTP object from make_http; got %s",
157 conn.class)
158 end
159
160 if uri.scheme == 'https'
161 if supports_ssl?(conn)
162
163 conn.use_ssl = true
164
165 if @ca_file
166 set_verified(conn, true)
167 conn.ca_file = @ca_file
168 else
169 Util.log("WARNING: making https request to #{uri} without verifying " +
170 "server certificate; no CA path was specified.")
171 set_verified(conn, false)
172 end
173 else
174 raise RuntimeError, "SSL support not found; cannot fetch #{uri}"
175 end
176 end
177
178 return conn
179 end
180
181 def fetch(url, body=nil, headers=nil, redirect_limit=REDIRECT_LIMIT)
182 unparsed_url = url.dup
183 url = URI::parse(url)
184 if url.nil?
185 raise FetchingError, "Invalid URL: #{unparsed_url}"
186 end
187
188 headers ||= {}
189 headers['User-agent'] ||= USER_AGENT
190 headers['Range'] ||= "0-#{MAX_RESPONSE_KB*1024}"
191
192 begin
193 conn = make_connection(url)
194 response = nil
195
196 response = conn.start {
197 # Check the certificate against the URL's hostname
198 if supports_ssl?(conn) and conn.use_ssl?
199 conn.post_connection_check(url.host)
200 end
201
202 if body.nil?
203 conn.request_get(url.request_uri, headers)
204 else
205 headers["Content-type"] ||= "application/x-www-form-urlencoded"
206 conn.request_post(url.request_uri, body, headers)
207 end
208 }
209 rescue RuntimeError => why
210 raise why
211 rescue OpenSSL::SSL::SSLError => why
212 raise SSLFetchingError, "Error connecting to SSL URL #{url}: #{why}"
213 rescue FetchingError => why
214 raise why
215 rescue Exception => why
216 # Things we've caught here include a Timeout::Error, which descends
217 # from SignalException.
218 raise FetchingError, "Error fetching #{url}: #{why}"
219 end
220
221 case response
222 when Net::HTTPRedirection
223 if redirect_limit <= 0
224 raise HTTPRedirectLimitReached.new(
225 "Too many redirects, not fetching #{response['location']}")
226 end
227 begin
228 return fetch(response['location'], body, headers, redirect_limit - 1)
229 rescue HTTPRedirectLimitReached => e
230 raise e
231 rescue FetchingError => why
232 raise FetchingError, "Error encountered in redirect from #{url}: #{why}"
233 end
234 else
235 return HTTPResponse._from_net_response(response, unparsed_url)
236 end
237 end
238 end
239 end
Generated using the rcov code coverage analysis tool for Ruby version 0.7.0.