Skip to content

Instantly share code, notes, and snippets.

@ne-sachirou
Created May 27, 2014 07:38
Show Gist options
  • Save ne-sachirou/019339fe512e32916084 to your computer and use it in GitHub Desktop.
Save ne-sachirou/019339fe512e32916084 to your computer and use it in GitHub Desktop.
Try to implement HTTP2 client in Elixir.
defmodule Http2StudyClient do
@moduledoc """
http2study
"""
@doc "GET HTTP/2.0"
def get domain, port do
{:ok, sock} = :gen_tcp.connect domain, port, [:binary, {:packet, 0}]
try do
{:ok, data} = send_connection_preface sock
IO.inspect data
{:ok, data} = establish_connection sock
IO.inspect data
{:ok, data} = send_get_request sock
IO.inspect data
:ok = send_goaway sock
after
:ok = :gen_tcp.close sock
end
end
defp establish_connection sock do
{:ok, data} = send_settings sock
IO.inspect data
unless data = build_frame(0x01, 0, 0, <<>>) do
{:error, "Can't establish a connection."}
else
send_ack sock
end
end
defp send_connection_preface sock do
IO.puts "Sending connection preface"
:gen_tcp.send sock, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
case do_receive_once(sock) do
{:ok, data} ->
if Regex.match?(~r/^HTTP\//, data) do
{:error, "not supported."}
else
{:ok, data}
end
x -> x
end
end
defp send_settings sock do
IO.puts "Sending SETTINGS"
:gen_tcp.send sock, build_frame(0x4, 0, 0, <<>>)
do_receive_once sock
end
defp send_ack sock do
IO.puts "Sending ACK"
:gen_tcp.send sock, build_frame(0x01, 0, 0, <<>>)
do_receive_once sock
end
defp send_goaway sock do
IO.puts "Sending GOAWAY"
no_error = 0
:gen_tcp.send sock, build_frame(0x7, 0, 0, <<no_error::32>>)
:gen_tcp.close sock
end
defp send_get_request sock do
IO.puts "Sending GET request"
end_headers = 0x4
:gen_tcp.send sock, build_frame(0x1, end_headers, 1, <<
<<0::1, 0::1, 0::1, 1::1, 0::4, 0::1, size("scheme")::7, "scheme", 0::1, size("http")::7, "http">>,
<<0::1, 0::1, 0::1, 1::1, 0::4, 0::1, size("authority")::7, "authority", 0::1, size("nghttp2.org")::7, "nghttp2.org">>,
<<0::1, 0::1, 0::1, 1::1, 0::4, 0::1, size("path")::7, "path", 0::1, size("/")::7, "/">>,
<<0::1, 0::1, 0::1, 1::1, 0::4, 0::1, size("method")::7, "method", 0::1, size("GET")::7, "GET">>
>>)
do_receive_once sock
end
defp build_frame frame_type, flags, stream_id, payload do
header = <<0::2, size(payload)::14, frame_type::8, flags::8, 0::1, stream_id::31>>
<<header::binary, payload::binary>>
end
defp do_receive_once sock do
receive do
{:tcp, sock, bin} ->
<<0::2, payload_size::14, frame_type::8, flags::8, 0::1, stream_id::31, payload::binary>> = bin
case frame_type do
0x3 ->
<<error_code::32, _::binary>> = payload
{:error, "RST_STREAM error_code:#{error_code}"}
0x7 ->
<<error_code::32, _::binary>> = payload
{:error, "GOAWAY error_code:#{error_code}"}
_ ->
{:ok, bin}
end
{:tcp_closed, sock} ->
{:error, "closed."}
{:tcp_error, sock, reason} ->
{:error, reason}
after
5000 ->
{:error, "timeout."}
end
end
# defp do_receive_response sock, header \\ [], body \\ [] do
# end
end
Http2StudyClient.get 'nghttp2.org', 80
@heri16
Copy link

heri16 commented Jul 21, 2016

Does it work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment