Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Create .gitignore

,

Delete .nrepl-port

Update README.md

Update README.md
  • Loading branch information
bowbahdoe committed May 14, 2022
0 parents commit aa5a13d
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
.nrepl-port
*.iml
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Microhttp Ring Adapter

## What
Adapter for using [microhttp](https://github.com/ebarlas/microhttp)
as a ring server.

Doesn't support the required `:remote-addr` key properly because microhttp
does not support recovering that information. This isn't the biggest loss as
that information isn't reliable in modern cloud environments anyways, but
worth noting.

This was done as a proof of concept, but the code is simple enough that
I am confident it is as production ready as microhttp.

## deps.edn

```clojure
io.github.bowbahdoe/microhttp-ring-adapter {:git/tag "v0.0.1"}
```
3 changes: 3 additions & 0 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{:paths ["src"]
:deps {ring/ring-core {:mvn/version "1.9.5"}
org.microhttp/microhttp {:mvn/version "0.7"}}}
141 changes: 141 additions & 0 deletions src/dev/mccue/microhttp_ring_adapter.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
(ns dev.mccue.microhttp-ring-adapter
(:require [clojure.string :as string]
[ring.core.protocols :as ring-protocols])
(:import (org.microhttp Request Handler Response Header EventLoop Options DebugLogger)
(java.io ByteArrayInputStream ByteArrayOutputStream)))

(set! *warn-on-reflection* true)

(defn- ->req
[{:keys [host port]} ^Request microhttp-request]
(let [[uri query-string] (string/split (.uri microhttp-request)
#"\?")]
{:server-port port
:server-name host
:remote-addr "" ; TODO: microhttp doesn't have a way to recover this
:uri uri
:query-string query-string
:scheme :http
:request-method (keyword (string/lower-case (.method microhttp-request)))
:headers (reduce (fn [acc ^Header header]
(assoc acc
(string/lower-case (.name header))
(.value header)))
{}
(.headers microhttp-request))
:body (ByteArrayInputStream. (or (.body microhttp-request)
(byte-array 0)))}))

(def ^{:private true} status->reason
{100 "Continue"
101 "Switching Protocols"
200 "OK"
201 "Created"
202 "Accepted"
203 "Non-Authoritative Information"
204 "No Content"
205 "Reset Content"
206 "Partial Content"
300 "Multiple Choices"
301 "Moved Permanently"
302 "Found"
303 "See Other"
304 "Not Modified"
305 "Use Proxy"
307 "Temporary Redirect"
400 "Bad Request"
401 "Unauthorized"
402 "Payment Required"
403 "Forbidden"
404 "Not Found"
405 "Method Not Allowed"
406 "Not Acceptable"
407 "Proxy Authentication Required"
408 "Request Time-out"
409 "Conflict"
410 "Gone"
411 "Length Required"
412 "Precondition Failed"
413 "Request Entity Too Large"
414 "Request-URI Too Large"
415 "Unsupported Media Type"
416 "Requested range not satisfiable"
417 "Expectation Failed"
500 "Internal Server Error"
501 "Not Implemented"
502 "Bad Gateway"
503 "Service Unavailable"
504 "Gateway Time-out"
505 "HTTP Version not supported"})

(defn- <-res
[ring-response]
(let [out (ByteArrayOutputStream.)]
(ring-protocols/write-body-to-stream (:body ring-response)
ring-response
out)
(Response. (:status ring-response)
(or (status->reason (:status ring-response)) "IDK")
(reduce (fn [header-list [k v]]
(conj header-list (Header. k v)))
[]
(:headers ring-response))
(.toByteArray out))))

(defn- ring-handler->Handler
[{:keys [host port]} ring-handler]
(reify Handler
(handle [_ microhttp-request callback]
(let [ring-request (->req {:host host
:port port} microhttp-request)
microhttp-response (<-res (ring-handler ring-request))]
(.accept callback microhttp-response)))))

(defn create-microhttp-event-loop
([handler]
(create-microhttp-event-loop handler {}))
([handler options]
(let [microhttp-options (cond-> (Options.)
(some? (options :host))
(.withHost (:host options))

(some? (options :port))
(.withPort (:port options))

(some? (options :reuse-addr))
(.withReuseAddr (:reuse-addr options))

(some? (options :reuse-port))
(.withReusePort (:reuse-port options))

(some? (options :resolution))
(.withResolution (:resolution options))

(some? (options :request-timeout))
(.withRequestTimeout (:request-timeout options))

(some? (options :read-buffer-size))
(.withReadBufferSize (:read-buffer-size options))

(some? (options :accept-length))
(.withAcceptLength (:accept-length options))

(some? (options :max-request-size))
(.withMaxRequestSize (:max-request-size options)))
microhttp-logger (or (:logger options) (DebugLogger.))]
(EventLoop. microhttp-options
microhttp-logger
(ring-handler->Handler
{:host (.host microhttp-options)
:port (.port microhttp-options)}
handler)))))

(comment
(defn handler [req]
{:status 200
:headers {"Content-Type" "text/html"}
:body (str "<pre>"
(with-out-str
(clojure.pprint/pprint req))
"</pre>")})
(def event-loop (create-microhttp-event-loop #'handler {:port 1242})))

0 comments on commit aa5a13d

Please sign in to comment.