Git Product home page Git Product logo

foxhole's Introduction

Foxhole

Foxhole is a neko based standalone webserver, that closely imitates the neko.Web API. At its current stage it is primarily intended as a replacement for nekotools server, but it may become a viable option for production use.

It has support for parsing multipart request bodies and removes post body size limitations.

Basic Usage

You can write Foxhole based web applications almost exactly the same way you would write neko.Web based applications, except for the entry point. With the latter, you compile a neko module and use mod_neko or mod_tora or nekotools server as a container. The main entry point acts as the request handler, unless use use neko.Web.cacheModule to change it. Foxhole is different in that by using -lib foxhole the server is compiled into the module and you will have to launch it using foxhole.Web.run.

Here's what it comes down to:

class Main {
  static function respond() {
    //do something here
  }
  static function main() {
    //initialize database, load config etc.
    #if foxhole
      foxhole.Web.run({
        handler: respond,
      });    
    #else
      neko.Web.cacheModule(respond);
      respond();
    #end
  }
}

Should you not call foxhole.Web.run, the application will just quit. Should you call it multiple times, you will bind multiple servers. Whether or not that is actually useful remains to be seen. Doing so is advised against.

Watch mode

If you launch Foxhole with { watch: true } it will watch its own file and exit as soon as it changes. If your IDE starts your neko module as soon as it is compiled, then this will do the trick. Otherwise using this tiny helper you can make it run forever like so

haxe --run Forever neko <yourModule>.n

Parallelism

Foxhole uses multiple worker threads to execute requests. By default, progress in these threads is mutually exclusive. However with Foxhole.Web.inParallel(task) you can execute a task while giving other worker threads the opportunity to progress. Good use cases would be:

  • when doing expensive computations that are side effect free, e.g. transcoding some sort of data like parsing or marshalling JSON or XML or using haxe serialization or compressing/decompressing a binary blob
  • when doing expensive I/O that is safe to do in parallel, e.g. making a request to some API

The underlying implementation is really quite simple. There is one common lock, that each worker acquires before processing the request, and then releases when done. If you call inParallel the current worker releases that lock and starts executing the supplied task. Once done, it reacquires the lock. When in doubt, don't use inParallel and you will be fine.

Implementation details

Foxhole is based on tink_http, which provides an asynchronous cross platform API for handling HTTP. The server runs in an event loop on the main thread. Requests are then handed off to worker threads that handle them synchronously, as explained in the section above. The output is buffered and once the handler has completed, the buffer is streamed onto the outgoing connection. Streaming output will be supported in the future.

Production use

Foxhole is not field tested (nor properly unit tested). There are no known bugs. Instead, there are - in all likelihood - unknown bugs. You have been warned.

However, presuming that those bugs can be identified and resolved in the foreseeable future, Foxhole presents an interesting alternative for deploying neko based web applications. It is mostly likely not a very good idea to directly expose it to the outside world, but instead it should be proxied behind a reliable HTTP server as nginx and adequately supervised - a relatively common setup for nodejs. The proxy can take care of HTTPS offloading, static file serving, DoS protection and so forth.

Preliminary benchmarking seem to indicate that Foxhole introduces reasonable overhead (less than 1ms per request in apache benchmarks) and shows grace under pressure (stays under said 1ms even with 10000 concurrent requests). Since it interfaces with the outside world through HTTP rather than CGI, FastCGI, Tora or whatnot, it is easy to integrate with every webserver that is able to act as an HTTP proxy, which certainly covers all the established ones.

Given there is quite some room for optimization and its support to parallelize certain task, it may become a good choice for certain classes of performance critical applications.

Other targets

In general, Foxhole is not neko-specific. Both java and cpp support are within reach. This depends largely on tink_http being properly implemented on those platforms.

foxhole's People

Contributors

back2dos avatar benmerckx avatar thehippo 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

Watchers

 avatar  avatar  avatar  avatar  avatar

foxhole's Issues

getPostData throws

So I've been trying to get enough info from your code here to parse the body of a tink_http request to String. I kept getting nowhere and decided to test with foxhole itself and I'm running into the same issue. Running Web.getPostData() always throws:

[...]
Called from tink/io/Pipe.hx line 38
Called from tink/io/Pipe.hx line 44
Called from tink/io/Sink.hx line 179
Uncaught exception - Error: Uncaught exception: Invalid call tink.runloop.QueueWorker.execute:70

This happens somewhere in the pipeTo call, the handle is never reached.
I've been going through tink_io but I'm not really sure how to debug this.

Setup

I've been trying to setup a basic test of tink_http, but didn't get far. Then I figured I try using foxhole first since it should be easier to get going. I'm running into the same problem though. I set up your example like this:

class Main {
  static function respond() {
    Sys.println('This works');
  }
  static function main() {
    foxhole.Web.run({
      handler: respond,
      watch: true
    });
  }
}

This compiles but does not seem to keep running. Output is this:

Main.hx:24: count 0 -> Worker:ROOT_LOOP/worker#0
Main.hx:24: count 1 -> Worker:ROOT_LOOP/worker#1
(...)
Main.hx:24: count 28 -> Worker:ROOT_LOOP/worker#3
Main.hx:24: count 29 -> Worker:ROOT_LOOP/worker#4

After that the process stops and no requests are served. I've tried without watch:true and --run Forever as well, but that just seems to run the process indefinitely.

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.