protocol-http1 0.10.3 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of protocol-http1 might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6860775fdc28f8e8d173ff80296cefa5c5e1505e70ce264876959fb453beab5a
4
- data.tar.gz: 9985d69a3696bc98c2e01b78d29282220c9b90b968d0030d60ed95b26a3bab14
3
+ metadata.gz: 4e7551e3bc57f35d161475c5c35c8a9a897fa032f3bbc523a6187a033e9e4539
4
+ data.tar.gz: dc11c6473a5e88d61f709ece47c6c36b1c87edc8d3f316d649288039cd0491a0
5
5
  SHA512:
6
- metadata.gz: 33093bb74d0b00b2a3dfded4432c66276ff27d6fe4e1137b4f0b867b51ca5aa1add02512b3f9427b2520be1617bf3e2c039ed497c192a686cc237bb4a65f690f
7
- data.tar.gz: c845bf16f06d9798553a6241ffd3fae1692584bf6ff276437e1ab7828773200716b86be3b15a5808956ac74389852a2772c81b29fdbb6599a6da1bfe1a8bb9ee
6
+ metadata.gz: 6c077a0cd4de36989268ec7c4258d702f23469346a6cc66492b8fbd19a79796d9924f479cbc9fbe5c34efc1c642dc14b8b826e38589aa39d775244251a1acad8
7
+ data.tar.gz: 873cb095fe327997ffaa175b54379b91f88802ee5a4de8beedb7238c55794dda1627b0a605149968d53aca7f8023aad164574f371c33665672d9afa0f4157dc9
@@ -26,11 +26,15 @@ module Protocol
26
26
  module HTTP1
27
27
  module Body
28
28
  class Chunked < HTTP::Body::Readable
29
- # TODO maybe this should take a stream rather than a connection?
30
- def initialize(stream)
29
+ TRAILERS = 'trailers'
30
+ CRLF = "\r\n"
31
+
32
+ def initialize(stream, headers)
31
33
  @stream = stream
32
34
  @finished = false
33
35
 
36
+ @headers = headers
37
+
34
38
  @length = 0
35
39
  @count = 0
36
40
  end
@@ -49,6 +53,7 @@ module Protocol
49
53
  super
50
54
  end
51
55
 
56
+ # Follows the procedure outlined in https://tools.ietf.org/html/rfc7230#section-4.1.3
52
57
  def read
53
58
  return nil if @finished
54
59
 
@@ -56,13 +61,17 @@ module Protocol
56
61
 
57
62
  if length == 0
58
63
  @finished = true
59
- read_line
64
+
65
+ read_trailers
60
66
 
61
67
  return nil
62
68
  end
63
69
 
64
- chunk = @stream.read(length)
65
- read_line # Consume the trailing CRLF
70
+ # Read trailing CRLF:
71
+ chunk = @stream.read(length + 2)
72
+
73
+ # ...and chomp it off:
74
+ chunk.chomp!(CRLF)
66
75
 
67
76
  @length += length
68
77
  @count += 1
@@ -77,7 +86,20 @@ module Protocol
77
86
  private
78
87
 
79
88
  def read_line
80
- @stream.gets(chomp: true)
89
+ @stream.gets(CRLF, chomp: true)
90
+ end
91
+
92
+ def read_trailers
93
+ while line = read_line
94
+ # Empty line indicates end of headers:
95
+ break if line.empty?
96
+
97
+ if match = line.match(HEADER)
98
+ @headers.add(match[1], match[2])
99
+ else
100
+ raise BadHeader, "Could not parse header: #{line.dump}"
101
+ end
102
+ end
81
103
  end
82
104
  end
83
105
  end
@@ -281,14 +281,17 @@ module Protocol
281
281
 
282
282
  def write_fixed_length_body(body, length, head)
283
283
  @stream.write("content-length: #{length}\r\n\r\n")
284
- @stream.flush
285
284
 
286
285
  if head
286
+ @stream.flush
287
+
287
288
  body.close
288
289
 
289
290
  return
290
291
  end
291
292
 
293
+ @stream.flush unless body.ready?
294
+
292
295
  chunk_length = 0
293
296
  body.each do |chunk|
294
297
  chunk_length += chunk.bytesize
@@ -307,26 +310,37 @@ module Protocol
307
310
  end
308
311
  end
309
312
 
310
- def write_chunked_body(body, head)
313
+ def write_chunked_body(body, head, trailers = nil)
311
314
  @stream.write("transfer-encoding: chunked\r\n\r\n")
312
- @stream.flush
313
315
 
314
316
  if head
317
+ @stream.flush
318
+
315
319
  body.close
316
320
 
317
321
  return
318
322
  end
319
323
 
324
+ @stream.flush unless body.ready?
325
+
320
326
  body.each do |chunk|
321
327
  next if chunk.size == 0
322
328
 
323
329
  @stream.write("#{chunk.bytesize.to_s(16).upcase}\r\n")
324
330
  @stream.write(chunk)
325
331
  @stream.write(CRLF)
326
- @stream.flush
332
+
333
+ @stream.flush unless body.ready?
334
+ end
335
+
336
+ if trailers
337
+ @stream.write("0\r\n")
338
+ write_headers(trailers)
339
+ @stream.write("\r\n")
340
+ else
341
+ @stream.write("0\r\n\r\n")
327
342
  end
328
343
 
329
- @stream.write("0\r\n\r\n")
330
344
  @stream.flush
331
345
  end
332
346
 
@@ -335,31 +349,36 @@ module Protocol
335
349
  @persistent = false
336
350
 
337
351
  @stream.write("\r\n")
338
- @stream.flush
352
+ @stream.flush unless body.ready?
339
353
 
340
354
  if head
341
355
  body.close
342
356
  else
343
357
  body.each do |chunk|
344
358
  @stream.write(chunk)
345
- @stream.flush
359
+
360
+ @stream.flush unless body.ready?
346
361
  end
347
362
  end
348
363
 
349
364
  @stream.close_write
350
365
  end
351
366
 
352
- def write_body(version, body, head = false)
353
- if body.nil? or body.empty?
367
+ def write_body(version, body, head = false, trailers = nil)
368
+ if body.nil?
354
369
  write_connection_header(version)
355
370
  write_empty_body(body)
356
- elsif length = body.length
371
+ elsif length = body.length and (VERSION != HTTP11 && trailers.nil?)
357
372
  write_connection_header(version)
358
373
  write_fixed_length_body(body, length, head)
374
+ elsif body.empty?
375
+ # Even thought this code is the same as the first clause `body.nil?`, HEAD responses have an empty body but still carry a content length. `write_fixed_length_body` takes care of this appropriately.
376
+ write_connection_header(version)
377
+ write_empty_body(body)
359
378
  elsif @persistent and version == HTTP11
360
379
  write_connection_header(version)
361
380
  # We specifically ensure that non-persistent connections do not use chunked response, so that hijacking works as expected.
362
- write_chunked_body(body, head)
381
+ write_chunked_body(body, head, trailers)
363
382
  else
364
383
  @persistent = false
365
384
  write_connection_header(version)
@@ -367,8 +386,8 @@ module Protocol
367
386
  end
368
387
  end
369
388
 
370
- def read_chunked_body
371
- Body::Chunked.new(@stream)
389
+ def read_chunked_body(headers)
390
+ Body::Chunked.new(@stream, headers)
372
391
  end
373
392
 
374
393
  def read_fixed_body(length)
@@ -466,7 +485,7 @@ module Protocol
466
485
  end
467
486
 
468
487
  if transfer_encoding.last == CHUNKED
469
- return read_chunked_body
488
+ return read_chunked_body(headers)
470
489
  else
471
490
  # If a Transfer-Encoding header field is present in a response and
472
491
  # the chunked transfer coding is not the final encoding, the
@@ -479,7 +498,7 @@ module Protocol
479
498
  return read_remainder_body
480
499
  end
481
500
  end
482
-
501
+
483
502
  # 5. If a valid Content-Length header field is present without
484
503
  # Transfer-Encoding, its decimal value defines the expected message
485
504
  # body length in octets. If the sender closes the connection or
@@ -22,6 +22,6 @@
22
22
 
23
23
  module Protocol
24
24
  module HTTP1
25
- VERSION = "0.10.3"
25
+ VERSION = "0.13.1"
26
26
  end
27
27
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protocol-http1
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.3
4
+ version: 0.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-21 00:00:00.000000000 Z
11
+ date: 2020-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: protocol-http
@@ -16,16 +16,16 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.15'
19
+ version: '0.19'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.15'
26
+ version: '0.19'
27
27
  - !ruby/object:Gem::Dependency
28
- name: covered
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: bundler
42
+ name: covered
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rake
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '10.0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '10.0'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rspec
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -81,7 +67,7 @@ dependencies:
81
67
  - !ruby/object:Gem::Version
82
68
  version: '3.0'
83
69
  - !ruby/object:Gem::Dependency
84
- name: rspec-memory
70
+ name: rspec-files
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - "~>"
@@ -95,7 +81,7 @@ dependencies:
95
81
  - !ruby/object:Gem::Version
96
82
  version: '1.0'
97
83
  - !ruby/object:Gem::Dependency
98
- name: rspec-files
84
+ name: rspec-memory
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
87
  - - "~>"
@@ -108,21 +94,12 @@ dependencies:
108
94
  - - "~>"
109
95
  - !ruby/object:Gem::Version
110
96
  version: '1.0'
111
- description:
97
+ description:
112
98
  email:
113
- - samuel.williams@oriontransfer.co.nz
114
99
  executables: []
115
100
  extensions: []
116
101
  extra_rdoc_files: []
117
102
  files:
118
- - ".editorconfig"
119
- - ".gitignore"
120
- - ".rspec"
121
- - ".travis.yml"
122
- - Gemfile
123
- - README.md
124
- - Rakefile
125
- - examples/http1/request.rb
126
103
  - lib/protocol/http1.rb
127
104
  - lib/protocol/http1/body/chunked.rb
128
105
  - lib/protocol/http1/body/fixed.rb
@@ -131,18 +108,17 @@ files:
131
108
  - lib/protocol/http1/error.rb
132
109
  - lib/protocol/http1/reason.rb
133
110
  - lib/protocol/http1/version.rb
134
- - protocol-http1.gemspec
135
111
  homepage: https://github.com/socketry/protocol-http1
136
112
  licenses:
137
113
  - MIT
138
114
  metadata: {}
139
- post_install_message:
115
+ post_install_message:
140
116
  rdoc_options: []
141
117
  require_paths:
142
118
  - lib
143
119
  required_ruby_version: !ruby/object:Gem::Requirement
144
120
  requirements:
145
- - - "~>"
121
+ - - ">="
146
122
  - !ruby/object:Gem::Version
147
123
  version: '2.4'
148
124
  required_rubygems_version: !ruby/object:Gem::Requirement
@@ -151,8 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
127
  - !ruby/object:Gem::Version
152
128
  version: '0'
153
129
  requirements: []
154
- rubygems_version: 3.1.2
155
- signing_key:
130
+ rubygems_version: 3.0.3
131
+ signing_key:
156
132
  specification_version: 4
157
133
  summary: A low level implementation of the HTTP/1 protocol.
158
134
  test_files: []
@@ -1,6 +0,0 @@
1
- root = true
2
-
3
- [*]
4
- indent_style = tab
5
- indent_size = 2
6
-
data/.gitignore DELETED
@@ -1,13 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
12
- Gemfile.lock
13
- .covered.db
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --warnings
3
- --require spec_helper
@@ -1,20 +0,0 @@
1
- language: ruby
2
- dist: xenial
3
- cache: bundler
4
-
5
- matrix:
6
- include:
7
- - rvm: 2.4
8
- - rvm: 2.5
9
- - rvm: 2.6
10
- - rvm: 2.7
11
- - rvm: 2.6
12
- env: COVERAGE=PartialSummary,Coveralls
13
- - rvm: truffleruby
14
- - rvm: jruby-head
15
- env: JRUBY_OPTS="--debug -X+O"
16
- - rvm: ruby-head
17
- allow_failures:
18
- - rvm: truffleruby
19
- - rvm: ruby-head
20
- - rvm: jruby-head
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- # Specify your gem's dependencies in protocol-http1.gemspec
6
- gemspec
data/README.md DELETED
@@ -1,92 +0,0 @@
1
- # Protocol::HTTP1
2
-
3
- Provides a low-level implementation of the HTTP/1 protocol.
4
-
5
- [![Build Status](https://travis-ci.com/socketry/protocol-http1.svg?branch=master)](https://travis-ci.com/socketry/protocol-http1)
6
-
7
- ## Installation
8
-
9
- Add this line to your application's Gemfile:
10
-
11
- ```ruby
12
- gem 'protocol-http1'
13
- ```
14
-
15
- And then execute:
16
-
17
- $ bundle
18
-
19
- Or install it yourself as:
20
-
21
- $ gem install protocol-http1
22
-
23
- ## Usage
24
-
25
- Here is a basic HTTP/1.1 client:
26
-
27
- ```ruby
28
- require 'async'
29
- require 'async/io/stream'
30
- require 'async/http/endpoint'
31
- require 'protocol/http1/connection'
32
-
33
- Async do
34
- endpoint = Async::HTTP::Endpoint.parse("https://www.google.com/search?q=kittens", alpn_protocols: ["http/1.1"])
35
-
36
- peer = endpoint.connect
37
-
38
- puts "Connected to #{peer} #{peer.remote_address.inspect}"
39
-
40
- # IO Buffering...
41
- stream = Async::IO::Stream.new(peer)
42
- client = Protocol::HTTP1::Connection.new(stream)
43
-
44
- def client.read_line
45
- @stream.read_until(Protocol::HTTP1::Connection::CRLF) or raise EOFError
46
- end
47
-
48
- puts "Writing request..."
49
- client.write_request("www.google.com", "GET", "/search?q=kittens", "HTTP/1.1", [["Accept", "*/*"]])
50
- client.write_body(nil)
51
-
52
- puts "Reading response..."
53
- response = client.read_response("GET")
54
-
55
- puts "Got response: #{response.inspect}"
56
-
57
- puts "Closing client..."
58
- client.close
59
- end
60
- ```
61
-
62
- ## Contributing
63
-
64
- 1. Fork it
65
- 2. Create your feature branch (`git checkout -b my-new-feature`)
66
- 3. Commit your changes (`git commit -am 'Add some feature'`)
67
- 4. Push to the branch (`git push origin my-new-feature`)
68
- 5. Create new Pull Request
69
-
70
- ## License
71
-
72
- Released under the MIT license.
73
-
74
- Copyright, 2019, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
75
-
76
- Permission is hereby granted, free of charge, to any person obtaining a copy
77
- of this software and associated documentation files (the "Software"), to deal
78
- in the Software without restriction, including without limitation the rights
79
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
80
- copies of the Software, and to permit persons to whom the Software is
81
- furnished to do so, subject to the following conditions:
82
-
83
- The above copyright notice and this permission notice shall be included in
84
- all copies or substantial portions of the Software.
85
-
86
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
87
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
88
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
89
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
90
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
91
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
92
- THE SOFTWARE.
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task :default => :spec
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
4
-
5
- require 'async'
6
- require 'async/io/stream'
7
- require 'async/http/endpoint'
8
- require 'protocol/http1/connection'
9
- require 'pry'
10
-
11
- Async do
12
- endpoint = Async::HTTP::Endpoint.parse("https://www.google.com/search?q=kittens", alpn_protocols: ["http/1.1"])
13
-
14
- peer = endpoint.connect
15
-
16
- puts "Connected to #{peer} #{peer.remote_address.inspect}"
17
-
18
- # IO Buffering...
19
- stream = Async::IO::Stream.new(peer)
20
- client = Protocol::HTTP1::Connection.new(stream)
21
-
22
- def client.read_line
23
- @stream.read_until(Protocol::HTTP1::Connection::CRLF) or raise EOFError
24
- end
25
-
26
- puts "Writing request..."
27
- client.write_request("www.google.com", "GET", "/search?q=kittens", "HTTP/1.1", [["Accept", "*/*"]])
28
- client.write_body(nil)
29
-
30
- puts "Reading response..."
31
- response = client.read_response("GET")
32
-
33
- puts "Got response: #{response.inspect}"
34
-
35
- puts "Closing client..."
36
- client.close
37
- end
38
-
39
- puts "Exiting."
@@ -1,31 +0,0 @@
1
-
2
- require_relative 'lib/protocol/http1/version'
3
-
4
- Gem::Specification.new do |spec|
5
- spec.name = "protocol-http1"
6
- spec.version = Protocol::HTTP1::VERSION
7
- spec.authors = ["Samuel Williams"]
8
- spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
-
10
- spec.summary = "A low level implementation of the HTTP/1 protocol."
11
- spec.homepage = "https://github.com/socketry/protocol-http1"
12
- spec.license = "MIT"
13
-
14
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
15
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
- end
17
-
18
- spec.required_ruby_version = "~> 2.4"
19
-
20
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
- spec.require_paths = ["lib"]
22
-
23
- spec.add_dependency "protocol-http", "~> 0.15"
24
-
25
- spec.add_development_dependency "covered"
26
- spec.add_development_dependency "bundler"
27
- spec.add_development_dependency "rake", "~> 10.0"
28
- spec.add_development_dependency "rspec", "~> 3.0"
29
- spec.add_development_dependency "rspec-memory", "~> 1.0"
30
- spec.add_development_dependency "rspec-files", "~> 1.0"
31
- end