diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a4e4f7a..ef16c437 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - ruby: [ ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2 ] + ruby: [ ruby-3.0, ruby-3.1, ruby-3.2 ] os: [ ubuntu-latest ] steps: @@ -43,7 +43,7 @@ jobs: strategy: matrix: - ruby: [ jruby-9.3 ] + ruby: [ jruby-9.4 ] os: [ ubuntu-latest ] steps: @@ -76,7 +76,7 @@ jobs: - uses: ruby/setup-ruby@v1 with: - ruby-version: 2.6 + ruby-version: ruby-3.0 bundler-cache: true - name: bundle exec rubocop diff --git a/.rubocop.yml b/.rubocop.yml index e2956f84..545804e1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,8 @@ +require: + - rubocop-performance + - rubocop-rake + - rubocop-rspec + inherit_from: - .rubocop_todo.yml - .rubocop/layout.yml @@ -7,5 +12,6 @@ inherit_from: AllCops: DefaultFormatter: fuubar DisplayCopNames: true + DisplayStyleGuide: true NewCops: enable - TargetRubyVersion: 2.6 + TargetRubyVersion: 3.0 diff --git a/.rubocop/metrics.yml b/.rubocop/metrics.yml index 944c40ae..d079410e 100644 --- a/.rubocop/metrics.yml +++ b/.rubocop/metrics.yml @@ -1,4 +1,26 @@ Metrics/BlockLength: + CountAsOne: + - array + - heredoc + - method_call Exclude: - 'spec/**/*.rb' - - '*.gemspec' \ No newline at end of file + - '*.gemspec' + +Metrics/ClassLength: + CountAsOne: + - array + - heredoc + - method_call + +Metrics/MethodLength: + CountAsOne: + - array + - heredoc + - method_call + +Metrics/ModuleLength: + CountAsOne: + - array + - heredoc + - method_call diff --git a/.rubocop/style.yml b/.rubocop/style.yml index a3901789..35e4c5f1 100644 --- a/.rubocop/style.yml +++ b/.rubocop/style.yml @@ -17,6 +17,10 @@ Style/HashSyntax: Style/OptionHash: Enabled: true +# Using explicit `./` makes code more consistent +Style/RedundantCurrentDirectoryInPath: + Enabled: false + Style/RescueStandardError: Enabled: true EnforcedStyle: implicit diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 47d5f9fe..6687b116 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 100` -# on 2022-06-16 14:35:44 UTC using RuboCop version 1.30.1. +# on 2023-10-17 15:09:44 UTC using RuboCop version 1.57.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -8,13 +8,13 @@ # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Include. +# Configuration parameters: Severity, Include. # Include: **/*.gemspec Gemspec/DeprecatedAttributeAssignment: Exclude: - 'http.gemspec' -# Offense count: 53 +# Offense count: 55 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: leading, trailing @@ -30,7 +30,7 @@ Layout/DotPosition: - 'spec/lib/http_spec.rb' - 'spec/support/http_handling_shared.rb' -# Offense count: 176 +# Offense count: 206 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact @@ -54,16 +54,19 @@ Layout/SpaceInsideHashLiteralBraces: - 'spec/support/http_handling_shared.rb' - 'spec/support/ssl_helper.rb' -# Offense count: 4 +# Offense count: 6 +# Configuration parameters: AllowedParentClasses. Lint/MissingSuper: Exclude: - 'lib/http/features/auto_deflate.rb' - 'lib/http/features/instrumentation.rb' - 'lib/http/features/logging.rb' - 'lib/http/features/normalize_uri.rb' + - 'spec/lib/http/client_spec.rb' + - 'spec/lib/http/features/instrumentation_spec.rb' # Offense count: 8 -# Configuration parameters: IgnoredMethods, CountRepeatedAttributes, Max. +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Exclude: - 'lib/http/chainable.rb' @@ -74,34 +77,6 @@ Metrics/AbcSize: - 'lib/http/request.rb' - 'lib/http/response.rb' -# Offense count: 70 -# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods. -# IgnoredMethods: refine -Metrics/BlockLength: - Exclude: - - '**/*.gemspec' - - 'spec/lib/http/client_spec.rb' - - 'spec/lib/http/connection_spec.rb' - - 'spec/lib/http/content_type_spec.rb' - - 'spec/lib/http/features/auto_deflate_spec.rb' - - 'spec/lib/http/features/auto_inflate_spec.rb' - - 'spec/lib/http/features/instrumentation_spec.rb' - - 'spec/lib/http/features/logging_spec.rb' - - 'spec/lib/http/headers/mixin_spec.rb' - - 'spec/lib/http/headers_spec.rb' - - 'spec/lib/http/options/merge_spec.rb' - - 'spec/lib/http/redirector_spec.rb' - - 'spec/lib/http/request/body_spec.rb' - - 'spec/lib/http/request/writer_spec.rb' - - 'spec/lib/http/request_spec.rb' - - 'spec/lib/http/response/body_spec.rb' - - 'spec/lib/http/response/parser_spec.rb' - - 'spec/lib/http/response/status_spec.rb' - - 'spec/lib/http/response_spec.rb' - - 'spec/lib/http/uri_spec.rb' - - 'spec/lib/http_spec.rb' - - 'spec/support/http_handling_shared.rb' - # Offense count: 4 # Configuration parameters: CountComments, Max, CountAsOne. Metrics/ClassLength: @@ -112,25 +87,23 @@ Metrics/ClassLength: - 'lib/http/request.rb' # Offense count: 2 -# Configuration parameters: IgnoredMethods, Max. +# Configuration parameters: AllowedMethods, AllowedPatterns, Max. Metrics/CyclomaticComplexity: Exclude: - 'lib/http/chainable.rb' - 'lib/http/client.rb' -# Offense count: 18 -# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods. +# Offense count: 15 +# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Exclude: - 'lib/http/chainable.rb' - 'lib/http/client.rb' - 'lib/http/connection.rb' - - 'lib/http/features/auto_deflate.rb' - 'lib/http/features/auto_inflate.rb' - 'lib/http/headers.rb' - 'lib/http/options.rb' - 'lib/http/redirector.rb' - - 'lib/http/request.rb' - 'lib/http/response.rb' - 'lib/http/response/body.rb' - 'lib/http/timeout/global.rb' @@ -141,6 +114,268 @@ Metrics/ModuleLength: Exclude: - 'lib/http/chainable.rb' +# Offense count: 1 +# Configuration parameters: MinSize. +Performance/CollectionLiteralInLoop: + Exclude: + - 'Rakefile' + +# Offense count: 4 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: OnlySumOrWithInitialValue. +Performance/Sum: + Exclude: + - 'lib/http/redirector.rb' + - 'spec/lib/http/request/body_spec.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Performance/UnfreezeString: + Exclude: + - 'spec/lib/http/connection_spec.rb' + +# Offense count: 3 +RSpec/AnyInstance: + Exclude: + - 'spec/lib/http/client_spec.rb' + +# Offense count: 4 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/BeEq: + Exclude: + - 'spec/lib/http/client_spec.rb' + - 'spec/lib/http/redirector_spec.rb' + +# Offense count: 49 +# Configuration parameters: Prefixes, AllowedPatterns. +# Prefixes: when, with, without +RSpec/ContextWording: + Exclude: + - 'spec/lib/http/client_spec.rb' + - 'spec/lib/http/features/auto_inflate_spec.rb' + - 'spec/lib/http/headers_spec.rb' + - 'spec/lib/http/redirector_spec.rb' + - 'spec/lib/http/request_spec.rb' + - 'spec/lib/http/response/parser_spec.rb' + - 'spec/lib/http_spec.rb' + - 'spec/support/http_handling_shared.rb' + +# Offense count: 1 +# Configuration parameters: IgnoredMetadata. +RSpec/DescribeClass: + Exclude: + - '**/spec/features/**/*' + - '**/spec/requests/**/*' + - '**/spec/routing/**/*' + - '**/spec/system/**/*' + - '**/spec/views/**/*' + - 'spec/regression_specs.rb' + +# Offense count: 8 +RSpec/DescribeMethod: + Exclude: + - 'spec/lib/http/options/body_spec.rb' + - 'spec/lib/http/options/features_spec.rb' + - 'spec/lib/http/options/form_spec.rb' + - 'spec/lib/http/options/headers_spec.rb' + - 'spec/lib/http/options/json_spec.rb' + - 'spec/lib/http/options/merge_spec.rb' + - 'spec/lib/http/options/new_spec.rb' + - 'spec/lib/http/options/proxy_spec.rb' + +# Offense count: 132 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: SkipBlocks, EnforcedStyle. +# SupportedStyles: described_class, explicit +RSpec/DescribedClass: + Exclude: + - 'spec/lib/http/client_spec.rb' + - 'spec/lib/http/connection_spec.rb' + - 'spec/lib/http/features/auto_deflate_spec.rb' + - 'spec/lib/http/features/auto_inflate_spec.rb' + - 'spec/lib/http/features/instrumentation_spec.rb' + - 'spec/lib/http/options/body_spec.rb' + - 'spec/lib/http/options/features_spec.rb' + - 'spec/lib/http/options/form_spec.rb' + - 'spec/lib/http/options/headers_spec.rb' + - 'spec/lib/http/options/json_spec.rb' + - 'spec/lib/http/options/merge_spec.rb' + - 'spec/lib/http/options/new_spec.rb' + - 'spec/lib/http/options/proxy_spec.rb' + - 'spec/lib/http/request/body_spec.rb' + - 'spec/lib/http/request_spec.rb' + - 'spec/lib/http/response_spec.rb' + - 'spec/lib/http/uri/normalizer_spec.rb' + - 'spec/lib/http_spec.rb' + +# Offense count: 55 +# Configuration parameters: Max, CountAsOne. +RSpec/ExampleLength: + Exclude: + - 'spec/lib/http/client_spec.rb' + - 'spec/lib/http/connection_spec.rb' + - 'spec/lib/http/features/auto_deflate_spec.rb' + - 'spec/lib/http/features/logging_spec.rb' + - 'spec/lib/http/options/features_spec.rb' + - 'spec/lib/http/options/headers_spec.rb' + - 'spec/lib/http/options/merge_spec.rb' + - 'spec/lib/http/redirector_spec.rb' + - 'spec/lib/http/request/body_spec.rb' + - 'spec/lib/http/request/writer_spec.rb' + - 'spec/lib/http/response/body_spec.rb' + - 'spec/lib/http/uri_spec.rb' + - 'spec/lib/http_spec.rb' + - 'spec/support/http_handling_shared.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: CustomTransform, IgnoredWords, DisallowedExamples. +# DisallowedExamples: works +RSpec/ExampleWording: + Exclude: + - 'spec/support/http_handling_shared.rb' + +# Offense count: 15 +RSpec/ExpectInHook: + Exclude: + - 'spec/lib/http/connection_spec.rb' + - 'spec/lib/http/request/writer_spec.rb' + +# Offense count: 1 +# Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly. +# Include: **/*_spec*rb*, **/spec/**/* +RSpec/FilePath: + Exclude: + - 'spec/regression_specs.rb' + +# Offense count: 6 +# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns. +RSpec/IndexedLet: + Exclude: + - 'spec/lib/http/request/body_spec.rb' + +# Offense count: 2 +# Configuration parameters: AssignmentOnly. +RSpec/InstanceVariable: + Exclude: + - 'spec/lib/http/redirector_spec.rb' + +# Offense count: 40 +# Configuration parameters: . +# SupportedStyles: have_received, receive +RSpec/MessageSpies: + EnforcedStyle: receive + +# Offense count: 59 +# Configuration parameters: Max. +RSpec/MultipleExpectations: + Exclude: + - 'spec/lib/http/client_spec.rb' + - 'spec/lib/http/connection_spec.rb' + - 'spec/lib/http/features/auto_deflate_spec.rb' + - 'spec/lib/http/headers_spec.rb' + - 'spec/lib/http/options/body_spec.rb' + - 'spec/lib/http/options/features_spec.rb' + - 'spec/lib/http/options/form_spec.rb' + - 'spec/lib/http/options/headers_spec.rb' + - 'spec/lib/http/options/json_spec.rb' + - 'spec/lib/http/options/merge_spec.rb' + - 'spec/lib/http/options/proxy_spec.rb' + - 'spec/lib/http/redirector_spec.rb' + - 'spec/lib/http/response/body_spec.rb' + - 'spec/lib/http/response/parser_spec.rb' + - 'spec/lib/http/uri_spec.rb' + - 'spec/lib/http_spec.rb' + - 'spec/support/http_handling_shared.rb' + +# Offense count: 8 +# Configuration parameters: AllowSubject, Max. +RSpec/MultipleMemoizedHelpers: + Exclude: + - 'spec/lib/http/response_spec.rb' + - 'spec/lib/http/uri_spec.rb' + +# Offense count: 58 +# Configuration parameters: EnforcedStyle, IgnoreSharedExamples. +# SupportedStyles: always, named_only +RSpec/NamedSubject: + Exclude: + - 'spec/lib/http/features/auto_deflate_spec.rb' + - 'spec/lib/http/options_spec.rb' + - 'spec/lib/http/request/body_spec.rb' + - 'spec/lib/http/request_spec.rb' + - 'spec/lib/http/response/body_spec.rb' + - 'spec/lib/http/response/parser_spec.rb' + - 'spec/lib/http/response/status_spec.rb' + - 'spec/lib/http/response_spec.rb' + +# Offense count: 15 +# Configuration parameters: Max, AllowedGroups. +RSpec/NestedGroups: + Exclude: + - 'spec/lib/http/client_spec.rb' + - 'spec/lib/http/redirector_spec.rb' + - 'spec/lib/http/request_spec.rb' + - 'spec/lib/http_spec.rb' + +# Offense count: 2 +# Configuration parameters: AllowedPatterns. +# AllowedPatterns: ^expect_, ^assert_ +RSpec/NoExpectationExample: + Exclude: + - 'spec/lib/http/request/writer_spec.rb' + - 'spec/support/http_handling_shared.rb' + +# Offense count: 4 +RSpec/PendingWithoutReason: + Exclude: + - 'spec/lib/http/client_spec.rb' + - 'spec/lib/http_spec.rb' + +# Offense count: 4 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/ReceiveMessages: + Exclude: + - 'spec/lib/http/client_spec.rb' + +# Offense count: 1 +# Configuration parameters: Include. +# Include: **/*_spec*rb*, **/spec/**/* +RSpec/SpecFilePathSuffix: + Exclude: + - 'spec/regression_specs.rb' + +# Offense count: 14 +RSpec/StubbedMock: + Exclude: + - 'spec/lib/http/connection_spec.rb' + - 'spec/lib/http/request/writer_spec.rb' + - 'spec/lib/http/response/body_spec.rb' + +# Offense count: 1 +# Configuration parameters: EnforcedStyle, AllowedPatterns. +# SupportedStyles: snake_case, camelCase +RSpec/VariableName: + Exclude: + - 'spec/lib/http/headers_spec.rb' + +# Offense count: 11 +# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. +RSpec/VerifiedDoubles: + Exclude: + - 'spec/lib/http/connection_spec.rb' + - 'spec/lib/http/headers_spec.rb' + - 'spec/lib/http/response/body_spec.rb' + - 'spec/lib/http/response/status_spec.rb' + - 'spec/lib/http/response_spec.rb' + - 'spec/lib/http_spec.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Rake/Desc: + Exclude: + - 'Rakefile' + # Offense count: 1 Security/CompoundHash: Exclude: @@ -196,9 +431,15 @@ Style/OptionalBooleanParameter: - 'lib/http/timeout/per_operation.rb' - 'lib/http/uri.rb' +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +Style/ZeroLengthPredicate: + Exclude: + - 'lib/http/features/auto_deflate.rb' + # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns. +# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. # URISchemes: http, https Layout/LineLength: Exclude: diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6b79e597 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed + +- **BREAKING** Process features in reverse order (#766) + +### Removed + +- **BREAKING** Drop Ruby 2.x support + +[unreleased]: https://github.com/httprb/http/compare/v5.1.1...HEAD diff --git a/CHANGES.md b/CHANGES_OLD.md similarity index 100% rename from CHANGES.md rename to CHANGES_OLD.md diff --git a/Gemfile b/Gemfile index 0d4e7fe8..712de1c0 100644 --- a/Gemfile +++ b/Gemfile @@ -27,10 +27,10 @@ group :test do gem "backports" - gem "rubocop", "~> 1.30.0" - gem "rubocop-performance" - gem "rubocop-rake" - gem "rubocop-rspec" + gem "rubocop", "~> 1.57.0" + gem "rubocop-performance", "~> 1.19.1" + gem "rubocop-rake", "~> 0.6.0" + gem "rubocop-rspec", "~> 2.24.1" gem "simplecov", :require => false gem "simplecov-lcov", :require => false diff --git a/README.md b/README.md index 5ac0c511..db2a29a3 100644 --- a/README.md +++ b/README.md @@ -110,12 +110,10 @@ and call `#readpartial` on it repeatedly until it returns `nil`: This library aims to support and is [tested against][build-link] the following Ruby versions: -- JRuby 9.3 -- Ruby 2.6 -- Ruby 2.7 - Ruby 3.0 - Ruby 3.1 - Ruby 3.2 +- JRuby 9.4 If something doesn't work on one of these versions, it's a bug. diff --git a/http.gemspec b/http.gemspec index 995ce424..36b1d51b 100644 --- a/http.gemspec +++ b/http.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |gem| gem.require_paths = ["lib"] gem.version = HTTP::VERSION - gem.required_ruby_version = ">= 2.6" + gem.required_ruby_version = ">= 3.0" gem.add_runtime_dependency "addressable", "~> 2.8" gem.add_runtime_dependency "base64", "~> 0.1" @@ -33,8 +33,6 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency "http-form_data", "~> 2.2" gem.add_runtime_dependency "llhttp-ffi", "~> 0.5.0" - gem.add_development_dependency "bundler", "~> 2.0" - gem.metadata = { "source_code_uri" => "https://github.com/httprb/http", "wiki_uri" => "https://github.com/httprb/http/wiki", diff --git a/lib/http/client.rb b/lib/http/client.rb index af29f330..494b2913 100644 --- a/lib/http/client.rb +++ b/lib/http/client.rb @@ -16,7 +16,7 @@ class Client extend Forwardable include Chainable - HTTP_OR_HTTPS_RE = %r{^https?://}i.freeze + HTTP_OR_HTTPS_RE = %r{^https?://}i def initialize(default_options = {}) @default_options = HTTP::Options.new(default_options) @@ -132,7 +132,7 @@ def verify_connection!(uri) # If we get into a bad state (eg, Timeout.timeout ensure being killed) # close the connection to prevent potential for mixed responses. - return close if @state == :dirty + close if @state == :dirty end # Merges query params if needed diff --git a/lib/http/content_type.rb b/lib/http/content_type.rb index aa40d3a3..44757573 100644 --- a/lib/http/content_type.rb +++ b/lib/http/content_type.rb @@ -2,8 +2,8 @@ module HTTP class ContentType - MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}.freeze - CHARSET_RE = /;\s*charset=([^;]+)/i.freeze + MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)} + CHARSET_RE = /;\s*charset=([^;]+)/i attr_accessor :mime_type, :charset diff --git a/lib/http/headers.rb b/lib/http/headers.rb index 104552f8..7d48b46f 100644 --- a/lib/http/headers.rb +++ b/lib/http/headers.rb @@ -13,11 +13,11 @@ class Headers include Enumerable # Matches HTTP header names when in "Canonical-Http-Format" - CANONICAL_NAME_RE = /\A[A-Z][a-z]*(?:-[A-Z][a-z]*)*\z/.freeze + CANONICAL_NAME_RE = /\A[A-Z][a-z]*(?:-[A-Z][a-z]*)*\z/ # Matches valid header field name according to RFC. # @see http://tools.ietf.org/html/rfc7230#section-3.2 - COMPLIANT_NAME_RE = /\A[A-Za-z0-9!#$%&'*+\-.^_`|~]+\z/.freeze + COMPLIANT_NAME_RE = /\A[A-Za-z0-9!#$%&'*+\-.^_`|~]+\z/ # Class constructor. def initialize @@ -226,11 +226,11 @@ def coerce(object) # match {HEADER_NAME_RE} # @return [String] canonical HTTP header name def normalize_header(name) - return name if name =~ CANONICAL_NAME_RE + return name if CANONICAL_NAME_RE.match?(name) normalized = name.split(/[\-_]/).each(&:capitalize!).join("-") - return normalized if normalized =~ COMPLIANT_NAME_RE + return normalized if COMPLIANT_NAME_RE.match?(normalized) raise HeaderError, "Invalid HTTP header field name: #{name.inspect}" end diff --git a/lib/http/request.rb b/lib/http/request.rb index 484b370c..b32e8ee3 100644 --- a/lib/http/request.rb +++ b/lib/http/request.rb @@ -24,7 +24,7 @@ class UnsupportedMethodError < RequestError; end class UnsupportedSchemeError < RequestError; end # Default User-Agent header value - USER_AGENT = "http.rb/#{HTTP::VERSION}" + USER_AGENT = "http.rb/#{HTTP::VERSION}".freeze METHODS = [ # RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1 diff --git a/lib/http/request/writer.rb b/lib/http/request/writer.rb index b905cb9f..ce06c410 100644 --- a/lib/http/request/writer.rb +++ b/lib/http/request/writer.rb @@ -15,7 +15,7 @@ class Writer CHUNKED = "chunked" # End of a chunked transfer - CHUNKED_END = "#{ZERO}#{CRLF}#{CRLF}" + CHUNKED_END = "#{ZERO}#{CRLF}#{CRLF}".freeze def initialize(socket, body, headers, headline) @body = body diff --git a/lib/http/uri.rb b/lib/http/uri.rb index 9bc93adf..b890777f 100644 --- a/lib/http/uri.rb +++ b/lib/http/uri.rb @@ -38,7 +38,7 @@ class URI HTTPS_SCHEME = "https" # @private - PERCENT_ENCODE = /[^\x21-\x7E]+/.freeze + PERCENT_ENCODE = /[^\x21-\x7E]+/ # @private NORMALIZER = lambda do |uri| diff --git a/spec/lib/http/client_spec.rb b/spec/lib/http/client_spec.rb index ed55c394..fa1790ff 100644 --- a/spec/lib/http/client_spec.rb +++ b/spec/lib/http/client_spec.rb @@ -153,6 +153,7 @@ def simple_response(body, status = 200) describe "parsing params" do let(:client) { HTTP::Client.new } + before { allow(client).to receive :perform } it "accepts params within the provided URL" do @@ -497,7 +498,7 @@ def wrap_response(res) context "with HEAD request" do it "does not iterates through body" do - expect_any_instance_of(HTTP::Connection).to_not receive(:readpartial) + expect_any_instance_of(HTTP::Connection).not_to receive(:readpartial) client.head(dummy.endpoint) end @@ -512,7 +513,7 @@ def wrap_response(res) socket_spy = double chunks = [ - <<-RESPONSE.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n") + <<-RESPONSE.gsub(/^\s*\| */, "").gsub("\n", "\r\n") | HTTP/1.1 200 OK | Content-Type: text/html | Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22) @@ -524,8 +525,8 @@ def wrap_response(res) RESPONSE ] - allow(socket_spy).to receive(:close) { nil } - allow(socket_spy).to receive(:closed?) { true } + allow(socket_spy).to receive(:close).and_return(nil) + allow(socket_spy).to receive(:closed?).and_return(true) allow(socket_spy).to receive(:readpartial) { chunks.shift || :eof } allow(socket_spy).to receive(:write) { chunks[0].length } @@ -541,7 +542,7 @@ def wrap_response(res) context "when uses chunked transfer encoding" do let(:chunks) do [ - <<-RESPONSE.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n") << body + <<-RESPONSE.gsub(/^\s*\| */, "").gsub("\n", "\r\n") << body | HTTP/1.1 200 OK | Content-Type: application/json | Transfer-Encoding: chunked @@ -551,7 +552,7 @@ def wrap_response(res) ] end let(:body) do - <<-BODY.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n") + <<-BODY.gsub(/^\s*\| */, "").gsub("\n", "\r\n") | 9 | {"state": | 5 @@ -564,8 +565,8 @@ def wrap_response(res) before do socket_spy = double - allow(socket_spy).to receive(:close) { nil } - allow(socket_spy).to receive(:closed?) { true } + allow(socket_spy).to receive(:close).and_return(nil) + allow(socket_spy).to receive(:closed?).and_return(true) allow(socket_spy).to receive(:readpartial) { chunks.shift || :eof } allow(socket_spy).to receive(:write) { chunks[0].length } @@ -579,7 +580,7 @@ def wrap_response(res) context "with broken body (too early closed connection)" do let(:body) do - <<-BODY.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n") + <<-BODY.gsub(/^\s*\| */, "").gsub("\n", "\r\n") | 9 | {"state": BODY diff --git a/spec/lib/http/connection_spec.rb b/spec/lib/http/connection_spec.rb index 719cfdca..7cd3a6b9 100644 --- a/spec/lib/http/connection_spec.rb +++ b/spec/lib/http/connection_spec.rb @@ -24,7 +24,7 @@ before do expect(socket).to receive(:start_tls).and_raise(HTTP::TimeoutError) - expect(socket).to receive(:closed?) { false } + expect(socket).to receive(:closed?).and_return(false) expect(socket).to receive(:close) end @@ -37,7 +37,7 @@ before do connection.instance_variable_set(:@pending_response, true) expect(socket).to receive(:readpartial) do - <<-RESPONSE.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n") + <<-RESPONSE.gsub(/^\s*\| */, "").gsub("\n", "\r\n") | HTTP/1.1 200 OK | Content-Type: text | foo_bar: 123 @@ -58,20 +58,20 @@ before do connection.instance_variable_set(:@pending_response, true) expect(socket).to receive(:readpartial) do - <<-RESPONSE.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n") + <<-RESPONSE.gsub(/^\s*\| */, "").gsub("\n", "\r\n") | HTTP/1.1 200 OK | Content-Type: text | RESPONSE end - expect(socket).to receive(:readpartial) { "1" } - expect(socket).to receive(:readpartial) { "23" } - expect(socket).to receive(:readpartial) { "456" } - expect(socket).to receive(:readpartial) { "78" } - expect(socket).to receive(:readpartial) { "9" } - expect(socket).to receive(:readpartial) { "0" } - expect(socket).to receive(:readpartial) { :eof } - expect(socket).to receive(:closed?) { true } + expect(socket).to receive(:readpartial).and_return("1") + expect(socket).to receive(:readpartial).and_return("23") + expect(socket).to receive(:readpartial).and_return("456") + expect(socket).to receive(:readpartial).and_return("78") + expect(socket).to receive(:readpartial).and_return("9") + expect(socket).to receive(:readpartial).and_return("0") + expect(socket).to receive(:readpartial).and_return(:eof) + expect(socket).to receive(:closed?).and_return(true) end it "reads data in parts" do diff --git a/spec/lib/http/content_type_spec.rb b/spec/lib/http/content_type_spec.rb index 8175992c..2490a09d 100644 --- a/spec/lib/http/content_type_spec.rb +++ b/spec/lib/http/content_type_spec.rb @@ -4,42 +4,49 @@ describe ".parse" do context "with text/plain" do subject { described_class.parse "text/plain" } + its(:mime_type) { is_expected.to eq "text/plain" } its(:charset) { is_expected.to be_nil } end context "with tEXT/plaIN" do subject { described_class.parse "tEXT/plaIN" } + its(:mime_type) { is_expected.to eq "text/plain" } its(:charset) { is_expected.to be_nil } end context "with text/plain; charset=utf-8" do subject { described_class.parse "text/plain; charset=utf-8" } + its(:mime_type) { is_expected.to eq "text/plain" } its(:charset) { is_expected.to eq "utf-8" } end context 'with text/plain; charset="utf-8"' do subject { described_class.parse 'text/plain; charset="utf-8"' } + its(:mime_type) { is_expected.to eq "text/plain" } its(:charset) { is_expected.to eq "utf-8" } end context "with text/plain; charSET=utf-8" do subject { described_class.parse "text/plain; charSET=utf-8" } + its(:mime_type) { is_expected.to eq "text/plain" } its(:charset) { is_expected.to eq "utf-8" } end context "with text/plain; foo=bar; charset=utf-8" do subject { described_class.parse "text/plain; foo=bar; charset=utf-8" } + its(:mime_type) { is_expected.to eq "text/plain" } its(:charset) { is_expected.to eq "utf-8" } end context "with text/plain;charset=utf-8;foo=bar" do subject { described_class.parse "text/plain;charset=utf-8;foo=bar" } + its(:mime_type) { is_expected.to eq "text/plain" } its(:charset) { is_expected.to eq "utf-8" } end diff --git a/spec/lib/http/features/instrumentation_spec.rb b/spec/lib/http/features/instrumentation_spec.rb index 59394bc2..ddbb6666 100644 --- a/spec/lib/http/features/instrumentation_spec.rb +++ b/spec/lib/http/features/instrumentation_spec.rb @@ -35,7 +35,7 @@ def finish(_name, payload) ) end - it "should log the request" do + it "logs the request" do feature.wrap_request(request) expect(instrumenter.output[:start]).to eq(:request => request) @@ -53,7 +53,7 @@ def finish(_name, payload) ) end - it "should log the response" do + it "logs the response" do feature.wrap_response(response) expect(instrumenter.output[:finish]).to eq(:response => response) @@ -72,7 +72,7 @@ def finish(_name, payload) let(:error) { HTTP::TimeoutError.new } - it "should log the error" do + it "logs the error" do feature.on_error(request, error) expect(instrumenter.output[:finish]).to eq(:request => request, :error => error) diff --git a/spec/lib/http/features/logging_spec.rb b/spec/lib/http/features/logging_spec.rb index c3518594..5eeae06c 100644 --- a/spec/lib/http/features/logging_spec.rb +++ b/spec/lib/http/features/logging_spec.rb @@ -22,7 +22,7 @@ ) end - it "should log the request" do + it "logs the request" do feature.wrap_request(request) expect(logdev.string).to eq <<~OUTPUT @@ -49,7 +49,7 @@ ) end - it "should log the response" do + it "logs the response" do feature.wrap_response(response) expect(logdev.string).to eq <<~OUTPUT diff --git a/spec/lib/http/headers_spec.rb b/spec/lib/http/headers_spec.rb index abf62269..9225a508 100644 --- a/spec/lib/http/headers_spec.rb +++ b/spec/lib/http/headers_spec.rb @@ -185,7 +185,7 @@ before { headers.set "Content-Type", "application/json" } it "normalizes header name" do - expect(headers[:content_type]).to_not be_nil + expect(headers[:content_type]).not_to be_nil end it "returns it returns a single value" do @@ -200,7 +200,7 @@ end it "normalizes header name" do - expect(headers[:set_cookie]).to_not be_nil + expect(headers[:set_cookie]).not_to be_nil end it "returns array of associated values" do @@ -237,7 +237,7 @@ end it "returns a Hash" do - expect(headers.to_h).to be_a ::Hash + expect(headers.to_h).to be_a Hash end it "returns Hash with normalized keys" do @@ -278,9 +278,10 @@ end describe "#inspect" do - before { headers.set :set_cookie, %w[hoo=ray woo=hoo] } subject { headers.inspect } + before { headers.set :set_cookie, %w[hoo=ray woo=hoo] } + it { is_expected.to eq '#["hoo=ray", "woo=hoo"]}>' } end @@ -348,6 +349,7 @@ context "when header exists" do before { headers.add :accept, "text/plain" } + it { is_expected.to be false } end @@ -395,7 +397,7 @@ right.add :cookie, "woo=hoo" right.add :accept, "text/plain" - expect(left).to_not eq right + expect(left).not_to eq right end it "sensitive to header values order" do @@ -404,15 +406,15 @@ right.add :cookie, "woo=hoo" right.add :cookie, "hoo=ray" - expect(left).to_not eq right + expect(left).not_to eq right end end describe "#dup" do - before { headers.set :content_type, "application/json" } - subject(:dupped) { headers.dup } + before { headers.set :content_type, "application/json" } + it { is_expected.to be_a described_class } it { is_expected.not_to be headers } @@ -454,20 +456,20 @@ end describe "#merge" do + subject(:merged) do + headers.merge :accept => "plain/text", :cookie => %w[hoo=ray woo=hoo] + end + before do headers.set :host, "example.com" headers.set :accept, "application/json" end - subject(:merged) do - headers.merge :accept => "plain/text", :cookie => %w[hoo=ray woo=hoo] - end - it { is_expected.to be_a described_class } it { is_expected.not_to be headers } it "does not affects original headers" do - expect(merged.to_h).to_not eq headers.to_h + expect(merged.to_h).not_to eq headers.to_h end it "leaves headers not presented in other as is" do @@ -510,13 +512,7 @@ it "adds all headers" do expect(described_class.coerce(headers).to_a). - to match_array( - [ - %w[Set-Cookie hoo=ray], - %w[set_cookie woo=hoo], - %w[Set-Cookie ta=da] - ] - ) + to contain_exactly(%w[Set-Cookie hoo=ray], %w[set_cookie woo=hoo], %w[Set-Cookie ta=da]) end end diff --git a/spec/lib/http/options/body_spec.rb b/spec/lib/http/options/body_spec.rb index b9149997..b06f45a6 100644 --- a/spec/lib/http/options/body_spec.rb +++ b/spec/lib/http/options/body_spec.rb @@ -4,12 +4,12 @@ let(:opts) { HTTP::Options.new } it "defaults to nil" do - expect(opts.body).to be nil + expect(opts.body).to be_nil end it "may be specified with with_body" do opts2 = opts.with_body("foo") - expect(opts.body).to be nil + expect(opts.body).to be_nil expect(opts2.body).to eq("foo") end end diff --git a/spec/lib/http/options/form_spec.rb b/spec/lib/http/options/form_spec.rb index ddd123ad..46332279 100644 --- a/spec/lib/http/options/form_spec.rb +++ b/spec/lib/http/options/form_spec.rb @@ -4,12 +4,12 @@ let(:opts) { HTTP::Options.new } it "defaults to nil" do - expect(opts.form).to be nil + expect(opts.form).to be_nil end it "may be specified with with_form_data" do opts2 = opts.with_form(:foo => 42) - expect(opts.form).to be nil + expect(opts.form).to be_nil expect(opts2.form).to eq(:foo => 42) end end diff --git a/spec/lib/http/options/json_spec.rb b/spec/lib/http/options/json_spec.rb index 9354fa3e..6afd57da 100644 --- a/spec/lib/http/options/json_spec.rb +++ b/spec/lib/http/options/json_spec.rb @@ -4,12 +4,12 @@ let(:opts) { HTTP::Options.new } it "defaults to nil" do - expect(opts.json).to be nil + expect(opts.json).to be_nil end it "may be specified with with_json data" do opts2 = opts.with_json(:foo => 42) - expect(opts.json).to be nil + expect(opts.json).to be_nil expect(opts2.json).to eq(:foo => 42) end end diff --git a/spec/lib/http/redirector_spec.rb b/spec/lib/http/redirector_spec.rb index e57e7cbc..f61170fb 100644 --- a/spec/lib/http/redirector_spec.rb +++ b/spec/lib/http/redirector_spec.rb @@ -24,6 +24,7 @@ def redirect_response(status, location, set_cookie = {}) context "by default" do let(:redirector) { described_class.new } + it { is_expected.to be true } end end @@ -33,6 +34,7 @@ def redirect_response(status, location, set_cookie = {}) context "by default" do let(:redirector) { described_class.new } + it { is_expected.to eq 5 } end end @@ -178,7 +180,7 @@ def redirect_response(status, location, set_cookie = {}) context "with strict mode" do let(:options) { {:strict => true} } - it "it follows with original verb if it's safe" do + it "follows with original verb if it's safe" do req = HTTP::Request.new :verb => :head, :uri => "http://example.com" res = redirect_response 300, "http://example.com/1" @@ -216,7 +218,7 @@ def redirect_response(status, location, set_cookie = {}) context "with non-strict mode" do let(:options) { {:strict => false} } - it "it follows with original verb if it's safe" do + it "follows with original verb if it's safe" do req = HTTP::Request.new :verb => :head, :uri => "http://example.com" res = redirect_response 300, "http://example.com/1" @@ -226,7 +228,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was PUT" do + it "follows with GET if original request was PUT" do req = HTTP::Request.new :verb => :put, :uri => "http://example.com" res = redirect_response 300, "http://example.com/1" @@ -236,7 +238,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was POST" do + it "follows with GET if original request was POST" do req = HTTP::Request.new :verb => :post, :uri => "http://example.com" res = redirect_response 300, "http://example.com/1" @@ -246,7 +248,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was DELETE" do + it "follows with GET if original request was DELETE" do req = HTTP::Request.new :verb => :delete, :uri => "http://example.com" res = redirect_response 300, "http://example.com/1" @@ -262,7 +264,7 @@ def redirect_response(status, location, set_cookie = {}) context "with strict mode" do let(:options) { {:strict => true} } - it "it follows with original verb if it's safe" do + it "follows with original verb if it's safe" do req = HTTP::Request.new :verb => :head, :uri => "http://example.com" res = redirect_response 301, "http://example.com/1" @@ -300,7 +302,7 @@ def redirect_response(status, location, set_cookie = {}) context "with non-strict mode" do let(:options) { {:strict => false} } - it "it follows with original verb if it's safe" do + it "follows with original verb if it's safe" do req = HTTP::Request.new :verb => :head, :uri => "http://example.com" res = redirect_response 301, "http://example.com/1" @@ -310,7 +312,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was PUT" do + it "follows with GET if original request was PUT" do req = HTTP::Request.new :verb => :put, :uri => "http://example.com" res = redirect_response 301, "http://example.com/1" @@ -320,7 +322,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was POST" do + it "follows with GET if original request was POST" do req = HTTP::Request.new :verb => :post, :uri => "http://example.com" res = redirect_response 301, "http://example.com/1" @@ -330,7 +332,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was DELETE" do + it "follows with GET if original request was DELETE" do req = HTTP::Request.new :verb => :delete, :uri => "http://example.com" res = redirect_response 301, "http://example.com/1" @@ -346,7 +348,7 @@ def redirect_response(status, location, set_cookie = {}) context "with strict mode" do let(:options) { {:strict => true} } - it "it follows with original verb if it's safe" do + it "follows with original verb if it's safe" do req = HTTP::Request.new :verb => :head, :uri => "http://example.com" res = redirect_response 302, "http://example.com/1" @@ -384,7 +386,7 @@ def redirect_response(status, location, set_cookie = {}) context "with non-strict mode" do let(:options) { {:strict => false} } - it "it follows with original verb if it's safe" do + it "follows with original verb if it's safe" do req = HTTP::Request.new :verb => :head, :uri => "http://example.com" res = redirect_response 302, "http://example.com/1" @@ -394,7 +396,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was PUT" do + it "follows with GET if original request was PUT" do req = HTTP::Request.new :verb => :put, :uri => "http://example.com" res = redirect_response 302, "http://example.com/1" @@ -404,7 +406,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was POST" do + it "follows with GET if original request was POST" do req = HTTP::Request.new :verb => :post, :uri => "http://example.com" res = redirect_response 302, "http://example.com/1" @@ -414,7 +416,7 @@ def redirect_response(status, location, set_cookie = {}) end end - it "it follows with GET if original request was DELETE" do + it "follows with GET if original request was DELETE" do req = HTTP::Request.new :verb => :delete, :uri => "http://example.com" res = redirect_response 302, "http://example.com/1" diff --git a/spec/lib/http/request/body_spec.rb b/spec/lib/http/request/body_spec.rb index 6a6a92f0..9f93cf0d 100644 --- a/spec/lib/http/request/body_spec.rb +++ b/spec/lib/http/request/body_spec.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true RSpec.describe HTTP::Request::Body do - let(:body) { "" } subject { HTTP::Request::Body.new(body) } + let(:body) { "" } + describe "#initialize" do context "when body is nil" do let(:body) { nil } diff --git a/spec/lib/http/request/writer_spec.rb b/spec/lib/http/request/writer_spec.rb index 20f14181..290d4f6b 100644 --- a/spec/lib/http/request/writer_spec.rb +++ b/spec/lib/http/request/writer_spec.rb @@ -2,13 +2,13 @@ # frozen_string_literal: true RSpec.describe HTTP::Request::Writer do + subject(:writer) { described_class.new(io, body, headers, headerstart) } + let(:io) { StringIO.new } let(:body) { HTTP::Request::Body.new("") } let(:headers) { HTTP::Headers.new } let(:headerstart) { "GET /test HTTP/1.1" } - subject(:writer) { described_class.new(io, body, headers, headerstart) } - describe "#stream" do context "when multiple headers are set" do let(:headers) { HTTP::Headers.coerce "Host" => "example.org" } diff --git a/spec/lib/http/request_spec.rb b/spec/lib/http/request_spec.rb index da4ba074..75226f94 100644 --- a/spec/lib/http/request_spec.rb +++ b/spec/lib/http/request_spec.rb @@ -2,10 +2,6 @@ # frozen_string_literal: true RSpec.describe HTTP::Request do - let(:proxy) { {} } - let(:headers) { {:accept => "text/html"} } - let(:request_uri) { "http://example.com/foo?bar=baz" } - subject :request do HTTP::Request.new( :verb => :get, @@ -15,6 +11,10 @@ ) end + let(:proxy) { {} } + let(:headers) { {:accept => "text/html"} } + let(:request_uri) { "http://example.com/foo?bar=baz" } + it "includes HTTP::Headers::Mixin" do expect(described_class).to include HTTP::Headers::Mixin end @@ -44,12 +44,14 @@ context "and request URI has non-standard port" do let(:request_uri) { "http://example.com:3000/" } + it { is_expected.to eq "example.com:3000" } end end context "was explicitly given" do before { headers[:host] = "github.com" } + it { is_expected.to eq "github.com" } end end @@ -63,11 +65,14 @@ context "was explicitly given" do before { headers[:user_agent] = "MrCrawly/123" } + it { is_expected.to eq "MrCrawly/123" } end end describe "#redirect" do + subject(:redirected) { request.redirect "http://blog.example.com/" } + let(:headers) { {:accept => "text/html"} } let(:proxy) { {:proxy_username => "douglas", :proxy_password => "adams"} } let(:body) { "The Ultimate Question" } @@ -82,8 +87,6 @@ ) end - subject(:redirected) { request.redirect "http://blog.example.com/" } - its(:uri) { is_expected.to eq HTTP::URI.parse "http://blog.example.com/" } its(:verb) { is_expected.to eq request.verb } @@ -180,6 +183,7 @@ context "with new verb given" do subject { request.redirect "http://blog.example.com/", :get } + its(:verb) { is_expected.to be :get } end end @@ -216,6 +220,7 @@ context "with proxy" do let(:proxy) { {:user => "user", :pass => "pass"} } + it { is_expected.to eq "GET http://example.com/foo?bar=baz HTTP/1.1" } context "and HTTPS uri" do diff --git a/spec/lib/http/response/body_spec.rb b/spec/lib/http/response/body_spec.rb index 81c6864c..e976c94e 100644 --- a/spec/lib/http/response/body_spec.rb +++ b/spec/lib/http/response/body_spec.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true RSpec.describe HTTP::Response::Body do + subject(:body) { described_class.new(connection, :encoding => Encoding::UTF_8) } + let(:connection) { double(:sequence_id => 0) } let(:chunks) { ["Hello, ", "World!"] } @@ -9,8 +11,6 @@ allow(connection).to receive(:body_completed?) { chunks.empty? } end - subject(:body) { described_class.new(connection, :encoding => Encoding::UTF_8) } - it "streams bodies from responses" do expect(subject.to_s).to eq("Hello, World!") end @@ -33,7 +33,7 @@ context "without size given" do it "does not blows up" do - expect { body.readpartial }.to_not raise_error + expect { body.readpartial }.not_to raise_error end it "calls underlying connection readpartial without specific size" do @@ -56,15 +56,16 @@ end context "when body is gzipped" do + subject(:body) do + inflater = HTTP::Response::Inflater.new(connection) + described_class.new(inflater, :encoding => Encoding::UTF_8) + end + let(:chunks) do body = Zlib::Deflate.deflate("Hi, HTTP here ☺") len = body.length [body[0, len / 2], body[(len / 2)..]] end - subject(:body) do - inflater = HTTP::Response::Inflater.new(connection) - described_class.new(inflater, :encoding => Encoding::UTF_8) - end it "decodes body" do expect(subject.to_s).to eq("Hi, HTTP here ☺") diff --git a/spec/lib/http/response/parser_spec.rb b/spec/lib/http/response/parser_spec.rb index 97cd2b89..d8e3fcd2 100644 --- a/spec/lib/http/response/parser_spec.rb +++ b/spec/lib/http/response/parser_spec.rb @@ -2,6 +2,7 @@ RSpec.describe HTTP::Response::Parser do subject(:parser) { described_class.new } + let(:raw_response) do "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: application/json\r\nMyHeader: val\r\nEmptyHeader: \r\n\r\n{}" end diff --git a/spec/lib/http/response/status_spec.rb b/spec/lib/http/response/status_spec.rb index 6930ab97..6aded9af 100644 --- a/spec/lib/http/response/status_spec.rb +++ b/spec/lib/http/response/status_spec.rb @@ -7,12 +7,13 @@ end it "accepts any object that responds to #to_i" do - expect { described_class.new double :to_i => 200 }.to_not raise_error + expect { described_class.new double :to_i => 200 }.not_to raise_error end end describe "#code" do subject { described_class.new("200.0").code } + it { is_expected.to eq 200 } it { is_expected.to be_a Integer } end @@ -22,6 +23,7 @@ context "with unknown code" do let(:code) { 1024 } + it { is_expected.to be_nil } end @@ -161,6 +163,7 @@ context "with unknown code" do let(:code) { 1024 } + it { is_expected.to be_nil } end diff --git a/spec/lib/http/response_spec.rb b/spec/lib/http/response_spec.rb index 873ffd80..d5846792 100644 --- a/spec/lib/http/response_spec.rb +++ b/spec/lib/http/response_spec.rb @@ -1,11 +1,6 @@ # frozen_string_literal: true RSpec.describe HTTP::Response do - let(:body) { "Hello world!" } - let(:uri) { "http://example.com/" } - let(:headers) { {} } - let(:request) { HTTP::Request.new(:verb => :get, :uri => uri) } - subject(:response) do HTTP::Response.new( :status => 200, @@ -16,6 +11,11 @@ ) end + let(:body) { "Hello world!" } + let(:uri) { "http://example.com/" } + let(:headers) { {} } + let(:request) { HTTP::Request.new(:verb => :get, :uri => uri) } + it "includes HTTP::Headers::Mixin" do expect(described_class).to include HTTP::Headers::Mixin end @@ -39,11 +39,13 @@ context "with Content-Length: 5" do let(:headers) { {"Content-Length" => "5"} } + it { is_expected.to eq 5 } end context "with invalid Content-Length" do let(:headers) { {"Content-Length" => "foo"} } + it { is_expected.to be_nil } end end @@ -53,16 +55,19 @@ context "without Content-Type header" do let(:headers) { {} } + it { is_expected.to be_nil } end context "with Content-Type: text/html" do let(:headers) { {"Content-Type" => "text/html"} } + it { is_expected.to eq "text/html" } end context "with Content-Type: text/html; charset=utf-8" do let(:headers) { {"Content-Type" => "text/html; charset=utf-8"} } + it { is_expected.to eq "text/html" } end end @@ -72,16 +77,19 @@ context "without Content-Type header" do let(:headers) { {} } + it { is_expected.to be_nil } end context "with Content-Type: text/html" do let(:headers) { {"Content-Type" => "text/html"} } + it { is_expected.to be_nil } end context "with Content-Type: text/html; charset=utf-8" do let(:headers) { {"Content-Type" => "text/html; charset=utf-8"} } + it { is_expected.to eq "utf-8" } end end @@ -92,6 +100,7 @@ context "with known content type" do let(:content_type) { "application/json" } + it "returns parsed body" do expect(response.parse).to eq "foo" => "bar" end @@ -99,6 +108,7 @@ context "with unknown content type" do let(:content_type) { "application/deadbeef" } + it "raises HTTP::Error" do expect { response.parse }.to raise_error HTTP::Error end @@ -106,6 +116,7 @@ context "with explicitly given mime type" do let(:content_type) { "application/deadbeef" } + it "ignores mime_type of response" do expect(response.parse("application/json")).to eq "foo" => "bar" end @@ -139,11 +150,11 @@ end describe "#cookies" do + subject(:jar) { response.cookies } + let(:cookies) { ["a=1", "b=2; domain=example.com", "c=3; domain=bad.org"] } let(:headers) { {"Set-Cookie" => cookies} } - subject(:jar) { response.cookies } - it { is_expected.to be_an HTTP::CookieJar } it "contains cookies without domain restriction" do @@ -160,8 +171,6 @@ end describe "#connection" do - let(:connection) { double } - subject(:response) do HTTP::Response.new( :version => "1.1", @@ -171,6 +180,8 @@ ) end + let(:connection) { double } + it "returns the connection object used to instantiate the response" do expect(response.connection).to eq connection end @@ -178,10 +189,13 @@ describe "#chunked?" do subject { response } + context "when encoding is set to chunked" do let(:headers) { {"Transfer-Encoding" => "chunked"} } + it { is_expected.to be_chunked } end + it { is_expected.not_to be_chunked } end @@ -225,9 +239,6 @@ end describe "#body" do - let(:connection) { double(:sequence_id => 0) } - let(:chunks) { ["Hello, ", "World!"] } - subject(:response) do HTTP::Response.new( :status => 200, @@ -238,6 +249,9 @@ ) end + let(:connection) { double(:sequence_id => 0) } + let(:chunks) { ["Hello, ", "World!"] } + before do allow(connection).to receive(:readpartial) { chunks.shift } allow(connection).to receive(:body_completed?) { chunks.empty? } diff --git a/spec/lib/http/uri_spec.rb b/spec/lib/http/uri_spec.rb index 4a330d61..cfb68724 100644 --- a/spec/lib/http/uri_spec.rb +++ b/spec/lib/http/uri_spec.rb @@ -1,15 +1,17 @@ # frozen_string_literal: true RSpec.describe HTTP::URI do + subject(:ipv6_uri) { described_class.parse(example_ipv6_uri_string) } + let(:example_ipv6_address) { "2606:2800:220:1:248:1893:25c8:1946" } let(:example_http_uri_string) { "http://example.com" } let(:example_https_uri_string) { "https://example.com" } let(:example_ipv6_uri_string) { "https://[#{example_ipv6_address}]" } - subject(:http_uri) { described_class.parse(example_http_uri_string) } - subject(:https_uri) { described_class.parse(example_https_uri_string) } - subject(:ipv6_uri) { described_class.parse(example_ipv6_uri_string) } + let(:http_uri) { described_class.parse(example_http_uri_string) } + + let(:https_uri) { described_class.parse(example_https_uri_string) } it "knows URI schemes" do expect(http_uri.scheme).to eq "http" diff --git a/spec/lib/http_spec.rb b/spec/lib/http_spec.rb index 746a08ec..4bf848e8 100644 --- a/spec/lib/http_spec.rb +++ b/spec/lib/http_spec.rb @@ -259,6 +259,7 @@ context "with host only given" do subject { HTTP.persistent host } + it { is_expected.to be_an HTTP::Client } it { is_expected.to be_persistent } end @@ -278,6 +279,7 @@ context "with timeout specified" do subject(:client) { HTTP.persistent host, :timeout => 100 } + it "sets keep_alive_timeout" do options = client.default_options expect(options.keep_alive_timeout).to eq(100) @@ -310,9 +312,10 @@ end context "specifying per operation timeouts as frozen hash" do - let(:frozen_options) { {:read => 123}.freeze } subject(:client) { HTTP.timeout(frozen_options) } + let(:frozen_options) { {:read => 123}.freeze } + it "does not raise an error" do expect { client }.not_to raise_error end diff --git a/spec/support/http_handling_shared.rb b/spec/support/http_handling_shared.rb index ff13b0a3..8bbdf47a 100644 --- a/spec/support/http_handling_shared.rb +++ b/spec/support/http_handling_shared.rb @@ -35,7 +35,7 @@ let(:conn_timeout) { 1 } it "does not time out" do - expect { response }.to_not raise_error + expect { response }.not_to raise_error end end end @@ -53,7 +53,7 @@ let(:read_timeout) { 2.5 } it "does not time out", :flaky do - expect { client.get("#{server.endpoint}/sleep").body.to_s }.to_not raise_error + expect { client.get("#{server.endpoint}/sleep").body.to_s }.not_to raise_error end end end @@ -115,7 +115,7 @@ end it "re-uses the socket" do - expect(sockets_used).to_not include("") + expect(sockets_used).not_to include("") expect(sockets_used.uniq.length).to eq(1) end @@ -127,7 +127,7 @@ second_socket_id = client.get("#{server.endpoint}/socket/2").body.to_s - expect(first_socket_id).to_not eq(second_socket_id) + expect(first_socket_id).not_to eq(second_socket_id) end end @@ -153,7 +153,7 @@ context "with a socket issue" do it "transparently reopens", :flaky do first_socket_id = client.get("#{server.endpoint}/socket").body.to_s - expect(first_socket_id).to_not eq("") + expect(first_socket_id).not_to eq("") # Kill off the sockets we used # rubocop:disable Style/RescueModifier DummyServer::Servlet.sockets.each do |socket| @@ -167,7 +167,7 @@ # Should succeed since we create a new socket second_socket_id = client.get("#{server.endpoint}/socket").body.to_s - expect(second_socket_id).to_not eq(first_socket_id) + expect(second_socket_id).not_to eq(first_socket_id) end end @@ -182,7 +182,7 @@ let(:options) { {} } it "opens new sockets", :flaky do - expect(sockets_used).to_not include("") + expect(sockets_used).not_to include("") expect(sockets_used.uniq.length).to eq(2) end end