Code Monkey home page Code Monkey logo

lehs's Introduction

lehs

Lightweight Embeddable HTTP Server

lehs is a hyper-text transfer protocol (http) web server implemented in clojure. Rather than being a full-feature, scalable web service, it is meant to provide a light-weight and easy to use API for embedding a web server (and, consequently, a web interface) into your application.

How to start a lehs web service

Starting lehs is very easy. You only need to call one function: start-server

; Start lehs, listening for connections on port 8080 (http) and 9999 (https)
(lehs.core/start-server 8080 9999)

Lehs will run indefinitely, handling user-agent requests and sending responses.

To stop the server, use the function stop-server:

; stop the running lehs server
(stop-server)

You probably dont want to have your entire application hang on the web server, though, so start it as a future:

; Start lehs as a background task
(def lehs-result (future (lehs.core/start-server 8080 9999)))

; When it's finished, it will terminate with the symbol 'clean-exit:
(if (= 'clean-exit @lehs-result) (println "Lehs terminated correctly")
                                 (println "An error occurred"))

Adding resources

Doing it manually

Lehs stores all of the resources it handles in a referenced map structure. The resource itself, internally, is simply a function that returns a string, a byte array or a response map structure (see appendix). As arguments, the resource function must accept two maps: the first is the request map that the server received from the user agent, the second is the response (so far) that lehs has put together that will be sent back.

For example, to create a simple web page containing the text "Hello world!":

(defn hello-page [req res] "<html><body>Hello world!</html></body")

Then, to add the resource:

(lehs.resource/add-resource "/hello.htm" hello-page)

The easy way

Lehs provides a macro that makes it really easy to generate a new resource: defresource. The macro will generate your function header (deconstructing the request map for you) and automatically add the resource to lehs' resource map.

; Create and add our Hello world page, this time using defresource:
(defresource "/hello.htm" "<html><body>Hello world!</html></body>")

The request map is deconstructed into the following variables for your resource function to use:

Argument Description
method The request method (get, post, head etc...)
path The path to the resource from request line's URI
query The query part of the uri, which is actually a map of key-value pairs. The query ?a=1&b=2 is parsed into {:a 1, :b 2}
fragment The fragment part of the uri
headers Any headers that the user agent sent with the request. This is also a map, e.g. {:Accept "text/html"}
message The decoded message body. In the case of a form message body sent with a post request (application/x-www-form-urlencoded), it is decoded into a map. E.g. {:Name "Joe" :Occupation "Programmer" :Age "28"}

So, if you receive a request for the URI "/foo/greet.html?name=Joe", your resource function can use the values in the query to tailor the response:

(defresource "/foo/greet.html"
  (html5 [:html [:body [:h1 "Greetings!"] [:p (if (nil? (query :name)) "Hello!")
                                            (str "Hello, " (query :name) "!")]]]))

Resulting in some HTML like the following:

<html>
  <body>
    <h1>Greetings!</h1>
    <p>Hello, Joe!</p>
  </body>
</html>

Adding directory contents

To create resources from the contents of a directory (recursively), use the function `defresource-dir. If the contents of the directory ./data are the files

  • ./data/foo.htm
  • ./data/img/foo.png
  • ./data/css/foo.css

The result of the following would be to add a resource for each, as "/data/foo.htm", "/data/img/foo.png" and /data/css/foo.css, respectively. Each resource is created as a function the ignores the two arugments it is passed and returns a byte array of the file's contents.

; add contents of data directory to resources
(defresource-dir ".\\data\\")

Returning a response (map) structure

You may also return a map structure. The defresource macro provides your resource function with the variable res. This variable contains the preliminary response that lehs will send out.

Returning a map structure gives you fine-tuned control over the response that lehs sends back to the user agent. Using the variable res as a base, you can associate into the map structure additional response headers, the response code, etc. For example, the following snippet sets the location to include the fragment 'foo' and sets the response code to 201. It also returns in the message part a small web-page:

(defresource "/d"
   (assoc-in-many res [[[:headers :Location] "/d#foo"]
                       [[:res-ln :code] 201]
                       [[:message] "<html><body><p id=\"foo\">Foo</p></body></html>"]]))

Keep in mind that you don't need to calculate the message length or do any encoding; lehs does that for you automatically (except for properly escaping any HTML text you dont want being rendered).

See appendix for full response map format and a description of the function assoc-in-many.

Appendix

Request map format:

{:req-ln {:method :get|:post|:head|:delete
          :uri {:path "val",
                :query {:k1 "val", :k2 "val"}
                :fragment "val"}
          :version "val"}
 :headers {:k1 "val", :k2 "val"}
 :messsage {:foo "bar", :boo "far"}}

Response map format:

{:res-ln {:version "string" :code int  :reason-phrase "string"}
 :headers {:k1 "val",
           :k2 "val"}
 :message byte-array | "string"}

lehs.common

  • assoc-in-many Takes as arguments a map and a vector of map-paths to associate to new values. For example:
; define a simple map
(def m {:a 1 :b {:c 2}})
; set some paths using assoc-in-many
(assoc-in-many m [[[:b :d] 4] [[:a] 9]])
; result is {:a 9 :b {:c 2 :d 4}}

lehs's People

Contributors

cniles avatar

Stargazers

 avatar

Watchers

James Cloos avatar  avatar

lehs's Issues

Add support for HTTPS over SSL/TLS

Add support for running the server in HTTPS mode. Have lehs use the Java API for SSL/TLS to use the user-specified key to send all HTTP traffic encrypted.

Improve demo pages

Make the demo pages look presentable (they're really ugly right now...)

Change resource directories to be a dynamic

Directory resources should be changed so that when items are added or changed in the directory, the server can find them while it is running. As it is now, when a directory resource is added, all of its contents are added to the resource map when defresource-dir is called. Instead, have defresource-dir allocate a function that will look for resources that are subordinate to it.

This will require a change to how resources are resolved.

  1. Check if an exact match for the resource path exists. If so, use that.
  2. Check if a resource directory exists for each of the parent paths that lead up to the resource. If a resource directory exists for a parent path, check if a file exists under it that matches the resource. If so, return the file.

Documentation

TODO

  • Using HTTPS
  • Returning response map from a resource
  • Resource files (e.g. images, css scripts)
  • Resource directories

Support Connection: keep-alive

Server performance on (especially ssl) requests & responses can be improved by adding support keep persistent connections.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.