craze- HTTP Racing Library

Safe HaskellNone




Craze is a small module for performing multiple similar HTTP GET requests in parallel. This is performed through the raceGet function, which will perform all the requests and pick the first successful response that passes a certain check, meaning that the parallel requests are essentially racing against each other.

What is the usefulness of this?

If you are dealing with data source or API that is very unreliable (high latency, random failures) and there are no limitations on performing significantly more requests, then performing multiple requests (through direct connections, proxies, VPNs) may increase the chances of getting a successful response faster and more reliably.

However, if using a different data source or transport is a possibility, it is potentially a better option that this approach.


Performing two parallel GET requests against and returning the status code of the first successful one:

The providers generate two client configurations. The handler "parses" the response (in this case it just gets the status code). Finally, the checker filters out responses that we don't consider valid (anything that is not HTTP 200 in this case).

>>> :set -XOverloadedStrings
>>> :{
 let racer = (Racer
               { racerProviders =
                   [ simpleTagged [] "Client A"
                   , simpleTagged [] "Client B"
               , racerHandler = return . respStatus
               , racerChecker = (200 ==)
               , racerDebug = False
               , racerReturnLast = False
               } :: Racer [(String, String)] ByteString Int)
 in (raceGet racer "" >>= print)
Just 200



type RacerHandler headerTy bodyTy a = CurlResponse_ headerTy bodyTy -> IO a Source

A RacerHandler is simply a function for transforming a response after it is received. The handler is only applied to successful requests before they are checked by the RacerChecker.

This is primarily for extracting or parsing a CurlResponse_ before doing any further work. The type returned by the handler will be used as the input of the checker and will be the return type of functions like raceGet.

type RacerChecker a = a -> Bool Source

A function that computes whether or not a result is valid or not.

A racer will discard successful responses it get from its clients if they do not pass the checker.

This step allows the racer to potentially discard responses that, while technically successful, do not contain the expected result (e.g. APIs that return errors as HTTP 200s, rate limitting messages, or unexpected formats).

data Racer headerTy bodyTy a Source

A record describing the rules for racing requests.




racerHandler :: RacerHandler headerTy bodyTy a
racerChecker :: RacerChecker a
racerProviders :: [RacerProvider]

On a Racer, each RaceProvider represents a separate client configuration. When performing a race, each provider will be used to spwan a client and perform a request. This allows one to control the number of requests performed and with which CurlOptions.

racerDebug :: Bool

When set to True, debugging messages will be written to stdout.

racerReturnLast :: Bool

When set to True, the Racer will attempt to return the last response in the event that all responses failed to pass the checker. This can be used for identifying error conditions.

type RacerProvider = IO ProviderOptions Source

A provider is simply a factory function for ProviderOptions, which are used to configure a client.

data ProviderOptions Source

Configuration used to set up an individual client in the race.




poOptions :: [CurlOption]

Options to pass down to Curl.

poDelay :: Maybe Int

Number of microseconds to delay the request by.

Delays can be used to give other clients a headstart. This is useful in cases were some clients are more costly to use than others (e.g. Bandwidth costs, resource usage, etc).

poTag :: Text

A tag to identify this type provider.

Tags are not required to be unique, but they are generally more helpful if they are.

data RacerResult a Source

The result of a racing operation. This can be used to collect statistics on which providers win more often, etc.

data ClientStatus a Source

The status of running a single client.


Successful a

A successful response (passed the checker). A race will usually only have one successful response.

Failed a

A response that was received but failed to pass the checker.

Errored SomeException

An exception thrown while using the client.


The operation is still pending, was cancelled, or was never started.



raceGet :: (Eq a, CurlHeader ht, CurlBuffer bt, MonadIO m) => Racer ht bt a -> URLString -> m (Maybe a) Source

Perform a GET request on the provided URL using all providers in parallel.

Rough summary of the algorithm:

  • Start all requests
  • Wait for a request to finish.

    • If the request is successful, apply the handler on it.

      • If the result of the handler passes the checker, cancel all other requests, and return the result.
      • If the check fails, go back to waiting for another request to finish.
    • If the request fails, go back to waiting for another request to finish.

raceGetResult :: (Eq a, CurlHeader ht, CurlBuffer bt, MonadIO m) => Racer ht bt a -> URLString -> m (RacerResult a) Source

Same as raceGet, but returns a RacerResult which contains more information about the race performed.


RacerProvider provide client configurations. Craze comes bundled with a few built-in providers which can be used for quickly building client configurations.

simple :: Monad m => [CurlOption] -> m ProviderOptions Source

A simple provider. It does not delay requests.

simpleTagged :: Monad m => [CurlOption] -> Text -> m ProviderOptions Source

Like simple, but with a tag for identification.

delayed :: Monad m => [CurlOption] -> Int -> m ProviderOptions Source

A provider which will delay a request by the provided number of microseconds.

delayedTagged :: Monad m => [CurlOption] -> Int -> Text -> m ProviderOptions Source

Like delayed, but with a tag for identification.


defaultRacer :: Racer [(String, String)] ByteString ByteString Source

Deprecated: Use Data.Default.Class.def instead

A Racer with some default values.

Note: The handler will extract the response body as a ByteString and ignore everything else, hence the type:

Racer [(String, String)] ByteString ByteString

If this is not the desired behavior, or if the response should be parsed or processed, you should use the Racer constructor directly and provide all fields.

defaultProviderOptions :: ProviderOptions Source

Deprecated: Use Data.Default.Class.def instead

A default set of options for a provider.