Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ os:
- linux
- osx
julia:
- 0.4
- 0.5
- 1.0
- nightly
notifications:
email: false
# uncomment the following lines to override the default test script
#script:
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
# - julia -e 'Pkg.clone(pwd()); Pkg.build("PromiseExtractor"); Pkg.test("PromiseExtractor"; coverage=true)'
script: # the default script is equivalent to the following
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia -e 'using Pkg; Pkg.clone("https://github.com/tanmaykm/HPack.jl.git"); Pkg.clone(pwd()); Pkg.build("HTTP2"); Pkg.test("HTTP2"; coverage=true)';
62 changes: 33 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,60 +12,64 @@ julia> using HTTP2

## Simple Servers and Clients

The library can directly create simple servers and clients. For full support of
HTTP/2 Upgrade and HTTPS, use `HttpServer.jl` and `Requests.jl`.
The library can directly create simple servers and clients.

You only use this library directly if you need low-level functionality. An
example for the server is as follows. The code will be explained in the next
section.

```julia
using HTTP2
using Sockets
using Dates

port = 8888
server = listen(port)

println("Server started.")
while(true)
buffer = accept(server)
println("Processing a connection ...")
println("Waiting for a connection ...")
buffer = accept(server)
println("Processing a connection ...")

connection = Session.new_connection(buffer; isclient=false)
## Recv the client preface, and send an empty SETTING frame.
connection = HTTP2.Session.new_connection(buffer; isclient=false)

headers_evt = Session.take_evt!(connection)
stream_identifier = headers_evt.stream_identifier
## Recv the client preface, and send an empty SETTING frame.
headers_evt = HTTP2.Session.take_evt!(connection)
stream_identifier = headers_evt.stream_identifier

sending_headers = Headers(":status" => "200",
"server" => "HTTP2.jl",
"date" => Dates.format(now(Dates.UTC), Dates.RFC1123Format),
"content-type" => "text/html; charset=UTF-8")
sending_headers = HTTP2.Headers(":status" => "200",
"server" => "HTTP2.jl",
"date" => Dates.format(now(Dates.UTC), Dates.RFC1123Format),
"content-type" => "text/html; charset=UTF-8")
sending_body = convert(Vector{UInt8}, codeunits("hello"))

Session.put_act!(connection, Session.ActSendHeaders(stream_identifier, sending_headers, false))
Session.put_act!(connection, Session.ActSendData(stream_identifier, body, true))
@info("Resopnding", sending_headers, sending_body)

## We are done!
end
HTTP2.Session.put_act!(connection, HTTP2.Session.ActSendHeaders(stream_identifier, sending_headers, false))
HTTP2.Session.put_act!(connection, HTTP2.Session.ActSendData(stream_identifier, sending_body, true))
## We are done!
```

A client can be started in a similar way. Again the code will be explained in
the next section.

```julia
buffer = connect(dest, port)

## Create a HTTPConnection object
connection = Session.new_connection(buffer; isclient=true)

## Create a request with headers
headers = Headers(":method" => "GET",
":path" => url,
using HTTP2
using Sockets

@info("Opening connection", conn_id)
buffer = connect("127.0.0.1", 8888)
connection = HTTP2.Session.new_connection(buffer; isclient=true)
headers = HTTP2.Headers(":method" => "GET",
":path" => "/",
":scheme" => "http",
":authority" => "127.0.0.1:9000",
"accept" => "*/*",
"accept-encoding" => "gzip, deflate",
"user-agent" => "HTTP2.jl")

Session.put_act!(connection, Session.ActSendHeaders(Session.next_free_stream_identifier(connection), headers, true))

return (Session.take_evt!(connection).headers, Session.take_evt!(connection).data)
@info("Sending request", req_id)
HTTP2.Session.put_act!(connection, HTTP2.Session.ActSendHeaders(HTTP2.Session.next_free_stream_identifier(connection), headers, true))
(rcvd_headers, rcvd_data) = (HTTP2.Session.take_evt!(connection).headers, HTTP2.Session.take_evt!(connection).data)
```

## Connection Lifecycle
Expand Down
6 changes: 3 additions & 3 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
julia 0.4
HPack 0.2.0
HttpCommon 0.2.4
julia 1.0
HPack
MbedTLS
22 changes: 13 additions & 9 deletions src/Frame.jl
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
module Frame
import Base: ==, AbstractIOBuffer
import Base: ==
import ..HTTP2: readallbytes

@enum FRAME_TYPES DATA=0x0 HEADERS=0x1 PRIORITY=0x2 RST_STREAM=0x3 SETTINGS=0x4 PUSH_PROMISE=0x5 PING=0x6 GOAWAY=0x7 WINDOW_UPDATE=0x8 CONTINUATION=0x9

immutable FrameHeader
struct FrameHeader
length::UInt32
typ::FRAME_TYPES
flags::UInt8
stream_identifier::UInt32
end

function decode_header(buf)
length_arr = read(buf, 3)
length_arr = readallbytes(buf, 3)
length = UInt32(length_arr[1]) << 16 + UInt32(length_arr[2]) << 8 + UInt32(length_arr[3])

typ = FRAME_TYPES(read(buf, 1)[1])
flags = read(buf, 1)[1]
stream_identifier_arr = read(buf, 4)
typ = FRAME_TYPES(readallbytes(buf, 1)[1])
flags = readallbytes(buf, 1)[1]
stream_identifier_arr = readallbytes(buf, 4)
stream_identifier = UInt32(stream_identifier_arr[1]) << 24 + UInt32(stream_identifier_arr[2]) << 16 +
UInt32(stream_identifier_arr[3]) << 8 + UInt32(stream_identifier_arr[4])

Expand All @@ -37,11 +38,11 @@ function encode_header(header::FrameHeader)
write(buf, UInt8(header.stream_identifier >> 24), UInt8((header.stream_identifier >> 16) & 0x000000ff),
UInt8((header.stream_identifier >> 8) & 0x000000ff), UInt8(header.stream_identifier & 0x000000ff))

return takebuf_array(buf)
return take!(buf)
end


type UnimplementedError <: Exception end
struct UnimplementedError <: Exception end

include("Frame/utils.jl")
include("Frame/data.jl")
Expand All @@ -56,8 +57,11 @@ include("Frame/window_update.jl")
include("Frame/continuation.jl")

function decode(buf)
@show "trying to read header"
header = decode_header(buf)
payload = read(buf, header.length)
@show header.typ
@show header.length
payload = readallbytes(buf, header.length)
@assert length(payload) == header.length

if header.typ == DATA
Expand Down
2 changes: 1 addition & 1 deletion src/Frame/continuation.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
immutable ContinuationFrame
struct ContinuationFrame
is_end_headers::Bool
stream_identifier::UInt32
fragment::Array{UInt8, 1}
Expand Down
2 changes: 1 addition & 1 deletion src/Frame/data.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
immutable DataFrame
struct DataFrame
stream_identifier::UInt32
is_end_stream::Bool
data::Array{UInt8, 1}
Expand Down
20 changes: 10 additions & 10 deletions src/Frame/goaway.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
immutable GoawayFrame
struct GoawayFrame
last_stream_identifier::UInt32
error_code::UInt32
debug_data::Array{UInt8, 1}
debug_data::Vector{UInt8}
end

==(a::GoawayFrame, b::GoawayFrame) =
Expand All @@ -22,14 +22,14 @@ function decode_goaway(header, payload)
end

function encode_goaway(frame)
payload::Array{UInt8, 1} = [ UInt8(frame.last_stream_identifier >> 24) & 0x7f;
UInt8(frame.last_stream_identifier >> 16 & 0x000000ff);
UInt8(frame.last_stream_identifier >> 8 & 0x000000ff);
UInt8(frame.last_stream_identifier & 0x000000ff);
UInt8(frame.error_code >> 24);
UInt8(frame.error_code >> 16 & 0x000000ff);
UInt8(frame.error_code >> 8 & 0x000000ff);
UInt8(frame.error_code & 0x000000ff) ]
payload::Vector{UInt8} = [ UInt8(frame.last_stream_identifier >> 24) & 0x7f;
UInt8(frame.last_stream_identifier >> 16 & 0x000000ff);
UInt8(frame.last_stream_identifier >> 8 & 0x000000ff);
UInt8(frame.last_stream_identifier & 0x000000ff);
UInt8(frame.error_code >> 24);
UInt8(frame.error_code >> 16 & 0x000000ff);
UInt8(frame.error_code >> 8 & 0x000000ff);
UInt8(frame.error_code & 0x000000ff) ]
append!(payload, frame.debug_data)

return wrap_payload(payload, GOAWAY, 0x0, 0x0)
Expand Down
32 changes: 16 additions & 16 deletions src/Frame/headers.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
immutable HeadersFrame
struct HeadersFrame
is_end_stream::Bool
is_end_headers::Bool
is_priority::Bool
stream_identifier::UInt32
exclusive::Nullable{Bool}
dependent_stream_identifier::Nullable{UInt32}
weight::Nullable{UInt8}
exclusive::Union{Nothing,Bool}
dependent_stream_identifier::Union{Nothing,UInt32}
weight::Union{Nothing,UInt8}
fragment::Array{UInt8, 1}
end

Expand All @@ -14,9 +14,9 @@ end
a.is_end_headers == b.is_end_headers &&
a.is_priority == b.is_priority &&
a.stream_identifier == b.stream_identifier &&
(isnull(a.exclusive) || a.exclusive.value == b.exclusive.value) &&
(isnull(a.exclusive) || a.dependent_stream_identifier.value == b.dependent_stream_identifier.value) &&
(isnull(a.exclusive) || a.weight.value == b.weight.value) &&
((a.exclusive === nothing) || a.exclusive == b.exclusive) &&
((a.exclusive === nothing) || a.dependent_stream_identifier == b.dependent_stream_identifier) &&
((a.exclusive === nothing) || a.weight == b.weight) &&
a.fragment == b.fragment

function decode_headers(header, payload)
Expand All @@ -33,11 +33,11 @@ function decode_headers(header, payload)
weight = payload[5]

return HeadersFrame(is_end_stream, is_end_headers, is_priority, header.stream_identifier,
Nullable(exclusive), Nullable(dependent_stream_identifier),
Nullable(weight), getindex(payload, 6:length(payload)))
exclusive, dependent_stream_identifier,
weight, getindex(payload, 6:length(payload)))
else
return HeadersFrame(is_end_stream, is_end_headers, is_priority, header.stream_identifier,
Nullable{Bool}(), Nullable{UInt32}(), Nullable{UInt8}(), payload)
nothing, nothing, nothing, payload)
end
end

Expand All @@ -48,12 +48,12 @@ function encode_headers(frame)
(frame.is_priority ? 0x20 : 0x0)

if frame.is_priority
payload::Array{UInt8, 1} = [ UInt8(frame.dependent_stream_identifier.value >> 24) & 0x7f;
UInt8(frame.dependent_stream_identifier.value >> 16 & 0x000000ff);
UInt8(frame.dependent_stream_identifier.value >> 8 & 0x000000ff);
UInt8(frame.dependent_stream_identifier.value & 0x000000ff) ]
payload[1] = frame.exclusive.value ? (payload[1] | 0x80 ) : payload[1]
push!(payload, frame.weight.value)
payload::Array{UInt8, 1} = [ UInt8(frame.dependent_stream_identifier >> 24) & 0x7f;
UInt8(frame.dependent_stream_identifier >> 16 & 0x000000ff);
UInt8(frame.dependent_stream_identifier >> 8 & 0x000000ff);
UInt8(frame.dependent_stream_identifier & 0x000000ff) ]
payload[1] = frame.exclusive ? (payload[1] | 0x80 ) : payload[1]
push!(payload, frame.weight)
append!(payload, frame.fragment)
else
payload = frame.fragment
Expand Down
2 changes: 1 addition & 1 deletion src/Frame/ping.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
immutable PingFrame
struct PingFrame
is_ack::Bool
data::Array{UInt8, 1}
end
Expand Down
2 changes: 1 addition & 1 deletion src/Frame/priority.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
immutable PriorityFrame
struct PriorityFrame
stream_identifier::UInt32
exclusive::Bool
dependent_stream_identifier::UInt32
Expand Down
2 changes: 1 addition & 1 deletion src/Frame/push_promise.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
immutable PushPromiseFrame
struct PushPromiseFrame
is_end_headers::Bool
stream_identifier::UInt32
promised_stream_identifier::UInt32
Expand Down
2 changes: 1 addition & 1 deletion src/Frame/rst_stream.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
immutable RstStreamFrame
struct RstStreamFrame
stream_identifier::UInt32
error_code::UInt32
end
Expand Down
16 changes: 8 additions & 8 deletions src/Frame/settings.jl
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
@enum SETTING_IDENTIFIER SETTINGS_HEADER_TABLE_SIZE=0x1 SETTINGS_ENABLE_PUSH=0x2 SETTINGS_MAX_CONCURRENT_STREAMS=0x3 SETTINGS_INITIAL_WINDOW_SIZE=0x4 SETTINGS_MAX_FRAME_SIZE=0x5 SETTINGS_MAX_HEADER_LIST_SIZE=0x6

immutable SettingsFrame
struct SettingsFrame
is_ack::Bool
parameters::Nullable{Array{Tuple{SETTING_IDENTIFIER, UInt32}, 1}}
parameters::Union{Nothing,Array{Tuple{SETTING_IDENTIFIER, UInt32}, 1}}
end

SettingsFrame() = SettingsFrame(false, Nullable(Array{Tuple{Frame.SETTING_IDENTIFIER, UInt32}, 1}()))
SettingsFrame() = SettingsFrame(false, Array{Tuple{Frame.SETTING_IDENTIFIER, UInt32}, 1}())

==(a::SettingsFrame, b::SettingsFrame) =
a.is_ack == b.is_ack &&
(isnull(a.parameters) || a.parameters.value == b.parameters.value)
((a.parameters === nothing) || a.parameters == b.parameters)

type UnknownIdentifierError <: Exception end
struct UnknownIdentifierError <: Exception end

function decode_settings(header, payload)
@assert header.stream_identifier == 0x0
Expand All @@ -20,7 +20,7 @@ function decode_settings(header, payload)

if is_ack
@assert length(payload) == 0
return SettingsFrame(is_ack, Nullable{Array{Tuple{SETTING_IDENTIFIER, UInt32}}}())
return SettingsFrame(is_ack, nothing)
else
parameters = Array{Tuple{SETTING_IDENTIFIER, UInt32}, 1}()
for i = 1:div(length(payload), 6)
Expand All @@ -29,7 +29,7 @@ function decode_settings(header, payload)
UInt32(payload[(i-1)*6+5]) << 8 + UInt32(payload[(i-1)*6+6])
push!(parameters, (SETTING_IDENTIFIER(identifier), value))
end
return SettingsFrame(is_ack, Nullable(parameters))
return SettingsFrame(is_ack, parameters)
end
end

Expand All @@ -38,7 +38,7 @@ function encode_settings(frame)
return wrap_payload([], SETTINGS, 0x1, 0x0)
else
payload = Array{UInt8, 1}()
for val in frame.parameters.value
for val in frame.parameters
append!(payload, [ UInt8(UInt16(val[1]) >> 8);
UInt8(UInt16(val[1]) & 0x00ff);
UInt8(val[2] >> 24);
Expand Down
2 changes: 1 addition & 1 deletion src/Frame/window_update.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
immutable WindowUpdateFrame
struct WindowUpdateFrame
stream_identifier::UInt32
window_size_increment::UInt32
end
Expand Down
Loading