Skip to content

Commit

Permalink
feat(uri_normalizer): add support for users to provide custom uri nor…
Browse files Browse the repository at this point in the history
…malizer
  • Loading branch information
mamoonraja committed Feb 21, 2019
1 parent e21740d commit c862d3e
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 18 deletions.
4 changes: 2 additions & 2 deletions .rubocop.yml
Expand Up @@ -31,10 +31,10 @@ Metrics/ClassLength:

# TODO: Lower to 6
Metrics/CyclomaticComplexity:
Max: 8
Max: 9

Metrics/PerceivedComplexity:
Max: 8
Max: 9

# TODO: Lower to 80
Metrics/LineLength:
Expand Down
4 changes: 3 additions & 1 deletion lib/http/client.rb
Expand Up @@ -43,13 +43,15 @@ def build_request(verb, uri, opts = {}) # rubocop:disable Style/OptionHash
headers = make_request_headers(opts)
body = make_request_body(opts, headers)
proxy = opts.proxy
uri_normalizer = opts.features[:uri_normalizer]&.method(:normalize_uri)

req = HTTP::Request.new(
:verb => verb,
:uri => uri,
:headers => headers,
:proxy => proxy,
:body => body
:body => body,
:uri_normalizer => uri_normalizer
)

opts.features.inject(req) do |request, (_name, feature)|
Expand Down
1 change: 1 addition & 0 deletions lib/http/feature.rb
Expand Up @@ -20,3 +20,4 @@ def wrap_response(response)
require "http/features/auto_deflate"
require "http/features/logging"
require "http/features/instrumentation"
require "http/features/uri_normalizer"
3 changes: 2 additions & 1 deletion lib/http/features/auto_deflate.rb
Expand Up @@ -32,7 +32,8 @@ def wrap_request(request)
:uri => request.uri,
:headers => request.headers,
:proxy => request.proxy,
:body => deflated_body(request.body)
:body => deflated_body(request.body),
:uri_normalizer => request.uri_normalizer
)
end

Expand Down
32 changes: 32 additions & 0 deletions lib/http/features/uri_normalizer.rb
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module HTTP
module Features
class UriNormalizer < Feature
attr_reader :custom_uri_normalizer

def initialize(custom_uri_normalizer: DefaultUriNormalizer.new)
@custom_uri_normalizer = custom_uri_normalizer
end

def normalize_uri(uri)
custom_uri_normalizer.normalize_uri(uri)
end

class DefaultUriNormalizer
def normalize_uri(uri)
uri = HTTP::URI.parse uri
HTTP::URI.new(
:scheme => uri.normalized_scheme,
:authority => uri.normalized_authority,
:path => uri.normalized_path,
:query => uri.query,
:fragment => uri.normalized_fragment
)
end
end

HTTP::Options.register_feature(:uri_normalizer, self)
end
end
end
32 changes: 18 additions & 14 deletions lib/http/request.rb
Expand Up @@ -66,20 +66,24 @@ class UnsupportedSchemeError < RequestError; end
# Scheme is normalized to be a lowercase symbol e.g. :http, :https
attr_reader :scheme

attr_reader :uri_normalizer

# "Request URI" as per RFC 2616
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html
attr_reader :uri
attr_reader :proxy, :body, :version

# @option opts [String] :version
# @option opts [#to_s] :verb HTTP request method
# @option opts [#to_s] :uri_normalizer uri normalizer
# @option opts [HTTP::URI, #to_s] :uri
# @option opts [Hash] :headers
# @option opts [Hash] :proxy
# @option opts [String, Enumerable, IO, nil] :body
def initialize(opts)
@verb = opts.fetch(:verb).to_s.downcase.to_sym
@uri = normalize_uri(opts.fetch(:uri))
@uri_normalizer = opts[:uri_normalizer] ? opts.fetch(:uri_normalizer) : self.class.method(:normalize_uri)
@uri = @uri_normalizer.call(opts.fetch(:uri))
@scheme = @uri.scheme.to_s.downcase.to_sym if @uri.scheme

raise(UnsupportedMethodError, "unknown method: #{verb}") unless METHODS.include?(@verb)
Expand Down Expand Up @@ -196,6 +200,19 @@ def inspect
"#<#{self.class}/#{@version} #{verb.to_s.upcase} #{uri}>"
end

# @return [HTTP::URI] URI with all components but query being normalized.
def self.normalize_uri(uri)
uri = HTTP::URI.parse uri

HTTP::URI.new(
:scheme => uri.normalized_scheme,
:authority => uri.normalized_authority,
:path => uri.normalized_path,
:query => uri.query,
:fragment => uri.normalized_fragment
)
end

private

# @!attribute [r] host
Expand All @@ -212,18 +229,5 @@ def port
def default_host_header_value
PORTS[@scheme] != port ? "#{host}:#{port}" : host
end

# @return [HTTP::URI] URI with all componentes but query being normalized.
def normalize_uri(uri)
uri = HTTP::URI.parse uri

HTTP::URI.new(
:scheme => uri.normalized_scheme,
:authority => uri.normalized_authority,
:path => uri.normalized_path,
:query => uri.query,
:fragment => uri.normalized_fragment
)
end
end
end
32 changes: 32 additions & 0 deletions spec/lib/http_spec.rb
Expand Up @@ -430,6 +430,38 @@ def setsockopt(*args)
expect(response.to_s).to eq("#{body}-deflated")
end
end

context "with :uri_normalizer" do
class CustomUriNormalizer
def normalize_uri(uri)
uri = HTTP::URI.parse uri
HTTP::URI.new(
:scheme => uri.normalized_scheme,
:authority => uri.normalized_authority,
:path => "uri_normalizer/custom",
:query => uri.query,
:fragment => uri.normalized_fragment
)
end
end

it "Use the defaul Uri normalizer when user does not use uri normalizer" do
response = HTTP.get HTTP::URI.parse "#{dummy.endpoint}/uri_normalizer/%EF%BC%A1%EF%BC%A2%EF%BC%A3"
expect(response.to_s).to eq("default normalizer")
end

it "Use the custom Uri Normalizer method" do
client = HTTP.use(:uri_normalizer => {:custom_uri_normalizer => CustomUriNormalizer.new})
response = client.get("#{dummy.endpoint}/uri_normalizer/%EF%BC%A1%EF%BC%A2%EF%BC%A3")
expect(response.to_s).to eq("custom normalizer")
end

it "Use the default Uri normalizer when user does not specify custom uri normalizer" do
client = HTTP.use :uri_normalizer
response = client.get("#{dummy.endpoint}/uri_normalizer/%EF%BC%A1%EF%BC%A2%EF%BC%A3")
expect(response.to_s).to eq("default normalizer")
end
end
end

it "unifies socket errors into HTTP::ConnectionError" do
Expand Down
10 changes: 10 additions & 0 deletions spec/support/dummy_server/servlet.rb
Expand Up @@ -148,6 +148,16 @@ def do_#{method.upcase}(req, res)
res.body = req.body
end

get "/uri_normalizer/ABC" do |_req, res|
res.status = 200
res.body = "default normalizer"
end

get "/uri_normalizer/custom" do |_req, res|
res.status = 200
res.body = "custom normalizer"
end

post "/encoded-body" do |req, res|
res.status = 200

Expand Down

0 comments on commit c862d3e

Please sign in to comment.