Overview

The Julia Webstack is a family of modules for building web services in the Julia language. The documentation provided here is generally in order of highest to lowest level.

Morsel

Morsel is a Sintra-like micro framework for declaring routes and handling requests. It is built on top of HttpServer.jl and Meddle.jl.

Installation

julia> Pkg.add("Morsel")

Example

Here is a brief example that will return a few different messages for different routes. The line get(app, "/about") do ... is shorthand for only serving GET requests through that route.

using Morsel

app = Morsel.app()

route(app, GET | POST | PUT, "/") do req, res
    "This is the root"
end

get(app, "/about") do req, res
    "This app is running on Morsel"
end

start(app, 8000)

Expected Behavior:

The server will run on localhost:8000.

For GET, POST, and PUT requests for /, you will get "This is the root" as a response. For GET requests for /about, you will get "This app is running on Morsel".

Meddle

Meddle is a middleware stack for use with HttpServer.jl.

Installation:

julia> Pkg.add("Meddle")

Basic Example:

Define a 'stack' of middleware through which incoming Requests are processed:

using HttpServer
using Meddle

stack = middleware(DefaultHeaders, CookieDecoder, FileServer(pwd()), NotFound)
http = HttpHandler((req, res)->Meddle.handle(stack, req, res))

for event in split("connect read write close error")
    http.events[event] = (event->(client, args...)->println(client.id,": $event"))(event)
end
http.events["error"] = (client, err)->println(err)
http.events["listen"] = (port)->println("Listening on $port...")

server = Server(http)
run(server, 8000)

WebSockets

This is a server-side implementation of the WebSockets protocol in Julia. If you want to write a web app in Julia that uses WebSockets, you'll need this package.

This package works with HttpServer.jl, which is what you to set up a server that accepts HTTP(S) connections.

As a first example, we can create a WebSockets echo server:

using HttpServer
using WebSockets

wsh = WebSocketHandler() do req,client
        while true
            msg = read(client)
            write(client, msg)
        end
      end

server = Server(wsh)
run(server,8080)

This sets up a server running on localhost, port 8080. It will accept WebSockets connections. The function in wsh will be called once per connection; it takes over that connection. In this case, it reads each msg from the client and then writes the same message back: a basic echo server.

The function that you pass to the WebSocketHandler constructor takes two arguments: a Request from HttpCommon.jl, and a WebSocket from here.

What can you do with a WebSocket?

You can:

Installation/Setup

julia> Pkg.add("WebSockets")

At this point, you can use the examples below to test that it all works.

Chat client/server example:

  1. Move to the ~/.julia/WebSockets.jl directory
  2. Run julia examples/chat.jl
  3. In a web browser, open localhost:8000
  4. You should see a basic IRC-like chat application

Echo server example:

using HttpServer
using WebSockets

wsh = WebSocketHandler() do req,client
        while true
            msg = read(client)
            write(client, msg)
        end
      end

server = Server(wsh)
run(server,8080)

To play with a WebSockets echo server, you can:

  1. Paste the above code in to the Julia REPL
  2. Open localhost:8080 in Chrome
  3. Open the Chrome developers tools console
  4. Type ws = new WebSocket("ws://localhost:8080"); into the console
  5. Type ws.send("hi") into the console.
  6. Switch to the 'Network' tab; click on the request; click on the 'frames' tab.
  7. You will see the two frames containing "hi": one sent and one received.

HttpServer

This is a basic, non-blocking HTTP server in Julia.

You can write a web application using just this if you're happy dealing directly with values representing HTTP requests and responses.

The Request and Response types come from HttpCommon; see that section for documentation.

Installation/Setup

julia> Pkg.add("HttpServer")

Testing Your Installation

  1. Move to the ~/.julia/HttpServer.jl/ directory.
  2. Run julia examples/hello.jl.
  3. Open localhost:8000/hello/name in a browser.
  4. You should see a text greeting from the server in your browser.

Basic Example:

using HttpServer

# Julia's do syntax lets you more easily pass a function as an argument
http = HttpHandler() do req::Request, res::Response
    # if the requested path starts with `/hello/`, say hello
    # otherwise, return a 404 error
    Response( ismatch(r"^/hello/",req.resource) ? string("Hello ", split(req.resource,'/')[3], "!") : 404 )
end

# HttpServer supports setting handlers for particular events
http.events["error"]  = ( client, err ) -> println( err )
http.events["listen"] = ( port )        -> println("Listening on $port...")

server = Server( http ) #create a server from your HttpHandler
run( server, 8000 ) #never returns

HttpParser

This module provides a Julia wrapper around Joyent's http-parser library: http-parser

You can look at the code of HttpSever.jl as an example of using HttpParser.jl.

Installation

julia> Pkg.add("HttpParser")

Requirements

libhttp-parser needs to be available as a shared library. It should be built automatically by Pkg.

Installing libhttp-parser as a shared library manually

  1. clone https://github.com/joyent/http-parser
  2. cd http-parser
  3. make library # Outputs a .so file, should be a .dylib on OS X
  4. move the libhttp_parser.so to /usr/local/lib (rename to libhttp_parser.dylib if on OS X)

Test

  1. Move to ~/.julia/HttpParser/
  2. Run julia src/Test.jl
  3. Expect to see text indicating that all assertions have passed.

HttpCommon

This package provides types and helper functions for dealing with the HTTP protocol.

Installation

julia> Pkg.add("HttpCommon")

Request

A Request represents an HTTP request sent by a client to a server.

type Request
    method::String
    resource::String
    headers::Headers
    data::String
end

Response

A Response represents an HTTP response sent to a client by a server.

type Response
    status::Int
    headers::Headers
    data::HttpData
    finished::Bool
end

There are a variety of constructors for Response, which set sane defaults for unspecified values.

Response([statuscode::Int])
Response(statuscode::Int,[h::Headers],[d::HttpData])
Response(d::HttpData,[h::Headers])

Headers

Headers is a type alias for Dict{String,String}. There is a default constructor, headers, to produce a reasonable default set of headers. The defaults are as follows:

[ "Server" => "Julia/$VERSION",
  "Content-Type" => "text/html; charset=utf-8",
  "Content-Language" => "en",
  "Date" => RFC1123_datetime()]

The last setting, "Date" uses another HttpCommon function:

RFC1123_datetime([CalendarTime])

When an argument is not provided, the current time (now()) is used. RFC1123 describes the correct format for putting timestamps into HTTP headers.

STATUS_CODES

STATUS_CODES is a const Dict{Int,String}. It maps all the status codes defined in RFC's to their descriptions.

STATUS_CODES[200] #=> "OK"
STATUS_CODES[404] #=> "Not Found"
STATUS_CODES[418] #=> "I'm a teapot"
STATUS_CODES[500] #=> "Internal Server Error"

HttpMethodBitmasks

HttpCommon provides Int bitmasks to represent the HTTP request methods (GET, POST, PUT, UPDATE, DELETE, OPTIONS, HEAD). There are two dictionaries, HttpMethodNameToBitmask and HttpMethodBitmaskToName, to allow for mapping back and forth from String names to Int bitmasks.

The purpose of having bitmasks is that you can write GET | POST | UPDATE and end up with a bitmask representing the union of those HTTP methods.

escapeHTML(i::String)

escapeHTML will return a new String with the following characters escaped: &, <, >, ".

encodeURI(decoded::String)

encodeURI returns a new, URI-safe string. It escapes all non-URI characters (only letters, digits, -, _ , ., and ~ are allowed) in the standard way.

decodeURI(encoded::String)

decodeURI returns a new String with all the unsafe characters returned to their original meanings. It works with the output of encodeURI as well as other standard URI encoders.

parsequerystring(query::String)

parsequerystring takes a query string (as from a URL) and returns a Dict{String,String} representing the same data.

An example:

 q = "foo=bar&baz=%3Ca%20href%3D%27http%3A%2F%2Fwww.hackershool.com%27%3Ehello%20world%21%3C%2Fa%3E"
parsequerystring(q)
# => ["foo"=>"bar","baz"=>"<a href='http://www.hackershool.com'>hello world!</a>"]