aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/plum/connection.rb62
-rw-r--r--lib/plum/hpack/encoder.rb4
-rw-r--r--lib/plum/http_connection.rb78
-rw-r--r--lib/plum/https_connection.rb2
4 files changed, 71 insertions, 75 deletions
diff --git a/lib/plum/connection.rb b/lib/plum/connection.rb
index 2c2536a..2597790 100644
--- a/lib/plum/connection.rb
+++ b/lib/plum/connection.rb
@@ -55,9 +55,7 @@ module Plum
return if new_data.empty?
@buffer << new_data
- if @state == :negotiation
- negotiate!
- end
+ negotiate! if @state == :negotiation
if @state != :negotiation
while frame = Frame.parse!(@buffer)
@@ -65,6 +63,10 @@ module Plum
receive_frame(frame)
end
end
+ rescue ConnectionError => e
+ callback(:connection_error, e)
+ goaway(e.http2_error_type)
+ close
end
alias << receive
@@ -84,15 +86,15 @@ module Plum
end
def negotiate!
- if CLIENT_CONNECTION_PREFACE.start_with?(@buffer.byteslice(0, 24))
- if @buffer.bytesize >= 24
- @buffer.byteshift(24)
- @state = :waiting_settings
- settings(@local_settings)
- end
- else
+ unless CLIENT_CONNECTION_PREFACE.start_with?(@buffer.byteslice(0, 24))
raise ConnectionError.new(:protocol_error) # (MAY) send GOAWAY. sending.
end
+
+ if @buffer.bytesize >= 24
+ @buffer.byteshift(24)
+ @state = :waiting_settings
+ settings(@local_settings)
+ end
end
def new_stream(stream_id, **args)
@@ -107,26 +109,27 @@ module Plum
end
def validate_received_frame(frame)
- case @state
- when :waiting_settings
+ if @state == :waiting_settings
raise ConnectionError.new(:protocol_error) if frame.type != :settings
- @state = :negotiated
+ @state = :open
callback(:negotiated)
- when :waiting_continuation
+ end
+
+ if @state == :waiting_continuation
if frame.type != :continuation || frame.stream_id != @continuation_id
- raise Plum::ConnectionError.new(:protocol_error)
+ raise ConnectionError.new(:protocol_error)
end
if frame.flags.include?(:end_headers)
@state = :open
@continuation_id = nil
end
- else
- if [:headers].include?(frame.type)
- if !frame.flags.include?(:end_headers)
- @state = :waiting_continuation
- @continuation_id = frame.stream_id
- end
+ end
+
+ if [:headers].include?(frame.type)
+ if !frame.flags.include?(:end_headers)
+ @state = :waiting_continuation
+ @continuation_id = frame.stream_id
end
end
end
@@ -141,17 +144,11 @@ module Plum
if @streams.key?(frame.stream_id)
stream = @streams[frame.stream_id]
else
- if frame.stream_id.even? # stream started by client must have odd ID
- raise Plum::ConnectionError.new(:protocol_error)
- end
+ raise ConnectionError.new(:protocol_error) if frame.stream_id.even? # stream started by client must have odd ID
stream = new_stream(frame.stream_id)
end
stream.receive_frame(frame)
end
- rescue ConnectionError => e
- callback(:connection_error, e)
- goaway(e.http2_error_type)
- close
end
def receive_control_frame(frame)
@@ -179,11 +176,12 @@ module Plum
def receive_settings(frame, send_ack: true)
if frame.flags.include?(:ack)
raise ConnectionError.new(:frame_size_error) if frame.length != 0
+ callback(:settings_ack)
return
+ else
+ raise ConnectionError.new(:frame_size_error) if frame.length % 6 != 0
end
- raise ConnectionError.new(:frame_size_error) if frame.length % 6 != 0
-
old_remote_settings = @remote_settings.dup
@remote_settings.merge!(frame.parse_settings)
apply_remote_settings(old_remote_settings)
@@ -202,10 +200,10 @@ module Plum
raise Plum::ConnectionError.new(:frame_size_error) if frame.length != 8
if frame.flags.include?(:ack)
- on(:ping_ack)
+ callback(:ping_ack)
else
- on(:ping)
opaque_data = frame.payload
+ callback(:ping, opaque_data)
send_immediately Frame.ping(:ack, opaque_data)
end
end
diff --git a/lib/plum/hpack/encoder.rb b/lib/plum/hpack/encoder.rb
index 11e4485..a1a09e8 100644
--- a/lib/plum/hpack/encoder.rb
+++ b/lib/plum/hpack/encoder.rb
@@ -13,8 +13,8 @@ module Plum
def encode(headers)
out = ""
headers.each do |name, value|
- name = name.to_s.force_encoding(Encoding::BINARY)
- value = value.to_s.force_encoding(Encoding::BINARY)
+ name = name.to_s.b
+ value = value.to_s.b
if index = search(name, value)
out << encode_indexed(index)
elsif index = search(name, nil)
diff --git a/lib/plum/http_connection.rb b/lib/plum/http_connection.rb
index e5e63f6..4ffea73 100644
--- a/lib/plum/http_connection.rb
+++ b/lib/plum/http_connection.rb
@@ -5,58 +5,56 @@ module Plum
def initialize(io, local_settings = {})
require "http/parser"
super
+ @_headers = nil
+ @_body = ""
@_http_parser = setup_parser
- @_upgrade_retry = false # After sent 426 (Upgrade Required)
end
private
+ def negotiate!
+ super
+ rescue ConnectionError
+ # Upgrade from HTTP/1.1
+ offset = @_http_parser << @buffer
+ @buffer.byteshift(offset)
+ end
+
def setup_parser
- headers = nil
- body = ""
parser = HTTP::Parser.new
- parser.on_message_begin = proc { }
- parser.on_headers_complete = proc {|_headers| headers = _headers }
- parser.on_body = proc {|chunk| body << chunk }
- parser.on_message_complete = proc do |env|
- # Upgrade from HTTP/1.1
- heads = headers.map {|n, v| [n.downcase, v] }.to_h
- connection = heads["connection"] || ""
- upgrade = heads["upgrade"] || ""
- settings = heads["http2-settings"]
+ parser.on_headers_complete = proc {|_headers|
+ @_headers = _headers.map {|n, v| [n.downcase, v] }.to_h
+ }
+ parser.on_body = proc {|chunk| @_body << chunk }
+ parser.on_message_complete = proc {|env|
+ connection = @_headers["connection"] || ""
+ upgrade = @_headers["upgrade"] || ""
+ settings = @_headers["http2-settings"]
if (connection.split(", ").sort == ["Upgrade", "HTTP2-Settings"].sort &&
upgrade.split(", ").include?("h2c") &&
settings != nil)
- respond_switching_protocol
- self.on(:negotiated) {
- _frame = Frame.new(type: :settings, stream_id: 0, payload: Base64.urlsafe_decode64(settings))
- receive_settings(_frame, send_ack: false) # HTTP2-Settings
- process_first_request(parser, heads, body)
- }
+ switch_protocol(settings)
else
respond_not_supported
close
end
- end
+ }
parser
end
- def negotiate!
- begin
- super
- rescue ConnectionError
- # Upgrade from HTTP/1.1
- offset = @_http_parser << @buffer
- @buffer.byteshift(offset)
- end
- end
+ def switch_protocol(settings)
+ self.on(:negotiated) {
+ _frame = Frame.new(type: :settings, stream_id: 0, payload: Base64.urlsafe_decode64(settings))
+ receive_settings(_frame, send_ack: false) # HTTP2-Settings
+ process_first_request
+ }
- def respond_switching_protocol
resp = ""
resp << "HTTP/1.1 101 Switching Protocols\r\n"
resp << "Connection: Upgrade\r\n"
resp << "Upgrade: h2c\r\n"
+ resp << "Server: plum/#{Plum::VERSION}\r\n"
resp << "\r\n"
io.write(resp)
@@ -69,23 +67,25 @@ module Plum
resp << "HTTP/1.1 505 HTTP Version Not Supported\r\n"
resp << "Content-Type: text/plain\r\n"
resp << "Content-Length: #{data.bytesize}\r\n"
+ resp << "Server: plum/#{Plum::VERSION}\r\n"
resp << "\r\n"
resp << data
io.write(resp)
end
- def process_first_request(parser, heads, dat)
- stream = new_stream(1)
- heads = heads.merge({ ":method" => parser.http_method,
- ":path" => parser.request_url,
- ":authority" => heads["host"] })
- .reject {|n, v| ["connection", "http2-settings", "upgrade", "host"].include?(n) }
+ def process_first_request
encoder = HPACK::Encoder.new(0, indexing: false) # don't pollute connection's HPACK context
- headers = Frame.headers(1, encoder.encode(heads), :end_headers) # stream ID is 1
- headers.split_headers(local_settings[:max_frame_size]).each {|hfrag| stream.receive_frame(hfrag) }
- data = Frame.data(1, dat, :end_stream)
- data.split_data(local_settings[:max_frame_size]).each {|dfrag| stream.receive_frame(dfrag) }
+ stream = new_stream(1)
+ max_frame_size = local_settings[:max_frame_size]
+ headers = @_headers.merge({ ":method" => @_http_parser.http_method,
+ ":path" => @_http_parser.request_url,
+ ":authority" => @_headers["host"] })
+ .reject {|n, v| ["connection", "http2-settings", "upgrade", "host"].include?(n) }
+
+ headers_s = Frame.headers(1, encoder.encode(headers), :end_headers).split_headers(max_frame_size) # stream ID is 1
+ data_s = Frame.data(1, @_body, :end_stream).split_data(max_frame_size)
+ (headers_s + data_s).each {|frag| stream.receive_frame(frag) }
end
end
end
diff --git a/lib/plum/https_connection.rb b/lib/plum/https_connection.rb
index e96a785..a1def19 100644
--- a/lib/plum/https_connection.rb
+++ b/lib/plum/https_connection.rb
@@ -1,5 +1,3 @@
-using Plum::BinaryString
-
module Plum
class HTTPSConnection < Connection
def initialize(io, local_settings = {})