Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

HTTP library (server, client, parser) for the Julia language

branch: master
README.md

HTTP.jl

A Julia library defining the specification and providing the data-types for HTTP servers. It also provides a rudimentary BasicServer that can respond to simple HTTP requests. The spec and the server are in very active development and some breaking changes should be expected along the way.

Besides the spec and server it provides the Ocean library for more easily building your own apps on top of the HTTP.jl spec (ie. Ocean apps can be run on BasicServer with just one line of code).

Installation

Pkg.add("HTTP")

Getting started with HTTP.jl

The first component of HTTP.jl is the HTTP module. This provides the base Request, Response, and Cookie types as well as some basic helper methods for working with those types (however, actually parsing requests into Requestss is left to server implementations). Check out HTTP.jl for the actual code; it's quite readable.

HTTP.Util (in HTTP/Util.jl) also provides some helper methods for escaping and unescaping data.

Using BasicServer

See the BasicServer example to get an idea of the function API BasicServer expects (it's a lot like Rack).

Other notes

The current spec is heavily inspired by Ruby's Rack specification. The parser parts of the basic server are based off of the WEBrick Ruby HTTP server.

Ocean

Ocean is a Sinatra-like library for creating apps that run on the HTTP.jl API (they can currently only be served using BasicServer).

Hello World

This will create a basic server on port 8000 that responds with "Hello World" to requests at /.

require("HTTP/Ocean")

using Ocean

app = new_app()

get(app, "/", function(req, res, _)
  return "Hello World"
end)

BasicServer.bind(8000, binding(app), true)

You can also use Ocean without mucking up your scope with using:

require("HTTP/Ocean")

app = Ocean.app()

Ocean.get(app, "/", function(req, res, _)
  return "Hello World"
end)

BasicServer.bind(8000, Ocean.binding(app), true)

Route parameters

To capture route parameters format your route as a regular expression with capture groups. For example:

Ocean.get(app, r"^/(.+)", function(req, res, _)
  return _.params[1]
end)

A GET request to /test would give the response test.

If the route path is a string instead of a regex then _.params will be false.

Parameterized routes

A new (and possibly buggy) feature is parameterized routes. You can create a parameterized route path by calling Ocean.pr (or Ocean.param_route) or just doing pr"/object/:id" (this macro-string version is currently only available if you do using Ocean). Example:

# No-using version
Ocean.get(app, Ocean.pr("/:test"), function(req, res, _)
  return _.params["test"]
end)
# Using version
get(app, pr"/:test", function(req, res, _)
  return _.params["test"]
end)

Request data

Let's say we have the following POST handler:

Ocean.post(app, "/", function(req, res, _)
  println(req.data)
  return "foobar"
end)

POSTing the data test=testing would result in something like {"test"=>["testing"]} being printed to output. Of note is that the value is an array. This is because any key could have multiple values (such as the data test=testing1&test=testing2), so for consistency any key in req.data will map to an array of values.

Ocean provides the shorthand method gs (for get_single). To get the first value of the key "test" in the data dictionary you would call v = gs(req.data, "test") (it will return false if the key does not exist). To access this and other utility methods just do using Ocean.Util (look at Ocean/Util.jl to see what exactly Ocean.Util provides).

Multipart data

Plain multipart data (without a filename and content-type) will be accessible as a regular array of string(s) in the req.data dict. Multipart data with a filename and content-type is stored as Multipart objects (see type definition) in the req.data dict.

Redirects

Also provided by Ocean.Util (using Ocean.Util) is the redirect method. This will set the Location header in the response headers and the response status to 302 (default).

using Ocean.Util

Ocean.get(app, ..., function(req, res, _)
  return redirect(res, "/")
  # To do a 301 redirect:
  # return redirect(res, "/", 301)
end)

Redirects are also exposed through Ocean.Extra (see Using templates below):

Ocean.get(app, ..., function(req, res, _)
  return _.redirect("/")
end)

Templates and files

The third parameter to the request handler (assumed to be _) is a Ocean.Extra (type definition here) object that provides some data and functions for common tasks.

Reading files

The _.file(path::String) method will return a string of the file's contents or throw an error. If the file starts with / then it will open it as an absolute path; if it starts with any other string it will open it relative to the directory of the app file. The contents of files read are cached in app.cache with the key _file:$path. You can disable caching by making the second parameter false (eg. _.file(path, false)).

Using templates

Ocean provides a method in Extra to easily access ejl (embedded Julia) and Mustache template files (for Mustache you must require the Mustache package yourself with require("Mustache") or using Mustache). It uses the _.file method internally to read the path to the file; it also caches the compiled template data in app.cache (with the keys _ejl:$path and _mustache:$path) so that the templates don't have to be recompiled every request.

Rendering an ejl template:

_.template(:ejl, "view.ejl", {"value" => value})

Rendering a Mustache template:

_.template(:mustache, "view.mustache", {"value" => value})

Getting and setting cookies

These are basic handlers to get and set cookies:

require("HTTP/Ocean")
using Calendar
using Ocean.Util

Ocean.get(app, "/", function(req, res, _)
  v = gs(req.cookies, "test")
  if v != false
    return "Cookie: " * v
  else
    return "Cookie not set"
  end
end)

# Expects POST data like "test=..." and assigns it to the cookie "test".
Ocean.post(app, "/", function(req, res, _)
  postdata = gs(req.data, "test")

  cookie = HTTP.new_cookie("test", postdata, {:expires => Calendar.now() + Calendar.years(10)})
  HTTP.set_cookie(res, cookie)

  return redirect(res, "/")
end)

Contributing

Fork, commit, pull request! Tweet/email me or open an issue if you have any problems or ideas.

Next steps

On the drawing board:

  • Ocean: Better routing system (allow for fancy routes like "/model/:id" and such)
  • Ocean: Improve the ejl template system
  • HTTP/BasicServer/Ocean: Improve cookie system
    • Split up cookie system for complete and appropriate separation of concerns
    • Improve/clear up cookie system to make it more straightforward and consistent
  • HTTP: Middleware API
    • HTTP: Session middleware
  • BasicServer: File/binary upload
  • Client for performing HTTP requests

Changelog

0.0.3 (WIP)

Improvements

  • Cookie and session middleware
  • Simple (currently blocking) client for performing HTTP requests

0.0.2 (2013-03-06)

Improvements

  • Multipart handling
  • New template system
  • Basic middleware system
  • Add Mustache.jl template hooks
  • Add cookie handling
  • Add file, template, and redirect scoped methods to Extra objects
  • Add memo method to Util for easy memoization of data through an associative cache
  • Switch to new evented socket API (and add event-loop-free version of bind)

Fixes

  • Fix error reporting

0.0.1 (2013-02-15)

Improvements

  • Add cookie creation functionality
  • Make ref for RegexMatch in Ocean.Util work properly
  • Add Extra object for route handlers

Fixes

  • Make any shortcut only create a special "ANY" route (instead of separate routes for each request method)

0.0.0 (2013-02-07)

  • Initial version
Something went wrong with that request. Please try again.