Skip to content

Commit

Permalink
Backport HTTP parser upgrade (#489) from master
Browse files Browse the repository at this point in the history
  • Loading branch information
ixti committed Oct 21, 2019
1 parent 0f5b26d commit a0d4162
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 26 deletions.
7 changes: 4 additions & 3 deletions .travis.yml
Expand Up @@ -17,18 +17,19 @@ env: JRUBY_OPTS="$JRUBY_OPTS --debug"

rvm:
# Include JRuby first because it takes the longest
- jruby-9.1.16.0
- jruby-9.2.5.0
- 2.3
- 2.4
- 2.5
- 2.6

matrix:
fast_finish: true
include:
# Only run RuboCop and Yardstick metrics on the latest Ruby
- rvm: 2.5
- rvm: 2.6
env: SUITE="rubocop"
- rvm: 2.5
- rvm: 2.6
env: SUITE="yardstick"

branches:
Expand Down
8 changes: 8 additions & 0 deletions CHANGES.md
@@ -1,3 +1,10 @@
## 4.2.0 (WIP)

* [#489](https://github.com/httprb/http/pull/489)
Fix HTTP parser.
([@ixti], [@fxposter])


## 4.1.1 (2019-03-12)

* Add `HTTP::Headers::ACCEPT_ENCODING` constant.
Expand Down Expand Up @@ -771,4 +778,5 @@ end
[@tycoon]: https://github.com/tycooon
[@paul]: https://github.com/paul
[@RickCSong]: https://github.com/RickCSong
[@fxposter]: https://github.com/fxposter
[@mamoonraja]: https://github.com/mamoonraja
7 changes: 4 additions & 3 deletions README.md
Expand Up @@ -4,7 +4,7 @@
[![Build Status](https://secure.travis-ci.org/httprb/http.svg?branch=4-x-stable)](https://travis-ci.org/httprb/http)
[![Code Climate](https://codeclimate.com/github/httprb/http.svg?branch=4-x-stable)](https://codeclimate.com/github/httprb/http)
[![Coverage Status](https://coveralls.io/repos/httprb/http/badge.svg?branch=4-x-stable)](https://coveralls.io/r/httprb/http)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/httprb/http/blob/master/LICENSE.txt)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/httprb/http/blob/4-x-stable/LICENSE.txt)

[Documentation]

Expand Down Expand Up @@ -167,7 +167,8 @@ versions:
* Ruby 2.3.x
* Ruby 2.4.x
* Ruby 2.5.x
* JRuby 9.1.x.x
* Ruby 2.6.x
* JRuby 9.2.x.x

If something doesn't work on one of these versions, it's a bug.

Expand Down Expand Up @@ -197,5 +198,5 @@ dropped.

## Copyright

Copyright (c) 2011-2018 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
Copyright (c) 2011-2019 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
See LICENSE.txt for further details.
6 changes: 3 additions & 3 deletions http.gemspec
Expand Up @@ -27,10 +27,10 @@ Gem::Specification.new do |gem|

gem.required_ruby_version = ">= 2.3"

gem.add_runtime_dependency "http_parser.rb", "~> 0.6.0"
gem.add_runtime_dependency "http-form_data", "~> 2.0"
gem.add_runtime_dependency "http-cookie", "~> 1.0"
gem.add_runtime_dependency "addressable", "~> 2.3"
gem.add_runtime_dependency "http-cookie", "~> 1.0"
gem.add_runtime_dependency "http-form_data", "~> 2.0"
gem.add_runtime_dependency "http-parser", "~> 1.2.0"

gem.add_development_dependency "bundler", "~> 2.0"

Expand Down
2 changes: 0 additions & 2 deletions lib/http.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require "http/parser"

require "http/errors"
require "http/timeout/null"
require "http/timeout/per_operation"
Expand Down
54 changes: 40 additions & 14 deletions lib/http/response/parser.rb
@@ -1,41 +1,66 @@
# frozen_string_literal: true

require "http-parser"

module HTTP
class Response
# @api private
#
# NOTE(ixti): This class is a subject of future refactoring, thus don't
# expect this class API to be stable until this message disappears and
# class is not marked as private anymore.
class Parser
attr_reader :headers

def initialize
@parser = HTTP::Parser.new(self)
@state = HttpParser::Parser.new_instance { |i| i.type = :response }
@parser = HttpParser::Parser.new(self)

reset
end

# @return [self]
def add(data)
@parser << data
# XXX(ixti): API doc of HttpParser::Parser is misleading, it says that
# it returns boolean true if data was parsed successfully, but instead
# it's response tells if there was an error; So when it's `true` that
# means parse failed, and `false` means parse was successful.
# case of success.
return self unless @parser.parse(@state, data)

raise IOError, "Could not parse data"
end
alias << add

def headers?
!!@headers
@finished[:headers]
end

def http_version
@parser.http_version.join(".")
@state.http_version
end

def status_code
@parser.status_code
@state.http_status
end

#
# HTTP::Parser callbacks
#

def on_headers_complete(headers)
@headers = headers
def on_header_field(_response, field)
@field = field
end

def on_header_value(_response, value)
@headers.add(@field, value) if @field
end

def on_headers_complete(_reposse)
@finished[:headers] = true
end

def on_body(chunk)
def on_body(_response, chunk)
if @chunk
@chunk << chunk
else
Expand All @@ -57,20 +82,21 @@ def read(size)
chunk
end

def on_message_complete
@finished = true
def on_message_complete(_response)
@finished[:message] = true
end

def reset
@parser.reset!
@state.reset!

@finished = false
@headers = nil
@finished = Hash.new(false)
@headers = HTTP::Headers.new
@field = nil
@chunk = nil
end

def finished?
@finished
@finished[:message]
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/http/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module HTTP
VERSION = "4.1.1"
VERSION = "4.2.0.pre"
end
7 changes: 7 additions & 0 deletions spec/regression_specs.rb
Expand Up @@ -14,4 +14,11 @@
expect { HTTP.get(google_uri).to_s }.not_to raise_error
end
end

describe "#422" do
it "reads body when 200 OK response contains Upgrade header" do
res = HTTP.get("https://httpbin.org/response-headers?Upgrade=h2,h2c")
expect(res.parse(:json)).to include("Upgrade" => "h2,h2c")
end
end
end

0 comments on commit a0d4162

Please sign in to comment.