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 (ResourceT IO)Source

Reques type, wrapper for the conduit Request.

method :: Request m -> Method

HTTP request method, eg GET, POST.

secure :: Request m -> Bool

Whether to use HTTPS (ie, SSL).

port :: Request m -> Int

path :: Request m -> ByteString

Everything from the host to the query string.

requestHeaders :: Request m -> RequestHeaders

Custom HTTP request headers

As already stated in the introduction, the Content-Length and Host headers are set automatically by this module, and shall not be added to requestHeaders.

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.

rawBody :: Request m -> Bool

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

redirectCount :: Request m -> Int

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

checkStatus :: Request m -> 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.

decompress :: Request m -> ContentType -> Bool

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

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 RequestSource

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 m -> Request m

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



:: Request


-> DC Response 

Perform a simple HTTP(S) request.



:: PrivDesc DCLabel p 
=> Priv p


-> Request


-> DC Response 

Same as simpleHttp, but uses privileges.

simpleGetHttp :: String -> DC ResponseSource

Simple HTTP GET request.



:: DCPriv


-> String


-> DC Response 

Simple HTTP GET request.

simpleHeadHttp :: String -> DC ResponseSource

Simple HTTP HEAD request.



:: DCPriv


-> String


-> DC Response 

Simple HTTP HEAD request.