#line 2152 "/home/travis/build/felix-lang/felix/src/packages/web.fdoc"
  publish """
  Accepts connection and spawns fthread to handle request
  See webapp.flx for usage example
  """
  
  if PLAT_POSIX do
  PosixSignal::ignore_signal(PosixSignal::SIGPIPE);
  done
  
  open Socket;
  open Stream;
  
  open TerminalIByteStream[fd_t];
  open TerminalIOByteStream[socket_t];
  
  
  // this is a hack to make close work on a listenter
  // RF got this right the first time:
  // in the abstract a listener is NOT a socket
  // In fact, it is a socket server, with accept() a way to
  // read new sockets off it ..
  open TerminalIByteStream[socket_t];
  
  requires header '#include <stdlib.h>';
  
  class WebServer {
    open ServerConfig;
    open HTTPRequest;
    open HTTPConnection;
    open MIMEType;
    open Eq[mime_type];
    open Assoc_list;
    open HTTPHandler;
    open Logger;
  
    proc serve(conn:http_connection, request: http_request)
    {
      val s = conn.sock;
      iter (proc (handler:http_handler) {
        if not *conn.dirty  do
          if handler.handles(conn.config,request) do
            handler.handler_fn(conn,request);
          done
        else
          goto finished;
        done
        }) conn.config.handlers;
      finished:>
      return;
    }
  
    proc start_webserver(config:server_config) {
      val webby_port = config.port;
      config.log(INFO, "Server started, listenting on "+str config.port);
      // up the queue len for stress testing
      var p = webby_port;
      var listener: socket_t;
      mk_listener(&listener, &p, 10);
      var clock = Faio::mk_alarm_clock();
      // noinline is necessary to stop the closure being
      // inlined into the loop, preventing the socket variable k
      // being duplicated as it must be [a bug in Felix]
      noinline proc handler (var k:socket_t) ()
      {
        config.log(DEBUG,"Spawned fthread running for socket "+str k);
        // should spawn fthread here to allow for more io overlap
        val conn = http_connection(config ,k);
        var request:http_request;
        open HTTPRequest;
        open  Eq[http_method];
        open MIMEType;
        HTTPRequest::get_request(conn,&request);
         Faio::sleep(clock,config.delay);
        /*Get entity form parameters if method is post and
          content type is application/x-www-form-urlencoded */
        //if str(request.hmethod) == str(POST) do
        match get_header(request,"Content-Type") with
          | Some c => {
            match parse_media_type(c) with
              | Some (m,a) => {
                if str(m) == str(application x_DASH_www_DASH_form_DASH_urlencoded) do
                  HTTPRequest::get_entity_params(conn,&request,a);
                elif str(m) == str(form-data) do
                  HTTPRequest::get_multipart_params(conn,&request,a);
                else
                  request.entity_params=Empty[string*string];
                done
                }
              |_ =>  { request.entity_params=Empty[string*string]; }
            endmatch; }
          |_ => { request.entity_params=Empty[string*string]; }
        endmatch;
        serve(conn,request);
        Faio::sleep(clock,config.delay); // give OS time to empty its buffers
        // try this:
        // Advised by: koettermarkus@gmx.de, MANY THANKS!
  
        gen hack_recv: socket_t * &char * int * int -> int = "recv($1,$2,$3,$4)";
  
        var buf:char ^1025;
        var counter = 0;
        var extra = 0;
        shutdown(k,1); // shutdown write
        retry:>
          var b = hack_recv(k,C_hack::cast[&char] (&buf),1024,0);
          //println$ "Error code " + str b + " from read after shutdown";
          if b > 0 do
            extra += b;
            if extra > 2000 do
              config.log(WARNING,"Read too many extraneous bytes from OS buffer");
              goto force_close;
            done;
            goto retry;
          elif b == -1 do
          ++counter;
          if counter > 200 do
            config.log(WARNING,"Timeout waiting for write buffers to be flushed");
            goto force_close;
          done;
          Faio::sleep(clock,0.1); // 100 ms
          goto retry;
        done;
        assert b==0;
  
        force_close:>
        Socket::shutdown(k,2);
        ioclose(k);
  
      };
  
      noinline proc stuff {
        var s: socket_t;
        config.log(DEBUG,"Waiting for connection");
        accept(listener, &s);  // blocking
        config.log(DEBUG,"got connection "+str s);  // error check here
  
        //  - spawning an fthread is blocking the web server. don't know why
        config.log(DEBUG,"spawning fthread to handle connection "+str s);
        spawn_fthread$  handler s;
        collect(); // this hangs everything, no idea why!
      };
      while true do stuff; done
  
      config.log(INFO,"WEB SERVER SHUTDOWN");
      iclose (listener);
    }
  
  }