Skip to content

Commit bac4366

Browse files
committed
Add HTTP::URI::InvalidError and raise ArgumentError for nil/empty URIs
Define HTTP::URI::InvalidError and raise it from URI.parse for malformed URIs, moving error handling to the URI layer where it belongs. Raise ArgumentError for nil and empty string URIs, since those are programming mistakes rather than parse failures. Closes #723.
1 parent 7cd634d commit bac4366

6 files changed

Lines changed: 39 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3838
- Instrumentation feature now correctly starts a new span for each retry
3939
attempt, fixing `NoMethodError` with `ActiveSupport::Notifications` when
4040
using `.retriable` with the instrumentation feature (#826)
41-
- Raise `HTTP::Request::InvalidURIError` for invalid URIs (nil, empty string,
42-
missing scheme, malformed) instead of confusing `UnsupportedSchemeError` or
43-
`Addressable::URI::InvalidURIError` (#565)
41+
- Raise `HTTP::URI::InvalidError` for malformed or schemeless URIs and
42+
`ArgumentError` for nil or empty URIs, instead of confusing
43+
`UnsupportedSchemeError` or `Addressable::URI::InvalidURIError` (#565)
4444
- Strip `Authorization` header when following redirects to a different origin
4545
(scheme, host, or port) to prevent credential leakage (#770)
4646
- AutoInflate now preserves the response charset encoding instead of

lib/http/request.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ class UnsupportedMethodError < RequestError; end
2626
# The scheme of given URI was not understood
2727
class UnsupportedSchemeError < RequestError; end
2828

29-
# The URI given was not valid
30-
class InvalidURIError < RequestError; end
31-
3229
# Default User-Agent header value
3330
USER_AGENT = "http.rb/#{HTTP::VERSION}".freeze
3431

@@ -335,18 +332,19 @@ def default_host_header_value
335332
# @return [void]
336333
# @api private
337334
def parse_uri!(uri)
335+
raise ArgumentError, "uri is nil" if uri.nil?
336+
raise ArgumentError, "uri is empty" if uri.is_a?(String) && uri.empty?
337+
338338
@uri = @uri_normalizer.call(uri)
339339
@scheme = @uri.scheme.to_s.downcase.to_sym if @uri.scheme
340-
rescue TypeError, Addressable::URI::InvalidURIError
341-
raise InvalidURIError, "invalid URI: #{uri.inspect}"
342340
end
343341

344342
# Validate HTTP method and URI scheme
345343
# @return [void]
346344
# @api private
347345
def validate_method_and_scheme!
348346
raise(UnsupportedMethodError, "unknown method: #{verb}") unless METHODS.include?(@verb)
349-
raise(InvalidURIError, "invalid URI: #{@uri}") unless @scheme
347+
raise(HTTP::URI::InvalidError, "invalid URI: #{@uri}") unless @scheme
350348
raise(UnsupportedSchemeError, "unknown scheme: #{scheme}") unless SCHEMES.include?(@scheme)
351349
end
352350

lib/http/uri.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ module HTTP
77
class URI
88
extend Forwardable
99

10+
# The URI given was not valid
11+
class InvalidError < HTTP::RequestError; end
12+
1013
def_delegators :@uri, :scheme, :normalized_scheme, :scheme=
1114
def_delegators :@uri, :user, :normalized_user, :user=
1215
def_delegators :@uri, :password, :normalized_password, :password=
@@ -77,6 +80,8 @@ def self.parse(uri)
7780
return uri if uri.is_a?(self)
7881

7982
new(Addressable::URI.parse(uri))
83+
rescue TypeError, Addressable::URI::InvalidURIError
84+
raise InvalidError, "invalid URI: #{uri.inspect}"
8085
end
8186

8287
# Encodes key/value pairs as application/x-www-form-urlencoded

sig/http.rbs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,9 @@ module HTTP
562562
class URI
563563
extend Forwardable
564564

565+
class InvalidError < RequestError
566+
end
567+
565568
@uri: Addressable::URI
566569
@hash: Integer
567570

@@ -661,9 +664,6 @@ module HTTP
661664
class UnsupportedSchemeError < RequestError
662665
end
663666

664-
class InvalidURIError < RequestError
665-
end
666-
667667
USER_AGENT: String
668668
METHODS: Array[verb]
669669
SCHEMES: Array[Symbol]

test/http/request_test.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,29 @@
2121
assert_kind_of HTTP::Headers, request.headers
2222
end
2323

24-
it "raises InvalidURIError for URI without scheme" do
25-
err = assert_raises(HTTP::Request::InvalidURIError) do
24+
it "raises InvalidError for URI without scheme" do
25+
err = assert_raises(HTTP::URI::InvalidError) do
2626
HTTP::Request.new(verb: :get, uri: "example.com/")
2727
end
2828
assert_match(/invalid URI/, err.message)
2929
end
3030

31-
it "raises InvalidURIError for nil URI" do
32-
err = assert_raises(HTTP::Request::InvalidURIError) do
31+
it "raises ArgumentError for nil URI" do
32+
err = assert_raises(ArgumentError) do
3333
HTTP::Request.new(verb: :get, uri: nil)
3434
end
35-
assert_match(/invalid URI/, err.message)
35+
assert_equal "uri is nil", err.message
3636
end
3737

38-
it "raises InvalidURIError for empty string URI" do
39-
err = assert_raises(HTTP::Request::InvalidURIError) do
38+
it "raises ArgumentError for empty string URI" do
39+
err = assert_raises(ArgumentError) do
4040
HTTP::Request.new(verb: :get, uri: "")
4141
end
42-
assert_match(/invalid URI/, err.message)
42+
assert_equal "uri is empty", err.message
4343
end
4444

45-
it "raises InvalidURIError for malformed URI" do
46-
err = assert_raises(HTTP::Request::InvalidURIError) do
45+
it "raises InvalidError for malformed URI" do
46+
err = assert_raises(HTTP::URI::InvalidError) do
4747
HTTP::Request.new(verb: :get, uri: ":")
4848
end
4949
assert_match(/invalid URI/, err.message)

test/http/uri_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,20 @@
138138
# is_a?(self) returns true for subclasses; instance_of? does not
139139
assert_same sub_uri, HTTP::URI.parse(sub_uri)
140140
end
141+
142+
it "raises InvalidError for nil" do
143+
err = assert_raises(HTTP::URI::InvalidError) do
144+
HTTP::URI.parse(nil)
145+
end
146+
assert_equal "invalid URI: nil", err.message
147+
end
148+
149+
it "raises InvalidError for malformed URI" do
150+
err = assert_raises(HTTP::URI::InvalidError) do
151+
HTTP::URI.parse(":")
152+
end
153+
assert_equal 'invalid URI: ":"', err.message
154+
end
141155
end
142156

143157
describe ".form_encode" do

0 commit comments

Comments
 (0)