Skip to content

Instantly share code, notes, and snippets.

@ne-sachirou
Created May 27, 2014 07:38
Show Gist options
  • Select an option

  • Save ne-sachirou/019339fe512e32916084 to your computer and use it in GitHub Desktop.

Select an option

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

heri16 commented Jul 21, 2016

Copy link
Copy Markdown

Does it work?

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