This repository has been archived by the owner on Dec 7, 2018. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
29 changed files
with
1,761 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#!/usr/bin/env ruby | ||
# frozen_string_literal: true | ||
# Run with: bundle exec examples/h2/hello_world.rb | ||
|
||
require 'bundler/setup' | ||
require 'reel/h2' | ||
|
||
Reel::Logger.logger.level = ::Logger::DEBUG | ||
Reel::H2.verbose! | ||
|
||
addr, port = '127.0.0.1', 1234 | ||
|
||
puts "*** Starting server on http://#{addr}:#{port}" | ||
s = Reel::H2::Server::HTTP.new host: addr, port: port do |connection| | ||
connection.each_stream do |stream| | ||
stream.respond :ok, "hello, world!\n" | ||
stream.connection.goaway | ||
end | ||
end | ||
|
||
sleep |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#!/usr/bin/env ruby | ||
# frozen_string_literal: true | ||
# Run with: bundle exec examples/h2/https_hello_world.rb | ||
|
||
require 'bundler/setup' | ||
require 'reel/h2' | ||
|
||
port = 1234 | ||
addr = Socket.getaddrinfo('localhost', port).first[3] | ||
certs_dir = File.expand_path '../../../tmp/certs', __FILE__ | ||
|
||
tls = { | ||
cert: certs_dir + '/server.crt', | ||
key: certs_dir + '/server.key', | ||
# :extra_chain_cert => certs_dir + '/chain.pem' | ||
} | ||
|
||
puts "*** Starting server on https://#{addr}:#{port}" | ||
|
||
s = Reel::H2::Server::HTTPS.new host: addr, port: port, **tls do |connection| | ||
connection.each_stream do |stream| | ||
stream.goaway_on_complete | ||
|
||
if stream.request.path == '/favicon.ico' | ||
stream.respond :not_found | ||
else | ||
stream.respond :ok, "hello, world!\n" | ||
end | ||
end | ||
end | ||
|
||
sleep |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
#!/usr/bin/env ruby | ||
# frozen_string_literal: true | ||
# Run with: bundle exec examples/h2/push_promise.rb | ||
|
||
require 'bundler/setup' | ||
require 'reel/h2' | ||
|
||
Reel::Logger.logger.level = ::Logger::DEBUG | ||
Reel::H2.verbose! | ||
|
||
port = 1234 | ||
addr = Socket.getaddrinfo('localhost', port).first[3] | ||
certs_dir = File.expand_path '../../../tmp/certs', __FILE__ | ||
logo_png = File.read File.expand_path '../../../logo.png', __FILE__ | ||
push_promise = '<html>wait for it...<img src="/logo.png"/><script src="/pushed.js"></script></html>' | ||
pushed_js = '(()=>{ alert("hello h2 push promise!"); })();' | ||
|
||
sni = { | ||
'localhost' => { | ||
:cert => certs_dir + '/server.crt', | ||
:key => certs_dir + '/server.key', | ||
# :extra_chain_cert => certs_dir + '/chain.pem' | ||
} | ||
} | ||
|
||
puts "*** Starting server on https://#{addr}:#{port}" | ||
s = Reel::H2::Server::HTTPS.new host: addr, port: port, sni: sni do |connection| | ||
connection.each_stream do |stream| | ||
|
||
if stream.request.path == '/favicon.ico' | ||
stream.respond :not_found | ||
|
||
else | ||
stream.goaway_on_complete | ||
|
||
stream.push_promise '/logo.png', { 'content-type' => 'image/png' }, logo_png | ||
|
||
js_promise = stream.push_promise_for '/pushed.js', { 'content-type' => 'application/javascript' }, pushed_js | ||
js_promise.make_on stream | ||
|
||
stream.respond :ok, push_promise | ||
|
||
js_promise.keep | ||
end | ||
end | ||
end | ||
|
||
sleep |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# frozen_string_literal: true | ||
|
||
module Reel | ||
|
||
module H2 | ||
|
||
# http/2 psuedo-headers | ||
# | ||
AUTHORITY_KEY = ':authority' | ||
METHOD_KEY = ':method' | ||
PATH_KEY = ':path' | ||
SCHEME_KEY = ':scheme' | ||
STATUS_KEY = ':status' | ||
|
||
# turn on extra verbose debug logging | ||
# | ||
def self.verbose! | ||
@verbose = true | ||
end | ||
|
||
def self.verbose? | ||
@verbose = false unless defined?(@verbose) | ||
@verbose | ||
end | ||
|
||
end | ||
end | ||
|
||
require 'reel' | ||
require 'reel/h2/connection' | ||
require 'reel/h2/push_promise' | ||
require 'reel/h2/server' | ||
require 'reel/h2/server/https' | ||
require 'reel/h2/stream' | ||
require 'reel/h2/stream/request' | ||
require 'reel/h2/stream/response' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
require 'http/2' | ||
|
||
module Reel | ||
module H2 | ||
|
||
# handles reading data from the +@socket+ into the +HTTP2::Server+ +@parser+, | ||
# callbacks from the +@parser+, and closing of the +@socket+ | ||
# | ||
class Connection | ||
|
||
# each +@parser+ event method is wrapped in a block to call a local instance | ||
# method of the same name | ||
# | ||
PARSER_EVENTS = [ | ||
:frame, | ||
:frame_sent, | ||
:frame_received, | ||
:stream, | ||
:goaway | ||
] | ||
|
||
attr_reader :parser, :server, :socket | ||
|
||
def initialize socket:, server: | ||
@socket = socket | ||
@server = server | ||
@parser = ::HTTP2::Server.new | ||
@attached = true | ||
|
||
yield self if block_given? | ||
|
||
bind_events | ||
|
||
Logger.debug "new H2::Connection: #{self}" if H2.verbose? | ||
end | ||
|
||
# is this connection still attached to the server reactor? | ||
# | ||
def attached? | ||
@attached | ||
end | ||
|
||
# bind parser events to this instance | ||
# | ||
def bind_events | ||
PARSER_EVENTS.each do |e| | ||
on = "on_#{e}".to_sym | ||
@parser.on(e) { |x| __send__ on, x } | ||
end | ||
end | ||
|
||
# closes this connection's socket if attached | ||
# | ||
def close | ||
socket.close if socket && attached? | ||
end | ||
|
||
# is this connection's socket closed? | ||
# | ||
def closed? | ||
socket.closed? | ||
end | ||
|
||
# prevent this server reactor from handling this connection | ||
# | ||
def detach | ||
@attached = false | ||
self | ||
end | ||
|
||
# accessor for stream handler | ||
# | ||
def each_stream &block | ||
@each_stream = block if block_given? | ||
@each_stream | ||
end | ||
|
||
# queue a goaway frame | ||
# | ||
def goaway | ||
server.async.goaway self | ||
end | ||
|
||
# begins the read loop, handling all errors with a log message, | ||
# backtrace, and closing the +@socket+ | ||
# | ||
def read | ||
begin | ||
while attached? && !@socket.closed? && !(@socket.eof? rescue true) | ||
data = @socket.readpartial(4096) | ||
@parser << data | ||
end | ||
close | ||
|
||
rescue => e | ||
Logger.error "Exception: #{e.message} - closing socket" | ||
STDERR.puts e.backtrace | ||
close | ||
|
||
end | ||
end | ||
|
||
protected | ||
|
||
# +@parser+ event methods | ||
|
||
# called by +@parser+ with a binary frame to write to the +@socket+ | ||
# | ||
def on_frame bytes | ||
Logger.debug "Writing bytes: #{truncate_string(bytes.unpack("H*").first)}" if Reel::H2.verbose? | ||
|
||
# N.B. this is the important bit | ||
# | ||
@socket.write bytes | ||
rescue IOError, Errno::EPIPE => e | ||
Logger.error e.message | ||
close | ||
end | ||
|
||
def on_frame_sent f | ||
Logger.debug "Sent frame: #{truncate_frame(f).inspect}" if Reel::H2.verbose? | ||
end | ||
|
||
def on_frame_received f | ||
Logger.debug "Received frame: #{truncate_frame(f).inspect}" if Reel::H2.verbose? | ||
end | ||
|
||
# the +@parser+ calls this when a new stream has been initiated by the | ||
# client | ||
# | ||
def on_stream stream | ||
Reel::H2::Stream.new connection: self, stream: stream | ||
end | ||
|
||
# the +@parser+ calls this when a goaway frame is received from the client | ||
# | ||
def on_goaway event | ||
close | ||
end | ||
|
||
private | ||
|
||
def truncate_string s | ||
(String === s && s.length > 64) ? "#{s[0,64]}..." : s | ||
end | ||
|
||
def truncate_frame f | ||
f.reduce({}) { |h, (k, v)| h[k] = truncate_string(v); h } | ||
end | ||
|
||
end | ||
end | ||
end |
Oops, something went wrong.