hails- Multi-app web platform framework

Safe HaskellTrustworthy




Exports basic HTTP client functions inside the DC Monad. Computations are allowed to communicate over HTTP as long as they can read and write to a labeled origin. An origin is associated with two labels. When writing, the origin has a label of the form < "scheme://authority", |True >, where scheme is either 'http' or 'https', and authority is the domain name or IP address used in the request and port number of the connection. In other words, the secrecy component contains the origin information, while the integrity component is the same as that of public data. When reading, the origin has a label of the form < |True, "scheme://authority" >.

This means that DC computations can export data if the current label is not higher than that of the labeled origin, and read data that is no more trustworthy than that of the origin. Practically, this means that untrusted computation can export data so long as the they have not observed any data more sensitive than the label of the target domain. Reading (which also occurs on every request/write) further raises the current label to the join of the current label and origin.

For example, suppose some piece of data, myLoc, has the label:

aliceLocL = dcLabel ("alice" /\ "http://maps.googleapis.com:80") dcTrue

created as:

myLoc <- labelP alicePriv  aliceLocL "3101 24th Street, San Francisco, CA"

Then, untrusted code (with initial label set to public) running on behalf of "alice" , may perform the following operation:

let mapBase = "http://maps.googleapis.com/maps/api/geocode/json?sensor=false"
aliceLoc <- unlabelP alicePriv myLoc
resp <- simpleGetHttp $ mapBase ++ "&address=" ++ aliceLoc

In this case the unlabelP will raise the current label to the label:

< "http://maps.googleapis.com:80", |True >

by exercising "alice"s privilges. Directly, the simpleHttp will be permitted. However, if

let mapBase = "http://maps.evilalternatives.org/geocode/json?sensor=false"

an exception will be thrown since the current label does not flow to the label of mapBase.

This module uses 'http-conduit' as the underlying client, we recommend looking at the Network.HTTP.Conduit documentation on how to construct Requests. Here, we highlight some important details:

  • The headers Content-Length and Host are automatically set, and should not be added to requestHeaders.
  • By default, the functions in this package will not throw exceptions for non-2xx status codes. If you would like to use the default http-conduit behavior, you should use checkStatus, e.g.:
 req <- parseUrl mapBase
 resp <- simpleGetHttp $ req { checkStatus = \s@(Status sci _) hs ->
           if 200 <= sci && sci < 300
               then Nothing
               else Just $ toException $ StatusCodeException s hs }


Request type

type Request = Request Source

Reques type, wrapper for the conduit Request.

method :: Request -> Method

HTTP request method, eg GET, POST.

Since 0.1.0

secure :: Request -> Bool

Whether to use HTTPS (ie, SSL).

Since 0.1.0

host :: Request -> ByteString

Requested host name, used for both the IP address to connect to and the host request header.

Since 0.1.0

port :: Request -> Int

The port to connect to. Also used for generating the host request header.

Since 0.1.0

path :: Request -> ByteString

Everything from the host to the query string.

Since 0.1.0

queryString :: Request -> ByteString

Query string appended to the path.

Since 0.1.0

requestHeaders :: Request -> RequestHeaders

Custom HTTP request headers

The Content-Length and Transfer-Encoding headers are set automatically by this module, and shall not be added to requestHeaders.

If not provided by the user, Host will automatically be set based on the host and port fields.

Moreover, the Accept-Encoding header is set implicitly to gzip for convenience by default. This behaviour can be overridden if needed, by setting the header explicitly to a different value. In order to omit the Accept-Header altogether, set it to the empty string "". If you need an empty Accept-Header (i.e. requesting the identity encoding), set it to a non-empty white-space string, e.g. " ". See RFC 2616 section 14.3 for details about the semantics of the Accept-Header field. If you request a content-encoding not supported by this module, you will have to decode it yourself (see also the decompress field).

Note: Multiple header fields with the same field-name will result in multiple header fields being sent and therefore it's the responsibility of the client code to ensure that the rules from RFC 2616 section 4.2 are honoured.

Since 0.1.0

requestBody :: Request -> RequestBody

Request body to be sent to the server.

Since 0.1.0

rawBody :: Request -> Bool

If True, a chunked and/or gzipped body will not be decoded. Use with caution.

Since 0.1.0

redirectCount :: Request -> Int

How many redirects to follow when getting a resource. 0 means follow no redirects. Default value: 10.

Since 0.1.0

checkStatus :: Request -> Status -> ResponseHeaders -> CookieJar -> Maybe SomeException

Check the status code. Note that this will run after all redirects are performed. Default: return a StatusCodeException on non-2XX responses.

Since 0.1.0

decompress :: Request -> ByteString -> Bool

Predicate to specify whether gzipped data should be decompressed on the fly (see alwaysDecompress and browserDecompress). Argument is the mime type. Default: browserDecompress.

Since 0.1.0

Response type

data Response Source

A response sent by the app.




respStatus :: Status

Response status

respHeaders :: ResponseHeaders

Response headers

respBody :: ByteString

Response body

Simple HTTP interface

parseUrl :: String -> DC Request Source

Convert a URL into a Request.

This defaults some of the values in Request, such as setting method to GET and requestHeaders to [].

applyBasicAuth :: ByteString -> ByteString -> Request -> Request

Add a Basic Auth header (with the specified user name and password) to the given Request. Ignore error handling:

 applyBasicAuth "user" "pass" $ fromJust $ parseUrl url

Since 0.1.0

simpleHttp Source


:: Request


-> DC Response 

Perform a simple HTTP(S) request.

simpleHttpP Source


:: PrivDesc DCLabel p 
=> Priv p


-> Request


-> DC Response 

Same as simpleHttp, but uses privileges.

simpleGetHttp :: String -> DC Response Source

Simple HTTP GET request.

simpleGetHttpP Source


:: DCPriv


-> String


-> DC Response 

Simple HTTP GET request.

simpleHeadHttp :: String -> DC Response Source

Simple HTTP HEAD request.

simpleHeadHttpP Source


:: DCPriv


-> String


-> DC Response 

Simple HTTP HEAD request.


data HttpException :: *


StatusCodeException Status ResponseHeaders CookieJar 
InvalidUrlException String String 
TooManyRedirects [Response ByteString]

List of encountered responses containing redirects in reverse chronological order; including last redirect, which triggered the exception and was not followed.

UnparseableRedirect (Response ByteString)

Response containing unparseable redirect.

HttpParserException String 
FailedConnectionException String Int


FailedConnectionException2 String Int Bool SomeException


InvalidStatusLine ByteString 
InvalidHeader ByteString 
InternalIOException IOException 
ProxyConnectException ByteString Int (Either ByteString HttpException)


TlsException SomeException 
ResponseBodyTooShort Word64 Word64

Expected size/actual size.

Since 1.9.4


Since 1.9.4

InvalidDestinationHost ByteString 
HttpZlibException ZlibException

Since 0.3

InvalidProxyEnvironmentVariable Text Text

Environment name and value

Since 0.4.7


Detect a case where both the content-length header and transfer-encoding: chunked are used. Since 0.4.8.

Since 0.4.11 this exception isn't thrown anymore.