Skip to content
This repository has been archived by the owner on Jan 18, 2018. It is now read-only.
/ happy Public archive

Clojure(Script) HTTP async client library

Notifications You must be signed in to change notification settings

jeluard/happy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Happy License

Usage | Interceptor | Representor

Clojars Project.

A Clojure(Script) HTTP async client library with swappable implementation.

happy ships with a Clojure client based on OkHTTP and a ClojureScript client based on XMLHttpRequest.

Usage

happy main function is happy.core/send!. It allows to send an HTTP call represented as a map and receive via a handler function a response map.

(ns my.app
  (:require [happy.core :as h]
            [happy.client.xmlhttprequest :as hc]))

(h/set-default-client! (hc/create))

(let [c (h/send! {:method "GET" :url "http://google.com"} {:handler #(println "received " %)})]
  ; an HTTP call can be aborted
  (h/-abort c))

A request map has the following shape:

{:method        "GET"       ; an uppercase String identifying the HTTP method used
 :headers       {}          ; a String/String map of key/value headers
 :body          ""          ; a body payload whose type must match client implementation capacities}

When called, the :handler function will receive as single argument a response map with the following shape:

{:type          :response   ; a keyword identifying the response
                            ; can be `:response`, `:progress` or `:failure`

 ; if :type = :response
 :status        200         ; an integer of the HTTP status code
 :headers       {}          ; a String/(String or seq) map of key/value headers
 :body          ""          ; a payload whose type depends on client implementation

 ; if :type = :progress
 :direction     :sending    ; a keyword identifying if this is a `:receiving` or `:sending` progress
 :loaded        10          ; an integer of the count of currently loaded bytes, optional
 :total         150         ; an integer of total bytes, optional

 ; if :type = :failure
 :termination   :abort      ; a keyword whose value can be `:abort`, `:timeout` or `:network`
 :reason        ""          ; a String detailing the failure, optional
}

A handler is called only once per request with a :type of :response or :failure. If :report-progress? option is provided and the client implementation supports it :handler can be called a number of times with type :progress.

For simplicity both request and response are modeled after the ring SPEC.

Helper functions for common verbs are provided to simplify common calls.

(ns my.app
  (:require [happy.core :as h :refer [GET PUT]]
            [happy.client.xmlhttprequest :as hc]))

(h/set-default-client! (hc/create))

(GET "http://google.com" {} {:handler #(println "received " %)})
(PUT "http://my-app.com" {:data "some payload"})

Options

The second parameter to happy.core/send! is a map of options that will affect an HTTP call.

Each client can accept any option. Those must be advertised in the happy.core/-supports map as extra-options.

Common options are available (optional unless specified):

  • :client to define the client implementation, mandatory
  • :handler the callback function called when the HTTP call is executed
  • :timeout the maximum time allowed for the HTTP call to finish, in milliseconds
  • :request-body-as the type of :body send by the client. Client specific, default to :string
  • :response-body-as the type of :body received by the client. Client specific, default to :string
  • :report-progress? if :progress event are provided to the callback :handler
  • :request-interceptors the sequence of interceptors applied to a request
  • :response-interceptors the sequence of interceptors applied to a response

Options can also be set globally (stored in happy.core/default-options) using happy.core/swap-options!, happy.core/merge-options! and happy.core/set-default-client!.

(ns my.app
  (:require [happy.core :as h :refer [GET]]
            [happy.client.xmlhttprequest :as hc]))

(h/set-default-client! (hc/create))
(h/merge-options! {:report-progress? true
                   :handler #(println %)})

(GET "http://google.com")

Interceptor

Interceptors allow users to modify request and response part of an HTTP call. Interceptors are simple function returning their argument eventually modified and are applied in order.

happy bundles a couple interceptors.

A request interceptor is specified via :request-interceptors and receive as argument a sequence of the request map and the options map. A response interceptor is specified via :response-interceptors and receive as argument the response map.

(ns my.app
  (:require [happy.core :as h :refer [GET]]
            [happy.client.xmlhttprequest :as hc]))

(defn dump-request
  [[req om :as m]]
  (println "Request: " req)
  m)

(h/set-default-client! (hc/create))
(h/merge-options! {:request-interceptors [dump-request]})

(GET "http://google.com")

options can be modified in a request interceptor. This allows for instance to generate per call response interceptor, like in this timing interceptor:

(ns my.app
  (:require [happy.core :as h :refer [GET]]
            [happy.client.xmlhttprequest :as hc]))

(defn now [] (System/currentTimeMillis))
(defn timing-interceptor
  [[_ om :as v]]
  (let [i (now)]
    (assoc v 1 (update om :response-interceptors #(cons %2 %1) (fn [m _] (assoc m :timing (- (now) i)))))))

(h/set-default-client! (hc/create))
(h/merge-options! {:request-interceptors [timing-interceptor]})

(GET "http://google.com" {} {:handler #(println "Executed in " (:timing %) "ms")})

Representor

Representors encapsulate the logic of converting HTTP body between the user and the client implementation. Custom representors can be provided by implementing the happy.core/Representor protocol.

To have a representor used automatically as part of the HTTP call it must be defined using respectively the request-interceptors and response-interceptors options. Representors as interceptors are automatically applied based on request / response content-type and will replace :body with the result of their invocation. By specifying a mime-type via override-request-mime-type or override-response-mime-type a user can control with representor will be used.

Default representor for edn, json, transit and other common mime types are available and can be setup using the merge-representors! function defined in their respective namespace.

License

Copyright (C) 2015-2016 Julien Eluard

Distributed under the Eclipse Public License, the same as Clojure.

About

Clojure(Script) HTTP async client library

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published