diff --git a/lib/httpx/options.rb b/lib/httpx/options.rb index 06371b71..9ea4586a 100644 --- a/lib/httpx/options.rb +++ b/lib/httpx/options.rb @@ -13,6 +13,9 @@ module HTTPX CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60 REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil + # default value used for "user-agent" header, when not overridden. + USER_AGENT = "httpx.rb/#{VERSION}".freeze # rubocop:disable Style/RedundantFreeze + @options_names = [] class << self @@ -144,6 +147,7 @@ module HTTPX instance_variable_set(:"@#{k}", value) end + do_initialize freeze end @@ -369,6 +373,8 @@ module HTTPX end def option_headers(value) + value = value.dup if value.frozen? + headers_class.new(value) end @@ -395,6 +401,20 @@ module HTTPX Array(value) end + # called after all options are initialized + def do_initialize + hs = @headers + + # initialized default request headers + hs["user-agent"] = USER_AGENT unless hs.key?("user-agent") + hs["accept"] = "*/*" unless hs.key?("accept") + if hs.key?("range") + hs.delete("accept-encoding") + else + hs["accept-encoding"] = supported_compression_formats unless hs.key?("accept-encoding") + end + end + def access_option(obj, k, ivar_map) case obj when Hash diff --git a/lib/httpx/request.rb b/lib/httpx/request.rb index 067fa511..1da6303c 100644 --- a/lib/httpx/request.rb +++ b/lib/httpx/request.rb @@ -14,9 +14,6 @@ module HTTPX ALLOWED_URI_SCHEMES = %w[https http].freeze - # default value used for "user-agent" header, when not overridden. - USER_AGENT = "httpx.rb/#{VERSION}".freeze # rubocop:disable Style/RedundantFreeze - # the upcased string HTTP verb for this request. attr_reader :verb @@ -75,16 +72,6 @@ module HTTPX @headers = options.headers.dup merge_headers(params.delete(:headers)) if params.key?(:headers) - @headers["user-agent"] ||= USER_AGENT - @headers["accept"] ||= "*/*" - - # forego compression in the Range request case - if @headers.key?("range") - @headers.delete("accept-encoding") - else - @headers["accept-encoding"] ||= options.supported_compression_formats - end - @query_params = params.delete(:params) if params.key?(:params) @body = options.request_body_class.new(@headers, options, **params) @@ -166,6 +153,9 @@ module HTTPX # merges +h+ into the instance of HTTPX::Headers of the request. def merge_headers(h) @headers = @headers.merge(h) + return unless @headers.key?("range") + + @headers.delete("accept-encoding") end # the URI scheme of the request +uri+. diff --git a/sig/options.rbs b/sig/options.rbs index 59106ad4..873f391d 100644 --- a/sig/options.rbs +++ b/sig/options.rbs @@ -17,6 +17,7 @@ module HTTPX DEFAULT_OPTIONS: Hash[Symbol, untyped] REQUEST_BODY_IVARS: Array[Symbol] + USER_AGENT: String type timeout_type = :connect_timeout | :settings_timeout | :close_handshake_timeout | :operation_timeout | :keep_alive_timeout | :read_timeout | :write_timeout | :request_timeout type timeout = Hash[timeout_type, Numeric?] @@ -146,6 +147,8 @@ module HTTPX def initialize: (?options options) -> void + def do_initialize: (?options options) -> void + def access_option: (Hash[Symbol, untyped] | Object | nil obj, Symbol k, Hash[Symbol, Symbol]? ivar_map) -> untyped # integer diff --git a/test/compression_test.rb b/test/compression_test.rb deleted file mode 100644 index ebe54139..00000000 --- a/test/compression_test.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require_relative "test_helper" - -class CompressionTest < Minitest::Test - include HTTPX - - def test_ignore_encoding_on_range - request = HTTPX::Session.new.build_request("GET", "http://example.com") - assert request.headers.key?("accept-encoding") - range_request = HTTPX::Session.new.build_request("GET", "http://example.com", headers: { "range" => "bytes=100-200" }) - assert !range_request.headers.key?("accept-encoding") - end -end diff --git a/test/options_test.rb b/test/options_test.rb index 76fed323..50aad908 100644 --- a/test/options_test.rb +++ b/test/options_test.rb @@ -24,9 +24,12 @@ class OptionsTest < Minitest::Test def test_options_headers opt1 = Options.new - assert opt1.headers.to_a.empty?, "headers should be empty" - opt2 = Options.new(:headers => { "accept" => "*/*" }) - assert opt2.headers.to_a == [%w[accept */*]], "headers are unexpected" + assert opt1.headers["user-agent"].start_with?("httpx"), "should set default user-agent" + assert opt1.headers["accept"], "should set default accet" + assert opt1.headers.to_a.include?(%w[accept */*]), "accept headers are unexpected" + opt2 = Options.new(:headers => { "user-agent" => nil }) + assert opt2.headers["user-agent"] == "", "should remove default user-agent" + assert opt2.headers.to_a.include?(%w[accept */*]), "accept headers are unexpected" end def test_options_headers_with_instance @@ -118,7 +121,13 @@ class OptionsTest < Minitest::Test :supported_compression_formats => %w[gzip deflate], :compress_request_body => true, :decompress_response_body => true, - :headers => { "accept" => "xml", "foo" => "foo", "bar" => "bar" }, + :headers => { + "accept" => "xml", + "foo" => "foo", + "bar" => "bar", + "user-agent" => "httpx.rb/#{HTTPX::VERSION}", + "accept-encoding" => "gzip, deflate", + }, :max_concurrent_requests => nil, :request_class => bar.request_class, :response_class => bar.response_class, diff --git a/test/support/requests/compression.rb b/test/support/requests/compression.rb index b97a84e4..6588bf89 100644 --- a/test/support/requests/compression.rb +++ b/test/support/requests/compression.rb @@ -107,6 +107,18 @@ module Requests end end + def test_compression_ignore_encoding_on_range + uri = build_uri("/get") + response = HTTPX.get(uri) + verify_status(response, 200) + body = json_body(response) + assert body["headers"].key?("Accept-Encoding") + + response = HTTPX.get(uri, headers: { "range" => "bytes=100-200" }) + body = json_body(response) + assert !body["headers"].key?("Accept-Encoding") + end + private def inflate_test_data(string)