Giter Site home page Giter Site logo

shawn-mcginty / naboris Goto Github PK

View Code? Open in Web Editor NEW
71.0 2.0 4.0 3.63 MB

Simple, fast, minimalist http server for OCaml/ReasonML

Home Page: https://naboris.dev

License: MIT License

C++ 19.06% Reason 51.82% Shell 1.47% OCaml 0.25% JavaScript 3.29% Vue 19.84% SCSS 4.28%
reasonml ocaml http-server http

naboris's Introduction

naboris

Simple, fast, minimalist web framework for OCaml/ReasonML built on httpaf and lwt.

https://naboris.dev

Build Status opam version 0.1.3

// ReasonML
let serverConfig: Naboris.ServerConfig.t(unit) = Naboris.ServerConfig.create()
  |> Naboris.ServerConfig.setRequestHandler((route, req, res) => switch(Naboris.Route.path(route)) {
    | ["hello"] =>
      res
        |> Naboris.Res.status(200)
        |> Naboris.Res.text(req, "Hello world!");
    | _ =>
      res
        |> Naboris.Res.status(404)
        |> Naboris.Res.text(req, "Resource not found.");
  });

Lwt_main.run(Naboris.listenAndWaitForever(3000, serverConfig));
/* In a browser navigate to http://localhost:3000/hello */
(* OCaml *)
let server_config: unit Naboris.ServerConfig.t = Naboris.ServerConfig.create ()
  |> Naboris.ServerConfig.setRequestHandler(fun route req res ->
    match (Naboris.Route.path route) with
      | ["hello"] ->
        res
          |> Naboris.Res.text req "Hello world!";
      | _ ->
        res
          |> Naboris.Res.status 404
          |> Naboris.Res.text req "Resource not found.";
  ) in


let _ = Lwt_main.run(Naboris.listenAndWaitForever 3000 server_config)
(* In a browser navigate to http://localhost:3000/hello *)

Pre 1.0.0 the API may be changing a bit. A list of these changes will be kept below.

Contents

Installation

Note

Naboris makes heavy use of Lwt. For better performance it is highly recommended (however optional) to also install conf-libev which will configure Lwt to run with the libev scheduler. If you are using esy you will have to install conf-libev using a special package.

conf-libev also requires that the libev be installed. This can usually be done via your package manager.

brew install libev

or

apt install libev-dev

opam

opam install naboris

esy

"@opam/naboris": "^0.1.3"

dune

(libraries naboris)

Scaffolding

For a basic Reason project

git clone [email protected]:shawn-mcginty/naboris-re-scaffold.git
cd naboris-re-scaffold
npm run install
npm run build
npm run start

For a basic OCaml project

git clone [email protected]:shawn-mcginty/naboris-ml-scaffold.git
cd naboris-ml-scaffold
npm run install
npm run build
npm run start

Development

Any help would be greatly appreciated! ๐Ÿ‘

To run tests

esy install
npm run test

Breaking Changes

From To Breaking Change
0.1.2 0.1.3 secret argument added to all session configuration APIs.
0.1.0 0.1.1 ServerConfig.setSessionGetter changed to ServerConfig.setSessionConfig which also allows ~maxAge and ~sidKey to be passed in optionally.
0.1.0 0.1.1 All RequestHandler.t and Middleware.t now return Lwt.t(Res.t) instead of Lwt.t(unit)
0.1.0 0.1.1 Res.reportError now taxes exn as the first argument to match more closely the rest of the Res API.

naboris's People

Contributors

brianshukwit avatar shawn-mcginty avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

naboris's Issues

Unify Res API

Change Res redirect API to take req res as first arguments like most of the Res API

add redirect

Should be easy to redirect a request to another uri

OCaml example from readme

Hi!
I was trying to execute OCaml example from readme

(* OCaml *)
let server_config: unit Naboris.ServerConfig.t = Naboris.ServerConfig.create ()
  |> Naboris.ServerConfig.setRequestHandler(fun route req res ->
    match (Naboris.Route.path route) with
      | ["hello"] ->
        res
          |> Naboris.Res.text req "Hello world!";
      | _ ->
        res
          |> Naboris.Res.status 404
          |> Naboris.Res.text req "Resource not found.";
  ) in

Naboris.listenAndWaitForever 3000 server_config
(* In a browser navigate to http://localhost:3000/hello *)

but I didn't get success.

Here are the steps:

opam switch create . ocaml-base-compiler.4.09.0
eval $(opam env)
opam install merlin ocp-indent dune utop naboris
touch main.ml #paste OCaml example
touch dune #paste dune config
(executable
    (name main)
    (libraries naboris)
)
dune build main.exe

No errors.
Execute

dune exec ./main.exe 
# or
_build/default/main.exe 

nothing happens.
I'm beginner in OCaml, so maybe I did something wrong. I would appreciate any advice. Thanks.

Better documentation

A real frontend web app for documentation, something like expressjs.com. Probably find a free host or a cheap one, and a domain.

weirdness w/ chrome

Sometimes requests from chrome will lock the entire Naboris server up. If using cURL or incognito chrome everything works fine. Or even after clearing cookies/cache from chrome it will work again. Something with the state of chrome after a while causes it to spin forever. Need to track this down.

Add middleware hooks and unify API

add hooks for middleware

maybe beforeRouteRequest?

beforeRouteRequest could be a fn that takes something like Route.t -> Req.t -> Res.t -> (Route.t -> Req.t -> Res.t -> (Route.t * Req.t * Res.t) Lwt.t) -> (Route.t * Req.t * Res.t) Lwt.t) meaning it's last arg is the routeRequest function, which should be called to continue the lifecycle of the request.

maybe afterRouteRequest? I'm not as sure about that one

Static files OCaml example in readme

There are some typos in readme for OCaml static files example, but I was able to make it work like this:

let server_config: unit Naboris.ServerConfig.t = Naboris.ServerConfig.create ()
  |> Naboris.ServerConfig.setRequestHandler(fun route req res ->
    match ((Naboris.Route.meth route), (Naboris.Route.path route)) with
      | (Naboris.Method.GET, "static" :: static_path) ->
        Naboris.Res.static (Sys.getcwd() ^ "/static") static_path req res 
      | (Naboris.Method.GET, "/" :: []) ->
        Naboris.Res.static (Sys.getcwd() ^ "/static") ["/index.html"] req res
      | (Naboris.Method.GET, "" :: []) ->
        Naboris.Res.static (Sys.getcwd() ^ "/static") ["/index.html"] req res
      | _ ->
        res
          |> Naboris.Res.status 404
          |> Naboris.Res.text req "Resource not found.";
  )

let _ = Lwt_main.run begin
    Naboris.listenAndWaitForever 3000 server_config
end

By this index.html file from static folder is opened when gethttp://localhost:3000/ or http://localhost:3000.
Would be nice to have static folder by default or via parameter in ServerConfig for serving up static files. It can be useful for SPA.

Expose main lwt promise

Naboris.listen encapsulates the Lwt_main.runing of the promise. There should be a function to get the raw promise allowing the user to manage his own top level Lwt_main.run

Something like Naboris.listen_promise: int serverConfig -> 'a Lwt

Unix.EINVAL when serving static under load test

Fatal error: exception Unix.Unix_error(Unix.EINVAL, "select", "")

App code

let server_config: unit Naboris.ServerConfig.t = Naboris.ServerConfig.create ()
  |> Naboris.ServerConfig.setRequestHandler(fun route req res ->
    match ((Naboris.Route.meth route), (Naboris.Route.path route)) with
      | (Naboris.Method.GET, "static" :: static_path) ->
        Naboris.Res.static (Sys.getcwd() ^ "/static") static_path req res 
      | (Naboris.Method.GET, "/" :: []) ->
        Naboris.Res.static (Sys.getcwd() ^ "/static") ["/index.html"] req res
      | (Naboris.Method.GET, "" :: []) ->
        Naboris.Res.static (Sys.getcwd() ^ "/static") ["/index.html"] req res
      | _ ->
        res
          |> Naboris.Res.status 404
          |> Naboris.Res.text req "Resource not found.";
  )

let _ = Lwt_main.run begin
    Naboris.listenAndWaitForever 3000 server_config
end

vegeta targets file content
veg.txt
Request all files from static folder

GET http://localhost:3000/static/main-1f6385083dfd82fb9d36.css
GET http://localhost:3000/static/1.1f6385083dfd82fb9d36-build.js
GET http://localhost:3000/static/1f6385083dfd82fb9d36-build.js
GET http://localhost:3000/static/logo.png
GET http://localhost:3000

First test execution:

vegeta -cpus 1 attack -duration=20s -targets veg.txt | tee results.bin | vegeta report
Requests      [total, rate, throughput]  1000, 50.06, 50.05
Duration      [total, attack, wait]      19.979108875s, 19.976830593s, 2.278282ms
Latencies     [mean, 50, 95, 99, max]    4.25599ms, 2.605914ms, 6.75315ms, 12.32878ms, 160.517914ms
Bytes In      [total, mean]              134701400, 134701.40
Bytes Out     [total, mean]              0, 0.00
Success       [ratio]                    100.00%
Status Codes  [code:count]               200:1000  

Second one:

vegeta -cpus 1 attack -duration=20s -targets veg.txt | tee results.bin | vegeta report
Requests      [total, rate, throughput]  1000, 50.05, 23.13
Duration      [total, attack, wait]      21.837638678s, 19.979902873s, 1.857735805s
Latencies     [mean, 50, 95, 99, max]    278.843405ms, 2.774347ms, 1.122188958s, 3.228599022s, 4.313448889s
Bytes In      [total, mean]              68024207, 68024.21
Bytes Out     [total, mean]              0, 0.00
Success       [ratio]                    50.50%
Status Codes  [code:count]               0:495  200:505  
Error Set:
Get http://localhost:3000/static/1.1f6385083dfd82fb9d36-build.js: read tcp 127.0.0.1:62836->127.0.0.1:3000: read: connection reset by peer
Get http://localhost:3000/static/1f6385083dfd82fb9d36-build.js: read tcp 127.0.0.1:62838->127.0.0.1:3000: read: connection reset by peer
Get http://localhost:3000/static/main-1f6385083dfd82fb9d36.css: read tcp 127.0.0.1:62834->127.0.0.1:3000: read: connection reset by peer
Get http://localhost:3000/static/logo.png: dial tcp 0.0.0.0:0->[::1]:3000: connect: connection refused
Get http://localhost:3000: dial tcp 0.0.0.0:0->[::1]:3000: connect: connection refused
Get http://localhost:3000/static/main-1f6385083dfd82fb9d36.css: dial tcp 0.0.0.0:0->[::1]:3000: connect: connection refused
Get http://localhost:3000/static/1.1f6385083dfd82fb9d36-build.js: dial tcp 0.0.0.0:0->[::1]:3000: connect: connection refused
Get http://localhost:3000/static/1f6385083dfd82fb9d36-build.js: dial tcp 0.0.0.0:0->[::1]:3000: connect: connection refused

When -cpus more than 1 it always fails.

Seems like it is known issue of Lwt/OCaml ocsigen/lwt#529

Any performance numbers to share?

Naboris looks great, definitely much more user-friendly than other options out there.

Would be interested to see how Naboris performs on the techempower benchmark or even a simple hello world.

thanks

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.