Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSocket routes #34

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ end
start(app, 8000)
```

The `wsroute` function can be used to set up a route that opens a WebSocket connection.

```julia
wsroute(app, GET, "/echo") do req, sock
while true
msg = bytestring(read(sock))
write(sock, msg)
end
end
```

---

```julia
Expand All @@ -41,4 +52,4 @@ start(app, 8000)
::
Hacker School
:::::::::::::
```
```
1 change: 1 addition & 0 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
julia 0.3
HttpCommon
HttpServer
WebSockets
Meddle
42 changes: 35 additions & 7 deletions src/Morsel.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
module Morsel

using HttpServer,
WebSockets,
HttpCommon,
Meddle

export App,
app,
route,
wsroute,
namespace,
with,
get,
Expand Down Expand Up @@ -47,12 +49,15 @@ routing_tables() = (HttpMethodBitmask => RoutingTable)[method => RoutingTable()
#
type App
routes::Dict{HttpMethodBitmask, RoutingTable}
wsroutes::Dict{HttpMethodBitmask, RoutingTable}
state::Dict{Any,Any}
end
function app()
App(routing_tables(), Dict{Any,Any}())
App(routing_tables(), routing_tables(), Dict{Any,Any}())
end

get_routes(app, method, websock) = websock ? app.wsroutes[method] : app.routes[method]

# This defines a route and adds it to the `app.routes` dictionary. As HTTP
# methods are bitmasked integers they can be combined using the bitwise or
# operator, e.g. `GET | POST` refers to a `GET` method and a `POST` method.
Expand All @@ -70,7 +75,7 @@ end
# "Hello, world"
# end
#
function route(handler::Function, app::App, methods::Int, path::String)
function route(handler::Function, app::App, methods::Int, path::String; websocket=false)
prefix = get(app.state, :routeprefix, "")
withstack = get(app.state, :withstack, Midware[])
handle = handler
Expand All @@ -79,11 +84,14 @@ function route(handler::Function, app::App, methods::Int, path::String)
handle = (req::MeddleRequest, res::Response) -> Meddle.handle(stack, req, res)
end
for method in HttpMethodBitmasks
methods & method == method && register!(app.routes[method], prefix * path, handle)
if (methods & method == method)
register!(get_routes(app, method, websocket), prefix * path, handle)
end
end
app
end
route(a::App, m::Int, p::String, h::Function) = route(h, a, m, p)
route(a::App, m::Int, p::String, h::Function; kwargs...) = route(h, a, m, p; kwargs...)
wsroute(args...) = route(args...; websocket=true)

function namespace(thunk::Function, app::App, prefix::String)
beforeprefix = get(app.state, :routeprefix, "")
Expand Down Expand Up @@ -201,22 +209,42 @@ function start(app::App, port::Int)
for comp in split(req.state[:resource], '/')
!isempty(comp) && push!(path, comp)
end
methodizedRouteTable = app.routes[HttpMethodNameToBitmask[req.http_req.method]]
is_websock = haskey(req.state, :websocket)
methodizedRouteTable = get_routes(app, HttpMethodNameToBitmask[req.http_req.method], is_websock)
handler, params = match_route_handler(methodizedRouteTable, path)
for (k,v) in params
req.params[symbol(k)] = v
end

if handler != nothing
return prepare_response(handler(req, res), req, res)
if is_websock
handler(req, req.state[:websocket])
return respond(req, Response(200))
else
return prepare_response(handler(req, res), req, res)
end
end
respond(req, Response(404))
end

stack = middleware(DefaultHeaders, URLDecoder, CookieDecoder, BodyDecoder, MorselApp)
http = HttpHandler((req, res) -> Meddle.handle(stack, MeddleRequest(req,Dict{Symbol,Any}(),Dict{Symbol,Any}()), res))

websock = WebSocketHandler() do req, sock
try
Meddle.handle(stack, MeddleRequest(
req, Dict{Symbol, Any}([(:websocket, sock)]), Dict{Symbol,Any}()), Response(200))
close(sock)
catch ex
println(STDERR, "Error inside websocket loop!")
Base.error_show(STDERR, ex, catch_backtrace())
close(sock)
end
end

http.events["listen"] = (port) -> println("Morsel is listening on $port...")

server = Server(http)
server = Server(http, websock)
run(server, port)
end

Expand Down