An HTTP/2 Server written purely in Crystal
HTTP/2 is binary, instead of textual. HTTP/2 is fully multiplexed. This means that HTTP/2 can send multiple requests for data in parallel over a single TCP connection. This is the most advanced feature of the HTTP/2 protocol because it allows you to download web files via ASync mode from one server.
Full H2Spec and H2Load Coverage
h2load -n 100000 -c 100 -t 1 -T 2 -m 32 -H 'Accept-Encoding: gzip,deflate' https://127.0.0.1:9876
starting benchmark...
spawning thread #0: 100 total client(s). 100000 total requests
TLS Protocol: TLSv1.3
Cipher: TLS_AES_256_GCM_SHA384
Server Temp Key: ECDH P-256 256 bits
Application protocol: h2
progress: 10% done
progress: 20% done
progress: 30% done
progress: 40% done
progress: 50% done
progress: 60% done
progress: 70% done
progress: 80% done
progress: 90% done
progress: 100% done
finished in 13.91s, 7187.18 req/s, 407.42KB/s
requests: 100000 total, 100000 started, 100000 done, 100000 succeeded, 0 failed, 0 errored, 0 timeout
status codes: 100000 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 5.54MB (5804800) total, 585.94KB (600000) headers (space savings 76.92%), 1.14MB (1200000) data
min max mean sd +/- sd
time for request: 94.66ms 1.61s 417.91ms 159.33ms 90.55%
time for connect: 62.29ms 1.40s 736.79ms 394.58ms 57.00%
time to 1st byte: 1.44s 1.75s 1.67s 112.38ms 80.00%
req/s : 71.88 72.18 72.00 0.11 68.00%
-
Add the dependency to your
shard.yml
:dependencies: duo: github: azutoolkit/duo
-
Run
shards install
require "duo"
require "duo"
class EchoHandler
include Duo::Server::Handler
def call(context : Duo::Server::Context)
request, response = context.request, context.response
context.response << "Hello World!"
context
end
end
class NotFoundHandler
include Duo::Server::Handler
def call(context : Duo::Server::Context)
response = context.response
response.status = 404
response.headers["server"] = "h2/0.0.0"
response.headers["content-type"] = "text/plain"
response << "404 NOT FOUND\n"
end
end
ssl_context = OpenSSL::SSL::Context::Server.new
ssl_context.certificate_chain = File.join(__DIR__, "ssl", "example.crt")
ssl_context.private_key = File.join(__DIR__, "ssl", "example.key")
ssl_context.alpn_protocol = "h2"
host = ENV["HOST"]? || "::"
port = (ENV["PORT"]? || 9876).to_i
handlers = [
EchoHandler.new,
NotFoundHandler.new,
]
server = Duo::Server.new(host, port, ssl_context)
if ssl_context
puts "listening on https://#{host}:#{port}/"
else
puts "listening on http://#{host}:#{port}/"
end
server.listen(handlers)
require "socket"
require "openssl"
require "duo/connection"
Log.for("Duo(Duo)").level = Log::Severity::Debug
client = Duo::Client.new("localhost", 9876, !!ENV["TLS"]?)
10.times do |i|
headers = HTTP::Headers{
":method" => "GET",
":path" => "/",
"user-agent" => "crystal h2/0.0.0",
}
client.request(headers) do |headers, body|
puts "REQ ##{i}: #{headers.inspect}"
while line = body.gets
puts "REQ ##{i}: #{line}"
end
end
end
client.close
- Julien Portalier @ysbaddaden
- @636f7374
Contributing to Duo can be a rewarding way to learn, teach, and build experience in just about any skill you can imagine. You don’t have to become a lifelong contributor to enjoy participating in Duo.
- Fork it (https://github.com/azutoolkit/duo/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
- Elias J. Perez - creator and maintainer