Created
May 27, 2014 07:38
-
-
Save ne-sachirou/019339fe512e32916084 to your computer and use it in GitHub Desktop.
Try to implement HTTP2 client in Elixir.
This file contains hidden or 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
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Does it work?