<|start_filename|>docs/api/search/variables_3.js<|end_filename|>
var searchData=
[
  ['cache_5fcontrol',['cache_control',['../group__nodec__static.html#afd394de5b190c3770c6d3f915316c759',1,'http_static_config_t']]],
  ['code',['code',['../group__effect__exn.html#a2f415f8397a6e3e497814e50fbb3eb35',1,'lh_exception']]],
  ['content_5fmax_5fsize',['content_max_size',['../group__nodec__static.html#a51e0cb72c8eb93fef2ca61fb599b9a06',1,'http_static_config_t']]]
];


<|start_filename|>docs/api/group__effect__value.js<|end_filename|>
var group__effect__value =
[
    [ "lh_bool_value", "group__effect__value.html#ga9ee5f894ccf5ffdd91b8687e5d16b946", null ],
    [ "lh_int_value", "group__effect__value.html#ga11799a8a77ce5e6e76fcc04ea346973b", null ],
    [ "lh_lh_string_value", "group__effect__value.html#ga336a950128d6d89c85eb2a8b599c8bf2", null ],
    [ "lh_lh_voidptr_value", "group__effect__value.html#ga31b1c44a920420d7f3f85fdda1ef6db2", null ],
    [ "lh_long_value", "group__effect__value.html#ga6adf34d555ad2d09666254d3d923189b", null ],
    [ "lh_ptr_value", "group__effect__value.html#gac9ebc79475233b3cdc4d8468b05b78aa", null ],
    [ "lh_uint64_t_value", "group__effect__value.html#gabe964e091840d37f8cb46a747452cee5", null ],
    [ "lh_value_any_ptr", "group__effect__value.html#gab30af45b5f41cca5a1287d2bfa1d39a5", null ],
    [ "lh_value_bool", "group__effect__value.html#ga817d2d4a6080a05db53ef154a6218205", null ],
    [ "lh_value_fun_ptr", "group__effect__value.html#ga7f44e7912be4eabc993a976069056b79", null ],
    [ "lh_value_int", "group__effect__value.html#gac14a78aec4be59be7a920507f0e61a32", null ],
    [ "lh_value_lh_string", "group__effect__value.html#gaa42c543224030e5080188cb11b5b27bb", null ],
    [ "lh_value_lh_voidptr", "group__effect__value.html#gacdfc1731e309c33e055ac88b459bdcad", null ],
    [ "lh_value_long", "group__effect__value.html#gac069dce2efd9a6972e0e1467564a1acb", null ],
    [ "lh_value_null", "group__effect__value.html#ga7b9e682e24d41d0203c1156900398d39", null ],
    [ "lh_value_ptr", "group__effect__value.html#gab455eed48ff144c3b8ab0977d8838391", null ],
    [ "lh_value_uint64_t", "group__effect__value.html#gab17d96897899981289f52af69fb53889", null ],
    [ "lh_value_value", "group__effect__value.html#ga80a6a2b55e0d45bbe2171f396bdc4af0", null ],
    [ "lh_acquirefun", "group__effect__value.html#ga5734aa37e6622d1b505a0173e2545ba8", null ],
    [ "lh_actionfun", "group__effect__value.html#ga5a08b630153ff4e9c2120d0bb2debd89", null ],
    [ "lh_releasefun", "group__effect__value.html#ga6f614d89d8e6565a50317139cca21db3", null ],
    [ "lh_resultfun", "group__effect__value.html#ga540551bfce3a27024092a782553ceddf", null ],
    [ "lh_string", "group__effect__value.html#ga1f24f3a2029820ca75484a67853370a3", null ],
    [ "lh_value", "group__effect__value.html#ga18d15281353b7e38984b0ef2e36abb83", null ],
    [ "lh_voidfun", "group__effect__value.html#ga58b182dd21feb550d40436ef31d09614", null ],
    [ "lh_voidptr", "group__effect__value.html#ga7b4439b7ec3223634778002ea318acea", null ],
    [ "lh_fun_ptr_value", "group__effect__value.html#gaada7a7ceaab9a482320d4cfc904a4da6", null ]
];

<|start_filename|>src/mime.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>

#include "mime-db.h"

static const mime_type_t* mime_from_fname(const char* fname) {
  if (fname == NULL) return NULL;
  // find the extension
  const char* base = strrchr(fname, '/');
  if (base == NULL) base = fname;
  const char* ext = strchr(base, '.');
  if (ext == NULL) ext = fname;
              else ext++;
  // binary search for the extension
  int hi = mime_extensions_len - 1;
  int lo = 0;                    
  while (lo <= hi) {
    int i = lo + ((hi - lo) / 2);
    int cmp = nodec_stricmp(ext, mime_extensions[i].extension);
    if (cmp == 0) { 
      lo = hi = i;  
      break;
    }
    else if (cmp > 0) {
      lo = i + 1;
    }
    else {
      hi = i - 1;
    }
  }
  if (hi < lo) return NULL;
  assert(hi == lo);
  return &mime_types[mime_extensions[lo].index];
}

static const mime_type_t* mime_from_name(const char* name) {
  if (name == NULL) return NULL;
  // binary search for the extension
  int hi = mime_types_len - 1;
  int lo = 0;
  while (lo <= hi) {
    int i = lo + ((hi - lo) / 2);
    int cmp = nodec_stricmp(name, mime_types[i].name);
    if (cmp == 0) {
      lo = hi = i;
      break;
    }
    else if (cmp > 0) {
      lo = i + 1;
    }
    else {
      hi = i - 1;
    }
  }
  if (hi < lo) return NULL;
  assert(hi == lo);
  return &mime_types[lo];
}


const char* nodec_mime_info_from_fname(const char* fname, bool* compressible, const char** charset) {
  const mime_type_t* mime = mime_from_fname(fname);
  if (mime == NULL) {
    if (compressible != NULL) *compressible = false;
    if (charset != NULL) *charset = NULL;
    return NULL;
  }
  else {
    if (compressible != NULL) *compressible = mime->compressible;
    if (charset != NULL) *charset = mime->charset;
    return mime->name;
  }
}

const char* nodec_mime_from_fname(const char* fname) {
  return nodec_mime_info_from_fname(fname, NULL, NULL);
}

void nodec_info_from_mime(const char* mime_type,  const char** preferred_ext, bool* compressible, const char** charset) {
  const mime_type_t* mime = mime_from_name(mime_type);
  if (mime == NULL) {
    if (preferred_ext != NULL) *preferred_ext = NULL;
    if (compressible != NULL) *compressible = false;
    if (charset != NULL) *charset = NULL;
  }
  else {
    if (preferred_ext != NULL) *preferred_ext = mime->extension;
    if (compressible != NULL) *compressible = mime->compressible;
    if (charset != NULL) *charset = mime->charset;    
  }
}

const char* nodec_ext_from_mime(const char* mime_type) {
  const char* ext = NULL;
  nodec_info_from_mime(mime_type, &ext, NULL, NULL);
  return ext;
}

<|start_filename|>docs/api/group__effect__debug.js<|end_filename|>
var group__effect__debug =
[
    [ "lh_callocfun", "group__effect__debug.html#gae07f635b592d37224a693c4b96c3ca8b", null ],
    [ "lh_fatalfun", "group__effect__debug.html#gaaa958e853fd8e039d2bd95a3589d05d8", null ],
    [ "lh_freefun", "group__effect__debug.html#gabd736ee0a830b88c25a6e577e1afc273", null ],
    [ "lh_mallocfun", "group__effect__debug.html#gad6a1f6b1a5962f31b8a3d00fc134f190", null ],
    [ "lh_reallocfun", "group__effect__debug.html#ga647046150553dd8b90064a8f2b94c9f9", null ],
    [ "lh_calloc", "group__effect__debug.html#ga92212c836a97fbe9678c97093bded2c8", null ],
    [ "lh_check_memory", "group__effect__debug.html#gacc1e33676af3f1835024a9741a1f1318", null ],
    [ "lh_debug_wait_for_enter", "group__effect__debug.html#gaf2f2e9675af64eb749d5927d3208ea0e", null ],
    [ "lh_free", "group__effect__debug.html#gafd04836042b1a8668b04523991933221", null ],
    [ "lh_malloc", "group__effect__debug.html#ga72b06ce360f76629e77a23da18b71f49", null ],
    [ "lh_print_stats", "group__effect__debug.html#gad677b9f6ba5b92dac0729c3aa474f786", null ],
    [ "lh_realloc", "group__effect__debug.html#gad5238b41612231e43d6251e46a3ff97f", null ],
    [ "lh_register_malloc", "group__effect__debug.html#ga2640fed6ccd08b9f0c14b3bff1808413", null ],
    [ "lh_register_onfatal", "group__effect__debug.html#ga56ae0b80ee63d88b8e547974f8f790dd", null ],
    [ "lh_strdup", "group__effect__debug.html#gacd4f8378c32e897364593e1b881c9eb0", null ],
    [ "lh_strndup", "group__effect__debug.html#ga97cb6281b08f6507c8a2be612ec662b6", null ]
];

<|start_filename|>docs/api/group__nodec__main.js<|end_filename|>
var group__nodec__main =
[
    [ "nodec_main_fun_t", "group__nodec__main.html#ga50770568a93c37eaa128d15aefb9ff49", null ],
    [ "async_main", "group__nodec__main.html#ga8ca7a8eead029327f56e7181199eea4f", null ]
];

<|start_filename|>src/dns.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>

static void addrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) {
  async_req_resume((uv_req_t*)req, status >= 0 ? 0 : status);
}

struct addrinfo* async_getaddrinfo(const char* node, const char* service, const struct addrinfo* hints) {
  struct addrinfo* info = NULL;
  {using_req(uv_getaddrinfo_t,req) {
    nodec_check_msg(uv_getaddrinfo(async_loop(), req, &addrinfo_cb, node, service, hints), node);
    uv_errno_t err = asyncx_await_once((uv_req_t*)req);
    if (err != UV_EAI_NONAME) nodec_check_msg(err, node);
    if (err==0) info = req->addrinfo;
  }}
  return info;
}

void nodec_free_addrinfo(struct addrinfo* info) {
  if (info != NULL) uv_freeaddrinfo(info);
}

void nodec_free_addrinfov(lh_value infov) {
  nodec_free_addrinfo((struct addrinfo*)lh_ptr_value(infov));
}

static void nameinfo_cb(uv_getnameinfo_t* req, int status, const char* hostname, const char* service) {
  async_req_resume((uv_req_t*)req, status >= 0 ? 0 : status);
}

void async_getnameinfo(const struct sockaddr* addr, int flags, char** node, char** service) {
  if (node != NULL) *node = NULL;
  if (service != NULL) *service = NULL;
  {using_req(uv_getnameinfo_t, req) {
    nodec_check(uv_getnameinfo(async_loop(), req, &nameinfo_cb, addr, flags));
    async_await_once((uv_req_t*)req);
    if (node != NULL) *node = nodec_strndup(req->host, NI_MAXHOST);
    if (service != NULL) *service = nodec_strndup(req->service, NI_MAXSERV);
  }}
}

<|start_filename|>test/main-client.c<|end_filename|>
#include <stdio.h>
#include <nodec.h>
#include <nodec-primitive.h>


/*-----------------------------------------------------------------
  Client Test
-----------------------------------------------------------------*/
const char* http_request_parts[] = {
  "GET /index.html HTTP/1.1\r\n",
  "Host: 127.0.0.1:8080\r\n",
  "Connection: close\r\n",
  "\r\n",
  NULL
};

void test_as_client() {
  nodec_bstream_t* conn = async_tcp_connect("127.0.0.1:8080");
  {using_bstream(conn) {
    const char* s;
    for (size_t i = 0; (s = http_request_parts[i]) != NULL; i++) {
      printf("write: %s\n", s);
      async_write(as_stream(conn), s);
      async_wait(100);
    }
    printf("await response...\n");
    char* body = async_read_all(conn, 32*NODEC_MB);
    {using_free(body) {
      printf("received:\n%s", body);
    }}
  }}
}

/*-----------------------------------------------------------------
  write_one_char

  Writes a single character to the stream
-----------------------------------------------------------------*/

static void write_one_char(nodec_bstream_t* conn, const char* pch) {
  uv_buf_t buf = nodec_buf(pch,1);
  async_write_buf(as_stream(conn), buf); // send one byte
  async_wait(100); // put in a delay to force a new chunk
}

/*-----------------------------------------------------------------
  test_as_client2_connection

  Attempt to break the system by sending a request one byte at a
  time. Called by test_as_client2
-----------------------------------------------------------------*/

static void test_as_client2_connection(nodec_bstream_t* conn) {
  const char* s;
  for (size_t i = 0; (s = http_request_parts[i]) != NULL; i++)
    for (const char* pch = s; *pch; pch++)
      write_one_char(conn, pch);
  printf("await response...\n");
  char* body = async_read_all(conn, 32*NODEC_MB);
  {using_free(body) {
    printf("received:\n%s", body);
  }}
}

/*-----------------------------------------------------------------
  test_as_client_one_byte_at_a_time
-----------------------------------------------------------------*/

void test_as_client_one_byte_at_a_time() {
  nodec_bstream_t* conn = async_tcp_connect("127.0.0.1:8080");
  {using_bstream(conn) {
    test_as_client2_connection(conn);
  }}
}

static long file_get_size(FILE* file) {
  const long original_position = ftell(file);
  fseek(file, 0, SEEK_END);
  const long ans = ftell(file);
  fseek(file, original_position, SEEK_SET);
  return ans;
}

/*-----------------------------------------------------------------
  test_read_file

  reads the contents of a file into an uv_buf_t
-----------------------------------------------------------------*/

static uv_buf_t read_file(const char* path) {
  uv_buf_t ans = { 0, 0 };
  FILE* file = 0;
  const errno_t rc = fopen_s(&file, path, "r");
  if (rc == 0 && file != 0) {
    const long len = file_get_size(file);
    ans = nodec_buf_alloc(len);
    fread(ans.base, ans.len, 1, file);
    fclose(file);
  }
  else if (rc != 0 && file == 0) {
    nodec_throw(123); // what to put here?
  }
  return ans;
}

/*-----------------------------------------------------------------
  test_client_file

  Reads an HTTP client request from a file and sends it to
  the server.
-----------------------------------------------------------------*/

static void test_client_file(const char* path) {
  uv_buf_t buf = read_file(path);
  {using_buf(&buf) {
    nodec_bstream_t* conn = async_tcp_connect("127.0.0.1:8080");
    {using_bstream(conn) {
      async_write_buf(as_stream(conn), buf);
      printf("await response...\n");
      char* body = async_read_all(conn, 32*NODEC_MB);
      {using_free(body) {
        printf("received:\n%s", body);
      }}
    }}
  }}
}

/*-----------------------------------------------------------------
  Main
-----------------------------------------------------------------*/

static void entry() {
  printf("in the main loop\n");
  //test_as_client_one_byte_at_a_time();
  const char* path = ".\\..\\..\\test\\examples\\example1.http";
  //test_client_file(path);
  test_as_client();
}

int main() {
  async_main(entry);
  return 0;
}

<|start_filename|>docs/api/group__nodec__fs_structuv__dirent__t.js<|end_filename|>
var group__nodec__fs_structuv__dirent__t =
[
    [ "name", "group__nodec__fs.html#ac7ce6fb281bd218f7821f6c52a63a737", null ],
    [ "type", "group__nodec__fs.html#a7343234f6f445689c599b147eeac9658", null ]
];

<|start_filename|>docs/api/structlh__operation.js<|end_filename|>
var structlh__operation =
[
    [ "opfun", "structlh__operation.html#a0ccd260a5593683d8343aa0d9a75d7d7", null ],
    [ "opkind", "structlh__operation.html#a596d55fa9ca51ec0602b60b37e818453", null ],
    [ "optag", "structlh__operation.html#ad8b0fc0385aecd9216ec6df9df996539", null ]
];

<|start_filename|>examples/examples.h<|end_filename|>
#pragma once
#ifndef _nodec_examples_h

void ex_http_connect();
void ex_http_server_static();

void ex_https_connect();
void ex_https_server_static();

void ex_fs_search();

#endif

<|start_filename|>inc/nodec-extra-doc.h<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/

#pragma once
#error "don't include this file; it is for documentation only"

/// \addtogroup streams
/// \{

/// A `libuv` stream.
/// Try not to use these directly but wrap it into a buffered stream,
/// See nodec_bstream_alloc() and nodec_bstream_alloc_read().
typedef struct _uv_stream_t uv_stream_t;

/// \}

/// \addtogroup buffers
/// \{

/// A `libuv` buffer.
/// These are small and always passed by value.
typedef struct _uv_buf_t {
  /// The length of the buffer data.
  size_t len;
  /// Pointer to the buffer data.
  void*  base;
} uv_buf_t;

/// \}

/// \addtogroup nodec_fs
/// \{

/// A platform independent file handle.
typedef struct _uv_file* uv_file;

/// File open modes, see <a href="https://linux.die.net/man/2/open">open2()</a>.
/// The open mode must always include one of `UV_FS_O_RDONLY`, `UV_FS_O_WRONLY`,
/// or `UV_FS_O_RDWR`. 
typedef enum _nodec_open_flags_t {	
  UV_FS_O_APPEND,				///< Open in append mode.
  UV_FS_O_CREAT,        ///< Create a new file if it does not exist yet; `mode` specifies the permissions in this case.
  UV_FS_O_EXCL,         ///< Fail if the file already exists.
  UV_FS_O_RANDOM,       ///< Access is intended to be random.
  UV_FS_O_RDONLY,       ///< Open for reading only.
  UV_FS_O_RDWR,         ///< Open for reading and writing.
  UV_FS_O_SEQUENTIAL,   ///< Access is intended to be sequential.
  UV_FS_O_SHORT_LIVED,  ///< The file is intended to be short lived.
  UV_FS_O_TEMPORARY,    ///< The file is intended as a temporary file and should not be flushed. 
  UV_FS_O_TRUNC,        ///< If the file exists and  opened for writing, it is set to a zero length.
  UV_FS_O_WRONLY,       ///< Open for writing only.
  UV_FS_O_DIRECT,       ///< I/O is done directly from buffers that must be aligned.
  UV_FS_O_DIRECTORY,    ///< If the path is not a directory, fail to open
  UV_FS_O_DSYNC,        ///< Writing is blocking and only completes if flushed to disk.
  UV_FS_O_EXLOCK,       ///< Obtain an exclusive lock
  UV_FS_O_NOATIME,      ///< Do not update the access time of the file.
  UV_FS_O_NOCTTY,       ///< If the path identifies a terminal device, opening the path will not cause that terminal to become the controlling terminal for the process (if the process does not already have one). (not supported on Windows)
  UV_FS_O_NOFOLLOW,     ///< If the path is a symbolic link, fail to open. (not supported on Windows)
  UV_FS_O_NONBLOCK,			///< Open in non-blocking mode (not supported on Windows).
  UV_FS_O_SYMLINK,      ///< If the path is not a symbolic link, fail to open. (not supported on Windows) 
  UV_FS_O_SYNC          ///< The file is opened for synchronous I/O. Write operations will complete once all data and all metadata are flushed to disk.
} nodec_open_flags_t;

/// Permission mode for (user) reading.
#define R_OK 4
/// Permission mode for (user) writing.
#define W_OK 2
/// Permission mode for (user) execute.
#define X_OK 1

/// Portable structure to hold time stamps.
typedef struct {
  long tv_sec;      ///< Seconds since 1970-01-01UTC (Unix Epoch)
  long tv_nsec;     ///< Nanoseconds
} uv_timespec_t;

/// Portable structure for file information.
typedef struct {
  uint64_t st_dev;            ///< Device id of the file.
  uint64_t st_mode;           ///< File type and permission mode.
  uint64_t st_nlink;          ///< Number of hard links that point to this file.
  uint64_t st_uid;            ///< User ID associated with this file.             
  uint64_t st_gid;            ///< Group ID associated with this file.
  uint64_t st_rdev;
  uint64_t st_ino;            ///< INODE number of the file.
  uint64_t st_size;           ///< Size of the file in bytes.
  uint64_t st_blksize;        ///< Block size used for I/O operations.
  uint64_t st_blocks;         ///< Blocks allocated for this file.
  uint64_t st_flags;      
  uint64_t st_gen;
  uv_timespec_t st_atim;         ///< Last access time.
  uv_timespec_t st_mtim;         ///< Last modified time.
  uv_timespec_t st_ctim;         ///< Last time the file status changed.
  uv_timespec_t st_birthtim;     ///< Creation time.
} uv_stat_t;

/// Directory entry types.
typedef enum {
  UV_DIRENT_UNKNOWN,  ///< Unknown type.
  UV_DIRENT_FILE,     ///< A regular file.
  UV_DIRENT_DIR,      ///< A regular (sub)directory
  UV_DIRENT_LINK,     ///< A symbolic link.
  UV_DIRENT_FIFO,     ///< A FIFO queue.
  UV_DIRENT_SOCKET,   ///< A socket.
  UV_DIRENT_CHAR,     ///< A character device.
  UV_DIRENT_BLOCK     ///< A block device.
} uv_dirent_type_t;

/// Directory entries
typedef struct uv_dirent_s {
  const char* name;       ///< Name of the entry.
  uv_dirent_type_t type;  ///< Type of the entry.
} uv_dirent_t;

/// \}


/// \addtogroup http_setup
/// \{

/// The HTTP method enumeration. 
/// The first 5 (8) are commonly used while the others are used in various
/// protocols, like WebDAV, Subversion etc. 
typedef enum http_method {
  HTTP_DELETE, ///< DELETE method
  HTTP_GET, ///< GET method
  HTTP_HEAD, ///< HEAD method
  HTTP_POST, ///< POST method
  HTTP_PUT, ///< PUT method
  /* pathological */
  HTTP_CONNECT, ///< CONNECT method
  HTTP_OPTIONS, ///< OPTIONS method
  HTTP_TRACE, ///< TRACE method
  /* WebDAV */
  HTTP_COPY, ///< COPY method
  HTTP_LOCK, ///< LOCK method
  HTTP_MKCOL, ///< MKCOL method
  HTTP_MOVE, ///< MOVE method
  HTTP_PROPFIND, ///< PROPFIND method
  HTTP_PROPPATCH, ///< PROPPATCH method
  HTTP_SEARCH, ///< SEARCH method
  HTTP_UNLOCK, ///< UNLOCK method
  HTTP_BIND, ///< BIND method
  HTTP_REBIND, ///< REBIND method
  HTTP_UNBIND, ///< UNBIND method
  HTTP_ACL, ///< ACL method
  /* subversion */
  HTTP_REPORT, ///< REPORT method
  HTTP_MKACTIVITY, ///< MKACTIVITY method
  HTTP_CHECKOUT, ///< CHECKOUT method
  HTTP_MERGE, ///< MERGE method
  /* upnp */
  HTTP_MSEARCH, ///< MSEARCH method
  HTTP_NOTIFY, ///< NOTIFY method
  HTTP_SUBSCRIBE, ///< SUBSCRIBE method
  HTTP_UNSUBSCRIBE, ///< UNSUBSCRIBE method
  /* RFC-5789 */
  HTTP_PATCH, ///< PATCH method
  HTTP_PURGE, ///< PURGE method
  /* CalDAV */
  HTTP_MKCALENDAR, ///< MKCALENDAR method
  /* RFC-2068, section 19.6.1.2 */
  HTTP_LINK, ///< LINK method
  HTTP_UNLINK, ///< UNLINK method
  /* icecast */
  HTTP_SOURCE
} http_method_t;

/// HTTP server response status codes.
typedef enum http_status {
  HTTP_STATUS_CONTINUE,  ///< 100, Continue)                        
  HTTP_STATUS_SWITCHING_PROTOCOLS,  ///< 101, Switching Protocols)             
  HTTP_STATUS_PROCESSING,  ///< 102, Processing)                      
  HTTP_STATUS_OK,  ///< 200, OK)                              
  HTTP_STATUS_CREATED,  ///< 201, Created)                         
  HTTP_STATUS_ACCEPTED,  ///< 202, Accepted)                        
  HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION,  ///< 203, Non-Authoritative Information)   
  HTTP_STATUS_NO_CONTENT,  ///< 204, No Content)                      
  HTTP_STATUS_RESET_CONTENT,  ///< 205, Reset Content)                   
  HTTP_STATUS_PARTIAL_CONTENT,  ///< 206, Partial Content)                 
  HTTP_STATUS_MULTI_STATUS,  ///< 207, Multi-Status)                    
  HTTP_STATUS_ALREADY_REPORTED,  ///< 208, Already Reported)                
  HTTP_STATUS_IM_USED,  ///< 226, IM Used)                         
  HTTP_STATUS_MULTIPLE_CHOICES,  ///< 300, Multiple Choices)                
  HTTP_STATUS_MOVED_PERMANENTLY,  ///< 301, Moved Permanently)               
  HTTP_STATUS_FOUND,  ///< 302, Found)                           
  HTTP_STATUS_SEE_OTHER,  ///< 303, See Other)                       
  HTTP_STATUS_NOT_MODIFIED,  ///< 304, Not Modified)                    
  HTTP_STATUS_USE_PROXY,  ///< 305, Use Proxy)                       
  HTTP_STATUS_TEMPORARY_REDIRECT,  ///< 307, Temporary Redirect)              
  HTTP_STATUS_PERMANENT_REDIRECT,  ///< 308, Permanent Redirect)              
  HTTP_STATUS_BAD_REQUEST,  ///< 400, Bad Request)                     
  HTTP_STATUS_UNAUTHORIZED,  ///< 401, Unauthorized)                    
  HTTP_STATUS_PAYMENT_REQUIRED,  ///< 402, Payment Required)                
  HTTP_STATUS_FORBIDDEN,  ///< 403, Forbidden)                       
  HTTP_STATUS_NOT_FOUND,  ///< 404, Not Found)                       
  HTTP_STATUS_METHOD_NOT_ALLOWED,  ///< 405, Method Not Allowed)              
  HTTP_STATUS_NOT_ACCEPTABLE,  ///< 406, Not Acceptable)                  
  HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED,  ///< 407, Proxy Authentication Required)   
  HTTP_STATUS_REQUEST_TIMEOUT,  ///< 408, Request Timeout)                 
  HTTP_STATUS_CONFLICT,  ///< 409, Conflict)                        
  HTTP_STATUS_GONE,  ///< 410, Gone)                            
  HTTP_STATUS_LENGTH_REQUIRED,  ///< 411, Length Required)                 
  HTTP_STATUS_PRECONDITION_FAILED,  ///< 412, Precondition Failed)             
  HTTP_STATUS_PAYLOAD_TOO_LARGE,  ///< 413, Payload Too Large)               
  HTTP_STATUS_URI_TOO_LONG,  ///< 414, URI Too Long)                    
  HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE,  ///< 415, Unsupported Media Type)          
  HTTP_STATUS_RANGE_NOT_SATISFIABLE,  ///< 416, Range Not Satisfiable)           
  HTTP_STATUS_EXPECTATION_FAILED,  ///< 417, Expectation Failed)              
  HTTP_STATUS_MISDIRECTED_REQUEST,  ///< 421, Misdirected Request)             
  HTTP_STATUS_UNPROCESSABLE_ENTITY,  ///< 422, Unprocessable Entity)            
  HTTP_STATUS_LOCKED,  ///< 423, Locked)                          
  HTTP_STATUS_FAILED_DEPENDENCY,  ///< 424, Failed Dependency)               
  HTTP_STATUS_UPGRADE_REQUIRED,  ///< 426, Upgrade Required)                
  HTTP_STATUS_PRECONDITION_REQUIRED,  ///< 428, Precondition Required)           
  HTTP_STATUS_TOO_MANY_REQUESTS,  ///< 429, Too Many Requests)               
  HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE,  ///< 431, Request Header Fields Too Large) 
  HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS,  ///< 451, Unavailable For Legal Reasons)   
  HTTP_STATUS_INTERNAL_SERVER_ERROR,  ///< 500, Internal Server Error)           
  HTTP_STATUS_NOT_IMPLEMENTED,  ///< 501, Not Implemented)                 
  HTTP_STATUS_BAD_GATEWAY,  ///< 502, Bad Gateway)                     
  HTTP_STATUS_SERVICE_UNAVAILABLE,  ///< 503, Service Unavailable)             
  HTTP_STATUS_GATEWAY_TIMEOUT,  ///< 504, Gateway Timeout)                 
  HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,  ///< 505, HTTP Version Not Supported)      
  HTTP_STATUS_VARIANT_ALSO_NEGOTIATES,  ///< 506, Variant Also Negotiates)         
  HTTP_STATUS_INSUFFICIENT_STORAGE,  ///< 507, Insufficient Storage)            
  HTTP_STATUS_LOOP_DETECTED,  ///< 508, Loop Detected)                   
  HTTP_STATUS_NOT_EXTENDED,  ///< 510, Not Extended)                    
  HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED  ///< 511, Network Authentication Required) 
} http_status_t;

/// \}



<|start_filename|>docs/api/search/groups_3.js<|end_filename|>
var searchData=
[
  ['effect_20handlers',['Effect Handlers',['../group__effect.html',1,'']]],
  ['effect_20definition_20helpers',['Effect Definition helpers',['../group__effect__def.html',1,'']]],
  ['exceptions_20and_20finally_20clauses',['Exceptions and Finally clauses',['../group__effect__exn.html',1,'']]]
];


<|start_filename|>docs/api/search/groups_0.js<|end_filename|>
var searchData=
[
  ['asynchrony',['Asynchrony',['../group__nodec__async.html',1,'']]]
];


<|start_filename|>docs/api/search/groups_2.js<|end_filename|>
var searchData=
[
  ['debugging',['Debugging',['../group__effect__debug.html',1,'']]]
];


<|start_filename|>docs/api/search/all_10.js<|end_filename|>
var searchData=
[
  ['tty',['TTY',['../group__nodec__tty.html',1,'']]],
  ['tcp_20connections',['TCP Connections',['../group__tcp.html',1,'']]],
  ['tcp_5fserver_5fconfig',['tcp_server_config',['../group__tcp.html#ga178d806e948c857f5599ee9462d18720',1,'nodec.h']]],
  ['tcp_5fserver_5fconfig_5ft',['tcp_server_config_t',['../group__tcp.html#structtcp__server__config__t',1,'']]],
  ['timeout',['timeout',['../group__tcp.html#aa1a3af7b0980e3c45cc618c2bf7f076f',1,'tcp_server_config_t']]],
  ['timeout_5ftotal',['timeout_total',['../group__tcp.html#a4af6558b9d474340373020ccfef5026c',1,'tcp_server_config_t']]],
  ['tv_5fnsec',['tv_nsec',['../group__nodec__fs.html#af1552c618f27286c9734be801d0ecf3f',1,'uv_timespec_t']]],
  ['tv_5fsec',['tv_sec',['../group__nodec__fs.html#a6c1e6a2cd2d9f10dc4df27ab3a79bc9d',1,'uv_timespec_t']]],
  ['type',['type',['../group__nodec__fs.html#a7343234f6f445689c599b147eeac9658',1,'uv_dirent_t']]]
];


<|start_filename|>src/stream.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>


void nodec_stream_free(nodec_stream_t* stream) {
  if (stream->stream_free!=NULL) stream->stream_free(stream);
}

void nodec_stream_freev(lh_value streamv) {
  nodec_stream_free((nodec_stream_t*)lh_ptr_value(streamv));
}

void async_shutdown(nodec_stream_t* stream) {
  if (stream->shutdown != NULL) stream->shutdown(stream);
}

uv_buf_t async_read_bufx(nodec_stream_t* stream, bool* buf_owned) {
  if (stream->read_bufx == NULL) nodec_check_msg(UV_EINVAL, "trying to read from a write stream");
  return stream->read_bufx(stream, buf_owned);
}

uv_buf_t async_read_buf(nodec_stream_t* stream) {
  bool owned = false;
  uv_buf_t buf = async_read_bufx(stream, &owned);
  if (owned || nodec_buf_is_null(buf)) return buf;
  // copy into owned buffer
  uv_buf_t dest = nodec_buf_alloc(buf.len);
  memcpy(dest.base, buf.base, buf.len);
  return dest;
}

void async_write_bufs(nodec_stream_t* stream, uv_buf_t bufs[], size_t count) {
  if (stream->write_bufs == NULL) nodec_check_msg(UV_EINVAL, "trying to write to a read stream");
  stream->write_bufs(stream, bufs, count);
}

void nodec_stream_init(nodec_stream_t* stream,
  async_read_bufx_fun*  read_bufx,
  async_write_bufs_fun* write_bufs,
  async_shutdown_fun*   shutdown,
  nodec_stream_free_fun* stream_free)
{
  stream->read_bufx = read_bufx;
  stream->write_bufs = write_bufs;
  stream->shutdown = shutdown;
  stream->stream_free = stream_free;
}

void nodec_stream_release(nodec_stream_t* stream) {
  // nothing
}


// Return first available data as a string
char* async_read(nodec_stream_t* stream) {
  uv_buf_t buf = async_read_buf(stream);
  if (nodec_buf_is_null(buf)) return NULL;
  assert(buf.base[buf.len] == 0);
  return (char*)buf.base;
}

void async_write_buf(nodec_stream_t* stream, uv_buf_t buf) {
  async_write_bufs(stream, &buf, 1);
}

void async_write(nodec_stream_t* stream, const char* s) {
  uv_buf_t buf = nodec_buf(s, strlen(s));
  async_write_buf(stream, buf);
}


void async_vprintf(nodec_stream_t* stream, const char* fmt, va_list args) {
  char buf[512];
  vsnprintf(buf, 512, fmt, args); 
  buf[511] = 0;
  async_write(stream, buf);
}

void async_printf(nodec_stream_t* stream, const char* fmt, ...) {
  va_list args;
  va_start(args, stream);
  async_vprintf(stream, fmt, args);
  va_end(args);
}


/* ----------------------------------------------------------------------------
Read chunks

read data in a list of buffers (`chunks`) and provide asynchronous functions to read
from these chunks potentially waiting until data is read.
-----------------------------------------------------------------------------*/

static void chunks_init(chunks_t* chunks) {
  memset(chunks, 0, sizeof(*chunks));
}

// push a buffer on the chunks queue
static uv_errno_t chunksx_push(chunks_t* chunks, const uv_buf_t buf, size_t nread) {
  assert(buf.len >= nread);
  if(nread == 0) {
    nodec_buf_free(buf);
    return 0;
  }
  chunk_t* chunk = nodecx_alloc(chunk_t);
  if (chunk == NULL) return UV_ENOMEM;
  // initalize
  chunk->next = NULL;
  chunk->buf = buf;
  // link it 
  if (chunks->last != NULL) {
    chunks->last->next = chunk;
  }
  else {
    chunks->first = chunk;
  }
  chunks->last = chunk;
  chunks->available += nread;
  // ensure buf len is correct
  assert(nread <= chunk->buf.len);
  if (nread < chunk->buf.len) {
    if (chunk->buf.len > 64 && (nread / 4) * 5 <= chunk->buf.len) {
      // more than 64bytes and more than 20% wasted; we reallocate if possible
      void* newbase = nodecx_realloc(chunk->buf.base, nread + 1);
      if (newbase != NULL) chunk->buf.base = newbase;
      chunk->buf.len = (uv_buf_len_t)nread;
    }
    else {
      // just adjust the length and waste some space
      chunk->buf.len = (uv_buf_len_t)nread;
    }
  }
  return 0;
}

static void chunks_push(chunks_t* chunks, const uv_buf_t buf, size_t nread) {
  nodec_check(chunksx_push(chunks, buf, nread));
}

// push a buffer on the head of the chunks queue; buf is taken as is and not resized
static void chunks_push_back(chunks_t* chunks, const uv_buf_t buf) {
  if (nodec_buf_is_null(buf)) return;
  chunk_t* chunk = nodec_alloc(chunk_t);
  // initalize
  chunk->next = chunks->first;
  chunk->buf = buf;
  // link it 
  chunks->first = chunk;
  if (chunks->last == NULL) chunks->last = chunk;
  chunks->available += buf.len;
}

// Free all memory for a chunks queue
static void chunks_release(chunks_t* chunks) {
  chunk_t* chunk = chunks->first;
  while (chunk != NULL) {
    nodec_free(chunk->buf.base);
    chunk_t* next = chunk->next;
    nodec_free(chunk);
    chunk = next;
  }
  chunks->first = chunks->last = NULL;
  chunks->available = 0;
}

// Return the first available buffer in the chunks
uv_buf_t chunks_read_buf(chunks_t* chunks) {
  chunk_t* chunk = chunks->first;
  if (chunk == NULL) {
    assert(chunks->available == 0);
    return nodec_buf_null();
  }
  else {
    uv_buf_t buf = chunk->buf;
    // unlink the first chunk
    chunks->first = chunk->next;
    if (chunks->first == NULL) chunks->last = NULL;
    nodec_free(chunk);
    assert(chunks->available >= buf.len);
    chunks->available -= buf.len;
    assert(chunks->available > 0 || chunks->first == NULL);
    return buf;
  }
}

// Return all available chunks in one buffer
static uv_buf_t chunks_read_buf_all(chunks_t* chunks) {
  chunk_t* chunk = chunks->first;
  if (chunk == NULL) {
    // no available data
    assert(chunks->available == 0);
    return nodec_buf_null();
  }
  else if (chunk->next == NULL) {
    // just one buffer, return it in place
    return chunks_read_buf(chunks);
  }
  else {
    // put all bufs into one larger buf
    uv_buf_t buf = nodec_buf_alloc(chunks->available);
    size_t total = 0;
    size_t nread = 0;
    do {
      uv_buf_t rbuf = chunks_read_buf(chunks);
      nread = rbuf.len;
      assert(nread + total <= buf.len);
      memcpy(buf.base + total, rbuf.base, nread);
      total += nread;
      nodec_buf_free(rbuf);
    } while (nread > 0 && total < buf.len);
    assert(total == buf.len);
    assert(chunks->available == 0);
    assert(chunks->first == NULL);
    return buf;
  }
}


// We can look out for a pattern of at most 8 characters.
// This is used for efficient readline and http header reading.
// Doing it with the 8 last characters remembered makes it much easier 
// to cross chunk boundaries when searching.
typedef struct _find_t {
  const chunk_t* chunk;         // current chunk
  size_t         offset;        // current offset in chunk
  size_t         seen;          // total number of bytes scanned
  uint64_t       last8;         // last 8 characters seen
  uint64_t       pattern;       // the pattern as an 8 character block
  uint64_t       pattern_mask;  // the relevant parts if a pattern is less than 8 characters
} find_t;


static void chunks_find_init(const  chunks_t* chunks, find_t* f, const void* pat, size_t pattern_len) {
  assert(pattern_len <= 8);
  const char* pattern = pat;
  if (pattern_len > 8) lh_throw_str(EINVAL, "pattern too long");
  f->chunk = chunks->first;
  f->offset = 0;
  f->seen = 0;
  f->pattern = 0;
  f->pattern_mask = 0;
  f->last8 = 0;

  // initialize pattern and the mask
  for (size_t i = 0; i < pattern_len; i++) {
    f->pattern = (f->pattern << 8) | pattern[i];
    f->pattern_mask = (f->pattern_mask << 8) | 0xFF;
  }
  // initialize last8 to something different than (any initial part of) pattern
  char c = 0;
  while (memchr(pattern, c, pattern_len) != NULL) c++;
  for (size_t i = 0; i < sizeof(f->last8); i++) {
    f->last8 = (f->last8 << 8) | c;
  }
}

static size_t chunks_find(const chunks_t* chunks, find_t* f) {
  // initialize the initial chunk if needed
  if (f->chunk == NULL) {
    f->chunk = chunks->first;
    if (f->chunk == NULL) return 0;
  }
  // go through all chunks
  do {
    // go through one chunk
    while (f->offset < f->chunk->buf.len) {
      // shift in the new character
      uint8_t b = f->chunk->buf.base[f->offset];
      f->last8 = (f->last8 << 8) | b;
      f->seen++;
      f->offset++;
      // compare against the pattern
      if ((f->last8 & f->pattern_mask) == f->pattern) {
        return f->seen; // found
      }
    }
    // move on to the next chunk if it is there yet
    if (f->chunk->next != NULL) {
      f->chunk = f->chunk->next;
      f->offset = 0;
    }
  } while (f->offset < f->chunk->buf.len);  // done for now
  return 0;
}

/* ----------------------------------------------------------------------------
Buffered streams
Abstract class: buffered streams and basic uv_stream_t derive from this
-----------------------------------------------------------------------------*/

bool async_bstream_read_chunk(nodec_bstream_t* bstream, nodec_chunk_read_t mode, size_t read_to_eof_max) {
  if (bstream->read_chunk == NULL) nodec_check_msg(UV_EINVAL, "trying to read from a write stream");
  return bstream->read_chunk(bstream, mode, read_to_eof_max);
}

void nodec_pushback_buf(nodec_bstream_t* bstream, uv_buf_t buf) {
  if (bstream->pushback_buf == NULL) nodec_check_msg(UV_EINVAL, "trying to read from a write stream");
  bstream->pushback_buf(bstream, buf);
}

void nodec_chunks_pushback_buf(nodec_bstream_t* bstream, uv_buf_t buf) {
  chunks_push_back(&bstream->chunks, buf);
}

void nodec_chunks_push(nodec_bstream_t* bstream, uv_buf_t buf) {
  chunks_push(&bstream->chunks, buf, buf.len);
}

size_t nodec_chunks_available(nodec_bstream_t* bstream) {
  return bstream->chunks.available;
}

uv_buf_t nodec_chunks_read_buf(nodec_bstream_t* bstream) {
  return chunks_read_buf(&bstream->chunks);
}




nodec_stream_t* as_stream(nodec_bstream_t* bstream) {
  return &bstream->stream_t;
}

void nodec_bstream_init(nodec_bstream_t* bstream,
  async_read_chunk_fun* read_chunk,
  nodec_pushback_buf_fun* pushback_buf,
  async_read_bufx_fun*  read_bufx,
  async_write_bufs_fun* write_bufs,
  async_shutdown_fun*   shutdown,
  nodec_stream_free_fun* stream_free)
{
  bstream->source = NULL;
  nodec_stream_init(&bstream->stream_t, read_bufx, write_bufs, shutdown, stream_free);
  bstream->read_chunk = read_chunk;
  bstream->pushback_buf = pushback_buf;
  chunks_init(&bstream->chunks);
}

void nodec_bstream_release(nodec_bstream_t* bstream) {
  chunks_release(&bstream->chunks);
  nodec_stream_release(&bstream->stream_t);
}

// Return a single buffer that contains the entire stream contents
uv_buf_t async_read_buf_all(nodec_bstream_t* bstream, size_t read_max) {
  while (!async_bstream_read_chunk(bstream, CREAD_TO_EOF, read_max)) {
    // wait until eof or read_max
  }
  return chunks_read_buf_all(&bstream->chunks);
}


char* async_read_all(nodec_bstream_t* bstream, size_t read_max) {
  uv_buf_t buf = async_read_buf_all(bstream, read_max);
  if (buf.base == NULL) return NULL;
  assert(buf.base[buf.len] == 0);
  return (char*)buf.base;
}


// Read into a preallocated buffer until the buffer is full or eof.
// Returns the number of bytes read. `at_eof` can be NULL;
//
// Todo: If we drain the current chunks first, we could after that 
// 'allocate' in the given buffer from the callback directly and
// not actually copy the memory but write to it directly.
size_t async_read_into(nodec_bstream_t* bstream, uv_buf_t buf) {
  if (buf.base == NULL || buf.len == 0) return 0;
  size_t total = 0;
  size_t nread = 0;
  do {
    // read available or from source
    bool owned = false;
    uv_buf_t rbuf = async_read_bufx(&bstream->stream_t,&owned);
    {using_buf_owned(owned, &rbuf) {
      nread = rbuf.len;
      if (nread > 0) {
        // copy into buffer
        size_t ncopy = (total + nread > buf.len ? buf.len - total : nread);
        memcpy(buf.base + total, rbuf.base, ncopy);
        total += ncopy;
        if (ncopy < nread) {
          // push back the bytes that didn't fit
          if (owned) {
            // note: we don't reallocate since chunks are short-lived anyway
            memmove(rbuf.base, rbuf.base + ncopy, rbuf.len - ncopy);
            rbuf.len -= (uv_buf_len_t)ncopy;
          }
          else {
            uv_buf_t dest = nodec_buf_alloc(rbuf.len - ncopy);
            memcpy(dest.base, rbuf.base + ncopy, dest.len);
            rbuf = dest; // overwrite, as we don't own rbuf anyway
          }
          nodec_pushback_buf(bstream, rbuf);
          rbuf = nodec_buf_null(); // null out `rbuf` so it does not get freed          
        }
      }
    }}
  } while (nread > 0 && total < buf.len);
  return total;
}



// --------------------------------------------------------------------
// Reading up to a pattern

static size_t async_bstream_find(nodec_bstream_t* bstream, const void* pat, size_t pat_len, size_t read_max) {
  find_t find;
  chunks_find_init(&bstream->chunks, &find, pat, pat_len);
  size_t toread = 0;
  bool eof = false;
  while (!eof && (toread = chunks_find(&bstream->chunks, &find)) == 0 && (read_max == 0 || nodec_chunks_available(bstream) < read_max)) {
    eof = async_bstream_read_chunk(bstream, CREAD_EVEN_IF_AVAILABLE, 0); // read direct and push into the chunks
  }
  return toread;
}

uv_buf_t async_read_buf_including(nodec_bstream_t* bstream, size_t* toread, const void* pat, size_t pat_len, size_t read_max) {
  if (toread != NULL) *toread = 0;
  size_t n = async_bstream_find(bstream, pat, pat_len, read_max);
  if (toread != NULL) *toread = n;
  if (n == 0) return nodec_buf_null();
  return chunks_read_buf_all(&bstream->chunks);
}

uv_buf_t async_read_buf_upto(nodec_bstream_t* bstream, const void* pat, size_t pat_len, size_t read_max) {
  size_t toread = 0;
  uv_buf_t buf = async_read_buf_including(bstream, &toread, pat, pat_len, read_max);
  if (nodec_buf_is_null(buf) || toread == buf.len) return buf;
  assert(toread < buf.len);
  size_t n = buf.len - toread;
  uv_buf_t xbuf = nodec_buf_alloc(n);
  memcpy(xbuf.base, buf.base + toread, n);
  nodec_pushback_buf(bstream, xbuf);
  return nodec_buf_fit(buf, toread);
}

uv_buf_t async_read_buf_line(nodec_bstream_t* bstream) {
  return async_read_buf_upto(bstream, "\n", 1, 64*NODEC_KB);
}

char* async_read_line(nodec_bstream_t* bstream) {
  uv_buf_t buf = async_read_buf_line(bstream);
  if (buf.base == NULL) return NULL;
  assert(buf.base[buf.len] == 0);
  return (char*)buf.base;
}


/* ----------------------------------------------------------------------------
  Some explicit error-returning functions
-----------------------------------------------------------------------------*/

typedef struct _write_buf_args_t {
  nodec_stream_t* s;
  uv_buf_t        buf;
} write_buf_args_t;

static lh_value _asyncx_write_buf(lh_value argsv) {
  write_buf_args_t* args = (write_buf_args_t*)lh_ptr_value(argsv);
  async_write_buf(args->s, args->buf);
  return lh_value_null;
}

uv_errno_t asyncx_write_buf(nodec_stream_t* s, uv_buf_t buf) {
  lh_exception* exn = NULL;
  write_buf_args_t args = { s, buf };
  lh_try(&exn, &_asyncx_write_buf, lh_value_any_ptr(&args));
  return (exn != NULL ? exn->code : 0);
}


typedef struct _read_buf_args_t {
  nodec_bstream_t* s;
  uv_buf_t         buf;
} read_buf_args_t;

static lh_value _asyncx_read_into(lh_value argsv) {
  read_buf_args_t* args = (read_buf_args_t*)lh_ptr_value(argsv);
  return lh_value_long((long)(async_read_into(args->s, args->buf)));
}

uv_errno_t asyncx_read_into(nodec_bstream_t* s, uv_buf_t buf, size_t* nread) {
  if (nread != NULL) *nread = 0;
  lh_exception* exn = NULL;
  read_buf_args_t args = { s, buf };
  lh_value nreadv = lh_try(&exn, &_asyncx_read_into, lh_value_any_ptr(&args));
  if (exn != NULL) return exn->code;
  if (nread != NULL) *nread = (size_t)lh_long_value(nreadv);
  return 0;
}


/* ----------------------------------------------------------------------------
Buffer any stream
-----------------------------------------------------------------------------*/

static uv_buf_t async_bufstream_read_bufx(nodec_stream_t* stream, bool* owned) {
nodec_bstream_t* bs = (nodec_bstream_t*)stream;
  if (nodec_chunks_available(bs) > 0) {
    if (owned != NULL) *owned = true;
    return chunks_read_buf(&bs->chunks);
  }
  else {
    return async_read_bufx(bs->source, owned);
  }
}

static void async_bufstream_write_bufs(nodec_stream_t* stream, uv_buf_t bufs[], size_t count) {
  nodec_bstream_t* bs = (nodec_bstream_t*)stream;
  async_write_bufs(bs->source, bufs, count);
}

static bool async_bufstream_read_chunk(nodec_bstream_t* bs, nodec_chunk_read_t read_mode, size_t read_to_eof_max) {
  if (read_mode == CREAD_NORMAL && nodec_chunks_available(bs) > 0) {
    return false;
  }
  else {
    uv_buf_t buf;
    if (read_mode != CREAD_TO_EOF) read_to_eof_max = 0;
    do {
      buf = async_read_buf(bs->source);
      chunks_push(&bs->chunks, buf, buf.len);
    } while (!nodec_buf_is_null(buf) && nodec_chunks_available(bs) < read_to_eof_max);
    return (nodec_buf_is_null(buf));
  }
}

static void async_bufstream_shutdown(nodec_stream_t* stream) {
  nodec_bstream_t* bs = (nodec_bstream_t*)stream;
  async_shutdown(bs->source);
}

static void async_bufstream_free(nodec_stream_t* stream) {
  nodec_bstream_t* bs = (nodec_bstream_t*)stream;
  nodec_stream_free(bs->source);
  nodec_bstream_release(bs);
  nodec_free(bs);
}

nodec_bstream_t* nodec_bstream_alloc_on(nodec_stream_t* source) {
  nodec_bstream_t* bs = nodec_zero_alloc(nodec_bstream_t);
  nodec_bstream_init(bs,
    &async_bufstream_read_chunk, &nodec_chunks_pushback_buf,
    &async_bufstream_read_bufx, &async_bufstream_write_bufs,
    &async_bufstream_shutdown, &async_bufstream_free);
  bs->source = source;
  return bs;
}




/* ----------------------------------------------------------------------------
Await shutdown
-----------------------------------------------------------------------------*/

static void async_await_shutdown(uv_shutdown_t* req, uv_stream_t* stream) {
  async_await_owned((uv_req_t*)req, stream);
}

static void async_shutdown_resume(uv_shutdown_t* req, uverr_t status) {
  async_req_resume((uv_req_t*)req, status);
}


/* ----------------------------------------------------------------------------
Await write requests
-----------------------------------------------------------------------------*/

/*
static void async_await_write(uv_write_t* req, uv_stream_t* owner) {
  async_await_owned((uv_req_t*)req, owner);
}
*/

static uv_errno_t asyncx_await_write(uv_write_t* req, uv_stream_t* owner) {
  return asyncx_await_owned((uv_req_t*)req, owner);
}


static void async_write_resume(uv_write_t* req, uverr_t status) {
  async_req_resume((uv_req_t*)req, status);
}

uv_errno_t asyncx_uv_write_bufs(uv_stream_t* stream, uv_buf_t* bufs, size_t buf_count) {
  if (bufs == NULL || buf_count <= 0) return 0;
  uv_errno_t err = 0;
  {using_req(uv_write_t, req) {
    // Todo: verify it is ok to have bufs on the stack or if we need to heap alloc them first for safety
    err = uv_write(req, stream, bufs, (unsigned)buf_count, &async_write_resume);
    if (err == 0) err = asyncx_await_write(req, stream);
  }}
  return err;
}

static uv_errno_t asyncx_uv_write_buf(uv_stream_t* stream, uv_buf_t buf) {
  return asyncx_uv_write_bufs(stream, &buf, 1);
}

static void async_uv_write_bufs(uv_stream_t* stream, uv_buf_t bufs[], size_t buf_count) {
  nodec_check_data(asyncx_uv_write_bufs(stream, bufs, buf_count),stream);
}

void async_uv_write_buf(uv_stream_t* stream, uv_buf_t buf) {
  nodec_check_data(asyncx_uv_write_buf(stream, buf),stream);
}

void async_uv_stream_shutdown(uv_stream_t* stream) {
  if (stream == NULL) return;
  if (stream->write_queue_size > 0) {
    {using_req(uv_shutdown_t, req) {
      nodec_check(uv_shutdown(req, stream, &async_shutdown_resume));
      async_await_shutdown(req, stream);
    }}
  }
}


struct _nodec_uv_stream_t {
  nodec_bstream_t bstream_t;     // a buffered stream; with source=NULL
  uv_stream_t*    stream;        // backlink, when reading stream->data == this
  size_t          alloc_size;    // current chunk allocation size (<= alloc_max), doubled on every new read
  size_t          alloc_max;     // maximal chunk allocation size (usually about 64kb)
  size_t          read_to_eof_max;   // set > 0 to improve perfomance for reading a stream up to eof
  uv_req_t*       req;           // request object for waiting
  volatile size_t     read_total;    // total bytes read until now (available <= total)
  volatile bool       eof;           // true if end-of-file reached
  volatile uv_errno_t err;           // !=0 on error
};


nodec_bstream_t* as_bstream(nodec_uv_stream_t* stream) {
  return &stream->bstream_t;
}


static void nodecx_uv_stream_push(nodec_uv_stream_t* rs, const uv_buf_t buf, size_t nread) {
  if (nread == 0 || buf.base == NULL) return;
  rs->err = chunksx_push(&rs->bstream_t.chunks, buf, nread);    
  if (rs->err != 0) {
    nodec_free(buf.base);
  }
  else {
    rs->read_total += nread;
    if (rs->read_to_eof_max > 0) {
      rs->read_to_eof_max = (rs->read_to_eof_max < nread ? 0 : rs->read_to_eof_max - nread);      
    }
  }  
}

static void nodec_uv_stream_try_resume(nodec_uv_stream_t* rs) {
  assert(rs != NULL);
  uv_req_t* req = rs->req;
  if (req == NULL) return;
  async_req_resume(req, rs->err);
}

/*
static void nodec_uv_stream_try_resumev(void* rsv) {
  nodec_uv_stream_try_resume((nodec_uv_stream_t*)rsv);
}
*/

static void nodec_uv_stream_freereq(nodec_uv_stream_t* rs) {
  if (rs != NULL && rs->req != NULL) {
    nodec_req_free(rs->req);  // on explicit cancelation, don't immediately free
    rs->req = NULL;
  }
}

static void nodec_uv_stream_freereqv(lh_value rsv) {
  nodec_uv_stream_freereq(lh_ptr_value(rsv));
}

static  uverr_t asyncx_uv_stream_await(nodec_uv_stream_t* rs, bool wait_even_if_available, uint64_t timeout) {
  if (rs == NULL) return UV_EINVAL;
  if ((wait_even_if_available || nodec_chunks_available(&rs->bstream_t) == 0) && rs->err == 0 && !rs->eof) {
    // await an event
    if (rs->req != NULL) lh_throw_str(UV_EINVAL, "only one strand can await a read stream");
    uv_req_t* req = nodec_zero_alloc(uv_req_t);
    rs->req = req;
    {defer(nodec_uv_stream_freereqv, lh_value_ptr(rs)) {
      rs->err = asyncx_await(req, timeout, rs->stream);
      //async_await_owned(req, rs->stream);
      // fprintf(stderr,"back from await\n");
    }}
  }
  if (rs->eof) return UV_EOF;
  return rs->err;
}

static bool async_uv_stream_await(nodec_uv_stream_t* rs, bool wait_even_if_available) {
  if (rs == NULL) return true;
  uverr_t err = asyncx_uv_stream_await(rs, wait_even_if_available, 0);
  if (err != UV_EOF) nodec_check(err);
  return (err == UV_EOF);
}

uv_errno_t asyncx_uv_stream_await_available(nodec_uv_stream_t* stream, int64_t timeout) {
  return asyncx_uv_stream_await(stream, false, timeout);
}


static void nodec_uv_stream_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
  if (handle == NULL) return;
  nodec_uv_stream_t* rs = (nodec_uv_stream_t*)handle->data;
  if (rs == NULL) return;
  // allocate
  size_t len = (rs->alloc_size > 0 ? rs->alloc_size : suggested_size);
  buf->base = nodecx_malloc(len + 1);  // always allow a zero at the end
  buf->len  = (buf->base == NULL ? 0 : (uv_buf_len_t)len);
  // increase allocation size
  if (rs->alloc_size > 0 && rs->alloc_size < rs->alloc_max) {
    size_t newsize = 2 * rs->alloc_size;
    rs->alloc_size = (newsize > rs->alloc_max || newsize < rs->alloc_size ? rs->alloc_max : newsize);
  }
}


static void _nodec_uv_stream_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
  nodec_uv_stream_t* rs = (nodec_uv_stream_t*)stream->data;
  if (nread <= 0 || rs == NULL) {
    if (buf != NULL && buf->base != NULL) {
      nodec_free(buf->base);
      //buf->base = NULL;
    }
  }

  if (rs != NULL) {
    if (nread > 0) {
      // always terminate with zero
      assert(buf->len >= nread);
      buf->base[nread] = 0;
      // data available
      nodecx_uv_stream_push(rs, *buf, (size_t)nread);
      if (rs->read_to_eof_max == 0 || rs->eof) {
        nodec_uv_stream_try_resume(rs);
      }
    }
    else if (nread < 0) {
      // done reading (error or UV_EOF)
      if (nread == UV_ECANCELED) nread = UV_EOF;
      rs->err = (uv_errno_t)(nread == UV_EOF ? 0 : nread);
      if (nread == UV_EOF) {
        rs->eof = true;
      }
      uv_read_stop(stream);       // no more reading
      nodec_uv_stream_try_resume(rs);
    }
    else {
      // E_AGAIN or E_WOULDBLOCK (but not EOF)
      // Todo: just ignore? or try resume?
    }
  }
  return;
}

void nodec_uv_stream_read_restart(nodec_uv_stream_t* rs) {
  uv_errno_t err = uv_read_start(rs->stream, &nodec_uv_stream_alloc_cb, &_nodec_uv_stream_cb);
  if (err != 0 && err != UV_EALREADY) nodec_check(err);
}

void nodec_uv_stream_read_start(nodec_uv_stream_t* rs, size_t alloc_init, size_t alloc_max) {
  if (rs->stream->data == NULL) {
    rs->stream->data = rs;
  }
  assert(rs != NULL && rs->stream->data == rs);
  rs->read_to_eof_max = 0;
  //rs->read_max = (read_max > 0 ? read_max : 1024 * 1024 * 1024);  // 1Gb by default
  rs->alloc_size = (alloc_init == 0 ? 1024 : alloc_init);  // start small but double at every new read
  rs->alloc_max = (alloc_max == 0 ? 64 * 1024 : alloc_max);
  nodec_uv_stream_read_restart(rs);
}


void nodec_uv_stream_read_stop(nodec_uv_stream_t* rs) {
  if (rs->stream->data == NULL) return;
  nodec_check(uv_read_stop(rs->stream));
}


static void close_handle_cb(uv_handle_t* h) {
  /*
  if ((h->type == UV_STREAM || h->type == UV_TCP || h->type == UV_TTY) && h->data != NULL) {
  read_stream_free((read_stream_t*)h->data);
  }
  */
  nodec_free(h);
}

void nodec_handle_free(uv_handle_t* h) {
  // Todo: this is philosophically "wrong" as the callback is called outside of
  // our framework (i.e. we should do an await). 
  // but we're ok since the callback does just a free.
  if (h != NULL && !uv_is_closing(h)) {
    uv_close(h, &close_handle_cb);
  }
  else {
    close_handle_cb(h);
  }
  nodec_owner_release(h);
}

void nodec_uv_stream_free(uv_stream_t* stream) {
  if (stream->data != NULL) {
    // read stream
    uv_read_stop(stream);
    stream->data = NULL;
  }
  nodec_handle_free((uv_handle_t*)stream);
}

void nodec_uv_stream_freev(lh_value uvstreamv) {
  nodec_uv_stream_free((uv_stream_t*)lh_ptr_value(uvstreamv));
}
static uv_buf_t async_uv_stream_read_bufx(nodec_stream_t* s, bool* owned) {
  nodec_uv_stream_t* rs = (nodec_uv_stream_t*)s;
  if (owned != NULL) *owned = true;
  async_uv_stream_await(rs, false);
  assert(rs->eof || nodec_chunks_available(&rs->bstream_t) > 0);
  return chunks_read_buf(&rs->bstream_t.chunks);
}

static bool async_uv_stream_read_chunk(nodec_bstream_t* s, nodec_chunk_read_t read_mode, size_t read_to_eof_max) {
  nodec_uv_stream_t* rs = (nodec_uv_stream_t*)s;
  if (read_mode == CREAD_TO_EOF) {
    rs->read_to_eof_max = read_to_eof_max; // reduces resumes
    while (!async_uv_stream_await(rs, true)) {
      // wait until eof
    }
    rs->read_to_eof_max = 0; 
  }
  else {
    async_uv_stream_await(rs, (read_mode == CREAD_EVEN_IF_AVAILABLE));
  }
  return rs->eof;
}

static void async_uv_stream_write_bufsx(nodec_stream_t* s, uv_buf_t bufs[], size_t buf_count) {
  nodec_uv_stream_t* rs = (nodec_uv_stream_t*)s;
  async_uv_write_bufs(rs->stream, bufs, buf_count);
}


static void async_uv_stream_shutdownx(nodec_stream_t* stream) {
  nodec_uv_stream_t* rs = (nodec_uv_stream_t*)stream;
  async_uv_stream_shutdown(rs->stream);
}


static void nodec_uv_stream_freex(nodec_stream_t* stream) {
  nodec_uv_stream_t* rs = (nodec_uv_stream_t*)stream;
  nodec_uv_stream_free(rs->stream);
  rs->stream = NULL;
  nodec_bstream_release(&rs->bstream_t);
  nodec_free(rs);
}


nodec_uv_stream_t* nodec_uv_stream_alloc(uv_stream_t* stream) {
  nodec_uv_stream_t* rs = NULL;
  {on_abort(nodec_uv_stream_freev, lh_value_any_ptr(stream)) {
    rs = nodec_zero_alloc(nodec_uv_stream_t);
    nodec_bstream_init(&rs->bstream_t,
      &async_uv_stream_read_chunk, &nodec_chunks_pushback_buf,
      &async_uv_stream_read_bufx, &async_uv_stream_write_bufsx,
      &async_uv_stream_shutdownx, &nodec_uv_stream_freex);
    rs->stream = stream;
  }}
  return rs;
}

nodec_bstream_t* nodec_bstream_alloc(uv_stream_t* stream) {
  nodec_uv_stream_t* rs = nodec_uv_stream_alloc(stream);
  return &rs->bstream_t;
}

nodec_bstream_t* nodec_bstream_alloc_read_ex(uv_stream_t* stream, size_t alloc_init, size_t alloc_max) {
  nodec_uv_stream_t* rs = nodec_uv_stream_alloc(stream);
  nodec_uv_stream_read_start(rs, alloc_init, alloc_max);
  return &rs->bstream_t;
}

nodec_bstream_t* nodec_bstream_alloc_read(uv_stream_t* stream) {
  return nodec_bstream_alloc_read_ex(stream, 0, 0);
}


/* ----------------------------------------------------------------------------
  (G)Zip streams
-----------------------------------------------------------------------------*/

#ifdef USE_ZLIB

#include <zlib/zlib.h>

typedef struct _nodec_zstream_t {
  nodec_bstream_t bstream;
  z_stream        read_strm;
  z_stream        write_strm;
  uv_buf_t        write_buf;
  nodec_stream_t* source;
  bool            own_source;
  size_t          chunk_size;
  size_t          nread;
  size_t          nwritten;
  int             compress_level;
  bool            gzip;
} nodec_zstream_t;

static void* nodec_zalloc(void* opaque, unsigned size, unsigned count) {
  return nodecx_calloc(size, count);
}

static void nodec_zfree(void* opaque, void* p) {
  nodec_free(p);
}

static void nodec_zstream_init_read(nodec_zstream_t* zs) {
  if (zs->read_strm.zalloc == NULL) {
    zs->read_strm.zalloc = &nodec_zalloc;
    zs->read_strm.zfree = &nodec_zfree;
    zs->read_strm.opaque = Z_NULL;
    zs->read_strm.avail_in = 0;
    zs->read_strm.next_in = Z_NULL;
    int res = inflateInit2(&zs->read_strm, 15 + (zs->gzip ? 32 : 0)); // read either gzip or zlib
    if (res != 0) nodec_check_msg(UV_ENOMEM, "cannot initialize inflate");
  }
}

static void nodec_zstream_init_write(nodec_zstream_t* zs) {
  if (zs->write_strm.zalloc == NULL) {
    zs->write_strm.zalloc = &nodec_zalloc;
    zs->write_strm.zfree = &nodec_zfree;
    zs->write_strm.opaque = Z_NULL;
    zs->write_strm.avail_in = 0;
    zs->write_strm.next_in = Z_NULL;
    int res = deflateInit2(&zs->write_strm, zs->compress_level, Z_DEFLATED, 15 + (zs->gzip ? 16 : 0), 8, Z_DEFAULT_STRATEGY);
    if (res != 0) nodec_check_msg(UV_ENOMEM, "cannot initialize deflate");
    zs->write_buf = nodec_buf_alloc(zs->chunk_size);
  }
}

static bool async_zstream_read_chunks(nodec_zstream_t* zs) 
{
  nodec_zstream_init_read(zs);
  bool owned = false;
  int res = 0;
  uv_buf_t src = async_read_bufx(zs->source, &owned);
  // if (nodec_buf_is_null(src)) return true;  // eof
  {using_buf_owned(owned, &src) {
    zs->read_strm.avail_in = src.len;
    zs->read_strm.next_in = (void*)src.base;
    zs->nread += src.len;
    // decompress while data available into chunks
    do {
      uv_buf_t dest = nodec_buf_alloc(zs->chunk_size);
      zs->read_strm.avail_out = dest.len;
      zs->read_strm.next_out = (void*)dest.base;
      res = inflate(&zs->read_strm, (src.len==0 ? Z_SYNC_FLUSH: Z_NO_FLUSH));
      if (res >= Z_OK) { // can be Z_STREAM_END
        size_t nwritten = dest.len - zs->read_strm.avail_out;
        chunks_push(&zs->bstream.chunks, dest, nwritten);
      }
      else {
        nodec_check_msg(UV_EINVAL, "invalid gzip data");
      }
    } while (res == Z_OK && zs->read_strm.avail_out == 0);  // while fully written buffers
  }}
  return (res == Z_STREAM_END);
}

static uv_buf_t async_zstream_read_bufx(nodec_stream_t* stream, bool* owned) {
  nodec_zstream_t* zs = (nodec_zstream_t*)stream;
  if (owned != NULL) *owned = true;
  if (nodec_chunks_available(&zs->bstream) == 0) {
    async_zstream_read_chunks(zs);
  }
  // hopefully by now we have available chunks
  return nodec_chunks_read_buf(&zs->bstream);
}

static bool async_zstream_read_chunk(nodec_bstream_t* s, nodec_chunk_read_t read_mode, size_t read_to_eof_max) {
  nodec_zstream_t* zs = (nodec_zstream_t*)s;
  if (read_mode == CREAD_NORMAL && nodec_chunks_available(&zs->bstream) > 0) {
    return false;
  }
  else {
    bool eof = false;
    if (read_mode != CREAD_TO_EOF) read_to_eof_max = 0;
    do {
      eof = async_zstream_read_chunks(zs);
    } while (!eof && nodec_chunks_available(&zs->bstream) < read_to_eof_max);
    return eof;
  }
}

static void async_zstream_write_buf(nodec_zstream_t* zs, uv_buf_t src, int flush) {
  nodec_zstream_init_write(zs);
  zs->nwritten += src.len;
  zs->write_strm.avail_in = src.len;
  zs->write_strm.next_in = (void*)src.base;
  int res = 0;
  do {
    zs->write_strm.avail_out = zs->write_buf.len;
    zs->write_strm.next_out = (void*)zs->write_buf.base;
    res = deflate(&zs->write_strm, flush);
    if (res >= Z_OK) {  // Z_OK || Z_STREAM_END
      size_t nwritten = zs->write_buf.len - zs->write_strm.avail_out;
      if (nwritten > 0) {
        async_write_buf(zs->source, nodec_buf(zs->write_buf.base, nwritten));
      }
    }
  } while (res >= Z_OK && zs->write_strm.avail_out == 0);  // while full buffers
}

static void async_zstream_write_bufs(nodec_stream_t* s, uv_buf_t bufs[], size_t buf_count) {
  nodec_zstream_t* zs = (nodec_zstream_t*)s;
  for (size_t i = 0; i < buf_count; i++) {
    uv_buf_t src = bufs[i];
    if (nodec_buf_is_null(src)) continue;
    async_zstream_write_buf(zs, src, Z_NO_FLUSH);
  }
}

static void async_zstream_shutdown(nodec_stream_t* stream) {
  nodec_zstream_t* zs = (nodec_zstream_t*)stream;
  if (zs->nwritten > 0) {
    async_zstream_write_buf(zs, nodec_buf_null(), Z_FINISH);
  }
  if (zs->own_source) async_shutdown(zs->source);
}

static void nodec_zstream_free(nodec_stream_t* stream) {
  nodec_zstream_t* zs = (nodec_zstream_t*)stream;
  if (zs->read_strm.zalloc != NULL) inflateEnd(&zs->read_strm);
  if (zs->write_strm.zalloc != NULL) deflateEnd(&zs->write_strm);
  nodec_buf_free(zs->write_buf);
  if (zs->own_source) nodec_stream_free(zs->source);
  nodec_free(zs);
}


nodec_bstream_t* nodec_zstream_alloc_ex(nodec_stream_t* stream, bool own_stream, int compress_level, bool gzip) {
  int res = 0;
  nodec_zstream_t* zs = nodecx_alloc(nodec_zstream_t);
  if (zs == NULL) { res = UV_ENOMEM; goto err; }
  zs->own_source = own_stream;
  zs->write_buf = nodec_buf_null();
  zs->chunk_size = 64 * 1024;       // (de)compress in 64kb chunks
  zs->nread = 0;
  zs->nwritten = 0;
  zs->source = stream;
  zs->gzip = gzip;
  zs->compress_level = compress_level;
  nodec_bstream_init(&zs->bstream,
    &async_zstream_read_chunk, &nodec_chunks_pushback_buf,
    &async_zstream_read_bufx, &async_zstream_write_bufs,
    &async_zstream_shutdown, &nodec_zstream_free);
  memset(&zs->read_strm, 0, sizeof(z_stream));
  memset(&zs->write_strm, 0, sizeof(z_stream));  
  return &zs->bstream;
err:
  if (zs != NULL) nodec_free(zs);
  if (stream != NULL && own_stream) nodec_stream_free(stream);
  nodec_check(res);
  return NULL;
}

nodec_bstream_t* nodec_zstream_alloc(nodec_stream_t* stream, bool own_stream) {
  return nodec_zstream_alloc_ex(stream, own_stream, 6, true);
}



#endif // NO_ZLIB




<|start_filename|>docs/api/structyieldargs.js<|end_filename|>
var structyieldargs =
[
    [ "argcount", "structyieldargs.html#ab797acdcd17ed8d49fd79980409be48b", null ],
    [ "args", "structyieldargs.html#a2dd60935d436953098e73cbdd77fdcd2", null ]
];

<|start_filename|>docs/api/search/enums_1.js<|end_filename|>
var searchData=
[
  ['lh_5fopkind',['lh_opkind',['../group__effect__handlers.html#gaa1b51c33da7448e42829ec71678631fc',1,'libhandler.h']]]
];


<|start_filename|>inc/nodec-internal.h<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/

#pragma once
#ifndef __nodec_internal_h
#define __nodec_internal_h

#include "nodec.h"
#include "nodec-primitive.h"

// ---------------------------------------------------------------------------------
// Private functions used internally to the NodeC library
// todo: is there a better way to define internally only visible routines?
// ---------------------------------------------------------------------------------

lh_value   _channel_async_handler(channel_t* channel, lh_actionfun* action, lh_value arg);
void       _channel_async_req_resume(lh_resume r, lh_value local, uv_req_t* req, uverr_t err);

// These are callback functions to resume requests:
// Calling this will resume the `async_await` call on that request. 
// A call to these will resume at most once! (and be ignored after that)
void       async_req_resume(uv_req_t* uvreq, uverr_t err);
void       async_fs_resume(uv_fs_t* req);

void       nodec_req_force_free(uv_req_t* uvreq);
void       nodec_req_force_freev(lh_value uvreq);

void       nodec_req_free(uv_req_t* uvreq);
void       nodec_req_freev(lh_value uvreq);

void       nodec_owner_release(void* owner);

#define using_req(req_tp,name) \
  req_tp* name = nodec_zero_alloc(req_tp); \
  defer(nodec_req_freev,lh_value_ptr(name))

// A request that is always freed even when canceled. Use this only if
// it is guaranteed that the request is never used again (for example for timers)
#define using_free_req(req_tp,name) \
  req_tp* name = nodec_zero_alloc(req_tp); \
  defer(nodec_req_force_freev,lh_value_ptr(name))


// Await an asynchronous request but return an explicit error value instead of throwing.
// Use with care since these still throw on cancelation requests.
uv_errno_t   asyncx_nocancel_await(uv_req_t* uvreq);  // never throws and cannot be canceled
uv_errno_t   asyncxx_await(uv_req_t* uvreq, uint64_t timeout, void* owner);  // never throws
uv_errno_t   asyncx_await(uv_req_t* req, uint64_t timeout, void* owner);     // throws on cancel
uv_errno_t   asyncx_await_fs(uv_fs_t* req);

// Set a timeout callback 
typedef void uv_timeoutfun(void* arg);
uv_errno_t   _uv_set_timeout(uv_loop_t* loop, uv_timeoutfun* cb, void* arg, uint64_t timeout);
void         nodec_timer_free(uv_timer_t* timer, bool owner_release);

int          channel_receive_nocancel(channel_t* channel, lh_value* data, lh_value* arg);

#define UV_ETHROWCANCEL   (-10000)
#define UV_EHTTP          (-20000)

lh_value async_write_http_exnv(lh_value exnv);

// ---------------------------------------------------------------------------------
// TTY
// ---------------------------------------------------------------------------------

implicit_declare(tty)

lh_value _nodec_tty_allocv();
void     _nodec_tty_freev(lh_value ttyv);
void     async_tty_shutdown();

/// Enable the console in a scope.
/// Inside the given scope, one can use async_tty_readline() and async_tty_write().
///
/// \b Example:
/// ```
/// {using_tty() {
///   async_tty_write("\033[41;37m");         // ANSI escape to set red color
///   async_tty_write("what is your name? ");
///   const char* s = async_tty_readline();
///   {using_free(s) {
///     printf("I got: %s\n", s);
///   }}
/// }}
/// ```
#define using_tty()  \
    using_implicit_defer_exit(async_tty_shutdown(),_nodec_tty_freev,_nodec_tty_allocv(),tty)


// ---------------------------------------------------------------------------------
// Loggin
// ---------------------------------------------------------------------------------

implicit_declare(log)

lh_value _nodec_log_allocv(nodec_log_level_t level);
void     _nodec_log_freev(lh_value logv);
void     nodec_log_shutdown();

#define using_log(level)  \
    using_implicit_defer_exit(nodec_log_shutdown(),_nodec_log_freev,_nodec_log_allocv(level),log)


// ---------------------------------------------------------------------------------
// LibUV streams 
// ---------------------------------------------------------------------------------

// used in http.c for writing errors
uv_errno_t asyncx_uv_write_bufs(uv_stream_t* stream, uv_buf_t* bufs, size_t buf_count);

// used in tty.c
void async_uv_write_buf(uv_stream_t* stream, uv_buf_t buf);
void async_uv_stream_shutdown(uv_stream_t* stream);
void nodec_uv_stream_free(uv_stream_t* stream);

void nodec_tls_stream_handshake(nodec_tls_stream_t* ts);



#endif

<|start_filename|>docs/api/search/typedefs_1.js<|end_filename|>
var searchData=
[
  ['lh_5facquirefun',['lh_acquirefun',['../group__effect__value.html#ga5734aa37e6622d1b505a0173e2545ba8',1,'libhandler.h']]],
  ['lh_5factionfun',['lh_actionfun',['../group__effect__value.html#ga5a08b630153ff4e9c2120d0bb2debd89',1,'libhandler.h']]],
  ['lh_5fcallocfun',['lh_callocfun',['../group__effect__debug.html#gae07f635b592d37224a693c4b96c3ca8b',1,'libhandler.h']]],
  ['lh_5feffect',['lh_effect',['../group__effect__handlers.html#gaf06dcf1847501605b52051581f50cb8d',1,'libhandler.h']]],
  ['lh_5ffatalfun',['lh_fatalfun',['../group__effect__debug.html#gaaa958e853fd8e039d2bd95a3589d05d8',1,'libhandler.h']]],
  ['lh_5ffreefun',['lh_freefun',['../group__effect__debug.html#gabd736ee0a830b88c25a6e577e1afc273',1,'libhandler.h']]],
  ['lh_5fmallocfun',['lh_mallocfun',['../group__effect__debug.html#gad6a1f6b1a5962f31b8a3d00fc134f190',1,'libhandler.h']]],
  ['lh_5fopfun',['lh_opfun',['../group__effect__handlers.html#gaf79f5f56c6360a3ca0115773b1a21bec',1,'libhandler.h']]],
  ['lh_5foptag',['lh_optag',['../group__effect__handlers.html#gaad5d615b9d98a63df083512caa78d78a',1,'libhandler.h']]],
  ['lh_5freallocfun',['lh_reallocfun',['../group__effect__debug.html#ga647046150553dd8b90064a8f2b94c9f9',1,'libhandler.h']]],
  ['lh_5freleasefun',['lh_releasefun',['../group__effect__value.html#ga6f614d89d8e6565a50317139cca21db3',1,'libhandler.h']]],
  ['lh_5fresultfun',['lh_resultfun',['../group__effect__value.html#ga540551bfce3a27024092a782553ceddf',1,'libhandler.h']]],
  ['lh_5fresume',['lh_resume',['../group__effect__handlers.html#gace940505f4ef9f09e2aeeca789c5fdea',1,'libhandler.h']]],
  ['lh_5fstring',['lh_string',['../group__effect__value.html#ga1f24f3a2029820ca75484a67853370a3',1,'libhandler.h']]],
  ['lh_5fvalue',['lh_value',['../group__effect__value.html#ga18d15281353b7e38984b0ef2e36abb83',1,'libhandler.h']]],
  ['lh_5fvoidfun',['lh_voidfun',['../group__effect__value.html#ga58b182dd21feb550d40436ef31d09614',1,'libhandler.h']]],
  ['lh_5fvoidptr',['lh_voidptr',['../group__effect__value.html#ga7b4439b7ec3223634778002ea318acea',1,'libhandler.h']]]
];


<|start_filename|>docs/api/search/variables_9.js<|end_filename|>
var searchData=
[
  ['len',['len',['../group__buffers.html#a1f258533d380bfb77f226e03f590388d',1,'uv_buf_t']]],
  ['local_5facquire',['local_acquire',['../group__effect__handlers.html#a197b2abb85288eda9fc73dddb02213c9',1,'lh_handlerdef']]],
  ['local_5frelease',['local_release',['../group__effect__handlers.html#ac03ed465c68a46c167612e712160fd61',1,'lh_handlerdef']]]
];


<|start_filename|>docs/api/group__tcp.js<|end_filename|>
var group__tcp =
[
    [ "Low level TCP Connections", "group__tcpx.html", null ],
    [ "tcp_server_config_t", "group__tcp.html#structtcp__server__config__t", [
      [ "backlog", "group__tcp.html#a64ed27e28dbbddbcf1fe1fed3538014b", null ],
      [ "max_interleaving", "group__tcp.html#ace7d6ad760cdb393909b6b4496929d4d", null ],
      [ "timeout", "group__tcp.html#aa1a3af7b0980e3c45cc618c2bf7f076f", null ],
      [ "timeout_total", "group__tcp.html#a4af6558b9d474340373020ccfef5026c", null ]
    ] ],
    [ "tcp_server_config", "group__tcp.html#ga178d806e948c857f5599ee9462d18720", null ],
    [ "nodec_tcp_connection_fun_t", "group__tcp.html#ga6e7766bd8a316202634ad28c1c2a7d6d", null ],
    [ "async_tcp_connect", "group__tcp.html#ga4e0db7845d0a6de8355c58e172e7d1f1", null ],
    [ "async_tcp_connect_at", "group__tcp.html#ga1794f0147b6b441b5eabac6cb6dbbd5d", null ],
    [ "async_tcp_connect_at_host", "group__tcp.html#ga1375271b46adc8f5945c65ebe2196f3a", null ],
    [ "async_tcp_server_at", "group__tcp.html#gae3b5fdace85b868927f9d7eed5b661d0", null ]
];

<|start_filename|>docs/api/group__effect__handlers_structlh__operation.js<|end_filename|>
var group__effect__handlers_structlh__operation =
[
    [ "opfun", "group__effect__handlers.html#a0ccd260a5593683d8343aa0d9a75d7d7", null ],
    [ "opkind", "group__effect__handlers.html#a596d55fa9ca51ec0602b60b37e818453", null ],
    [ "optag", "group__effect__handlers.html#ad8b0fc0385aecd9216ec6df9df996539", null ]
];

<|start_filename|>docs/api/group__effect__def.js<|end_filename|>
var group__effect__def =
[
    [ "Linear Handlers", "group__effect__linear.html", null ],
    [ "Exceptions and Finally clauses", "group__effect__exn.html", "group__effect__exn" ],
    [ "Implicit Parameters", "group__effect__implicit.html", "group__effect__implicit" ]
];

<|start_filename|>src/http_url.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>

struct _nodec_url_t {
  struct http_parser_url parts;
  uv_buf_t    urlmem;
  const char* original;
};

void nodec_url_free(nodec_url_t* url) {
  nodec_bufref_free(&url->urlmem);
  nodec_free(url);
}

void nodec_url_freev(lh_value urlv) {
  nodec_url_free((nodec_url_t*)lh_ptr_value(urlv));
}

static nodec_url_t*  nodecx_parse_urlx(const char* url, bool onlyhost) {
  int err;
  nodec_url_t* nurl = nodec_zero_alloc(nodec_url_t);
  {on_abort(nodec_url_freev, lh_value_ptr(nurl)) {
    // we prepend "http://" if no protocol is specified
    if (strstr(url, "://") == NULL && !onlyhost) {
      uv_buf_t buf = nodec_buf_str(nodec_strdup("http://"));     
      nurl->urlmem = nodec_buf_append_into(buf,nodec_buf_str(url));
    }
    else {
      nurl->urlmem = nodec_buf_str(nodec_strdup(url));
    }
    nurl->original = url;
    // and parse it
    err = http_parser_parse_url(nurl->urlmem.base, nurl->urlmem.len, (onlyhost ? 1 : 0), &nurl->parts);
    if (err == 0) {
      // 0-terminate all fields in our private memory
      for (enum http_parser_url_fields f = 0; f < UF_MAX; f++) {
        if (((1 << f) & nurl->parts.field_set) != 0) {
          size_t ofs = nurl->parts.field_data[f].off;
          size_t len = nurl->parts.field_data[f].len;
          if (f == UF_PATH) { // don't consider starting slash
            ofs++; 
            len--;
          }
          assert(ofs + len <= nurl->urlmem.len);
          nurl->urlmem.base[ofs + len] = 0;
        }
      }
    }
  }}
  if (err != 0) {
    nodec_url_free(nurl);
    return NULL;
  }
  else {
    return nurl;
  }
}

static nodec_url_t*  nodec_parse_urlx(const char* url, bool onlyhost) {
  nodec_url_t* nurl = nodecx_parse_urlx(url, onlyhost);
  if (nurl == NULL) {
    char buf[256];
    snprintf(buf, 256, "invalid url: %s", url);
    lh_throw_strdup(EINVAL, buf);
  }
  return nurl;
}

nodec_url_t*  nodecx_parse_url(const char* url) {
  return nodecx_parse_urlx(url, false);
}

nodec_url_t*  nodecx_parse_host(const char* host) {
  return nodecx_parse_urlx(host, true);
}

nodec_url_t*  nodec_parse_url(const char* url) {
  return nodec_parse_urlx(url, false);
}

nodec_url_t*  nodec_parse_host(const char* host) {
  return nodec_parse_urlx(host,true);
}

static const char* nodec_url_field(const nodec_url_t* url, enum http_parser_url_fields f) {
  if (((1 << f) & url->parts.field_set) == 0) {
    return NULL;
  }
  else {
    size_t ofs = url->parts.field_data[f].off;
    size_t len = url->parts.field_data[f].len;
    if (len > 0 && url->urlmem.base[ofs] == 0) {
      // for things like "path" that include the initial "/"
      ofs++;
      len--;
    }
    assert(ofs + len <= url->urlmem.len);
    assert(url->urlmem.base[ofs + len] == 0);
    return &url->urlmem.base[ofs];
  }
}

const char* nodec_url_schema(const nodec_url_t* url) {
  return nodec_url_field(url, UF_SCHEMA);
}
const char* nodec_url_host(const nodec_url_t* url) {
  return nodec_url_field(url, UF_HOST);
}
const char* nodec_url_path(const nodec_url_t* url) {
  return nodec_url_field(url, UF_PATH);
}
const char* nodec_url_query(const nodec_url_t* url) {
  return nodec_url_field(url, UF_QUERY);
}
const char* nodec_url_fragment(const nodec_url_t* url) {
  return nodec_url_field(url, UF_FRAGMENT);
}
const char* nodec_url_userinfo(const nodec_url_t* url) {
  return nodec_url_field(url, UF_USERINFO);
}
const char* nodec_url_port_str(const nodec_url_t* url) {
  return nodec_url_field(url, UF_PORT);
}

uint16_t nodec_url_port(const nodec_url_t* url) {
  return url->parts.port;
}
bool nodec_url_is_ip6(const nodec_url_t* url) {
  return (strchr(nodec_url_host(url), ':') != NULL);
}


<|start_filename|>docs/api/group__http__req__resp.js<|end_filename|>
var group__http__req__resp =
[
    [ "async_req_read_body", "group__http__req__resp.html#ga90c24eeea159e619c53f08c349930434", null ],
    [ "async_req_read_body_str", "group__http__req__resp.html#gab2f0f9c13b27e4d3960a40fa4123fa49", null ],
    [ "http_req", "group__http__req__resp.html#ga6fbdd916c136f477bbc3b45402907241", null ],
    [ "http_req_body", "group__http__req__resp.html#gac25ef2db063b5b4f33e4fb3042137dab", null ],
    [ "http_req_content_length", "group__http__req__resp.html#ga5639e997c65e093372f3f369decdabbb", null ],
    [ "http_req_header", "group__http__req__resp.html#gab3b9ae435e3ff47e65c120849637570e", null ],
    [ "http_req_header_next", "group__http__req__resp.html#ga5a4e7beef65369fe9dd0af4e950e0296", null ],
    [ "http_req_method", "group__http__req__resp.html#ga1bbbf2a9c6763cc8abd61c64e3d56be9", null ],
    [ "http_req_parsed_url", "group__http__req__resp.html#gaa071ab8ae8e6548f41f3945a3a8944ee", null ],
    [ "http_req_path", "group__http__req__resp.html#ga79ccdc504575e50c2c7f7898f9f4e098", null ],
    [ "http_req_print", "group__http__req__resp.html#gabbd2e133173ef6dcbb5ae9f0dd2b8888", null ],
    [ "http_req_url", "group__http__req__resp.html#ga453c19b95ff4853887cc38eab789dc0d", null ],
    [ "http_resp", "group__http__req__resp.html#gafcc54624869b5d7c85b79843901fefd7", null ],
    [ "http_resp_add_header", "group__http__req__resp.html#gad6f9bebbcdf5329df19b69d0ff915e12", null ],
    [ "http_resp_send_body_buf", "group__http__req__resp.html#ga19397cedfdd8a668cf4956891fce3e76", null ],
    [ "http_resp_send_body_str", "group__http__req__resp.html#gab7bbb21ed41c9c4adc79aef33f08ddca", null ],
    [ "http_resp_send_ok", "group__http__req__resp.html#gac7cf19d0f1a0d2704f8882b218bdfeec", null ],
    [ "http_resp_send_status", "group__http__req__resp.html#ga9299a8391a97562030e97d19dec8e611", null ],
    [ "http_resp_send_status_body", "group__http__req__resp.html#gac4b657f50f7b004fdc226c7aad4cef5c", null ],
    [ "http_resp_status_sent", "group__http__req__resp.html#ga2e5c04d158afc7ab3575ba3ddf5c98a0", null ],
    [ "http_strand_id", "group__http__req__resp.html#gaf6c636b98224eb09dfb01bb1779ec08f", null ]
];

<|start_filename|>docs/api/group__tcp_structtcp__server__config__t.js<|end_filename|>
var group__tcp_structtcp__server__config__t =
[
    [ "backlog", "group__tcp.html#a64ed27e28dbbddbcf1fe1fed3538014b", null ],
    [ "max_interleaving", "group__tcp.html#ace7d6ad760cdb393909b6b4496929d4d", null ],
    [ "timeout", "group__tcp.html#aa1a3af7b0980e3c45cc618c2bf7f076f", null ],
    [ "timeout_total", "group__tcp.html#a4af6558b9d474340373020ccfef5026c", null ]
];

<|start_filename|>src/memory.c<|end_filename|>
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h> 
#include <malloc.h>

/*-----------------------------------------------------------------
  string/memory utility functions
-----------------------------------------------------------------*/

bool nodec_starts_with(const char* s, const char* prefix) {
  if (s == NULL || prefix == NULL) return false;
  size_t m = strlen(prefix);
  if (m == 0) return true;
  return (strncmp(s, prefix, m) == 0);
}

bool nodec_starts_withi(const char* s, const char* prefix) {
  if (s == NULL || prefix == NULL) return false;
  size_t m = strlen(prefix);
  if (m == 0) return true;
  return (nodec_strnicmp(s, prefix, m) == 0);
}


bool nodec_ends_with(const char* s, const char* pat) {
  if (s == NULL || pat == NULL) return false;
  size_t m = strlen(pat);
  if (m == 0) return true;
  size_t n = strlen(s);
  if (n < m) return false;
  return (strncmp(s + (n - m), pat, m) == 0);
}

bool nodec_ends_withi(const char* s, const char* pat) {
  if (s == NULL || pat == NULL) return false;
  size_t m = strlen(pat);
  if (m == 0) return true;
  size_t n = strlen(s);
  if (n < m) return false;
  return (nodec_strnicmp(s + (n - m), pat, m) == 0);
}

uv_errno_t nodec_strncpy(char* dest, size_t destsz, const char* src, size_t count) {
  if (dest == NULL || destsz == 0) return UV_EINVAL;
  *dest = 0;
  if (count == 0) return 0;
  if (src == NULL) return UV_EINVAL;
  if (destsz < count - 1) return UV_E2BIG;
  while (*src != 0 && count > 0) {
    *dest = *src;
    dest++;
    src++;
    count--;
  }
  *dest = 0;
  return (count > 0 ? UV_E2BIG : 0);
}

int nodec_strnicmp(const char* s, const char* t, size_t n) {
  if (s == NULL) return (t == NULL ? 0 : -1);
  if (t == NULL) return 1;
  if (n == 0) return 0;
  char c;
  char d;
  do {
    c = *s; s++;
    d = *t; t++;
    n--;
    if (c != d) {
      if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a';
      if (d >= 'A' && d <= 'Z') d = d - 'A' + 'a';
      if (c != d) return (c - d);
    }
  } while (c != 0 && d != 0 && n > 0);
  return 0;
}

int nodec_stricmp(const char* s, const char* t) {
  return nodec_strnicmp(s, t, SIZE_MAX);
}

// Search for byte pattern in byte source array.
const void* nodec_memmem(const void* src, size_t src_len, const void* pat, size_t pat_len)
{
  const char* csrc = (const char*)src;
  const char* cpat = (const char*)pat;
  
  if (src_len==0 || pat_len==0) return NULL;
  if (src_len < pat_len) return NULL;
  if (pat_len==1) return memchr(src, (int)cpat[0], src_len);

  for (size_t i = 0; i < src_len - pat_len + 1; i++) {
    if (csrc[i] == cpat[0] && memcmp(csrc+i, cpat, pat_len) == 0) {
      return csrc+i;
    }
  }
  return NULL;
}

/*-----------------------------------------------------------------
  uv_buf_t functions
-----------------------------------------------------------------*/

uv_buf_t nodec_buf(const void* data, size_t len) {
  return uv_buf_init((char*)data, (uv_buf_len_t)(len));
}

uv_buf_t nodec_buf_str(const char* s) {
  return nodec_buf(s, (s==NULL ? 0 : strlen(s)) );
}

uv_buf_t nodec_buf_strdup(const char* s) {
  return nodec_buf_str(nodec_strdup(s));
}


uv_buf_t nodec_buf_null() {
  return nodec_buf(NULL, 0);
}

uv_buf_t nodec_buf_alloc(size_t len) {
  uv_buf_t buf = nodec_buf(nodec_malloc(len + 1), len);  // always allow one more for zero termination
  buf.base[len] = 0;
  return buf;
}

uv_buf_t nodec_buf_realloc(uv_buf_t buf, size_t len) {
  // if (buf.len >= len) return buf;
  if (len == SIZE_MAX) lh_throw_errno(EOVERFLOW);
  uv_buf_t newbuf = nodec_buf(nodec_realloc(buf.base,len + 1), len);  // always allow one more for zero termination
  newbuf.base[len] = 0;
  return newbuf;
}

void nodec_buf_free(uv_buf_t buf) {
  if (buf.base != NULL) nodec_free(buf.base);
}

void nodec_bufref_free(uv_buf_t* buf) {
  nodec_buf_free(*buf);
  *buf = nodec_buf_null();
}

void nodec_bufref_freev(lh_value bufref) {
  nodec_bufref_free((uv_buf_t*)lh_ptr_value(bufref));  
}

void nodec_bufref_nofreev(lh_value bufref) {
  // do nothing
}

bool nodec_buf_is_null(uv_buf_t buf) {
  return (buf.base == NULL || buf.len == 0);
}

uv_buf_t nodec_buf_ensure_ex(uv_buf_t buf, size_t needed, size_t initial_size, size_t max_increase) {
  if (buf.len >= needed) return buf;  // already big enough
  size_t newlen = 0;
  if (nodec_buf_is_null(buf)) {
    newlen = (initial_size == 0 ? 8*1024 : initial_size);    // 8KB default
  }
  else {
    if (max_increase == 0) max_increase = 4 * 1024 * 1024; // 4MB max increase default
    newlen = buf.len + (buf.len > max_increase ? max_increase : buf.len);  // double up to max increase
  }
  if (newlen < needed)  newlen = needed;   // ensure at least `needed` space
  return nodec_buf_realloc(buf, newlen);  // and reallocate
}

uv_buf_t nodec_buf_ensure(uv_buf_t buf, size_t needed) {
  return nodec_buf_ensure_ex(buf, needed, 0, 0);
}

uv_buf_t nodec_buf_append_into(uv_buf_t buf1, uv_buf_t buf2) {
  size_t needed = buf1.len + buf2.len;
  uv_buf_t buf = nodec_buf_ensure_ex(buf1, needed, needed, 0);
  memcpy(buf.base + buf1.len, buf2.base, buf2.len);
  buf.base[needed] = 0;
  return buf;
}

uv_buf_t nodec_buf_fit(uv_buf_t buf, size_t needed) {
  if (nodec_buf_is_null(buf) || buf.len < needed) {
    return nodec_buf_ensure_ex(buf, needed, needed, 0);
  }
  else if (buf.len > 128 && (needed / 4) * 5 <= buf.len) {
    // more than 128 bytes and more than 20% wasted; we reallocate if possible
    return nodec_buf_realloc(buf, needed);
  }
  else {
    return nodec_buf(buf.base, needed);
  }
}

/*-----------------------------------------------------------------
  Wrappers for malloc
-----------------------------------------------------------------*/
// Set up different allocation functions
static lh_mallocfun*  custom_malloc = NULL;
static lh_callocfun*  custom_calloc = NULL;
static lh_reallocfun* custom_realloc = NULL;
static lh_freefun*    custom_free = NULL;

void nodec_register_malloc(lh_mallocfun* _malloc, lh_callocfun* _calloc, lh_reallocfun* _realloc, lh_freefun* _free) {
  lh_register_malloc(_malloc, _calloc, _realloc, _free);
  custom_malloc = _malloc;
  custom_calloc = _calloc;
  custom_realloc = _realloc;
  custom_free = _free;
}

void  nodec_freev(lh_value p) {
  nodec_free(lh_ptr_value(p));
}

void  nodec_free_bufrefv(lh_value p) {
  uv_buf_t* buf = (uv_buf_t*)lh_ptr_value(p);
  nodec_free(buf->base);
  buf->base = NULL;
  buf->len = 0;
}

void* _nodecx_malloc(size_t size) {
  return (custom_malloc==NULL ? malloc(size) : custom_malloc(size));
}

void* _nodecx_calloc(size_t count, size_t size) {
  return (custom_calloc == NULL ? calloc(count, size) : custom_calloc(count, size));
}

void* _nodecx_realloc(void* p, size_t newsize) {
  return (custom_realloc == NULL ? realloc(p,newsize) : custom_realloc(p,newsize));
}

void  _nodec_free(const void* p) {
  if (p != NULL) {
    if (custom_free == NULL) free((void*)p); else custom_free(p);
  }
}


void* check_nonnull(void* p) {
  if (p == NULL) nodec_check(UV_ENOMEM);
  return p;
}

void* _nodec_malloc(size_t size) {
  return check_nonnull(_nodecx_malloc(size));
}

void* _nodec_calloc(size_t count, size_t size) {
  return check_nonnull(_nodecx_calloc(count,size));
}

void* _nodec_realloc(void* p, size_t newsize) {
  return check_nonnull(_nodecx_realloc(p,newsize));
}

static char* _nodec_strndup(const char* s, size_t max) {
  size_t n = (max == SIZE_MAX ? max : max + 1);
  char* t = nodec_alloc_n(n,char);
  #ifdef _MSC_VER
  strncpy_s(t, n, s, max);
  #else
  strncpy(t, s, max);
  #endif
  t[max] = 0;
  return t;
}

char* nodec_strdup(const char* s) {
  if (s == NULL) return NULL;
  size_t n = strlen(s);
  return _nodec_strndup(s, n);
}

char* nodec_strndup(const char* s, size_t max) {
  if (s == NULL) return NULL;
  return _nodec_strndup(s, max);
}


/*-----------------------------------------------------------------
  Memory checks
-----------------------------------------------------------------*/
void nodec_check_memory() {
  #if defined(_MSC_VER) && !defined(__clang__)
  # if defined(_DEBUG)
  fprintf(stderr, "\nchecked memory leaks.\n");
  _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
  _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
  _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
  _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
  _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
  _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
  _CrtDumpMemoryLeaks();
  # endif
  #endif
}


<|start_filename|>docs/api/search/variables_7.js<|end_filename|>
var searchData=
[
  ['http_5fstatic_5fimplicit_5fexts',['http_static_implicit_exts',['../group__nodec__static.html#gae8ac7620c8122033e3239beee552339f',1,'nodec.h']]]
];


<|start_filename|>docs/api/modules.js<|end_filename|>
var modules =
[
    [ "Buffer's", "group__buffers.html", "group__buffers" ],
    [ "Loggin", "group__log.html", "group__log" ],
    [ "Asynchrony", "group__nodec__async.html", "group__nodec__async" ],
    [ "File System", "group__nodec__fs.html", "group__nodec__fs" ],
    [ "Streams", "group__streams.html", "group__streams" ],
    [ "IP addresses", "group__nodec__ip.html", "group__nodec__ip" ],
    [ "URL", "group__nodec__url.html", "group__nodec__url" ],
    [ "MIME Types.", "group__nodec__mime.html", "group__nodec__mime" ],
    [ "TCP Connections", "group__tcp.html", "group__tcp" ],
    [ "HTTP Connections", "group__http.html", "group__http" ],
    [ "TTY", "group__nodec__tty.html", "group__nodec__tty" ],
    [ "Various", "group__nodec__various.html", "group__nodec__various" ],
    [ "Memory Allocation.", "group__nodec__alloc.html", "group__nodec__alloc" ],
    [ "Effect Handlers", "group__effect.html", "group__effect" ]
];

<|start_filename|>inc/nodec-primitive.h<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/

#pragma once
#ifndef __nodec_primitive_h
#define __nodec_primitive_h

#include "nodec.h"

// ---------------------------------------------------------------------------------
// NodeC primitive functions that might be used by certain clients
// but are not generally exposed.
// ---------------------------------------------------------------------------------

typedef int uverr_t;

void nodec_throw(uverr_t err);
void nodec_throw_msg(uverr_t err, const char* msg);
void nodec_throw_data(uverr_t err, void* data);
void nodec_throw_msg_data(uverr_t err, const char* msg, void* data);

// Check the an error value and throw if it is not zero.
void nodec_check(uverr_t err);
void nodec_check_msg(uverr_t err, const char* msg);
void nodec_check_data(uverr_t err,void* data);
void nodec_check_msg_data(uverr_t err, const char* msg, void* data);

#if (defined(_WIN32) || defined(_WIN64))
typedef ULONG  uv_buf_len_t;
#else
typedef size_t uv_buf_len_t;
#endif



/* ----------------------------------------------------------------------------
Asynchronous primitives
-----------------------------------------------------------------------------*/

// Return the current event loop (ambiently bound by the async handler)
uv_loop_t* async_loop();

// Await an asynchronous request. Throws on error. 
// If canceled, the request is deallocated when the original callback is invoked.
// This is used for 'one of' callbacks, like `fs_stat`.
void       async_await_once(uv_req_t* req);

uv_errno_t asyncx_await_once(uv_req_t* uvreq);
uv_errno_t asyncx_await_owned(uv_req_t* uvreq, void* owner);

// Await an asynchronous request. 
// If canceled, the request is deallocated when the `owner` (usually a `uv_handle_t*`)
// is released. This is used for streams or timers.
void       async_await_owned(uv_req_t* req, void* owner);



/* ----------------------------------------------------------------------------
Channels
-----------------------------------------------------------------------------*/
typedef void (channel_release_elem_fun)(lh_value data, lh_value arg, int err);

channel_t*    channel_alloc(ssize_t queue_max);
channel_t*    channel_alloc_ex(ssize_t queue_max, lh_releasefun* release, lh_value release_arg, channel_release_elem_fun* release_elem);
void          channel_free(channel_t* channel);
void          channel_freev(lh_value vchannel);
#define using_channel(name) channel_t* name = channel_alloc(-1); defer(&channel_freev,lh_value_ptr(name))

uv_errno_t    channel_emit(channel_t* channel, lh_value data, lh_value arg, int err);
int           channel_receive(channel_t* channel, lh_value* data, lh_value* arg);
bool          channel_is_full(channel_t* channel);


uv_errno_t asyncx_write_buf(nodec_stream_t* s, uv_buf_t buf);
uv_errno_t asyncx_read_into(nodec_bstream_t* s, uv_buf_t buf, size_t* nread);

// ---------------------------------------------------------------------------------
// Internal stream functions
// Needed to define custom streams.
// ---------------------------------------------------------------------------------

typedef void     (nodec_stream_free_fun)(nodec_stream_t* stream);
typedef void     (async_shutdown_fun)(nodec_stream_t* stream);
typedef uv_buf_t (async_read_bufx_fun)(nodec_stream_t* stream, bool* buf_owned);
typedef void     (async_write_bufs_fun)(nodec_stream_t* stream, uv_buf_t bufs[], size_t count);

struct _nodec_stream_t {
  async_read_bufx_fun*    read_bufx;
  async_write_bufs_fun*   write_bufs;
  async_shutdown_fun*     shutdown;
  nodec_stream_free_fun*  stream_free;
};

void nodec_stream_init(nodec_stream_t* stream,
  async_read_bufx_fun*  read_bufx,
  async_write_bufs_fun* write_bufs,
  async_shutdown_fun*   shutdown,
  nodec_stream_free_fun* stream_free);
void nodec_stream_release(nodec_stream_t* stream);

typedef struct _chunk_t {
  struct _chunk_t* next;
  uv_buf_t     buf;
} chunk_t;

typedef struct _chunks_t {
  chunk_t*     first;
  chunk_t*     last;
  size_t       available;
} chunks_t;


typedef enum _nodec_chunk_read_t {
  CREAD_NORMAL,
  CREAD_EVEN_IF_AVAILABLE,
  CREAD_TO_EOF
} nodec_chunk_read_t;

typedef void (nodec_pushback_buf_fun)(nodec_bstream_t* bstream, uv_buf_t buf);
typedef bool (async_read_chunk_fun)(nodec_bstream_t* bstream, nodec_chunk_read_t mode, size_t read_eof_max);

struct _nodec_bstream_t {
  nodec_stream_t          stream_t;
  async_read_chunk_fun*   read_chunk;
  nodec_pushback_buf_fun* pushback_buf;
  chunks_t                chunks;
  nodec_stream_t*         source;
};

void nodec_bstream_init(nodec_bstream_t* bstream,
  async_read_chunk_fun* read_chunk,
  nodec_pushback_buf_fun* pushback_buf,
  async_read_bufx_fun*  read_bufx,
  async_write_bufs_fun* write_bufs,
  async_shutdown_fun*   shutdown,
  nodec_stream_free_fun* stream_free);

void nodec_bstream_release(nodec_bstream_t* bstream);

void   nodec_chunks_pushback_buf(nodec_bstream_t* bstream, uv_buf_t buf);
void   nodec_chunks_push(nodec_bstream_t* bstream, uv_buf_t buf);
size_t nodec_chunks_available(nodec_bstream_t* bstream);
uv_buf_t nodec_chunks_read_buf(nodec_bstream_t* bstream);


// ---------------------------------------------------------------------------------
// Internal streams over uv_stream_t
// ---------------------------------------------------------------------------------

typedef struct _nodec_uv_stream_t nodec_uv_stream_t;
nodec_bstream_t* as_bstream(nodec_uv_stream_t* stream);

#define using_uv_stream(s) using_bstream(as_bstream(s))


nodec_uv_stream_t* nodec_uv_stream_alloc(uv_stream_t* stream);
void nodec_uv_stream_read_start(nodec_uv_stream_t* rs, size_t alloc_init, size_t alloc_max);
void nodec_uv_stream_read_restart(nodec_uv_stream_t* rs);
void nodec_uv_stream_read_stop(nodec_uv_stream_t* rs);

// Used to implement keep-alive in tcp.c
uv_errno_t asyncx_uv_stream_await_available(nodec_uv_stream_t* stream, int64_t timeout);


typedef struct _tcp_connection_args {
  nodec_tcp_connection_fun_t* connection_fun;
  int                 id;
  nodec_bstream_t*    client;
  lh_value            arg;
  uint64_t            timeout_total;
  uint64_t            timeout_keepalive;
  lh_actionfun*       on_exn;
  nodec_uv_stream_t*  uvclient;
} tcp_connection_args;

typedef void (nodec_tcp_connection_wrap_t)(const tcp_connection_args* args, lh_value arg );

void nodec_tcp_connection_wrap(const tcp_connection_args* args, lh_value ignored_arg);

void async_tcp_server_at_ex(const struct sockaddr* addr,
  tcp_server_config_t* config,
  nodec_tcp_connection_fun_t* servefun,
  nodec_tcp_connection_wrap_t* wrapfun,
  lh_actionfun* on_exn,
  lh_value serve_arg,
  lh_value wrap_arg);

void nodec_http_serve(int id, nodec_bstream_t* client, lh_value servefunv);

#endif

<|start_filename|>src/tls-mbedtls.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-internal.h"
#include "nodec-primitive.h"
#include <assert.h>

#ifdef USE_MBEDTLS

#include <mbedtls/include/mbedtls/ssl.h>
#include <mbedtls/include/mbedtls/net.h>
#include <mbedtls/include/mbedtls/error.h>
#include <mbedtls/include/mbedtls/entropy.h>
#include <mbedtls/include/mbedtls/ctr_drbg.h>
#include <mbedtls/include/mbedtls/debug.h>



struct _nodec_ssl_config_t {
  mbedtls_ssl_config        mbedtls_config;
  // the rest of the fields are just there to free automatically 
  // with the configuration
  mbedtls_ctr_drbg_context* drbg;
  mbedtls_entropy_context*  entropy;
  mbedtls_x509_crt*         chain;
  mbedtls_pk_context*       private_key;
};


/* ----------------------------------------------------------------------------
  TLS utility
-----------------------------------------------------------------------------*/

static void nodec_throw_tls(int mbedtls_err, const char* msg) {
  nodec_throw_msg( nodec_error_from(mbedtls_err,ERRKIND_MBEDTLS), msg);
}

static void debug_callback(void* ctx, int level, const char* file, int line, const char* str) {
  FILE* stream = (FILE*)ctx;
  fprintf(stream, "%s:%04d: %s", file, line, str);
  fflush(stream);
}


/* ----------------------------------------------------------------------------
  TLS streams
-----------------------------------------------------------------------------*/

struct _nodec_tls_stream_t {
  nodec_bstream_t     bstream;
  mbedtls_ssl_context ssl;
  nodec_bstream_t*    source;
  bool                handshaked;
  size_t              read_chunk_size;
};

void nodec_tls_stream_handshake(nodec_tls_stream_t* ts) {
  if (ts->handshaked) return;
  int res = mbedtls_ssl_handshake(&ts->ssl);
  if (res != 0) nodec_throw_tls(res, "unable to perform SSL handshake");
  ts->handshaked = true;
}

static bool async_tls_stream_read_chunks(nodec_tls_stream_t* ts)
{
  uv_buf_t buf = nodec_buf_alloc(ts->read_chunk_size);
  int res = mbedtls_ssl_read(&ts->ssl, (unsigned char*)buf.base, (size_t)buf.len);
  if (res <= 0) {
    // free the buffer and potentially throw
    nodec_buf_free(buf);
    if (res == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY || res == MBEDTLS_ERR_NET_CONN_RESET) {
      // ok, just closed
      res = 0;  // make it like eof
    }
    else if (res < 0) {
      nodec_throw_tls(res, "unable to read from SSL stream");
    }
  }
  else {
    // push into the read chunks
    buf = nodec_buf_fit(buf, res); // TODO: should we do this?
    nodec_chunks_push(&ts->bstream, buf);
  }
  return (res == 0 || res < (size_t)buf.len);
}

static uv_buf_t async_tls_stream_read_bufx(nodec_stream_t* stream, bool* owned) {
  nodec_tls_stream_t* ts = (nodec_tls_stream_t*)stream;
  if (owned != NULL) *owned = true;
  if (nodec_chunks_available(&ts->bstream) == 0) {
    async_tls_stream_read_chunks(ts);
  }
  // hopefully by now we have available chunks
  return nodec_chunks_read_buf(&ts->bstream);
}

static bool async_tls_stream_read_chunk(nodec_bstream_t* s, nodec_chunk_read_t read_mode, size_t read_to_eof_max) {
  nodec_tls_stream_t* ts = (nodec_tls_stream_t*)s;
  if (read_mode == CREAD_NORMAL && nodec_chunks_available(&ts->bstream) > 0) {
    return false;
  }
  else {
    bool eof = false;
    if (read_mode != CREAD_TO_EOF) read_to_eof_max = 0;
    do {
      eof = async_tls_stream_read_chunks(ts);
    } while (!eof && nodec_chunks_available(&ts->bstream) < read_to_eof_max);
    return eof;
  }
}

static void async_tls_stream_write_buf(nodec_tls_stream_t* ts, uv_buf_t src) {
  size_t nwritten = 0;
  while (nwritten < (size_t)src.len) {
    int res = mbedtls_ssl_write(&ts->ssl, (const unsigned char*)src.base + nwritten, (size_t)src.len - nwritten);
    if (res == 0) {
      // todo: or throw broken pipe?
      break;
    }
    else if (res < 0) {
      nodec_throw_tls(res, "unable to write to SSL stream");
      break; // paranoia
    }
    else {
      nwritten += res;
    }
  }  
}

static void async_tls_stream_write_bufs(nodec_stream_t* s, uv_buf_t bufs[], size_t buf_count) {
  nodec_tls_stream_t* ts = (nodec_tls_stream_t*)s;
  for (size_t i = 0; i < buf_count; i++) {
    uv_buf_t src = bufs[i];
    if (nodec_buf_is_null(src)) continue;
    async_tls_stream_write_buf(ts, src);
  }
}

static void async_tls_stream_shutdown(nodec_stream_t* stream) {
  nodec_tls_stream_t* ts = (nodec_tls_stream_t*)stream;
  mbedtls_ssl_close_notify(&ts->ssl);
  //mbedtls_ssl_session_reset(&ts->ssl);
  async_shutdown(as_stream(ts->source));
}

static void nodec_tls_stream_free(nodec_stream_t* stream) {
  nodec_tls_stream_t* ts = (nodec_tls_stream_t*)stream;
  mbedtls_ssl_free(&ts->ssl);
  //nodec_stream_free(as_stream(ts->source));
  nodec_free(ts);
}

/* ----------------------------------------------------------------------------
   Basic reading and writing for the TLS library
-----------------------------------------------------------------------------*/

static int tls_stream_net_send(void* sv, const unsigned char* buf, size_t len) {
  nodec_bstream_t* s = (nodec_bstream_t*)sv;
  uv_errno_t err = asyncx_write_buf(as_stream(s), nodec_buf(buf, len));
  if (err != 0) return (err < 0 ? err : UV_EINVAL);
  return (int)len;
}


static int tls_stream_net_recv(void* sv, unsigned char* buf, size_t len) {
  nodec_bstream_t* s = (nodec_bstream_t*)sv;
  size_t nread = 0;
  uv_errno_t err = asyncx_read_into(s, nodec_buf(buf, len), &nread);
  if (err != 0) return (err < 0 ? err : UV_EINVAL);
    return (int)nread;
}

nodec_bstream_t* nodec_tls_stream_alloc(nodec_bstream_t* stream, const nodec_ssl_config_t* config) {
  int res = 0;
  nodec_tls_stream_t* ts = nodecx_zero_alloc(nodec_tls_stream_t);
  if (ts == NULL) { res = UV_ENOMEM; goto err; }
  nodec_bstream_init(&ts->bstream,
    &async_tls_stream_read_chunk, &nodec_chunks_pushback_buf,
    &async_tls_stream_read_bufx, &async_tls_stream_write_bufs,
    &async_tls_stream_shutdown, &nodec_tls_stream_free);
  ts->source = stream;
  ts->read_chunk_size = 14 * NODEC_KB;
  mbedtls_ssl_init(&ts->ssl);
  if (mbedtls_ssl_setup(&ts->ssl, &config->mbedtls_config) != 0) { res = UV_ENOMEM;  goto err; }
  mbedtls_ssl_set_bio(&ts->ssl, stream, tls_stream_net_send, tls_stream_net_recv, NULL);
  return &ts->bstream;
err:
  if (ts != NULL) nodec_free(ts);
  if (stream != NULL) nodec_stream_free(as_stream(stream));
  nodec_check(res);
  return NULL;
}

/* ----------------------------------------------------------------------------
  TLS configurations
-----------------------------------------------------------------------------*/

void nodec_ssl_config_free(nodec_ssl_config_t* config) {
  if (config == NULL) return;
  if (config->chain != NULL) {
    mbedtls_x509_crt_free(config->chain);
    nodec_free(config->chain);
  }
  if (config->private_key != NULL) {
    mbedtls_pk_free(config->private_key);
    nodec_free(config->private_key);
  }
  if (config->drbg != NULL) {
    mbedtls_ctr_drbg_free(config->drbg);
    nodec_free(config->drbg);
  }
  if (config->entropy != NULL) {
    (config->entropy);
    nodec_free(config->entropy);
  }
  mbedtls_ssl_config_free(&config->mbedtls_config);
  nodec_free(config);
}

void nodec_ssl_config_freev(lh_value configv) {
  nodec_ssl_config_t* config = (nodec_ssl_config_t*)lh_ptr_value(configv);
  nodec_ssl_config_free(config);
}

nodec_ssl_config_t* nodec_ssl_config_server(uv_buf_t cert, uv_buf_t key, const char* password ) {
  nodec_ssl_config_t* config = nodec_zero_alloc(nodec_ssl_config_t);
  {on_abort(nodec_ssl_config_freev, lh_value_any_ptr(config)) {
    mbedtls_ssl_config_init(&config->mbedtls_config);
    int res = mbedtls_ssl_config_defaults(&config->mbedtls_config, 
      MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
    if (res != 0) nodec_throw_tls(res, "cannot configure TLS" );
    mbedtls_ssl_conf_dbg(&config->mbedtls_config, &debug_callback, stderr);
    mbedtls_debug_set_threshold(1);

    // init random numbers
    config->drbg = nodec_alloc(mbedtls_ctr_drbg_context);
    mbedtls_ctr_drbg_init(config->drbg);
    config->entropy = nodec_alloc(mbedtls_entropy_context);
    mbedtls_entropy_init(config->entropy);
    res = mbedtls_ctr_drbg_seed(config->drbg, &mbedtls_entropy_func, config->entropy, "", 0);
    if (res != 0) nodec_throw_tls(res, "cannot initialize random number generator");
    mbedtls_ssl_conf_rng(&config->mbedtls_config, mbedtls_ctr_drbg_random, config->drbg);

    // set our own certificate
    config->chain = nodec_alloc(mbedtls_x509_crt);
    mbedtls_x509_crt_init(config->chain);
    res = mbedtls_x509_crt_parse(config->chain, (const unsigned char*)cert.base, (size_t)cert.len + 1); // include zero byte at end
    if (res != 0) nodec_throw_tls(res, "cannot parse x509 certificate");
    config->private_key = nodec_alloc(mbedtls_pk_context);
    mbedtls_pk_init(config->private_key);
    res = mbedtls_pk_parse_key(config->private_key, (const unsigned char*)key.base, (size_t)key.len + 1, (const unsigned char*)password, strlen(password));
    if (res != 0) nodec_throw_tls(res, "invalid password");
    res = mbedtls_ssl_conf_own_cert(&config->mbedtls_config, config->chain, config->private_key);
    if (res != 0) nodec_throw_tls(res, "cannot set server certificate");

    // TODO: enable session tickets? rfc5070
  }}
  return config;
}

nodec_ssl_config_t* nodec_ssl_config_server_from(const char* cert_path, const char* key_path, const char* password) {
  nodec_ssl_config_t* config = NULL;
  uv_buf_t cert = async_fs_read_buf_from(cert_path);
  {using_buf(&cert) {
    uv_buf_t key = async_fs_read_buf_from(key_path);
    {using_buf(&key) {
      config = nodec_ssl_config_server(cert, key, password);
    }}
  }}
  return config;       
}


nodec_ssl_config_t* nodec_ssl_config_client() {
  nodec_ssl_config_t* config = nodec_zero_alloc(nodec_ssl_config_t);
  {on_abort(nodec_ssl_config_freev, lh_value_any_ptr(config)) {
    mbedtls_ssl_config_init(&config->mbedtls_config);
    int res = mbedtls_ssl_config_defaults(&config->mbedtls_config,
      MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
    if (res != 0) nodec_throw_tls(res, "cannot configure client TLS");
    mbedtls_ssl_conf_dbg(&config->mbedtls_config, &debug_callback, stderr);
    mbedtls_debug_set_threshold(1);

    // init random numbers
    config->drbg = nodec_alloc(mbedtls_ctr_drbg_context);
    mbedtls_ctr_drbg_init(config->drbg);
    config->entropy = nodec_alloc(mbedtls_entropy_context);
    mbedtls_entropy_init(config->entropy);
    static const char* client_seed = "nodec_client";
    res = mbedtls_ctr_drbg_seed(config->drbg, &mbedtls_entropy_func, config->entropy, client_seed, strlen(client_seed));
    if (res != 0) nodec_throw_tls(res, "cannot initialize random number generator");
    mbedtls_ssl_conf_rng(&config->mbedtls_config, mbedtls_ctr_drbg_random, config->drbg);

    // initialize certificate chain
    config->chain = nodec_alloc(mbedtls_x509_crt);
    mbedtls_x509_crt_init(config->chain);
    mbedtls_ssl_conf_ca_chain(&config->mbedtls_config, config->chain, NULL);
    
    // TODO: enable session tickets? rfc5070
  }}
  return config;
}


nodec_errno_t nodecx_ssl_config_add_ca(nodec_ssl_config_t* config, uv_buf_t cert) {
  int res = mbedtls_x509_crt_parse(config->chain, (const unsigned char*)cert.base, (size_t)cert.len);
  return (res == 0 ? 0 : nodec_error_from(res, ERRKIND_MBEDTLS));
}

void async_ssl_config_add_system_certs(nodec_ssl_config_t* config) {
#ifdef _WIN32
#pragma comment(lib, "Crypt32.lib")
  HCERTSTORE store;
  PCCERT_CONTEXT cert;
  if ((store = CertOpenSystemStore(0, L"ROOT")) != NULL) {
    cert = NULL;
    while ((cert = CertEnumCertificatesInStore(store, cert)) != NULL) {
      if (cert->dwCertEncodingType == X509_ASN_ENCODING &&
        cert->pbCertEncoded != NULL && cert->cbCertEncoded > 0)
      {
        uv_buf_t buf = nodec_buf(cert->pbCertEncoded, cert->cbCertEncoded);
        nodecx_ssl_config_add_ca(config, buf);
      }
    }
    CertCloseStore(store, 0);
  }
#else
// TODO
#endif
}


#endif // USE_MBEDTLS

<|start_filename|>docs/api/group__effect__exn_structlh__exception.js<|end_filename|>
var group__effect__exn_structlh__exception =
[
    [ "_is_alloced", "group__effect__exn.html#a42489ecd1468adadd6bd9c8d6fc7850e", null ],
    [ "code", "group__effect__exn.html#a2f415f8397a6e3e497814e50fbb3eb35", null ],
    [ "data", "group__effect__exn.html#a9a780c96e58c551826cb9d7055e0e939", null ],
    [ "msg", "group__effect__exn.html#a400106a97a1ab0617f39aa7a4e8618b0", null ]
];

<|start_filename|>Makefile<|end_filename|>
#-------------------------------------------------------------------------
#  Copyright 2016, <NAME>.
#-------------------------------------------------------------------------

.PHONY : clean dist init tests staticlib main

include out/makefile.config

ifndef $(VARIANT)
VARIANT=debug
endif

# NodeC library
MAIN       = nodec
CONFIGDIR  = out/$(CONFIG)
OUTDIR 		 = $(CONFIGDIR)/$(MAIN)/$(VARIANT)
INCLUDES   = -Iinc -I$(CONFIGDIR) -Ideps -Ideps/libuv/include

# nodecx library: The ..X variants are for clients
# nodecx includes all needed static libraries (libuv,libz etc) and uses
# a single include path. All under "out/nodecx/$(VARIANT)"
MAINX      = $(MAIN)x
OUTDIRX    = $(CONFIGDIR)/$(MAINX)/$(VARIANT)
INCLUDESX  = -I$(OUTDIRX)/include

ifeq ($(VARIANT),release)
CCFLAGS    = $(CCFLAGSOPT) -DNDEBUG $(INCLUDES)
CCFLAGSX   = $(CCFLAGSOPT) -DNDEBUG $(INCLUDESX)
else ifeq ($(VARIANT),testopt)
CCFLAGS    = $(CCFLAGSOPT) $(INCLUDES)
CCFLAGSX   = $(CCFLAGSOPT) $(INCLUDESX)
else ifeq ($(VARIANT),debug)
CCFLAGS    = $(CCFLAGSDEBUG) $(INCLUDES)
CCFLAGSX   = $(CCFLAGSDEBUG) $(INCLUDESX)
else
VARIANTUNKNOWN=1
endif

# Use VALGRIND=1 to memory check under valgrind
ifeq ($(VALGRIND),1)
VALGRINDX=yes
else ifeq ($(VALGRIND),yes)
VALGRINDX=yes
else
VALGRINDX=
endif

ifeq ($(VALGRINDX),yes)
VALGRINDX=valgrind --leak-check=full --show-leak-kinds=all --suppressions=./valgrind.supp
endif

# Uncomment to generate assembly for nodec
# SHOWASM    = -Wa,-aln=$@.s

# -------------------------------------
# Sources
# -------------------------------------

SRCFILES = async.c interleave.c channel.c memory.c \
					 dns.c fs.c stream.c tcp.c timer.c tty.c log.c \
           http.c http_request.c http_static.c http_url.c  mime.c\
					 https.c tls-mbedtls.c

CEXAMPLES= main.c \
           ex-http-connect.c ex-http-server-static.c \
           ex-https-connect.c ex-https-server-static.c \
					 ex-fs-search.c

TESTFILES= main.c

BENCHFILES=


SRCS     = $(patsubst %,src/%,$(SRCFILES)) $(patsubst %,src/%,$(ASMFILES)) deps/http-parser/http_parser.c
OBJS  	 = $(patsubst %.c,$(OUTDIR)/%$(OBJ), $(SRCFILES)) $(patsubst %$(ASM),$(OUTDIR)/%$(OBJ),$(ASMFILES)) $(OUTDIR)/http_parser$(OBJ)
NLIB     = $(OUTDIR)/lib$(MAIN)$(LIB)
NLIBX    = $(OUTDIRX)/lib/lib$(MAINX)$(LIB)

# link statically
LIBS     =  $(NLIBX)

DEPSLIBS = deps/libhandler/out/$(CONFIG)/$(VARIANT)/libhandler.a deps/libuv/out/lib/libuv.a deps/zlib/out/lib/libz.a \
					 deps/mbedtls/library/libmbedcrypto.a deps/mbedtls/library/libmbedx509.a deps/mbedtls/library/libmbedtls.a

# for libuv
EXTRA-LIBS= -lrt -lpthread -lnsl -ldl

TESTSRCS = $(patsubst %,test/%,$(TESTFILES))
TESTMAIN = $(OUTDIR)/nodec-tests$(EXE)

BENCHSRCS= $(patsubst %,test/%,$(BENCHFILES))
BENCHMAIN= $(OUTDIR)/nodec-bench$(EXE)

EXAMPLEOUT = $(CONFIGDIR)/nodec-examples/$(VARIANT)
EXAMPLESRCS= $(patsubst %,examples/%,$(CEXAMPLES))
EXAMPLEMAIN= $(EXAMPLEOUT)/nodec-examples$(EXE)

# -------------------------------------
# Main targets
# -------------------------------------

main: init staticlib

examples: init staticlib examplemain
		@echo ""
		@echo "run example"
		$(EXAMPLEMAIN)


tests: init staticlib testmain
	@echo ""
	@echo "run tests"
	$(VALGRINDX) $(TESTMAIN)

bench: init staticlib benchmain
	@echo ""
	@echo "run benchmark"
	$(BENCHMAIN)


# -------------------------------------
# build example
# -------------------------------------

examplemain: $(EXAMPLEMAIN)

$(EXAMPLEMAIN): $(EXAMPLESRCS) $(NLIBX)
	$(CC) $(CCFLAGSX)  $(LINKFLAGOUT)$@ $(EXAMPLESRCS) $(LIBS) $(EXTRA-LIBS)


# -------------------------------------
# build tests
# -------------------------------------

testmain: $(TESTMAIN)

$(TESTMAIN): $(TESTSRCS) $(NLIBX)
	$(CC) $(CCFLAGSX)  $(LINKFLAGOUT)$@ $(TESTSRCS) $(LIBS) $(EXTRA-LIBS)




# -------------------------------------
# build benchmark
# -------------------------------------

benchmain: $(BENCHMAIN)

$(BENCHMAIN): $(BENCHSRCS) $(NLIB)
	$(CC) $(CCFLAGSX) $(LINKFLAGOUT)$@  $(BENCHSRCS) $(LIBS) -lm $(EXTRA-LIBS)



# -------------------------------------
# build the static library
# -------------------------------------

staticlib: init $(NLIBX)

$(NLIBX): $(NLIB) $(DEPSLIBS)
	@if test -d "$(OUTDIRX)/lib"; then :; else $(MKDIR) "$(OUTDIRX)/lib"; fi
	./libmerge.sh $(OUTDIRX)/lib lib$(MAINX).a $^
	@if test -d "$(OUTDIRX)/include/libhandler/inc"; then :; else $(MKDIR) "$(OUTDIRX)/include/libhandler/inc"; fi
	@if test -d "$(OUTDIRX)/include/libuv/include"; then :; else $(MKDIR) "$(OUTDIRX)/include/libuv/include"; fi
	@if test -d "$(OUTDIRX)/include/uv"; then :; else $(MKDIR) "$(OUTDIRX)/include/uv"; fi
	@if test -d "$(OUTDIRX)/include/http-parser"; then :; else $(MKDIR) "$(OUTDIRX)/include/http-parser"; fi
	$(CP) -f deps/libhandler/inc/libhandler.h    $(OUTDIRX)/include/libhandler/inc
	$(CP) -f deps/http-parser/http_parser.h $(OUTDIRX)/include/http-parser
	$(CP) -f deps/libuv/out/include/uv.h $(OUTDIRX)/include/libuv/include/uv.h
	$(CP) -fa deps/libuv/out/include/uv/* $(OUTDIRX)/include/uv
	$(CP) -f inc/nodec.h $(OUTDIRX)/include
	$(CP) -f inc/nodec-primitive.h $(OUTDIRX)/include

$(NLIB): $(OBJS)
	$(AR) $(ARFLAGS)  $(ARFLAGOUT)$@ $(OBJS)

$(OUTDIR)/%$(OBJ): src/%.c
	$(CC) $(CCFLAGS) $(CCFLAG99) $(CCFLAGOUT)$@ -c $< $(SHOWASM)

$(OUTDIR)/%$(OBJ): src/%$(ASM)
	$(CC) $(ASMFLAGS)  $(ASMFLAGOUT)$@ -c $<

$(OUTDIR)/http_parser$(OBJ): deps/http-parser/http_parser.c
	$(CC) $(CCFLAGS) $(CCFLAG99) $(CCFLAGOUT)$@ -c $< $(SHOWASM)


$(EXAMPLEOUT)/%$(OBJ): examples/%.c
		$(CC) $(CCFLAGS) $(CCFLAG99) $(CCFLAGOUT)$@ -c $< $(SHOWASM)


# -------------------------------------
# other targets
# -------------------------------------

docs:

clean:
	rm -rf $(CONFIGDIR)/*/*
	touch $(CONFIGDIR)/makefile.depend

init:
	@echo "use 'make help' for help"
	@echo "build variant: $(VARIANT), configuration: $(CONFIG)"
	@if test "$(VARIANTUNKNOWN)" = "1"; then echo ""; echo "Error: unknown build variant: $(VARIANT)"; echo "Use one of 'debug', 'release', or 'testopt'"; false; fi
	@if test -d "$(OUTDIR)/asm"; then :; else $(MKDIR) "$(OUTDIR)/asm"; fi
	@if test -d "$(OUTDIRX)"; then :; else $(MKDIR) "$(OUTDIRX)"; $(MKDIR) "$(OUTDIRX)/lib"; $(MKDIR) "$(OUTDIRX)/include"; fi
	@if test -d "$(EXAMPLEOUT)"; then :; else $(MKDIR) "$(EXAMPLEOUT)"; fi


help:
	@echo "Usage: make <target>"
	@echo "Or   : make VARIANT=<variant> <target>"
	@echo "Or   : make VALGRIND=1 tests"
	@echo ""
	@echo "Variants:"
	@echo "  debug       : Build a debug version (default)"
	@echo "  testopt     : Build an optimized version but with assertions"
	@echo "  release     : Build an optimized release version"
	@echo ""
	@echo "Targets:"
	@echo "  main        : Build a static library (default)"
	@echo "  tests       : Run tests"
	@echo "  mainxx      : Build a static library for C++"
	@echo "  testsxx     : Run tests for C++"
	@echo "  bench       : Run benchmarks, use 'VARIANT=release'"
	@echo "  clean       : Clean output directory"
	@echo "  depend      : Generate dependencies"
	@echo ""
	@echo "Configuration:"
	@echo "  output dir  : $(OUTDIR)"
	@echo "  c-compiler  : $(CC) $(CCFLAGS)"
	@echo ""

# dependencies
# [gcc -MM] generates the dependencies without the full
# directory name, ie.
#  evaluator.o: ...
# instead of
#  core/evaluator.o: ..
# we therefore use [sed] to append the directory name
depend: init
	$(CCDEPEND) $(INCLUDES) src/*.c > $(CONFIGDIR)/temp.depend
	sed -e "s|\(.*\.o\)|$(CONFIGDIR)/\$$(VARIANT)/\1|g" $(CONFIGDIR)/temp.depend > $(CONFIGDIR)/makefile.depend
	$(CCDEPEND) $(INCLUDES) test/*.c > $(CONFIGDIR)/temp.depend
	sed -e "s|\(.*\.o\)|$(CONFIGDIR)/\$$(VARIANT)/\1|g" $(CONFIGDIR)/temp.depend >> $(CONFIGDIR)/makefile.depend
	$(RM) $(CONFIGDIR)/temp.depend

include $(CONFIGDIR)/makefile.depend


<|start_filename|>docs/api/navtreeindex1.js<|end_filename|>
var NAVTREEINDEX1 =
{
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bac3d4da4de851d5c8f95748145b59716a":[0,9,0,1,33],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bac5f77dc9692876a7f19cabdc12d42c5d":[0,9,0,1,54],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bac96829d2c2cb76feb1549f0fac72c69e":[0,9,0,1,50],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bac9c5b4e80aa858cfe2763656db1f16e3":[0,9,0,1,49],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bacc4bfe6b3babd391f70e3c72ea7b5013":[0,9,0,1,40],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bace45e0a70d239d5883e023655d9c381c":[0,9,0,1,13],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bad000a2e30c534c201201dd74fac8d2f9":[0,9,0,1,7],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bad34cd21de350cd4fa83b8099e3993b91":[0,9,0,1,3],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bad397c30fb0f937e965c1f23e19763a5c":[0,9,0,1,0],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bad771b2a0ab88db11b2719c8e5086fb48":[0,9,0,1,22],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151badd02813da14cfdc7fe83029b8779ea4b":[0,9,0,1,53],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bae301c12d0cf56920659cb7b947a95267":[0,9,0,1,16],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151baec5b1bd4cc762efd4aeb2ab00fb7a2ae":[0,9,0,1,58],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151baefdfc7b525c87b911d6e92a30e36cfec":[0,9,0,1,52],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151baf06c31278cb67d7eec4b2b8157b9ad25":[0,9,0,1,25],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151baf60c8de8de4eb8fe02562aff8ee96f1f":[0,9,0,1,11],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bafac24097912a70f224166528ce44b83b":[0,9,0,1,23],
"group__log.html":[0,1],
"group__log.html#ga397b9a049d0edb35a8c82b5e31908b2e":[0,1,0],
"group__nodec__alloc.html":[0,12],
"group__nodec__alloc.html#ga0d6776c7ec091d6230510370d65be21e":[0,12,1],
"group__nodec__alloc.html#ga12562044b20322d894cfe686eef25c15":[0,12,2],
"group__nodec__alloc.html#ga93ffdec72fa29b5f091e428cbd4245a9":[0,12,0],
"group__nodec__async.html":[0,2],
"group__nodec__async.html#ga09a47279d849d24f2c113dc725e33791":[0,2,3],
"group__nodec__async.html#ga29149a2bf87759864793e9cfe65f1354":[0,2,1],
"group__nodec__async.html#ga38aa41fe213fe1f727c553e2c15ae0f2":[0,2,8],
"group__nodec__async.html#ga8c7468880da0b583535f0a2c37b2d94b":[0,2,0],
"group__nodec__async.html#ga973b4300618f50aff84777252c3c92bb":[0,2,2],
"group__nodec__async.html#ga9c9746a92cec7b3e7d03589a77b81e90":[0,2,5],
"group__nodec__async.html#gaa9a1df94b296d11d2e32a36727c54dca":[0,2,6],
"group__nodec__async.html#gab3b0f512ea7973c4c49b2f84991f8b71":[0,2,10],
"group__nodec__async.html#gab8d5d41174e20252ff88e131520aa887":[0,2,4],
"group__nodec__async.html#gacd5465dcbbc0318d617e622c09fdc4a4":[0,2,11],
"group__nodec__async.html#gaed5490358b60ef9e9a0acbe1587e176d":[0,2,7],
"group__nodec__async.html#gaf6699450ad5b09e545266361c2ea353f":[0,2,12],
"group__nodec__async.html#gafc630d8de8ba70b0988f45c2f1b6132a":[0,2,9],
"group__nodec__fs.html":[0,3],
"group__nodec__fs.html#a05b9207a1a204eae78b2c7dc0d3812ab":[0,3,1,12],
"group__nodec__fs.html#a1442738c45923c0a23f7ee0c2837f584":[0,3,1,9],
"group__nodec__fs.html#a14bc9565a316b65e422dde506d4c6428":[0,3,1,6],
"group__nodec__fs.html#a198c4352c514e9eff06e7363c3562a8f":[0,3,1,10],
"group__nodec__fs.html#a324b6bb9a3dcb70259e0195379c5f9ef":[0,3,1,1],
"group__nodec__fs.html#a454ac6f7cef9a00d73baf2028fe3a7cb":[0,3,1,14],
"group__nodec__fs.html#a6c1e6a2cd2d9f10dc4df27ab3a79bc9d":[0,3,0,1],
"group__nodec__fs.html#a7343234f6f445689c599b147eeac9658":[0,3,2,1],
"group__nodec__fs.html#a7785fcfd6909d22b07f8461219ca2133":[0,3,1,4],
"group__nodec__fs.html#a7cb4ce091e3b7418f368451dd60e535e":[0,3,1,3],
"group__nodec__fs.html#a89f339b980cacb16da47d7a7c04f7e8a":[0,3,1,11],
"group__nodec__fs.html#a90374cf48291ecde29394d1982b9eec8":[0,3,1,0],
"group__nodec__fs.html#aa6d1907b84accf5662140cb0834e479d":[0,3,1,8],
"group__nodec__fs.html#ab5036343ef8adaabecc2362f5905b9cb":[0,3,1,7],
"group__nodec__fs.html#ac1f0764990fe34d0b21f7e3af9fef946":[0,3,1,5],
"group__nodec__fs.html#ac56d6239041749e7bc4ccc472aa800b1":[0,3,1,15],
"group__nodec__fs.html#ac7ce6fb281bd218f7821f6c52a63a737":[0,3,2,0],
"group__nodec__fs.html#af0cc8e07491b42cd8b82d797ec5af0b4":[0,3,1,13],
"group__nodec__fs.html#af1552c618f27286c9734be801d0ecf3f":[0,3,0,0],
"group__nodec__fs.html#af549bc8ae4793b83fa25b97fc5518e67":[0,3,1,2],
"group__nodec__fs.html#ga02e4f8c156fb631d167cd0488ae7d5cf":[0,3,16],
"group__nodec__fs.html#ga0c35c148bf40627f47bc6c2eeb19ea77":[0,3,9],
"group__nodec__fs.html#ga1e1887c16a425c58545788efb5b7cd9b":[0,3,19],
"group__nodec__fs.html#ga24a7a0ceb1e8f5f44cb1038bb829030d":[0,3,3],
"group__nodec__fs.html#ga2f19335f15eaf41304f16e195ae8eba2":[0,3,26],
"group__nodec__fs.html#ga340bba31d68b120ee5416378d133d4e3":[0,3,7],
"group__nodec__fs.html#ga3937ef3449947843429fdc056ed7031c":[0,3,23],
"group__nodec__fs.html#ga3cc87b8f36d127ef984319f00e76a40c":[0,3,20],
"group__nodec__fs.html#ga3ebae44242383c0a5a16b42641e2230a":[0,3,22],
"group__nodec__fs.html#ga5e995ef7690698b62bf016b14fbc45ca":[0,3,6],
"group__nodec__fs.html#ga6106956895e925e621b1ddfb478d3091":[0,3,10],
"group__nodec__fs.html#ga61fb79bbfca6d44d44f575ace13a53e1":[0,3,8],
"group__nodec__fs.html#ga6dd6912567933b893bc5d548f7c93b6e":[0,3,4],
"group__nodec__fs.html#ga87628a7a27ae3ad2f0605e9cace40f9c":[0,3,24],
"group__nodec__fs.html#ga8c16a77ce261f6b8e012fe83cb15c58d":[0,3,13],
"group__nodec__fs.html#ga8d7a8b33060538966cd96341b3f50f7f":[0,3,12],
"group__nodec__fs.html#ga97387ae4fd2fddfcaf964ab3941bd184":[0,3,15],
"group__nodec__fs.html#gaa6fc903d284054a673cc931e276a848b":[0,3,17],
"group__nodec__fs.html#gab17cb1a692145e20d98e980c343a1335":[0,3,11],
"group__nodec__fs.html#gac2bf74e83604a17f5cef879ec99cb4f6":[0,3,14],
"group__nodec__fs.html#gac2c5469313c7fc23b4578d546ff5dde2":[0,3,25],
"group__nodec__fs.html#gae3b908d2c12c6bb8d2ed8493d69a0e6a":[0,3,18],
"group__nodec__fs.html#gae5acf4043c0903cda7436b108e29e8e6":[0,3,5],
"group__nodec__fs.html#gafa78969b259943998622377b987cff5c":[0,3,21],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a01151e2942507154f8e694a950183720":[0,3,10,1],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a0b92d55ae479bd0f30ea3320470955c7":[0,3,10,7],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a0c5d789e712be5a9a5ead4be23dbb689":[0,3,10,20],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a0d3b834b425d85f5cda06e0a502c7e03":[0,3,10,18],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a0f3915adbc9debeee85b7aea1c0de2fa":[0,3,10,6],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a10938f94907d49f2ef35b72c12b91f78":[0,3,10,16],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a341aae3e5d903898b8a3155c4d4456de":[0,3,10,3],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a396c5ed401245f1d9a07571852db822a":[0,3,10,14],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a55de3a2fc80e58c471437064df836fcd":[0,3,10,12],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a56ce6efd72a88357032a119468975998":[0,3,10,15],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a5e55c321069fbde51d1c73c7b6380db3":[0,3,10,0],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a7b4d0987e0ad7d469157390da427b744":[0,3,10,2],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a8180afffb5f59a3f056015cfcc425730":[0,3,10,8],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a92e37f26361e14c4b682957ed3953d44":[0,3,10,17],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a9d4d3b852ae0eeffd11d3bb2cc05f928":[0,3,10,9],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091aa6ab9adaedf4911b17a998aaf84e2618":[0,3,10,10],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091ab04df9a4b5eab498be39ccbc9ee33793":[0,3,10,11],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091ab7bd494e74106113bccb7159ab5269cc":[0,3,10,5],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091aea8b2813a8dbc3239b75518fac33a782":[0,3,10,4],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091aedd8398c1ed440fa728bf1e9401e70c4":[0,3,10,13],
"group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091af98cfcc223663708b6dbf9c725fd2b72":[0,3,10,19],
"group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335a0fea07d0ee17d3cd9394682eb937be8b":[0,3,11,4],
"group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335a3ab846891d83853555d15168a01d37c1":[0,3,11,6],
"group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335a8328c19ef64ea79b0526b3dae9f3a555":[0,3,11,2],
"group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335a947786154c585ee4d79c61f260565b9b":[0,3,11,0],
"group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335aa070fc602168c3bf34807eba51cd48a2":[0,3,11,7],
"group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335ab72e22fb2ed44083615274b541a16360":[0,3,11,3],
"group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335ad63276ed636f67c2fb72c1876e500d8c":[0,3,11,1],
"group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335ad6adf83f62c4773850963ef9a8fa26de":[0,3,11,5],
"group__nodec__fs.html#structuv__dirent__t":[0,3,2],
"group__nodec__fs.html#structuv__stat__t":[0,3,1],
"group__nodec__fs.html#structuv__timespec__t":[0,3,0],
"group__nodec__https.html":[0,9,4],
"group__nodec__ip.html":[0,5],
"group__nodec__ip.html#ga66bb31de758bc23d65745d0dd68a7858":[0,5,0],
"group__nodec__ip.html#ga6c2bfc89980866ce4b3abc76128ee31a":[0,5,1],
"group__nodec__mime.html":[0,7],
"group__nodec__mime.html#ga3561c055d8e934a49e64105f78dfd4d6":[0,7,3],
"group__nodec__mime.html#ga7b0602fccd1407557b341e4d4a5c808b":[0,7,2],
"group__nodec__mime.html#gaa4dc59d468820d27e2001dfc66b018fa":[0,7,0],
"group__nodec__mime.html#gaccb1fd0c6fa867bb593a6b58cf8756a4":[0,7,1],
"group__nodec__static.html":[0,9,3],
"group__nodec__static.html#a28d756b30046e7e7ee6b88d4dfe3dcce":[0,9,3,0,8],
"group__nodec__static.html#a31d97bb6440fc5992cb56fc84d0725fa":[0,9,3,0,6],
"group__nodec__static.html#a35e38162a485696d0259c3789b690f1d":[0,9,3,0,4],
"group__nodec__static.html#a51e0cb72c8eb93fef2ca61fb599b9a06":[0,9,3,0,1],
"group__nodec__static.html#a6b2390b3780a8912fb6d571aa7a08ba6":[0,9,3,0,5],
"group__nodec__static.html#a80faaf3f2b7b7d3d9a7271af9f7e667c":[0,9,3,0,3],
"group__nodec__static.html#ab77a0887c2beea113ae3a647bd5051ed":[0,9,3,0,7],
"group__nodec__static.html#ae9dca6966cebb3b3dae85e79b561ece8":[0,9,3,0,2],
"group__nodec__static.html#afd394de5b190c3770c6d3f915316c759":[0,9,3,0,0],
"group__nodec__static.html#gaaa8f361e185522a1dea817d83c8d784b":[0,9,3,2],
"group__nodec__static.html#gae8ac7620c8122033e3239beee552339f":[0,9,3,3],
"group__nodec__static.html#gafee4501f26dcb4de2253ee690fcc9cfa":[0,9,3,1],
"group__nodec__static.html#structhttp__static__config__t":[0,9,3,0],
"group__nodec__tty.html":[0,10],
"group__nodec__tty.html#ga09a924d56ad28e210260a0b5a1e1851f":[0,10,4],
"group__nodec__tty.html#ga281ad555b956e2811c1b7e328d5455ad":[0,10,0],
"group__nodec__tty.html#ga309e4777a21bb0f6070bcc902d532081":[0,10,1],
"group__nodec__tty.html#ga81a44fa3f9c9379a67f8e6a66f226beb":[0,10,3],
"group__nodec__tty.html#gaa9ea83fc379a6e3cafbe62cc49281d48":[0,10,2],
"group__nodec__tty.html#gab5cd068957e59c93f9c6651c1c5740d4":[0,10,6],
"group__nodec__tty.html#gabeddbbfc2657a4c39ee218c69d63e664":[0,10,5],
"group__nodec__tty.html#gacd7e806a18f986c9310fa889565647e3":[0,10,7],
"group__nodec__url.html":[0,6],
"group__nodec__url.html#gaa2a37a3b5cc32bd3cb0c7eebe1eb397f":[0,6,0],
"group__nodec__various.html":[0,11],
"group__nodec__various.html#ga12d650b02a11f47f9d14b2267438bde7":[0,11,0],
"group__nodec__various.html#ga1e6a44db408192675d8923ac4ea03ebc":[0,11,11],
"group__nodec__various.html#ga1f6e5d595c708809db28efd4c4506241":[0,11,10],
"group__nodec__various.html#ga21d7c9d7a03822b0263fb3431e7a3e3b":[0,11,12],
"group__nodec__various.html#ga43af10da1f49d2ef6fb7af2118ad5960":[0,11,13],
"group__nodec__various.html#ga50770568a93c37eaa128d15aefb9ff49":[0,11,3],
"group__nodec__various.html#ga52af212bb34613e5079d1ddf3eb870c0":[0,11,8],
"group__nodec__various.html#ga5fd5694004902170bbff8bff8b92ad88":[0,11,6],
"group__nodec__various.html#ga73adc7fccbee908a44707c93620b35b6":[0,11,1],
"group__nodec__various.html#ga8ca7a8eead029327f56e7181199eea4f":[0,11,4],
"group__nodec__various.html#gaa91b31eb8f2fe9557323440b9e1240b7":[0,11,9],
"group__nodec__various.html#gab48824ff3f0fab32ddef8ac00155ab10":[0,11,5],
"group__nodec__various.html#gad8c50b6c480fd1c09e40a5eaa36f87bb":[0,11,2],
"group__nodec__various.html#gaf84590dca27af53d91189e599f524b54":[0,11,7],
"group__streams.html":[0,4],
"group__streams.html#ga025bb7d59c9dae3b4899e0874d08dc68":[0,4,20],
"group__streams.html#ga0d8f8fb4145849530ac2e9c7cdef8ec5":[0,4,6],
"group__streams.html#ga1285146007f649dc4afab2d4c97ed0a8":[0,4,7],
"group__streams.html#ga254f878556059c35448ad927ab982f36":[0,4,21],
"group__streams.html#ga29902a3b1e06b6d1b87e11b12bfbb11a":[0,4,28],
"group__streams.html#ga333329e70821cc682fdeaa333f210260":[0,4,19],
"group__streams.html#ga3371300a9eea0177e792b08bb5b3bf3d":[0,4,0],
"group__streams.html#ga3402e9960bf043d2d361225e18483645":[0,4,22],
"group__streams.html#ga353b56dd825e99a056c7cd6ebfd1635a":[0,4,10],
"group__streams.html#ga3c8beb53a775b37e696fdf5e8e31d6d6":[0,4,9],
"group__streams.html#ga46bab5d4f8f6d45e6eb3587b796683a2":[0,4,25],
"group__streams.html#ga4f53610d2e3294ec02084a8c618828aa":[0,4,18],
"group__streams.html#ga5086fb89c05a1dc0ecf923af3e953403":[0,4,5],
"group__streams.html#ga530b49b85b811a2377209a55b2e670cb":[0,4,17],
"group__streams.html#ga756ccec235ed35716f4c725a948a7943":[0,4,14],
"group__streams.html#ga7691ad253e2f25057450847e697fc210":[0,4,12],
"group__streams.html#ga7a87a30d292c709502440b0d95b9cb0b":[0,4,27],
"group__streams.html#ga8406e8a53fce782cb90557358c6290c3":[0,4,8],
"group__streams.html#ga8b99efcef9771afc6fad163fbd51d00b":[0,4,30],
"group__streams.html#ga944deab13d009fe3b6b94a8c7c4943b9":[0,4,13],
"group__streams.html#gaa1b461b8603be0c65dbf1e6037b01668":[0,4,4],
"group__streams.html#gaa1bf780a5ccea514dc1592f8a0eccb58":[0,4,23],
"group__streams.html#gaa2323dea0f12493ae97032fe7e6dcd55":[0,4,26],
"group__streams.html#gaac0281d255b7277db65d1e3e30d550fa":[0,4,29],
"group__streams.html#gabdf6ced2f262d5a4285465e347af1d79":[0,4,11],
"group__streams.html#gac952f2f3a280250a382a36419f7b16da":[0,4,24],
"group__streams.html#gadaf4d5e2ac9a12cc14da8fa006d85916":[0,4,2],
"group__streams.html#gae0d28422e33ff8ba269b4982ec7160aa":[0,4,15],
"group__streams.html#gaee920c8bd173ce7bf3b57f42cbcf3f5f":[0,4,3],
"group__streams.html#gaf1b8928176f1a3e88f2683c66324bab0":[0,4,16],
"group__streams.html#gaf8015d9228ada99910bc414d748536e8":[0,4,1],
"group__tcp.html":[0,8],
"group__tcp.html#a4af6558b9d474340373020ccfef5026c":[0,8,1,3],
"group__tcp.html#a64ed27e28dbbddbcf1fe1fed3538014b":[0,8,1,0],
"group__tcp.html#aa1a3af7b0980e3c45cc618c2bf7f076f":[0,8,1,2],
"group__tcp.html#ace7d6ad760cdb393909b6b4496929d4d":[0,8,1,1],
"group__tcp.html#ga1375271b46adc8f5945c65ebe2196f3a":[0,8,6],
"group__tcp.html#ga178d806e948c857f5599ee9462d18720":[0,8,2],
"group__tcp.html#ga1794f0147b6b441b5eabac6cb6dbbd5d":[0,8,5],
"group__tcp.html#ga4e0db7845d0a6de8355c58e172e7d1f1":[0,8,4],
"group__tcp.html#ga6e7766bd8a316202634ad28c1c2a7d6d":[0,8,3],
"group__tcp.html#gae3b5fdace85b868927f9d7eed5b661d0":[0,8,7],
"group__tcp.html#structtcp__server__config__t":[0,8,1],
"group__tcpx.html":[0,8,0],
"index.html":[],
"modules.html":[0],
"pages.html":[]
};


<|start_filename|>docs/api/search/enums_3.js<|end_filename|>
var searchData=
[
  ['uv_5fdirent_5ftype_5ft',['uv_dirent_type_t',['../group__nodec__fs.html#gab17cb1a692145e20d98e980c343a1335',1,'nodec-extra-doc.h']]]
];


<|start_filename|>docs/api/search/variables_f.js<|end_filename|>
var searchData=
[
  ['timeout',['timeout',['../group__tcp.html#aa1a3af7b0980e3c45cc618c2bf7f076f',1,'tcp_server_config_t']]],
  ['timeout_5ftotal',['timeout_total',['../group__tcp.html#a4af6558b9d474340373020ccfef5026c',1,'tcp_server_config_t']]],
  ['tv_5fnsec',['tv_nsec',['../group__nodec__fs.html#af1552c618f27286c9734be801d0ecf3f',1,'uv_timespec_t']]],
  ['tv_5fsec',['tv_sec',['../group__nodec__fs.html#a6c1e6a2cd2d9f10dc4df27ab3a79bc9d',1,'uv_timespec_t']]],
  ['type',['type',['../group__nodec__fs.html#a7343234f6f445689c599b147eeac9658',1,'uv_dirent_t']]]
];


<|start_filename|>src/https.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>

static void nodec_https_serve(int id, nodec_bstream_t* client, lh_value servefunv) {
  nodec_tls_stream_t* ts = (nodec_tls_stream_t*)client;
  nodec_tls_stream_handshake(ts);
  nodec_http_serve(id, client, servefunv);
}

static void https_connection_wrap(const tcp_connection_args* args, lh_value sslv) {
  nodec_ssl_config_t* ssl_config = (nodec_ssl_config_t*)lh_ptr_value(sslv);
  nodec_bstream_t* client = nodec_tls_stream_alloc(args->client, ssl_config);
  {using_bstream(client) {
    tcp_connection_args targs = *args;
    targs.client = client; // overwrite encrypted client 
    nodec_tcp_connection_wrap(&targs, lh_value_null);
  }}
}

void async_https_server_at(const char* host, tcp_server_config_t* tcp_config, nodec_ssl_config_t* ssl_config, nodec_http_servefun* servefun) {
  tcp_server_config_t default_config = tcp_server_config();
  if (tcp_config == NULL) tcp_config = &default_config;
  tcp_config->timeout_total = 0;
  struct sockaddr* addr = nodec_parse_sockaddr(host);
  {using_sockaddr(addr) {
    async_tcp_server_at_ex(addr, tcp_config,
      &nodec_https_serve,
      &https_connection_wrap,
      &async_write_http_exnv,
      lh_value_ptr(servefun),
      lh_value_any_ptr(ssl_config));
  }}
}


lh_value async_https_connect(nodec_ssl_config_t* ssl_config, const char* url, http_connect_fun* connectfun, lh_value arg) {
  lh_value result = lh_value_null;
  nodec_bstream_t* connx = async_tcp_connect(url);
  {using_bstream(connx) {
    nodec_bstream_t* conn = nodec_tls_stream_alloc(connx, ssl_config);
    {using_bstream(conn) {
      const nodec_url_t* u = nodec_parse_url(url);
      {using_url(u) {
        const char* host = nodec_url_host(u);
        result = async_http_connect_on(host, conn, connectfun, arg);
      }}
    }}
  }}
  return result;
}

<|start_filename|>docs/api/search/all_9.js<|end_filename|>
var searchData=
[
  ['implicit_20parameters',['Implicit Parameters',['../group__effect__implicit.html',1,'']]],
  ['implicit_5fdeclare',['implicit_declare',['../group__effect__implicit.html#ga40e8799dc7d3bbaaca7fe49dc134fbe5',1,'libhandler.h']]],
  ['implicit_5fdefine',['implicit_define',['../group__effect__implicit.html#gac94bad95bb8fcb34dcc29f46564bf750',1,'libhandler.h']]],
  ['implicit_5fexts',['implicit_exts',['../group__nodec__static.html#a35e38162a485696d0259c3789b690f1d',1,'http_static_config_t']]],
  ['implicit_5fget',['implicit_get',['../group__effect__implicit.html#gad6572acb8b04d7b33ea661035fb0d8d2',1,'libhandler.h']]],
  ['implicit_5findex',['implicit_index',['../group__nodec__static.html#a6b2390b3780a8912fb6d571aa7a08ba6',1,'http_static_config_t']]],
  ['ip_20addresses',['IP addresses',['../group__nodec__ip.html',1,'']]]
];


<|start_filename|>docs/api/search/typedefs_3.js<|end_filename|>
var searchData=
[
  ['uv_5ffile',['uv_file',['../group__nodec__fs.html#ga0c35c148bf40627f47bc6c2eeb19ea77',1,'nodec-extra-doc.h']]],
  ['uv_5fstream_5ft',['uv_stream_t',['../group__streams.html#gaa1b461b8603be0c65dbf1e6037b01668',1,'nodec-extra-doc.h']]]
];


<|start_filename|>inc/nodec.h<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
 
#pragma once
#ifndef __nodec_h 
#define __nodec_h

#include <libhandler/inc/libhandler.h>
#include <libuv/include/uv.h>
#include <http-parser/http_parser.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <stdarg.h>

#define USE_TLS
#define USE_MBEDTLS
#define USE_ZLIB

typedef int32_t nodec_errno_t;

/*! \mainpage

This is the API documentation of the
<a class="external" href="https://github.com/koka-lang/nodec">NodeC</a> project

See the <a class="external" href="./modules.html">Modules</a> section for an
overview of the API functionality.

\b Naming:
- `async_` : for asynchronous functions that might interleave
- `nodec_` : synchronous functions that might throw exeptions or use other effects.
- `nodecx_`: synchronous functions that return an explicit error result.
- `<tp>_t`  : for types
- `using_`  : for scoped combinators, to be used with double curly braces:
              i.e. `{using_alloc(tp,name){ ... <use name> ... }}`
- `lh_value`: an #lh_value is used to mimic polymorphism. There are a host of 
              conversion functions, like lh_ptr_value() (from value to pointer)
              or lh_int_value() (from value to an int).

.
*/

// Forward declarations 
typedef struct _channel_t channel_t;
typedef int    nodec_open_flags_t;

/// \addtogroup nodec_various
/// \{

/// 1 KB.
#define NODEC_KB  (1024)
/// 1 MB.
#define NODEC_MB  (1024*NODEC_KB)
/// 1 GB
#define NODEC_GB  (1024*NODEC_MB)

/// Throw an error, either an `errno` or `uv_errno_t`.
/// \param err error code.
void nodec_throw(int err);

/// Throw an error, either an `errno` or `uv_errno_t`.
/// \param err error code.
/// \param msg custom message.
void nodec_throw_msg(int err, const char* msg);

/// Does a string start with a prefix?
/// \param s The string to analyze, can be NULL, in which case `false` is returned.
/// \param prefix The prefix to compare, can be NULL, in which case `false` is returned.
/// \returns True if the string starts with `prefix`.
bool nodec_starts_with(const char* s, const char* prefix);

/// Does a string start with a prefix?
/// Uses case-insensitive comparison.
/// \param s The string to analyze, can be NULL, in which case `false` is returned.
/// \param prefix The prefix to compare, can be NULL, in which case `false` is returned.
/// \returns True if the string starts with `prefix`.
bool nodec_starts_withi(const char* s, const char* prefix);

/// Does a string end with a postfix?
/// \param s The string to analyze, can be NULL, in which case `false` is returned.
/// \param postfix The postfix to compare, can be NULL, in which case `false` is returned.
/// \returns True if the string ends with `postfix`.
bool nodec_ends_with(const char* s, const char* postfix);

/// Does a string end with a postfix?
/// Uses case-insensitive comparison.
/// \param s The string to analyze, can be NULL, in which case `false` is returned.
/// \param postfix The postfix to compare, can be NULL, in which case `false` is returned.
/// \returns True if the string ends with `postfix`.
bool nodec_ends_withi(const char* s, const char* postfix);

/// Get the current time as a RFC1123 internet date.
/// Caches previous results to increase efficiency.
/// \returns The current time in <a href="https://tools.ietf.org/html/rfc1123#page-55">RFC1123</a> format, 
/// for example `Thu, 01 Jan 1972 00:00:00 GMT`.
const char* nodec_inet_date_now();

/// Format a time as a RFC1123 internet date.
/// Caches previous results to increase efficiency.
/// \param now The time to convert.
/// \returns The current time in <a href="https://tools.ietf.org/html/rfc1123#page-55">RFC1123</a> format, 
/// for example `Thu, 01 Jan 1972 00:00:00 GMT`.
const char* nodec_inet_date(time_t now);

/// Parse a RFC1123 internet date.
/// \param date  A RFC1123 formatted internet date, for example `Thu, 01 Jan 1972 00:00:00 GMT`. Can be NULL which is treated as an invalid date.
/// \param[out] t Set to the time according to `date`. If the `date` was not valid, the UNIX epoch is used.
/// \returns True if successful.
bool nodec_parse_inet_date(const char* date, time_t* t);

/// \}


/* ----------------------------------------------------------------------------
Buffers are `uv_buf_t` which contain a `base` pointer and the
available `len` bytes. These buffers are usually passed by value.
-----------------------------------------------------------------------------*/

/// \defgroup buffers Buffer's
/// Memory buffers.
/// Buffers are the `libuv` #uv_buf_t` structure which contain 
/// a pointer to the data and their length.
/// Buffer structures are small and always passed and returned _by value_.
///
/// The routines in this module ensure that all buffers actually are allocated
/// with `len + 1` where `base[len] == 0`, i.e. it is safe to print
/// the contents as a string as it will be zero terminated.
/// \{

/// Initialize a libuv buffer, which is a record with a data pointer and its length.
uv_buf_t nodec_buf(const void* base, size_t len);

/// Initialize a buffer from a string.
uv_buf_t nodec_buf_str(const char* s);

/// Initialize a buffer from a string.
/// This (re)allocates the string in the heap which is then owned by the buffer.
uv_buf_t nodec_buf_strdup(const char* s);

/// Create a null buffer, i.e. `nodec_buf(NULL,0)`.
uv_buf_t nodec_buf_null();

/// Create and allocate a buffer of a certain length.
uv_buf_t nodec_buf_alloc(size_t len);

/// Reallocate a buffer to a given new length.
/// See also nodec_buf_fit() and nodec_buf_ensure().
uv_buf_t nodec_buf_realloc(uv_buf_t buf, size_t len);

/// Ensure a buffer has at least `needed` size.
/// \param  buf The buffer to potentially reallocate. If `buf` is null, 
///             use 8kb for initial size, and after that use doubling of at most 
///             4mb increments.
/// \param needed the required size of the buffer.
/// \returns a new buffer that has at least `needed` length.
uv_buf_t nodec_buf_ensure(uv_buf_t buf, size_t needed);

/// Ensure a buffer has at least `needed` size.
/// Like nodec_buf_ensure() but with customizable `initial_size` and `max_increase`.
uv_buf_t nodec_buf_ensure_ex(uv_buf_t buf, size_t needed, size_t initial_size, size_t max_increase);

/// Fit a buffer to `needed` size. 
/// Might increase the buffer size, or
/// Reallocate to a smaller size if it was larger than 128 bytes with more than 
/// 20% wasted space.
/// \param buf   the buffer to fit.
/// \param needed the required space needed.
/// \returns the new potentially reallocated buffer with a length that fits `needed`.
uv_buf_t nodec_buf_fit(uv_buf_t buf, size_t needed);

/// Append the contents of `buf2` into `buf1`.
/// \param buf1 the destination buffer.
/// \param buf2 the source buffer.
/// \returns a potentially resized `buf1` that contains the contents of both.
uv_buf_t nodec_buf_append_into(uv_buf_t buf1, uv_buf_t buf2);


/// Is this a null buffer?.
/// \param buf the buffer.
/// \returns `true` if the buffer length is 0 or the `base` is `NULL`.
bool nodec_buf_is_null(uv_buf_t buf);

/// Free a buffer.
/// This is usually not used directly, but through using_buf().
void nodec_buf_free(uv_buf_t buf);

/// Free a buffer by reference.
/// This is usually not used directly, but through using_buf().
void nodec_bufref_free(uv_buf_t* buf);

/// Free a buffer by reference value.
/// This is usually not used directly, but through using_buf().
void nodec_bufref_freev(lh_value bufref);

/// Use a buffer in a scope, freeing automatically when exiting.
/// Pass the buffer by reference so it will free
/// the final memory that `bufref->base` points to (and not the initial value).
/// \param bufref  a reference to the buffer to free after use.
/// \b Example
/// ```
/// uv_buf_t buf = async_read_buf(stream);
/// {using_buf(&buf){
///    async_write_buf(out,buf);
/// }}
/// ```
#define using_buf(bufref)  defer(nodec_bufref_freev,lh_value_any_ptr(bufref))

/// Use a buffer in a scope, freeing automatically only when an exception is thrown.
/// Pass the buffer by reference so it will free
/// the final memory that `bufref->base` points to (and not the initial value).
/// \param bufref  a reference to the buffer to free after use.
#define using_buf_on_abort_free(bufref)   on_abort(nodec_bufref_freev,lh_value_any_ptr(bufref))

/// Don't free a buffer by reference value.
/// Usually not used directly, but used by using_buf_owned()
void nodec_bufref_nofreev(lh_value pv);

/// Use a potentially _owned_ buffer in a scope.
/// This is used for the `async_read_bufx()` calls where the returned buffer
/// might be owned (and must be freed), or not (and must not be freed).
/// Pass the buffer by reference so it will free
/// the final memory that `bufref->base` points to (and not the initial value).
/// \param owned   Only free the buffer if `owned` is true.
/// \param bufref  a reference to the buffer to free after use.
#define using_buf_owned(owned,bufref)  defer((owned ? nodec_bufref_freev : nodec_bufref_nofreev),lh_value_any_ptr(bufref))

/// \}


/* ----------------------------------------------------------------------------
  Logging
-----------------------------------------------------------------------------*/

/// \defgroup log Loggin
/// Logging information
/// \{

/// Log level.
typedef enum _nodec_log_level_t {
  LOG_ERROR,
  LOG_WARNING,
  LOG_INFO,
  LOG_VERBOSE,
  LOG_DEBUG,
  LOG_SILLY
} nodec_log_level_t;

#ifdef NDEBUG
# define LOG_DEFAULT LOG_WARNING
#else
# define LOG_DEFAULT LOG_DEBUG
#endif

void nodec_log_set(nodec_log_level_t level);
void nodec_log(nodec_log_level_t level, const char* msg);
void nodec_vlogf(nodec_log_level_t level, const char* fmt, va_list args);
void nodec_logf(nodec_log_level_t level, const char* fmt, ...);
void nodec_log_error(const char* fmt, ...);
void nodec_log_warning(const char* fmt, ...);
void nodec_log_info(const char* fmt, ...);
void nodec_log_verbose(const char* fmt, ...);
void nodec_log_debug(const char* fmt, ...);
void nodec_log_silly(const char* fmt, ...);

/// \}

/* ----------------------------------------------------------------------------
  Cancelation scope
-----------------------------------------------------------------------------*/

// private
implicit_declare(_cancel_scope)
lh_value _cancel_scope_alloc();

/// \defgroup nodec_async Asynchrony
/// Asynchronous utility functions.
/// \{

/// Execute under a cancelation scope.
/// \sa async_scoped_cancel()
#define using_cancel_scope()        using_implicit_defer(nodec_freev,_cancel_scope_alloc(),_cancel_scope)

/// Asynchronously cancel all outstanding requests under the same cancelation scope.
/// This is a powerful primitive that enables cancelation 
/// of operations and for example enables timeouts over any composition of
/// asynchronous operations. When async_scoped_cancel() is called, it cancels
/// all outstanding (interleaved) operations under the same innermost cancelation
/// scope (raising a cancel exception for all of them).
void async_scoped_cancel();

/// Is the current scope canceled?
bool async_scoped_is_canceled();



/* ----------------------------------------------------------------------------
  Asynchronous combinators
-----------------------------------------------------------------------------*/

/// Interleave a fixed number of strands.
/// Interleave with arguments `arg_results`. 
/// The result of each action is stored again in `arg_results`; when
/// an exception is raised, it is rethrown from `interleave` once all
/// its actions have finished. Interleave introduces a cancelation 
/// scope.
/// \param n  Number of strands to create.
/// \param actions Array of size `n` containing the action to execute for each strand.
/// \param arg_results Array of size `n` containing the argument values passed to each 
///                    action and also to store the result of each action.
void async_interleave(size_t n, lh_actionfun* actions[], lh_value arg_results[]);


/// Dynamically create an interleaved strand.
/// Only works in the `action` given to async_interleave_dynamic()!
lh_value async_strand_create(lh_actionfun* action, lh_value arg, lh_exception** exn);


/// Return the number of currently interleaved strands under the innermost interleaved handler.
size_t nodec_current_strand_count();

/// Interleave a dynamic number of strands.
/// \param action The initial action. This can use async_dynamic_spawn() to dynamically interleave more actions.
/// \param arg  Argument passed to action.
/// \returns The result from `action`. Only returns once `action` and all dynamically spawned actions are done.
lh_value async_interleave_dynamic(lh_actionfun action, lh_value arg);

/// General timeout routine over an `action`. 
/// \param action  The action to run.
/// \param arg     Argument passed to action.
/// \param timeout The time in milli-seconds after which `action` is canceled.
/// \param timedout If not NULL, gets set whether the action timed out or not.
/// \returns The result of `action` or `lh_value_null` if it timed out.
lh_value async_timeout(lh_actionfun* action, lh_value arg, uint64_t timeout, bool* timedout);

/// Type of actions with no arguments and no return value.
typedef void (nodec_actionfun_t)();

/// Return when the either action is done, canceling the other.
/// \returns `true` if the first action was finished first.
bool async_firstof(nodec_actionfun_t* action1, nodec_actionfun_t* action2);

/// Return the value of the first returning action, canceling the other.
lh_value async_firstof_ex(lh_actionfun* action1, lh_value arg1, lh_actionfun* action2, lh_value arg2, bool* first, bool ignore_exn);

/// Asynchronously wait for `timeout` milli-seconds.
void async_wait(uint64_t timeout);

/// Yield asynchronously to other strands.
/// Equivalent to async_wait() with a 0 timeout.
void async_yield();

/// \}


/* ----------------------------------------------------------------------------
  File system (fs)
-----------------------------------------------------------------------------*/


/// \defgroup nodec_fs File System
/// Asynchronous access to the file system.
/// \{

/// Stat a file without raising an exception on failure.
/// \param path The file path.
/// \param stat The result file information.
/// \returns 0 on success, and otherwise an error code (usually when the file does not exist).
uv_errno_t  asyncx_fs_stat(const char* path, uv_stat_t* stat);

/// Stat a file.
/// \param path The file path.
/// \returns The file information.
uv_stat_t   async_fs_stat(const char* path);

/// Stat a file handle.
/// \param file The opened file handle.
/// \returns The file information.
uv_stat_t   async_fs_fstat(uv_file file);


/// Open a file.
/// \param path The file path
/// \param flags The file open flags.
/// \param mode The file permissions mode if it is created (using #UV_FS_O_CREAT).
/// \returns The opened file handle on success.
uv_file     async_fs_open(const char* path, nodec_open_flags_t flags, int permissions);

/// Open a file.
/// Returns an error code instead of throwing an exception.
/// \param path The file path
/// \param flags The file open flags.
/// \param mode The file permissions mode if it is created (using #UV_FS_O_CREAT).
/// \param[out] file Set to the file handle on success
/// \returns A possible error code, or 0 on succes.
uv_errno_t  asyncx_fs_open(const char* path, nodec_open_flags_t flags, int permissions, uv_file* file );

/// Close a file.
/// \param file The file handle to be closed.
void        async_fs_close(uv_file file);

/// Read a file into a buffer.
/// \param file The file handle
/// \param buf  The buffer to read into.
/// \param file_offset The offset to read from in the file.
/// \returns The number of bytes read.
size_t      async_fs_read_into(uv_file file, uv_buf_t buf, int64_t file_offset);

/// Read part of a file.
/// Most efficient way to read files.
/// \param file The file to read from.
/// \param max  The maximum number of bytes to read.
/// \param file_offset The offset to read from in the file.
/// \returns a buffer contiaining the bytes read.
uv_buf_t    async_fs_read_buf(uv_file file, size_t max, int64_t file_offset);

/// Read an entire file (or up to `max` bytes)
/// \param file The file to read from.
/// \param max  The maximum number of bytes to read.
/// \returns A buffer with the file contents.
uv_buf_t    async_fs_read_buf_all(uv_file file, size_t max);

/// Used to iterate over the contents of a directory.
typedef uv_fs_t nodec_scandir_t;

/// \cond
void        nodec_scandir_free(nodec_scandir_t* scanreq);
void        nodec_scandir_freev(lh_value scanreqv);
/// \endcond

/// Use a #nodec_scandir_t in a scope.
/// \param scandir A #nodec_scandir_t returned from async_scandir()
#define using_fs_scandir(scandir)  defer(nodec_scandir_freev,lh_value_ptr(scandir))

/// Initiate scanning a directory.
/// \param path The directory path.
nodec_scandir_t* async_fs_scandir(const char* path);

/// Iterate over the directory contents.
/// \param[in,out] scanreq The iteration state.
/// \param[out] dirent The directory entry information if successful.
/// \returns True while there are valid directory entries.
/// \b Example:
/// ```
/// nodec_scandir_t* scandir = async_scandir("/foo");
/// {using_scandir(scandir){
///    uv_dirent_t dirent;
///    while( async_scandir_next(scandir,&dirent) ) {
///      printf("entry: %s\n", dirent.name);
///    }
/// }}
/// ```
bool async_fs_scandir_next(nodec_scandir_t* scanreq, uv_dirent_t* dirent);

// ----------------------------------
// File system convenience functions

/// Read full contents of a file as a string.
/// \param path The file path.
/// \returns The contents of the file as a string (assuming no 0 bytes)
char*       async_fs_read_from(const char* path);

/// Read full contents of a file as a buffer.
/// \param path The file path
/// \returns A heap allocated buffer containing the entire file contents.
uv_buf_t    async_fs_read_buf_from(const char* path);

/// Type of scoped file functions.
typedef lh_value(nodec_file_fun)(uv_file file, const char* path, lh_value arg);

/// Safely open a file, use it, and close it again.
/// \param path The file path
/// \param flags The file open flags.
/// \param mode The file permissions mode if it is created (using #UV_FS_O_CREAT).
/// \param action The file open action: called with the opened file handle.
/// \param arg Extra argument passed to `action`.
/// \returns The return value of `action`.
lh_value    using_async_fs_open(const char* path, nodec_open_flags_t flags, int permissions, nodec_file_fun* action, lh_value arg);

/// Safely open a file, use it, and close it again.
/// Does not throw an exception on a file open error but returns an `uv_errno_t` instead.
/// \param path The file path
/// \param flags The file open flags.
/// \param mode The file permissions mode if it is created (using #UV_FS_O_CREAT).
/// \param action The file open action: called with the opened file handle.
/// \param arg Extra argument passed to `action`.
/// \param[out] result The return value of `action`.
/// \returns A possible error code or 0 on success.
uv_errno_t  using_asyncx_fs_open(const char* path, nodec_open_flags_t flags, int permissions, nodec_file_fun* action, lh_value arg, lh_value* result);

/// \}

/* ----------------------------------------------------------------------------
  Streams
-----------------------------------------------------------------------------*/
///\defgroup streams Streams
/// Streaming data.
/// The main interfaces are plain streams as #nodec_stream_t and
/// buffered streams #nodec_bstream_t.
///\{

/// Basic unbuffered streams.
typedef struct _nodec_stream_t  nodec_stream_t;

// Primitives

/// Free a basic stream.
/// Usually this is not used directly but instead using_stream() is used.
void      nodec_stream_free(nodec_stream_t* stream);

/// Free a basic stream as an `lh_value`.
/// Usually this is not used directly but instead using_stream() is used.
void      nodec_stream_freev(lh_value streamv);

/// Shutdown a stream. 
///
/// This closes the stream gracefully for writes (nodec_stream_free()
/// does this too but closes the stream abruptly).
/// Usually this is not used directly but instead using_stream() is used.
void      async_shutdown(nodec_stream_t* stream);

/// Use a stream in a scope, freeing the stream when exiting.
///
/// This safely uses the stream in a scope and frees the stream afterwards
/// (even if an exception is thrown).
/// Uses async_shutdown(stream) too if no exception was thrown.
/// \param s   the #nodec_stream_t to free after use.
///
/// \b Example
/// ```
/// nodec_stream_t s = ...
/// {using_stream(s){
///    async_write(s, "hello");
/// }}
/// ```
#define using_stream(s) \
    defer_exit(async_shutdown(s),&nodec_stream_freev,lh_value_ptr(s))

/// Asynchronously read a buffer from a stream.
/// This is the most efficient form of reading since it can even read by providing
/// _unowned_ view buffers but it is also most complex as returned unowned buffers are
/// only valid until the next read call. See async_read_buf() for more robust 
/// reading that always returns callee owned buffers.
/// \param stream  stream to read from.
/// \param[out] buf_owned this is set to `true` if the returned buffer is _owned_, i.e. should be freed by the callee.
/// \returns a buffer with the data. This buffer is becomes
///    the callee's responsibility if `buf_owned` is true; otherwise the buffer should
///    _not_ be freed by the callee. See using_buf_owned() to do this automatically.
///    Moreover, if a buffer is returned with `*buf_owned` false, the buffer only
///    stays valid until the next read from that stream. 
///
/// \b Example
/// ```
/// nodec_stream_t stream = http_req_body();
/// {using_stream(stream){
///    uv_buf_t buf;
///    bool owned;
///    while( (buf = async_read_bufx(stream,&owned), !nodec_buf_is_null(buf)) ) {
///      {using_buf_owned(owned,buf){
///         printf("data read: %s\n", buf.base);
///      }}
///    }
/// }}
/// ```
uv_buf_t  async_read_bufx(nodec_stream_t* stream, bool* buf_owned);

/// Asynchronously read a buffer from a stream.
/// \param stream  stream to read from.
/// \returns uv_buf_t  a buffer with the data. This buffer is becomes
///    the callee's responsibility (see using_buf()) and is passed without
///    copying from the lower level read routines.
uv_buf_t  async_read_buf(nodec_stream_t* stream);

/// Asynchronously read a string from a stream.
char*     async_read(nodec_stream_t* stream);

/// Write an array of buffers to a stream.
/// \param stream  the stream to write to.
/// \param bufs    the array of buffers to write.
/// \param count the number of buffers in the array.
void      async_write_bufs(nodec_stream_t* stream, uv_buf_t bufs[], size_t count);

/// Write a buffer to a stream.
void      async_write_buf(nodec_stream_t* stream, uv_buf_t buf);

/// Write a string to a stream.
/// \param stream stream to write to.
/// \param s      the string to write.
void      async_write(nodec_stream_t* stream, const char* s);

/// Write a formatted string to a stream.
/// Writes at most 511 bytes after formatting.
/// \param stream stream to write to.
/// \param fmt    the format string.
/// \param args   the arguments for the format string.
void      async_vprintf(nodec_stream_t* stream, const char* fmt, va_list args);

/// Write a formatted string to a stream.
/// Writes at most 511 bytes after formatting.
/// \param stream stream to write to.
/// \param fmt    the format string.
/// \param ...    the arguments for the format string.
void      async_printf(nodec_stream_t* stream, const char* fmt, ...);

/// The type of buffered streams. 
/// Derives from a basic #nodec_stream_t and can be cast to it using as_stream().
/// Buffered streams have added functionality, like being able to read
/// a stream up to a certain pattern is encountered, or reading the full
/// stream as a single buffer.
typedef struct _nodec_bstream_t nodec_bstream_t;

/// Safely cast a buffered stream to a basic stream.
nodec_stream_t*  as_stream(nodec_bstream_t* stream);

/// Use a buffered stream in a scope, freeing the stream when exiting.
///
/// This safely uses the stream in a scope and frees the stream afterwards
/// (even if an exception is thrown).
/// Uses async_shutdown(stream) too if no exception was thrown.
/// \param s   the #nodec_bstream_t to free after use.
///
/// \b Example
/// ```
/// nodec_bstream_t s = nodec_tcp_connect("http://www.google.com");
/// {using_bstream(s){
///    uv_buf_t buf = async_read_buf_all(s);
///    {using_buf(&buf){
///       printf("received: %s\n", buf.base);
///    }}
/// }}
/// ```
#define using_bstream(s)  using_stream(as_stream(s))  

/// Read the entire stream as a single buffer.
/// \param bstream   the stream to read from.
/// \param read_max  maximum number of bytes to read. Use 0 (or `SIZE_MAX`) for unlimited.
/// \returns the data read in a callee owned buffer (see using_buf()).
uv_buf_t  async_read_buf_all(nodec_bstream_t* bstream, size_t read_max);

/// Read the entire stream as a string.
/// \param bstream   the stream to read from.
/// \param read_max  maximum number of bytes to read. Use 0 (or `SIZE_MAX`) for unlimited.
/// \returns the data read in a callee owned string (see using_str()).
char*     async_read_all(nodec_bstream_t* bstream, size_t read_max);

/// Read a stream into a pre-allocated buffer.
/// \param bstream  the stream to read from.
/// \param buf      the buffer to read into; reads up to either the end of the stream
///                 or up to the `buf.len`.
/// \returns the number of bytes read.
size_t    async_read_into(nodec_bstream_t* bstream, uv_buf_t buf);

/// Read a stream until some pattern is encountered.
/// The returned buffer will contain `pat` unless the end-of-stream was 
/// encountered or `read_max` bytes were read. The return buffer may contain 
/// more data following the
/// the pattern `pat`. This is more efficient than async_read_buf_upto which may involve
/// some memory copying to split buffers.
/// \param bstream the stream to read from.
/// \param[out] toread  the number of bytes just including the pattern `pat`. 0 on failure.
/// \param[in]  pat     the pattern to scan for.
/// \param[in] pat_len   the length of the patter. For efficiency reasons, this can be at
///                      most 8 (bytes).
/// \param[in] read_max stop reading after `read_max` bytes have been seen. Depending on 
///          the internal buffering, the returned buffer might still contain more bytes.
/// \returns buffer with the read bytes where `buf.len >= *toread`. Returns
///          a null buffer (see nodec_buf_is_null()) if an end-of-stream is encountered.
uv_buf_t  async_read_buf_including(nodec_bstream_t* bstream, size_t* toread, const void* pat, size_t pat_len, size_t read_max);

/// Read a stream until some pattern is encountered.
/// The returned buffer will contain `pat` at its end unless the end-of-stream was 
/// encountered or `read_max` bytes were read. 
/// \param bstream the stream to read from.
/// \param[in]  pat     the pattern to scan for.
/// \param[in] pat_len   the length of the patter. For efficiency reasons, this can be at
///                      most 8 (bytes).
/// \param[in] read_max stop reading after `read_max` bytes have been seen. Depending on 
///          the internal buffering, the returned buffer might still contain more bytes.
/// \returns buffer with the read bytes. Returns
///          a null buffer (see nodec_buf_is_null()) if an end-of-stream is encountered.
uv_buf_t  async_read_buf_upto(nodec_bstream_t* bstream, const void* pat, size_t pat_len, size_t read_max);

/// Read the first line of a buffered stream.
uv_buf_t  async_read_buf_line(nodec_bstream_t* bstream);

/// Read the first line as a string from a buffered stream.
char*     async_read_line(nodec_bstream_t* bstream);

/// Push back a buffer onto a read stream. 
/// The data pushed back will be read on the next read call.
void nodec_pushback_buf(nodec_bstream_t* bstream, uv_buf_t buf);

/// Create a buffered stream from a plain stream
nodec_bstream_t* nodec_bstream_alloc_on(nodec_stream_t* source);


/// Low level: Create a buffered stream from an internal `uv_stream_t`.
///
/// \param stream   the underlying `uv_stream_t`. Freed when the #nodec_bstream_t is freed.
/// \returns a buffered stream.
nodec_bstream_t* nodec_bstream_alloc(uv_stream_t* stream);

/// Low level: Create a buffered stream from an internal `uv_stream_t` for reading.
///
/// \param stream   the underlying `uv_stream_t`. Freed when the #nodec_bstream_t is freed.
/// \returns a buffered stream.
nodec_bstream_t* nodec_bstream_alloc_read(uv_stream_t* stream);

/// Low level: Create a buffered stream from an internal `uv_stream_t` for reading.
///
/// \param stream   the underlying `uv_stream_t`. Freed when the #nodec_bstream_t is freed.
/// \param alloc_init the initial allocation size for read buffers. Use 0 for default (8k). Doubles on further data until `alloc_max`.
/// \param alloc_max  the maximal allocation size for read buffers.
/// \returns a buffered stream.
nodec_bstream_t* nodec_bstream_alloc_read_ex(uv_stream_t* stream, size_t alloc_init, size_t alloc_max);


#ifdef USE_ZLIB

/// Create a stream over another stream that is gzip'd.
/// Reading unzips, while writing zips again.
/// Uses a default compression level of 6 and gzip format (instead of deflate).
/// \param stream the gzipped stream 
/// \param own_stream If true, then the returned stream
///               owns the input `stream` and takes care of shutdown and free.
/// \returns a new buffered stream that automatically (de)compresses.
nodec_bstream_t* nodec_zstream_alloc(nodec_stream_t* stream, bool own_stream);


/// Create a stream over another stream that is gzip'd.
/// Reading unzips automatically, while writing zips again.
/// \param stream the gzipped stream -- after allocation the returned stream
///               owns this stream and takes care of shutdown and free.
/// \param own_stream If true, then the returned stream
///               owns the input `stream` and takes care of shutdown and free.
/// \param compress_level  the compression level to use between 1 and 9 (6 by default, which
///                         is a good balance between speed and compression ratio).
/// \param gzip   if `true` uses the gzip format for compression while reading both
///               deflate and gzip streams. If false, uses deflate for both compression
///               and decompression.
/// \returns a new buffered stream that automatically (de)compresses.
nodec_bstream_t* nodec_zstream_alloc_ex(nodec_stream_t* stream, bool own_stream, int compress_level, bool gzip);

#endif

/// \}


/* ----------------------------------------------------------------------------
  IP4 and IP6 Addresses
-----------------------------------------------------------------------------*/

/// \defgroup nodec_ip IP addresses
/// Raw IP4 or IP6 addresses.
/// \{
void nodec_ip4_addr(const char* ip, int port, struct sockaddr_in* addr);
void nodec_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr);

/// Define `name` as an ip4 address
#define define_ip4_addr(ip,port,name)  \
  struct sockaddr_in name##_ip4; nodec_ip4_addr(ip,port,&name##_ip4); \
  struct sockaddr* name = (struct sockaddr*)&name##_ip4;

/// Define `name` as an ip4 address
#define define_ip6_addr(ip,port,name)  \
  struct sockaddr_in name##_ip6; nodec_ip6_addr(ip,port,&name##_ip6); \
  struct sockaddr* name = (struct sockaddr*)&name##_ip6;

void nodec_sockname(const struct sockaddr* addr, char* buf, size_t bufsize);

void async_getnameinfo(const struct sockaddr* addr, int flags, char** node, char** service);
struct addrinfo* async_getaddrinfo(const char* node, const char* service, const struct addrinfo* hints);

void nodec_free_addrinfo(struct addrinfo* info);
void nodec_free_addrinfov(lh_value infov);
#define using_addrinfo(name)  defer(nodec_free_addrinfov,lh_value_ptr(name))
/// \}

/* ----------------------------------------------------------------------------
URL's
-----------------------------------------------------------------------------*/
/// \defgroup nodec_url URL
/// URL parsing.
/// \{

/// Structured URL
typedef struct _nodec_url_t nodec_url_t;

void nodec_url_free(nodec_url_t* url);
void nodec_url_freev(lh_value urlv);

nodec_url_t*  nodecx_parse_url(const char* url);
nodec_url_t*  nodecx_parse_host(const char* host);
nodec_url_t*  nodec_parse_url(const char* url);
nodec_url_t*  nodec_parse_host(const char* host);

#define using_url(url)        defer(nodec_url_freev,lh_value_ptr(url))
#define usingx_url(url)       defer(nodec_url_freev,lh_value_ptr(url))

const char* nodec_url_schema(const nodec_url_t* url);
const char* nodec_url_host(const nodec_url_t* url);
const char* nodec_url_path(const nodec_url_t* url);
const char* nodec_url_query(const nodec_url_t* url);
const char* nodec_url_fragment(const nodec_url_t* url);
const char* nodec_url_userinfo(const nodec_url_t* url);
const char* nodec_url_port_str(const nodec_url_t* url);
uint16_t    nodec_url_port(const nodec_url_t* url);
bool        nodec_url_is_ip6(const nodec_url_t* url);

#define using_sockaddr(sa)    defer(nodec_freev, lh_value_ptr(sa))
struct sockaddr* nodec_parse_sockaddr(const char* host);

/// \}


/* ----------------------------------------------------------------------------
  Mime types
-----------------------------------------------------------------------------*/
/// \defgroup nodec_mime MIME Types.
/// MIME type functionality.
/// The default MIME information is generated from the
/// <a href="https://www.npmjs.com/package/mime-db">mime-db</a> project.
/// \{

/// Mime info from a filename.
/// \param fname The file name.
/// \param[out] compressible Set to `true` if this kind of file can be further compressed by gzip.
/// \param[out] charset      Set to the default character set for this MIME type. (do not free).
/// \returns The MIME type. (do not free).
const char* nodec_mime_info_from_fname(const char* fname, bool* compressible, const char** charset);

/// Mime name from a file name.
/// \param fname The file name.
/// \returns The MIME type. (do not free).
const char* nodec_mime_from_fname(const char* fname);

/// Information for a MIME type.
/// \param mime_type The MIME type.
/// \param[out] preferred_ext  The preferred extension.
/// \param[out] compressible Set to `true` if this kind of file can be further compressed by gzip.
/// \param[out] charset      Set to the default character set for this MIME type. (do not free).
void        nodec_info_from_mime(const char* mime_type, const char** preferred_ext, bool* compressible, const char** charset);

/// The preferred extension for a MIME type.
/// \param mime_type The MIME type.
/// \returns The preferrred extensions (do not free).
const char* nodec_ext_from_mime(const char* mime_type);

/// \}


/* ----------------------------------------------------------------------------
  TCP
-----------------------------------------------------------------------------*/
/// \defgroup tcp TCP Connections
/// Communication over the TCP protocol (reliable streaming).
/// \{

/// \defgroup tcpx Low level TCP Connections
/// Low level functions to access TCP.
/// \{

typedef struct _channel_t tcp_channel_t;
void            channel_freev(lh_value vchannel);
#define using_tcp_channel(ch)  defer(channel_freev,lh_value_ptr(ch))

uv_tcp_t*       nodec_tcp_alloc();
void            nodec_tcp_free(uv_tcp_t* tcp);
void            nodec_tcp_freev(lh_value tcp);

void            nodec_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags);
tcp_channel_t*  nodec_tcp_listen(uv_tcp_t* tcp, int backlog, bool channel_owns_tcp);
uv_stream_t*    async_tcp_channel_receive(tcp_channel_t* ch);

/// \}


/// Establish a TCP connection.
/// \param addr   Connection address. 
/// \param host   Host name, only used for error messages and can be `NULL`.
nodec_bstream_t* async_tcp_connect_at(const struct sockaddr* addr, const char* host /* can be NULL, used for errors */);

/// Establish a TCP connection.
/// \param host     Host address. 
/// \param service  Can be a port (`"8080"`) or service (`"https"`). Uses `"http"` when `NULL`.
nodec_bstream_t* async_tcp_connect_at_host(const char* host, const char* service /*NULL="http"*/);

/// Establish a TCP connection.
/// \param host     Host address as a url, like `"http://www.bing.com"`.
nodec_bstream_t* async_tcp_connect(const char* host);

// TCP server

/// TCP Server configuration options.
typedef struct _tcp_server_config_t {
  int       backlog;           ///< maximal pending OS request queue length (default 64).
  int       max_interleaving;  ///< maximal number concurrent requests (default 1000).
  uint64_t  timeout;           ///< ms between connection requests allowed; 0 = infinite, default 5000.
  uint64_t  timeout_total;     ///< total allowed connection time in ms; 0 = infinite, default 0.
} tcp_server_config_t;

/// Default TCP server configuration.
#define tcp_server_config()    { 64, 1000, 5000, 0 }

/// The server callback when listening on a TCP connection.
/// \param id       The identity of the current asynchronous strand.
/// \param client   The connecting client data stream.
/// \param arg      The `lh_value` passed from async_tcp_server_at().
typedef void    (nodec_tcp_connection_fun_t)(int id, nodec_bstream_t* client, lh_value arg);

/// Create a TCP server.
///
/// \param addr       The socket address to serve.
/// \param config     The TCP server configuration, can be `NULL` in which case tcp_server_config() is used.
/// \param servefun   The callback called when a client connects.
/// \param on_exn     Optional function that is called when an exception happens in `servefun`.
/// \param arg        Optional argument to pass on to `servefun`, can be `lh_value_null`.
void async_tcp_server_at(const struct sockaddr* addr, tcp_server_config_t* config, nodec_tcp_connection_fun_t* servefun, lh_actionfun* on_exn, lh_value arg);

/// \}



/* ----------------------------------------------------------------------------
  HTTP
-----------------------------------------------------------------------------*/
/// \defgroup http HTTP Connections
/// Communication over the HTTP 1.1 protocol.
/// \{


/// \addtogroup http_in_out 
/// \{

/// HTTP incoming data object
typedef struct _http_in_t http_in_t;

/// HTTP outgoing data object
typedef struct _http_out_t http_out_t;

/// \}


/// \defgroup http_setup HTTP Setup
/// Initiating HTTP servers or clients.
/// \{
typedef enum http_status http_status_t;
typedef enum http_method http_method_t;

void throw_http_err(http_status_t status);
void throw_http_err_str(http_status_t status, const char* msg);
void throw_http_err_strdup(http_status_t status, const char* msg);
const char* nodec_http_status_str(http_status_t code);
const char* nodec_http_method_str(http_method_t method);


// HTTP(S) server

typedef void (nodec_http_servefun)();

void async_http_server_at(const char* host, tcp_server_config_t* config, nodec_http_servefun* servefun);


// HTTP(S) connection

typedef lh_value (http_connect_fun)(http_in_t* in, http_out_t* out, lh_value arg);

lh_value async_http_connect_on(const char* host, nodec_bstream_t* connection, http_connect_fun* connectfun, lh_value arg);
lh_value async_http_connect(const char* url, http_connect_fun* connectfun, lh_value arg);

/// \}

/* ----------------------------------------------------------------------------
  HTTP incoming connection
-----------------------------------------------------------------------------*/
/// \defgroup http_in_out HTTP Input and Output.
/// HTTP incoming and outgoing data.
/// These are the request- and response objects for the server,
/// and the response- and request objects for a client connection.
/// Servers bind these implicitly to http_req() and http_resp() for
/// the current request- and response objects so that the explicit
/// #http_in_t and #http_out_t objects don't need to be threaded around
/// explicitly.
/// \{

void   http_in_clear(http_in_t* in);
void   http_in_clearv(lh_value inv);

// Initialize a incoming connection at a server or client by reading the headers block
size_t async_http_in_init(http_in_t* in, uv_stream_t* stream, bool is_request );

// Query the incoming connection
const char*   http_in_url(http_in_t* in);            // server
http_method_t http_in_method(http_in_t* in);         // server
http_status_t   http_in_status(http_in_t* in);       // client
const char* http_in_status_info(http_in_t* in);    // client
uint64_t      http_in_content_length(http_in_t* in); 
const char*   http_in_header(http_in_t* in, const char* name);
const char*   http_in_header_next(http_in_t* in, const char** value, size_t* iter);
bool          http_in_header_contains(http_in_t* req, const char* name, const char* pattern);

// Read comma separated header fields; `*iter` should start as NULL.
// Returns NULL when done.
const char* http_header_next_field(const char* header, size_t* len, const char** iter);

// use this on connections to wait for a response
size_t async_http_in_read_headers(http_in_t* in);

/// Get a buffered stream to read the body.
/// \param in       the HTTP input.
/// \returns NULL if the stream has no body or was already completely read.
/// Use async_read_bufx() to read most efficiently without memory copies.
nodec_bstream_t* http_in_body(http_in_t* in);

/// Read the full body; the returned buf should be deallocated by the caller.
/// Uses `Content-Length` when possible to read into a pre-allocated buffer of the right size.
uv_buf_t async_http_in_read_body(http_in_t* in, size_t read_max);


/// Print a response for debugging purposes;
void http_in_status_print(http_in_t* in);

/*-----------------------------------------------------------------
  HTTP outgoing
-----------------------------------------------------------------*/

void http_out_init(http_out_t* out, nodec_stream_t* stream);
void http_out_init_server(http_out_t* out, nodec_stream_t* stream, const char* server_name);
void http_out_init_client(http_out_t* out, nodec_stream_t* stream, const char* host_name);

void http_out_clear(http_out_t* out);
void http_out_clearv(lh_value respv);

#define using_http_out(stream,out)  http_out_t _resp; http_out_t* out = &_resp; http_out_init(out,stream); defer(http_out_clearv,lh_value_any_ptr(out))

// Add headers
void http_out_add_header(http_out_t* out, const char* field, const char* value);

// Send headers and get a possible body write stream

/// Use this as a `content_length` to use a _chunked_ transfer encoding
/// in http_out_send_status_body() and http_resp_send_status_body().
#define NODEC_CHUNKED ((size_t)(-1))

void             http_out_send_status(http_out_t* out, http_status_t status);
nodec_stream_t*  http_out_send_status_body(http_out_t* out, http_status_t status, size_t content_length, const char* content_type);
void             http_out_send_request(http_out_t* out, http_method_t method, const char* url);
nodec_stream_t*  http_out_send_request_body(http_out_t* out, http_method_t method, const char* url, size_t content_length, const char* content_type);

bool http_out_status_sent(http_out_t* out);
/// \}


/*-----------------------------------------------------------------
  HTTP server implicitly bound request and response
-----------------------------------------------------------------*/
/// \defgroup http_req_resp HTTP Server Requests and Responses.
/// Implicit bindings for HTTP incoming- and outgoing data for a server.
/// The HTTP(S) server implicitly binds the current request
/// and response objects (as dynamically bound implicit parameters).
/// They can be accessed through `http_req_xxx` for requests and
/// `http_resp_xxx` for responses.
/// \{

/// The identity of the current asynchronous strand serving the request.
int         http_strand_id();

/// Return the current HTTP request.
http_in_t*  http_req();

/// Return the current HTTP response.
http_out_t* http_resp();

/// Add a header to the current HTTP response.
/// \param name  the name of the header
/// \param value the value of the header
/// the name and value are copied into the header block and can be freed afterwards.
void            http_resp_add_header(const char* name, const char* value);


/// Return `true` if the HTTP response status was sent.
bool http_resp_status_sent();

/// Send the headers and the response status without a body.
/// \param status The HTTP response status.
void  http_resp_send_status(http_status_t status);

/// Send the headers and response status, and return a reponse body stream.
/// \param status  The HTTP response status
/// \param content_length  The length of the response body. Use #NODEC_CHUNKED for
///   a _chunked_ transfer encoding where the content length
///   is unknown ahead of time.
/// \param content_type    The mime content type (like `text/html`). The `charset`
///   can be specified too (like `text/html;charset=UTF-8`) but
///   is otherwise determined automatically from the mime type.
///   if the `content_type` is `NULL` no _Content-Type_ header
///   is added.
/// \returns A stream to write the body to; this is automatically chunked if
///   #NODEC_CHUNKED was passed as the `content_length`.
nodec_stream_t* http_resp_send_status_body(http_status_t status, size_t content_length, const char* content_type);

/// Send the headers and OK response without a body.
/// Just a shorthand for http_resp_send_status(HTTP_STATUS_OK).
void http_resp_send_ok();

/// Send headers, status, and full body as a buffer.
void http_resp_send_body_buf(http_status_t status, uv_buf_t buf, const char* content_type);

/// Send headers, status, and full body as a string.
void http_resp_send_body_str(http_status_t status, const char* body, const char* content_type);


/// Return the current request url.
const char*   http_req_url();

/// Return the parsed full request url, including the path, hash, query etc.
const nodec_url_t* http_req_parsed_url();


/// Return the current request path.
const char*   http_req_path();

/// Return the current request method.
http_method_t http_req_method();

/// The value of the _Content-Length_ header.
/// \returns 0 if the _Content-Length_ header was not present.
uint64_t      http_req_content_length();

/// Return the value of an HTTP request header.
/// \param name the header name.
/// \returns the header value (owned by the request object) or NULL
///  if the header was not present. If there were multiple headers
///  the values are joined with commas into one value. See http_header_next_field()
/// for iterating through them.
const char*   http_req_header(const char* name);

/// Iterate through all headers.
/// \param[out] value  the value of the current header, or NULL if done iterating.
/// \param[in,out] iter   the iteration state, should be initialized to 0.
/// \returns the name of the current header or NULL if done iterating.
const char*   http_req_header_next(const char** value, size_t* iter);

/// Return the current HTTP request body as a buffered stream.
/// Handles chunked bodies, and does automatic decompression if the 
/// `Content-Encoding` was `gzip`.
nodec_bstream_t* http_req_body();

/// Read the full HTTP request body.
/// The returned buffer should be deallocated by the caller.
/// \param read_max   maximum bytes to read (or 0 for unlimited reading)
/// \returns the buffer with the full request body.
/// Handles `chunked` request bodies, and does automatic decompression 
/// if the request body had a Content-Encoding of `gzip`.
uv_buf_t      async_req_read_body(size_t read_max);

/// Read the full body as a string. 
/// Only works if the body cannot contain 0 characters.
/// The returned string should be deallocated by the caller.
/// \param read_max   maximum bytes to read (or 0 for unlimited reading)
/// \returns the buffer with the full request body.
/// Handles `chunked` request bodies, and does automatic decompression 
/// if the request body had a Content-Encoding of `gzip`.
const char*   async_req_read_body_str(size_t read_max);

/// Print a request for debugging purposes.
void http_req_print();


/// \}


/* ----------------------------------------------------------------------------
  HTTP Static web server
-----------------------------------------------------------------------------*/
/// \defgroup nodec_static HTTP Static Content Server.
/// Serve static content over HTTP 1.1.
/// \{

/// HTTP Static server configuration.
typedef struct _http_static_config_t {
  bool use_last_modified;      ///< Generate `Last-Modified` headers for efficient caching, By default true. 
                               /// The server checks `If-Modified-Since` headers with efficient 304 responses.
  bool use_etag;               ///< Generate cluster-safe ETag's for efficient caching, By default true. 
                               ///< However, the server also generates `Last-Modified` headers and respects both
                               ///< `If-Modified-Since` and `If-None-Match` headers with efficient 304 responses.
  const char*  cache_control;  ///< If not NULL, specifies the cache control header. By default `"public, max-age=604800"`.
  const char** implicit_exts;  ///< NULL terminated array of implicit extensions, by default `{".html",".htm",NULL}`.
  const char* implicit_index;  ///< If not NULL, specifies the default index file, by default `"index.html"`.
  bool   gzip_vary;            ///< If compression is enabled, add the VARY header. By default true.
  size_t gzip_min_size;        ///< Enable gzip compression above this file size (default is 1KB) (if the MIME type is compressible). Set to `SIZE_MAX` to disable compression.
  size_t content_max_size;     ///< Maximum size of content to serve (by default `SIZE_MAX`).
  size_t read_buf_size;        ///< Read buffer chunk size, by default 64KB.
} http_static_config_t;

/// Default file extensions for the static content server.
/// Initialized to `{".html",".htm",NULL}`.
extern const char* http_static_implicit_exts[];

/// Return the default static server configuration.
#define http_static_default_config() { true, true, "public, max-age=604800", http_static_implicit_exts, "index", true, 1024, SIZE_MAX, 64*1024 }

/// Serve static files under a `root` directory. 
/// \param config The configuration. Can be NULL for the default configuration.
/// Supports efficient `304 Not-Modified` responses with `Last-Modified` and
/// and `ETag` headers, and can do dynamic gzip compression (if enabled).
void http_serve_static(const char* root, const http_static_config_t* config);

/// \}


/* ----------------------------------------------------------------------------
  HTTPS
-----------------------------------------------------------------------------*/

/// \defgroup nodec_https HTTPS secure connections
/// Secure connections over HTTPS
/// \{

typedef struct _nodec_ssl_config_t nodec_ssl_config_t;

void nodec_ssl_config_free(nodec_ssl_config_t* config);
void nodec_ssl_config_freev(lh_value configv);

#define using_ssl_config(cfg)     defer(nodec_ssl_config_freev,lh_value_ptr(cfg))

nodec_ssl_config_t* nodec_ssl_config_server_from(const char* cert_path, const char* key_path, const char* password);
nodec_ssl_config_t* nodec_ssl_config_server(uv_buf_t cert, uv_buf_t key, const char* password);

nodec_ssl_config_t* nodec_ssl_config_client();

nodec_errno_t nodecx_ssl_config_add_ca(nodec_ssl_config_t* config, uv_buf_t cert);
void async_ssl_config_add_system_certs(nodec_ssl_config_t* config);


typedef struct _nodec_tls_stream_t nodec_tls_stream_t;

nodec_bstream_t* nodec_tls_stream_alloc(nodec_bstream_t* stream, const nodec_ssl_config_t* config);

void async_https_server_at(const char* host, tcp_server_config_t* tcp_config, nodec_ssl_config_t* ssl_config, nodec_http_servefun* servefun);
lh_value async_https_connect(nodec_ssl_config_t* config, const char* url, http_connect_fun* connectfun, lh_value arg);

/// \} HTTPS


/// \}  HTTP Connections

/* ----------------------------------------------------------------------------
  TTY
-----------------------------------------------------------------------------*/


/// \defgroup nodec_tty TTY 
/// Terminal input and output.
/// \{

/// Read from the console.
/// \returns The input read. Should be freed by the callee (see using_free()).
char* async_tty_readline();

/// Write to the console.
/// \param s The string to write to the console. Can contain ANSI escape sequences.
void  async_tty_write(const char* s);

/// Write to the console on stderr.
/// \param s The string to write to the console on stderr. Can contain ANSI escape sequences.
void  async_tty_write_err(const char* s);


/// Run an action until "enter" is pressed.
/// \param action The action to run.
void async_stop_on_enter(nodec_actionfun_t* action);

/// Write a formatted string to the console.
/// \param fmt The format string to write to the console. Can contain ANSI escape sequences.
/// \param ... Format arguments.
void  async_tty_printf(const char* fmt, ...);

/// Write a formatted string to the console.
/// \param fmt The format string to write to the console. Can contain ANSI escape sequences.
/// \param args Format arguments.
void  async_tty_vprintf(const char* fmt, va_list args);

/// Write a formatted string to the console on stderr.
/// \param fmt The format string to write to the console. Can contain ANSI escape sequences.
/// \param ... Format arguments.
void  async_tty_printf_err(const char* fmt, ...);

/// Write a formatted string to the console on stderr.
/// \param fmt The format string to write to the console. Can contain ANSI escape sequences.
/// \param args Format arguments.
void  async_tty_vprintf_err(const char* fmt, va_list args);

/// \}


/* ----------------------------------------------------------------------------
  Main entry point
  -----------------------------------------------------------------------------*/

/// \defgroup nodec_various Various
/// Various utility functions.
/// \{

/// Type of the asynchronous main function.
typedef void (nodec_main_fun_t)();

/// Run the passed in function as the main asynchronous function.
/// This starts the libuv event loop and sets up the asynchronous and
/// exception effect handlers.
/// \param entry The main asynchronous entry point.
/// \returns A possible error code or 0 on success.
uv_errno_t  async_main( nodec_main_fun_t* entry );

/// \} 

/* ----------------------------------------------------------------------------
  Safe allocation
-----------------------------------------------------------------------------*/

/// \defgroup nodec_alloc Memory Allocation.
/// Routines for safe memory allocation based on `using`.
/// These raise an exception on failure and always `free` even 
/// if exceptions are raised.
/// The code :
/// ```
/// {using_alloc(mystruct_t, name) {
///   ...
/// }}
/// ```
/// will safely allocate a `mystruct_t*` to `name` which can be used inside `...`
/// and will be deallocated safely if an exception is thrown or when exiting
/// the block scope.
/// \{

#if defined(_MSC_VER) && defined(_DEBUG)
// Enable debugging logs on msvc 
# undef _malloca // suppress warning
# define _CRTDBG_MAP_ALLOC
# include <crtdbg.h>
# define nodecx_malloc  malloc
# define nodecx_calloc  calloc
# define nodecx_realloc realloc
# define nodecx_free    free
# define nodec_malloc(sz)     (check_nonnull(malloc(sz)))
# define nodec_calloc(n,sz)   (check_nonnull(calloc(n,sz)))
# define nodec_realloc(p,sz)  (check_nonnull(realloc(p,sz)))
# define nodec_free           _nodec_free
#else
# define nodecx_malloc  _nodecx_malloc
# define nodecx_calloc  _nodecx_calloc
# define nodecx_realloc _nodecx_realloc
# define nodecx_free    free
# define nodec_malloc  _nodec_malloc
# define nodec_calloc  _nodec_calloc
# define nodec_realloc _nodec_realloc
# define nodec_free    _nodec_free
#endif

void  nodec_register_malloc(lh_mallocfun* _malloc, lh_callocfun* _calloc, lh_reallocfun* _realloc, lh_freefun* _free);
void  nodec_check_memory();
void* check_nonnull(void* p);   // throws on a non-null pointer

void* _nodecx_malloc(size_t size);
void* _nodecx_calloc(size_t count, size_t size);
void* _nodecx_realloc(void* p, size_t newsize);
void* _nodec_malloc(size_t size);
void* _nodec_calloc(size_t count, size_t size);
void* _nodec_realloc(void* p, size_t newsize);
void  _nodec_free(const void* p);

void  nodec_freev(lh_value p);
char* nodec_strdup(const char* s);
char* nodec_strndup(const char* s, size_t max);
const void* nodec_memmem(const void* src, size_t src_len, const void* pat, size_t pat_len);
int nodec_strnicmp(const char* s, const char* t, size_t n);
int nodec_stricmp(const char* s, const char* t);
uv_errno_t nodec_strncpy(char* dest, size_t destsz, const char* src, size_t count);

#define nodecx_alloc(tp)          ((tp*)(nodecx_malloc(sizeof(tp))))
#define nodecx_zero_alloc(tp)     ((tp*)(nodecx_calloc(1,sizeof(tp))))

/// Allocate memory for a type.
/// \param tp The type to allocate memory for.
/// \b Example
/// ```
/// my_struct_t* p = nodec_alloc(my_struct_t);
/// {using_free(p){
///    ...
/// }}
/// ```
#define nodec_alloc(tp)           ((tp*)(nodec_malloc(sizeof(tp))))
#define nodec_alloc_n(n,tp)       ((tp*)(nodec_malloc((n)*sizeof(tp))))
#define nodec_zero_alloc_n(n,tp)  ((tp*)(nodec_calloc(n,sizeof(tp))))
#define nodec_zero_alloc(tp)      nodec_zero_alloc_n(1,tp)
#define nodec_realloc_n(p,n,tp)   ((tp*)(nodec_realloc(p,(n)*sizeof(tp))))

/// Use a pointer in a scope and free afterwards.
/// Always frees the pointer, even if an exception is thrown.
/// \param name The name of the `void*`.
#define using_free(name)               defer(nodec_freev,lh_value_ptr(name))

/// Allocate and use a pointer in a scope and free afterwards.
/// Always frees the pointer, even if an exception is thrown.
/// \param tp   The type of value to allocate.
/// \param name The name of the `tp*`.
/// Equivalent to:
/// ```
/// tp* name = nodec_alloc(tp);
/// {using_free(name){
///   ...
/// }}
/// ```
#define using_alloc(tp,name)           tp* name = nodec_alloc(tp); using_free(name)

#define using_alloc_n(n,tp,name)       tp* name = nodec_alloc_n(n,tp); using_free(name)
#define using_zero_alloc_n(n,tp,name)  tp* name = nodec_zero_alloc_n(n,tp); using_free(name)
#define using_zero_alloc(tp,name)      using_zero_alloc_n(1,tp,name)

#define nodec_zero(tp,ptr)        memset(ptr,0,sizeof(tp));

/// \} 

// Error codes:
// positive or zero is ok. negative down to -0x10000 are libUV error codes
// From there we reserve two 64k blocks per external libray to give it
// error codes. We pick the point right in between the two 64k regions such
// that we can support both positive and negative error codes.

typedef enum _error_kind_t {
  ERRKIND_UV = 0,
  ERRKIND_HTTP = -0x20000,
  #ifdef USE_MBEDTLS
  ERRKIND_MBEDTLS = -0x40000,
  #endif
  ERRKIND_UNKNOWN = -0xFF0000
} error_kind_t;


nodec_errno_t nodec_error_from(long err, error_kind_t err_kind);
long       nodec_error_to(nodec_errno_t err, error_kind_t err_kind);
bool       nodec_is_error_of(nodec_errno_t err, error_kind_t err_kind);
void       nodec_strerror(nodec_errno_t err, const char* msg, uv_buf_t buf);

#endif // __nodec_h


<|start_filename|>docs/api/group__effect_structlh__handlerdef.js<|end_filename|>
var group__effect_structlh__handlerdef =
[
    [ "effect", "group__effect.html#a08413ea484f015bb79d395c349eb1a29", null ],
    [ "local_acquire", "group__effect.html#a197b2abb85288eda9fc73dddb02213c9", null ],
    [ "local_release", "group__effect.html#ac03ed465c68a46c167612e712160fd61", null ],
    [ "operations", "group__effect.html#a8588e38ec7973da55f90d239f7ec854f", null ],
    [ "resultfun", "group__effect.html#afe5be74288279eb0ec913ca483942ddd", null ]
];

<|start_filename|>docs/api/annotated_dup.js<|end_filename|>
var annotated_dup =
[
    [ "http_static_config_t", "group__nodec__static.html#structhttp__static__config__t", "group__nodec__static_structhttp__static__config__t" ],
    [ "lh_exception", "group__effect__exn.html#structlh__exception", "group__effect__exn_structlh__exception" ],
    [ "lh_handlerdef", "group__effect__handlers.html#structlh__handlerdef", "group__effect__handlers_structlh__handlerdef" ],
    [ "lh_operation", "group__effect__handlers.html#structlh__operation", "group__effect__handlers_structlh__operation" ],
    [ "lh_optag_", "group__effect__handlers.html#structlh__optag__", "group__effect__handlers_structlh__optag__" ],
    [ "tcp_server_config_t", "group__tcp.html#structtcp__server__config__t", "group__tcp_structtcp__server__config__t" ],
    [ "uv_buf_t", "group__buffers.html#structuv__buf__t", "group__buffers_structuv__buf__t" ],
    [ "uv_dirent_t", "group__nodec__fs.html#structuv__dirent__t", "group__nodec__fs_structuv__dirent__t" ],
    [ "uv_stat_t", "group__nodec__fs.html#structuv__stat__t", "group__nodec__fs_structuv__stat__t" ],
    [ "uv_timespec_t", "group__nodec__fs.html#structuv__timespec__t", "group__nodec__fs_structuv__timespec__t" ],
    [ "yieldargs", "group__effect__handlers.html#structyieldargs", "group__effect__handlers_structyieldargs" ]
];

<|start_filename|>docs/api/group__effect_structyieldargs.js<|end_filename|>
var group__effect_structyieldargs =
[
    [ "argcount", "group__effect.html#ab797acdcd17ed8d49fd79980409be48b", null ],
    [ "args", "group__effect.html#a2dd60935d436953098e73cbdd77fdcd2", null ]
];

<|start_filename|>docs/api/group__effect.js<|end_filename|>
var group__effect =
[
    [ "Generic Values", "group__effect__value.html", "group__effect__value" ],
    [ "Handlers", "group__effect__handlers.html", "group__effect__handlers" ],
    [ "Debugging", "group__effect__debug.html", "group__effect__debug" ],
    [ "Effect Definition helpers", "group__effect__def.html", null ],
    [ "Linear Handlers", "group__effect__linear.html", null ],
    [ "Exceptions and Finally clauses", "group__effect__exn.html", "group__effect__exn" ],
    [ "Implicit Parameters", "group__effect__implicit.html", "group__effect__implicit" ]
];

<|start_filename|>docs/api/structlh__exception.js<|end_filename|>
var structlh__exception =
[
    [ "_is_alloced", "structlh__exception.html#a42489ecd1468adadd6bd9c8d6fc7850e", null ],
    [ "code", "structlh__exception.html#a2f415f8397a6e3e497814e50fbb3eb35", null ],
    [ "data", "structlh__exception.html#a9a780c96e58c551826cb9d7055e0e939", null ],
    [ "msg", "structlh__exception.html#a400106a97a1ab0617f39aa7a4e8618b0", null ]
];

<|start_filename|>docs/api/group__effect__implicit.js<|end_filename|>
var group__effect__implicit =
[
    [ "implicit_declare", "group__effect__implicit.html#ga40e8799dc7d3bbaaca7fe49dc134fbe5", null ],
    [ "implicit_define", "group__effect__implicit.html#gac94bad95bb8fcb34dcc29f46564bf750", null ],
    [ "implicit_get", "group__effect__implicit.html#gad6572acb8b04d7b33ea661035fb0d8d2", null ],
    [ "using_implicit", "group__effect__implicit.html#gaea88b12f476f8d029fe6043febecae19", null ]
];

<|start_filename|>src/log.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-internal.h"
#include "nodec-primitive.h"
#include <assert.h>

typedef struct _log_t {
  bool              no_console;
  nodec_log_level_t level;
  uv_file           file;
} log_t;

static log_t* nodec_log_alloc(nodec_log_level_t level) {
  log_t* log = nodec_zero_alloc(log_t);
  log->level = level;
  return log;
}

lh_value _nodec_log_allocv(nodec_log_level_t level) {
  return lh_value_ptr(nodec_log_alloc(level));
}


static void nodec_log_free(log_t* log) {
  //if (log->file != 0) async_fs_close(log->file);
  nodec_free(log);
}

void _nodec_log_freev(lh_value logv) {
  nodec_log_free((log_t*)lh_ptr_value(logv));
}

implicit_define(log)

static log_t* log_get() {
  return (log_t*)lh_ptr_value(implicit_get(log));
}

void nodec_log_shutdown() {
  log_t* log = log_get();
  if (log->file > 0) {
    async_fs_close(log->file);
    log->file = -1;
  }
}

static const char* nodec_level_str(nodec_log_level_t level) {
  static const char* level_strs[6] = {
    "error",
    "warning",
    "info",
    "verbose",
    "debug",
    "silly"
  };
  if (level < 0 || level >= 6) level = 5;
  return level_strs[level];
}

void nodec_log(nodec_log_level_t level, const char* msg) {
  log_t* log = log_get();
  if (log->level < level) return;
  if (!log->no_console) {
    const char* levelmsg = nodec_level_str(level);
    if (log->level <= LOG_WARNING) 
      async_tty_printf_err("%s: %s\n", levelmsg, msg);
    else 
      async_tty_printf("%s: %s\n", levelmsg, msg);
  }
  // TODO: file logging
}

void nodec_log_set(nodec_log_level_t level) {
  log_t* log = log_get();
  if (level < LOG_ERROR) level = LOG_ERROR;
  log->level = level;
}

void nodec_vlogf(nodec_log_level_t level, const char* fmt, va_list args) {
  log_t* log = log_get();
  if (log->level >= level) {
    char buf[512];
    vsnprintf(buf, 512, fmt, args); buf[511] = 0;
    nodec_log(level, buf);
  }
}

void nodec_logf(nodec_log_level_t level, const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  nodec_vlogf(level, fmt, args);
  va_end(args);
}

void nodec_log_error(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  nodec_vlogf(LOG_ERROR, fmt, args);
  va_end(args);
}

void nodec_log_warning(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  nodec_vlogf(LOG_WARNING, fmt, args);
  va_end(args);
}

void nodec_log_info(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  nodec_vlogf(LOG_INFO, fmt, args);
  va_end(args);
}

void nodec_log_verbose(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  nodec_vlogf(LOG_VERBOSE, fmt, args);
  va_end(args);
}

void nodec_log_debug(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  nodec_vlogf(LOG_DEBUG, fmt, args);
  va_end(args);
}

void nodec_log_silly(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  nodec_vlogf(LOG_SILLY, fmt, args);
  va_end(args);
}


<|start_filename|>docs/api/search/groups_6.js<|end_filename|>
var searchData=
[
  ['handlers',['Handlers',['../group__effect__handlers.html',1,'']]],
  ['http_20connections',['HTTP Connections',['../group__http.html',1,'']]],
  ['http_20input_20and_20output_2e',['HTTP Input and Output.',['../group__http__in__out.html',1,'']]],
  ['http_20server_20requests_20and_20responses_2e',['HTTP Server Requests and Responses.',['../group__http__req__resp.html',1,'']]],
  ['http_20setup',['HTTP Setup',['../group__http__setup.html',1,'']]],
  ['https_20secure_20connections',['HTTPS secure connections',['../group__nodec__https.html',1,'']]],
  ['http_20static_20content_20server_2e',['HTTP Static Content Server.',['../group__nodec__static.html',1,'']]]
];


<|start_filename|>docs/api/search/groups_7.js<|end_filename|>
var searchData=
[
  ['implicit_20parameters',['Implicit Parameters',['../group__effect__implicit.html',1,'']]],
  ['ip_20addresses',['IP addresses',['../group__nodec__ip.html',1,'']]]
];


<|start_filename|>src/mime-db.h<|end_filename|>
// Generated from 'mime-db', see: <https://www.npmjs.com/package/mime-db>
// On 2018-08-05T03:54:44.355Z

#include <stdlib.h>

typedef struct _mime_type_t {
  const char* name;			 // mime type name
  const char* extension;  // preferred extension, can be NULL
  const char* extensions; // possible extensions separated by semi-colons, can be NULL
  const char* charset;    // default char set, can be NULL
  bool compressible;      // can this content be gzipped?
} mime_type_t;

typedef struct _mime_ext_t {
  const char* extension;
  const int   index; 	   // index into the `mime_types` array
} mime_ext_t;

static const int mime_types_len = 2055;
static const int mime_extensions_len = 1102;

// sorted mime types
static mime_type_t mime_types[] = {
  { "application/1d-interleaved-parityfec", NULL, NULL, NULL, false },
  { "application/3gpdash-qoe-report+xml", NULL, NULL, NULL, true },
  { "application/3gpp-ims+xml", NULL, NULL, NULL, true },
  { "application/a2l", NULL, NULL, NULL, false },
  { "application/activemessage", NULL, NULL, NULL, false },
  { "application/activity+json", NULL, NULL, NULL, true },
  { "application/alto-costmap+json", NULL, NULL, NULL, true },
  { "application/alto-costmapfilter+json", NULL, NULL, NULL, true },
  { "application/alto-directory+json", NULL, NULL, NULL, true },
  { "application/alto-endpointcost+json", NULL, NULL, NULL, true },
  { "application/alto-endpointcostparams+json", NULL, NULL, NULL, true },
  { "application/alto-endpointprop+json", NULL, NULL, NULL, true },
  { "application/alto-endpointpropparams+json", NULL, NULL, NULL, true },
  { "application/alto-error+json", NULL, NULL, NULL, true },
  { "application/alto-networkmap+json", NULL, NULL, NULL, true },
  { "application/alto-networkmapfilter+json", NULL, NULL, NULL, true },
  { "application/aml", NULL, NULL, NULL, false },
  { "application/andrew-inset", "ez", "ez", NULL, false },
  { "application/applefile", NULL, NULL, NULL, false },
  { "application/applixware", "aw", "aw", NULL, false },
  { "application/atf", NULL, NULL, NULL, false },
  { "application/atfx", NULL, NULL, NULL, false },
  { "application/atom+xml", "atom", "atom", NULL, true },
  { "application/atomcat+xml", "atomcat", "atomcat", NULL, true },
  { "application/atomdeleted+xml", NULL, NULL, NULL, true },
  { "application/atomicmail", NULL, NULL, NULL, false },
  { "application/atomsvc+xml", "atomsvc", "atomsvc", NULL, true },
  { "application/atxml", NULL, NULL, NULL, false },
  { "application/auth-policy+xml", NULL, NULL, NULL, true },
  { "application/bacnet-xdd+zip", NULL, NULL, NULL, false },
  { "application/batch-smtp", NULL, NULL, NULL, false },
  { "application/bdoc", "bdoc", "bdoc", NULL, false },
  { "application/beep+xml", NULL, NULL, NULL, true },
  { "application/calendar+json", NULL, NULL, NULL, true },
  { "application/calendar+xml", NULL, NULL, NULL, true },
  { "application/call-completion", NULL, NULL, NULL, false },
  { "application/cals-1840", NULL, NULL, NULL, false },
  { "application/cbor", NULL, NULL, NULL, false },
  { "application/cccex", NULL, NULL, NULL, false },
  { "application/ccmp+xml", NULL, NULL, NULL, true },
  { "application/ccxml+xml", "ccxml", "ccxml", NULL, true },
  { "application/cdfx+xml", NULL, NULL, NULL, true },
  { "application/cdmi-capability", "cdmia", "cdmia", NULL, false },
  { "application/cdmi-container", "cdmic", "cdmic", NULL, false },
  { "application/cdmi-domain", "cdmid", "cdmid", NULL, false },
  { "application/cdmi-object", "cdmio", "cdmio", NULL, false },
  { "application/cdmi-queue", "cdmiq", "cdmiq", NULL, false },
  { "application/cdni", NULL, NULL, NULL, false },
  { "application/cea", NULL, NULL, NULL, false },
  { "application/cea-2018+xml", NULL, NULL, NULL, true },
  { "application/cellml+xml", NULL, NULL, NULL, true },
  { "application/cfw", NULL, NULL, NULL, false },
  { "application/clue_info+xml", NULL, NULL, NULL, true },
  { "application/cms", NULL, NULL, NULL, false },
  { "application/cnrp+xml", NULL, NULL, NULL, true },
  { "application/coap-group+json", NULL, NULL, NULL, true },
  { "application/coap-payload", NULL, NULL, NULL, false },
  { "application/commonground", NULL, NULL, NULL, false },
  { "application/conference-info+xml", NULL, NULL, NULL, true },
  { "application/cose", NULL, NULL, NULL, false },
  { "application/cose-key", NULL, NULL, NULL, false },
  { "application/cose-key-set", NULL, NULL, NULL, false },
  { "application/cpl+xml", NULL, NULL, NULL, true },
  { "application/csrattrs", NULL, NULL, NULL, false },
  { "application/csta+xml", NULL, NULL, NULL, true },
  { "application/cstadata+xml", NULL, NULL, NULL, true },
  { "application/csvm+json", NULL, NULL, NULL, true },
  { "application/cu-seeme", "cu", "cu", NULL, false },
  { "application/cwt", NULL, NULL, NULL, false },
  { "application/cybercash", NULL, NULL, NULL, false },
  { "application/dart", NULL, NULL, NULL, true },
  { "application/dash+xml", "mpd", "mpd", NULL, true },
  { "application/dashdelta", NULL, NULL, NULL, false },
  { "application/davmount+xml", "davmount", "davmount", NULL, true },
  { "application/dca-rft", NULL, NULL, NULL, false },
  { "application/dcd", NULL, NULL, NULL, false },
  { "application/dec-dx", NULL, NULL, NULL, false },
  { "application/dialog-info+xml", NULL, NULL, NULL, true },
  { "application/dicom", NULL, NULL, NULL, false },
  { "application/dicom+json", NULL, NULL, NULL, true },
  { "application/dicom+xml", NULL, NULL, NULL, true },
  { "application/dii", NULL, NULL, NULL, false },
  { "application/dit", NULL, NULL, NULL, false },
  { "application/dns", NULL, NULL, NULL, false },
  { "application/dns+json", NULL, NULL, NULL, true },
  { "application/docbook+xml", "dbk", "dbk", NULL, true },
  { "application/dskpp+xml", NULL, NULL, NULL, true },
  { "application/dssc+der", "dssc", "dssc", NULL, false },
  { "application/dssc+xml", "xdssc", "xdssc", NULL, true },
  { "application/dvcs", NULL, NULL, NULL, false },
  { "application/ecmascript", "ecma", "ecma;es", NULL, true },
  { "application/edi-consent", NULL, NULL, NULL, false },
  { "application/edi-x12", NULL, NULL, NULL, false },
  { "application/edifact", NULL, NULL, NULL, false },
  { "application/efi", NULL, NULL, NULL, false },
  { "application/emergencycalldata.comment+xml", NULL, NULL, NULL, true },
  { "application/emergencycalldata.control+xml", NULL, NULL, NULL, true },
  { "application/emergencycalldata.deviceinfo+xml", NULL, NULL, NULL, true },
  { "application/emergencycalldata.ecall.msd", NULL, NULL, NULL, false },
  { "application/emergencycalldata.providerinfo+xml", NULL, NULL, NULL, true },
  { "application/emergencycalldata.serviceinfo+xml", NULL, NULL, NULL, true },
  { "application/emergencycalldata.subscriberinfo+xml", NULL, NULL, NULL, true },
  { "application/emergencycalldata.veds+xml", NULL, NULL, NULL, true },
  { "application/emma+xml", "emma", "emma", NULL, true },
  { "application/emotionml+xml", NULL, NULL, NULL, true },
  { "application/encaprtp", NULL, NULL, NULL, false },
  { "application/epp+xml", NULL, NULL, NULL, true },
  { "application/epub+zip", "epub", "epub", NULL, false },
  { "application/eshop", NULL, NULL, NULL, false },
  { "application/exi", "exi", "exi", NULL, false },
  { "application/fastinfoset", NULL, NULL, NULL, false },
  { "application/fastsoap", NULL, NULL, NULL, false },
  { "application/fdt+xml", NULL, NULL, NULL, true },
  { "application/fhir+json", NULL, NULL, NULL, true },
  { "application/fhir+xml", NULL, NULL, NULL, true },
  { "application/fido.trusted-apps+json", NULL, NULL, NULL, true },
  { "application/fits", NULL, NULL, NULL, false },
  { "application/font-sfnt", NULL, NULL, NULL, false },
  { "application/font-tdpfr", "pfr", "pfr", NULL, false },
  { "application/font-woff", NULL, NULL, NULL, false },
  { "application/framework-attributes+xml", NULL, NULL, NULL, true },
  { "application/geo+json", "geojson", "geojson", NULL, true },
  { "application/geo+json-seq", NULL, NULL, NULL, false },
  { "application/geoxacml+xml", NULL, NULL, NULL, true },
  { "application/gltf-buffer", NULL, NULL, NULL, false },
  { "application/gml+xml", "gml", "gml", NULL, true },
  { "application/gpx+xml", "gpx", "gpx", NULL, true },
  { "application/gxf", "gxf", "gxf", NULL, false },
  { "application/gzip", "gz", "gz", NULL, false },
  { "application/h224", NULL, NULL, NULL, false },
  { "application/held+xml", NULL, NULL, NULL, true },
  { "application/hjson", "hjson", "hjson", NULL, false },
  { "application/http", NULL, NULL, NULL, false },
  { "application/hyperstudio", "stk", "stk", NULL, false },
  { "application/ibe-key-request+xml", NULL, NULL, NULL, true },
  { "application/ibe-pkg-reply+xml", NULL, NULL, NULL, true },
  { "application/ibe-pp-data", NULL, NULL, NULL, false },
  { "application/iges", NULL, NULL, NULL, false },
  { "application/im-iscomposing+xml", NULL, NULL, NULL, true },
  { "application/index", NULL, NULL, NULL, false },
  { "application/index.cmd", NULL, NULL, NULL, false },
  { "application/index.obj", NULL, NULL, NULL, false },
  { "application/index.response", NULL, NULL, NULL, false },
  { "application/index.vnd", NULL, NULL, NULL, false },
  { "application/inkml+xml", "ink", "ink;inkml", NULL, true },
  { "application/iotp", NULL, NULL, NULL, false },
  { "application/ipfix", "ipfix", "ipfix", NULL, false },
  { "application/ipp", NULL, NULL, NULL, false },
  { "application/isup", NULL, NULL, NULL, false },
  { "application/its+xml", NULL, NULL, NULL, true },
  { "application/java-archive", "jar", "jar;war;ear", NULL, false },
  { "application/java-serialized-object", "ser", "ser", NULL, false },
  { "application/java-vm", "class", "class", NULL, false },
  { "application/javascript", "js", "js;mjs", "UTF-8", true },
  { "application/jf2feed+json", NULL, NULL, NULL, true },
  { "application/jose", NULL, NULL, NULL, false },
  { "application/jose+json", NULL, NULL, NULL, true },
  { "application/jrd+json", NULL, NULL, NULL, true },
  { "application/json", "json", "json;map", "UTF-8", true },
  { "application/json-patch+json", NULL, NULL, NULL, true },
  { "application/json-seq", NULL, NULL, NULL, false },
  { "application/json5", "json5", "json5", NULL, false },
  { "application/jsonml+json", "jsonml", "jsonml", NULL, true },
  { "application/jwk+json", NULL, NULL, NULL, true },
  { "application/jwk-set+json", NULL, NULL, NULL, true },
  { "application/jwt", NULL, NULL, NULL, false },
  { "application/kpml-request+xml", NULL, NULL, NULL, true },
  { "application/kpml-response+xml", NULL, NULL, NULL, true },
  { "application/ld+json", "jsonld", "jsonld", NULL, true },
  { "application/lgr+xml", NULL, NULL, NULL, true },
  { "application/link-format", NULL, NULL, NULL, false },
  { "application/load-control+xml", NULL, NULL, NULL, true },
  { "application/lost+xml", "lostxml", "lostxml", NULL, true },
  { "application/lostsync+xml", NULL, NULL, NULL, true },
  { "application/lxf", NULL, NULL, NULL, false },
  { "application/mac-binhex40", "hqx", "hqx", NULL, false },
  { "application/mac-compactpro", "cpt", "cpt", NULL, false },
  { "application/macwriteii", NULL, NULL, NULL, false },
  { "application/mads+xml", "mads", "mads", NULL, true },
  { "application/manifest+json", "webmanifest", "webmanifest", "UTF-8", true },
  { "application/marc", "mrc", "mrc", NULL, false },
  { "application/marcxml+xml", "mrcx", "mrcx", NULL, true },
  { "application/mathematica", "ma", "ma;nb;mb", NULL, false },
  { "application/mathml+xml", "mathml", "mathml", NULL, true },
  { "application/mathml-content+xml", NULL, NULL, NULL, true },
  { "application/mathml-presentation+xml", NULL, NULL, NULL, true },
  { "application/mbms-associated-procedure-description+xml", NULL, NULL, NULL, true },
  { "application/mbms-deregister+xml", NULL, NULL, NULL, true },
  { "application/mbms-envelope+xml", NULL, NULL, NULL, true },
  { "application/mbms-msk+xml", NULL, NULL, NULL, true },
  { "application/mbms-msk-response+xml", NULL, NULL, NULL, true },
  { "application/mbms-protection-description+xml", NULL, NULL, NULL, true },
  { "application/mbms-reception-report+xml", NULL, NULL, NULL, true },
  { "application/mbms-register+xml", NULL, NULL, NULL, true },
  { "application/mbms-register-response+xml", NULL, NULL, NULL, true },
  { "application/mbms-schedule+xml", NULL, NULL, NULL, true },
  { "application/mbms-user-service-description+xml", NULL, NULL, NULL, true },
  { "application/mbox", "mbox", "mbox", NULL, false },
  { "application/media-policy-dataset+xml", NULL, NULL, NULL, true },
  { "application/media_control+xml", NULL, NULL, NULL, true },
  { "application/mediaservercontrol+xml", "mscml", "mscml", NULL, true },
  { "application/merge-patch+json", NULL, NULL, NULL, true },
  { "application/metalink+xml", "metalink", "metalink", NULL, true },
  { "application/metalink4+xml", "meta4", "meta4", NULL, true },
  { "application/mets+xml", "mets", "mets", NULL, true },
  { "application/mf4", NULL, NULL, NULL, false },
  { "application/mikey", NULL, NULL, NULL, false },
  { "application/mmt-usd+xml", NULL, NULL, NULL, true },
  { "application/mods+xml", "mods", "mods", NULL, true },
  { "application/moss-keys", NULL, NULL, NULL, false },
  { "application/moss-signature", NULL, NULL, NULL, false },
  { "application/mosskey-data", NULL, NULL, NULL, false },
  { "application/mosskey-request", NULL, NULL, NULL, false },
  { "application/mp21", "m21", "m21;mp21", NULL, false },
  { "application/mp4", "mp4s", "mp4s;m4p", NULL, false },
  { "application/mpeg4-generic", NULL, NULL, NULL, false },
  { "application/mpeg4-iod", NULL, NULL, NULL, false },
  { "application/mpeg4-iod-xmt", NULL, NULL, NULL, false },
  { "application/mrb-consumer+xml", NULL, NULL, NULL, true },
  { "application/mrb-publish+xml", NULL, NULL, NULL, true },
  { "application/msc-ivr+xml", NULL, NULL, NULL, true },
  { "application/msc-mixer+xml", NULL, NULL, NULL, true },
  { "application/msword", "doc", "doc;dot", NULL, false },
  { "application/mud+json", NULL, NULL, NULL, true },
  { "application/mxf", "mxf", "mxf", NULL, false },
  { "application/n-quads", NULL, NULL, NULL, false },
  { "application/n-triples", NULL, NULL, NULL, false },
  { "application/nasdata", NULL, NULL, NULL, false },
  { "application/news-checkgroups", NULL, NULL, NULL, false },
  { "application/news-groupinfo", NULL, NULL, NULL, false },
  { "application/news-transmission", NULL, NULL, NULL, false },
  { "application/nlsml+xml", NULL, NULL, NULL, true },
  { "application/node", NULL, NULL, NULL, false },
  { "application/nss", NULL, NULL, NULL, false },
  { "application/ocsp-request", NULL, NULL, NULL, false },
  { "application/ocsp-response", NULL, NULL, NULL, false },
  { "application/octet-stream", "bin", "bin;dms;lrf;mar;so;dist;distz;pkg;bpk;dump;elc;deploy;exe;dll;deb;dmg;iso;img;msi;msp;msm;buffer", NULL, false },
  { "application/oda", "oda", "oda", NULL, false },
  { "application/odx", NULL, NULL, NULL, false },
  { "application/oebps-package+xml", "opf", "opf", NULL, true },
  { "application/ogg", "ogx", "ogx", NULL, false },
  { "application/omdoc+xml", "omdoc", "omdoc", NULL, true },
  { "application/onenote", "onetoc", "onetoc;onetoc2;onetmp;onepkg", NULL, false },
  { "application/oxps", "oxps", "oxps", NULL, false },
  { "application/p2p-overlay+xml", NULL, NULL, NULL, true },
  { "application/parityfec", NULL, NULL, NULL, false },
  { "application/passport", NULL, NULL, NULL, false },
  { "application/patch-ops-error+xml", "xer", "xer", NULL, true },
  { "application/pdf", "pdf", "pdf", NULL, false },
  { "application/pdx", NULL, NULL, NULL, false },
  { "application/pgp-encrypted", "pgp", "pgp", NULL, false },
  { "application/pgp-keys", NULL, NULL, NULL, false },
  { "application/pgp-signature", "asc", "asc;sig", NULL, false },
  { "application/pics-rules", "prf", "prf", NULL, false },
  { "application/pidf+xml", NULL, NULL, NULL, true },
  { "application/pidf-diff+xml", NULL, NULL, NULL, true },
  { "application/pkcs10", "p10", "p10", NULL, false },
  { "application/pkcs12", NULL, NULL, NULL, false },
  { "application/pkcs7-mime", "p7m", "p7m;p7c", NULL, false },
  { "application/pkcs7-signature", "p7s", "p7s", NULL, false },
  { "application/pkcs8", "p8", "p8", NULL, false },
  { "application/pkcs8-encrypted", NULL, NULL, NULL, false },
  { "application/pkix-attr-cert", "ac", "ac", NULL, false },
  { "application/pkix-cert", "cer", "cer", NULL, false },
  { "application/pkix-crl", "crl", "crl", NULL, false },
  { "application/pkix-pkipath", "pkipath", "pkipath", NULL, false },
  { "application/pkixcmp", "pki", "pki", NULL, false },
  { "application/pls+xml", "pls", "pls", NULL, true },
  { "application/poc-settings+xml", NULL, NULL, NULL, true },
  { "application/postscript", "ai", "ai;eps;ps", NULL, true },
  { "application/ppsp-tracker+json", NULL, NULL, NULL, true },
  { "application/problem+json", NULL, NULL, NULL, true },
  { "application/problem+xml", NULL, NULL, NULL, true },
  { "application/provenance+xml", NULL, NULL, NULL, true },
  { "application/prs.alvestrand.titrax-sheet", NULL, NULL, NULL, false },
  { "application/prs.cww", "cww", "cww", NULL, false },
  { "application/prs.hpub+zip", NULL, NULL, NULL, false },
  { "application/prs.nprend", NULL, NULL, NULL, false },
  { "application/prs.plucker", NULL, NULL, NULL, false },
  { "application/prs.rdf-xml-crypt", NULL, NULL, NULL, false },
  { "application/prs.xsf+xml", NULL, NULL, NULL, true },
  { "application/pskc+xml", "pskcxml", "pskcxml", NULL, true },
  { "application/qsig", NULL, NULL, NULL, false },
  { "application/raml+yaml", "raml", "raml", NULL, true },
  { "application/raptorfec", NULL, NULL, NULL, false },
  { "application/rdap+json", NULL, NULL, NULL, true },
  { "application/rdf+xml", "rdf", "rdf;owl", NULL, true },
  { "application/reginfo+xml", "rif", "rif", NULL, true },
  { "application/relax-ng-compact-syntax", "rnc", "rnc", NULL, false },
  { "application/remote-printing", NULL, NULL, NULL, false },
  { "application/reputon+json", NULL, NULL, NULL, true },
  { "application/resource-lists+xml", "rl", "rl", NULL, true },
  { "application/resource-lists-diff+xml", "rld", "rld", NULL, true },
  { "application/rfc+xml", NULL, NULL, NULL, true },
  { "application/riscos", NULL, NULL, NULL, false },
  { "application/rlmi+xml", NULL, NULL, NULL, true },
  { "application/rls-services+xml", "rs", "rs", NULL, true },
  { "application/route-apd+xml", NULL, NULL, NULL, true },
  { "application/route-s-tsid+xml", NULL, NULL, NULL, true },
  { "application/route-usd+xml", NULL, NULL, NULL, true },
  { "application/rpki-ghostbusters", "gbr", "gbr", NULL, false },
  { "application/rpki-manifest", "mft", "mft", NULL, false },
  { "application/rpki-publication", NULL, NULL, NULL, false },
  { "application/rpki-roa", "roa", "roa", NULL, false },
  { "application/rpki-updown", NULL, NULL, NULL, false },
  { "application/rsd+xml", "rsd", "rsd", NULL, true },
  { "application/rss+xml", "rss", "rss", NULL, true },
  { "application/rtf", "rtf", "rtf", NULL, true },
  { "application/rtploopback", NULL, NULL, NULL, false },
  { "application/rtx", NULL, NULL, NULL, false },
  { "application/samlassertion+xml", NULL, NULL, NULL, true },
  { "application/samlmetadata+xml", NULL, NULL, NULL, true },
  { "application/sbml+xml", "sbml", "sbml", NULL, true },
  { "application/scaip+xml", NULL, NULL, NULL, true },
  { "application/scim+json", NULL, NULL, NULL, true },
  { "application/scvp-cv-request", "scq", "scq", NULL, false },
  { "application/scvp-cv-response", "scs", "scs", NULL, false },
  { "application/scvp-vp-request", "spq", "spq", NULL, false },
  { "application/scvp-vp-response", "spp", "spp", NULL, false },
  { "application/sdp", "sdp", "sdp", NULL, false },
  { "application/secevent+jwt", NULL, NULL, NULL, false },
  { "application/senml+cbor", NULL, NULL, NULL, false },
  { "application/senml+json", NULL, NULL, NULL, true },
  { "application/senml+xml", NULL, NULL, NULL, true },
  { "application/senml-exi", NULL, NULL, NULL, false },
  { "application/sensml+cbor", NULL, NULL, NULL, false },
  { "application/sensml+json", NULL, NULL, NULL, true },
  { "application/sensml+xml", NULL, NULL, NULL, true },
  { "application/sensml-exi", NULL, NULL, NULL, false },
  { "application/sep+xml", NULL, NULL, NULL, true },
  { "application/sep-exi", NULL, NULL, NULL, false },
  { "application/session-info", NULL, NULL, NULL, false },
  { "application/set-payment", NULL, NULL, NULL, false },
  { "application/set-payment-initiation", "setpay", "setpay", NULL, false },
  { "application/set-registration", NULL, NULL, NULL, false },
  { "application/set-registration-initiation", "setreg", "setreg", NULL, false },
  { "application/sgml", NULL, NULL, NULL, false },
  { "application/sgml-open-catalog", NULL, NULL, NULL, false },
  { "application/shf+xml", "shf", "shf", NULL, true },
  { "application/sieve", NULL, NULL, NULL, false },
  { "application/simple-filter+xml", NULL, NULL, NULL, true },
  { "application/simple-message-summary", NULL, NULL, NULL, false },
  { "application/simplesymbolcontainer", NULL, NULL, NULL, false },
  { "application/slate", NULL, NULL, NULL, false },
  { "application/smil", NULL, NULL, NULL, false },
  { "application/smil+xml", "smi", "smi;smil", NULL, true },
  { "application/smpte336m", NULL, NULL, NULL, false },
  { "application/soap+fastinfoset", NULL, NULL, NULL, false },
  { "application/soap+xml", NULL, NULL, NULL, true },
  { "application/sparql-query", "rq", "rq", NULL, false },
  { "application/sparql-results+xml", "srx", "srx", NULL, true },
  { "application/spirits-event+xml", NULL, NULL, NULL, true },
  { "application/sql", NULL, NULL, NULL, false },
  { "application/srgs", "gram", "gram", NULL, false },
  { "application/srgs+xml", "grxml", "grxml", NULL, true },
  { "application/sru+xml", "sru", "sru", NULL, true },
  { "application/ssdl+xml", "ssdl", "ssdl", NULL, true },
  { "application/ssml+xml", "ssml", "ssml", NULL, true },
  { "application/stix+json", NULL, NULL, NULL, true },
  { "application/tamp-apex-update", NULL, NULL, NULL, false },
  { "application/tamp-apex-update-confirm", NULL, NULL, NULL, false },
  { "application/tamp-community-update", NULL, NULL, NULL, false },
  { "application/tamp-community-update-confirm", NULL, NULL, NULL, false },
  { "application/tamp-error", NULL, NULL, NULL, false },
  { "application/tamp-sequence-adjust", NULL, NULL, NULL, false },
  { "application/tamp-sequence-adjust-confirm", NULL, NULL, NULL, false },
  { "application/tamp-status-query", NULL, NULL, NULL, false },
  { "application/tamp-status-response", NULL, NULL, NULL, false },
  { "application/tamp-update", NULL, NULL, NULL, false },
  { "application/tamp-update-confirm", NULL, NULL, NULL, false },
  { "application/tar", NULL, NULL, NULL, true },
  { "application/taxii+json", NULL, NULL, NULL, true },
  { "application/tei+xml", "tei", "tei;teicorpus", NULL, true },
  { "application/thraud+xml", "tfi", "tfi", NULL, true },
  { "application/timestamp-query", NULL, NULL, NULL, false },
  { "application/timestamp-reply", NULL, NULL, NULL, false },
  { "application/timestamped-data", "tsd", "tsd", NULL, false },
  { "application/tlsrpt+gzip", NULL, NULL, NULL, false },
  { "application/tlsrpt+json", NULL, NULL, NULL, true },
  { "application/tnauthlist", NULL, NULL, NULL, false },
  { "application/trickle-ice-sdpfrag", NULL, NULL, NULL, false },
  { "application/trig", NULL, NULL, NULL, false },
  { "application/ttml+xml", NULL, NULL, NULL, true },
  { "application/tve-trigger", NULL, NULL, NULL, false },
  { "application/ulpfec", NULL, NULL, NULL, false },
  { "application/urc-grpsheet+xml", NULL, NULL, NULL, true },
  { "application/urc-ressheet+xml", NULL, NULL, NULL, true },
  { "application/urc-targetdesc+xml", NULL, NULL, NULL, true },
  { "application/urc-uisocketdesc+xml", NULL, NULL, NULL, true },
  { "application/vcard+json", NULL, NULL, NULL, true },
  { "application/vcard+xml", NULL, NULL, NULL, true },
  { "application/vemmi", NULL, NULL, NULL, false },
  { "application/vividence.scriptfile", NULL, NULL, NULL, false },
  { "application/vnd.1000minds.decision-model+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp-prose+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp-prose-pc3ch+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp-v2x-local-service-information", NULL, NULL, NULL, false },
  { "application/vnd.3gpp.access-transfer-events+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.bsf+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.gmop+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.mcdata-payload", NULL, NULL, NULL, false },
  { "application/vnd.3gpp.mcdata-signalling", NULL, NULL, NULL, false },
  { "application/vnd.3gpp.mcptt-affiliation-command+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.mcptt-floor-request+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.mcptt-info+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.mcptt-location-info+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.mcptt-mbms-usage-info+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.mcptt-signed+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.mid-call+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.pic-bw-large", "plb", "plb", NULL, false },
  { "application/vnd.3gpp.pic-bw-small", "psb", "psb", NULL, false },
  { "application/vnd.3gpp.pic-bw-var", "pvb", "pvb", NULL, false },
  { "application/vnd.3gpp.sms", NULL, NULL, NULL, false },
  { "application/vnd.3gpp.sms+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.srvcc-ext+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.srvcc-info+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.state-and-event-info+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp.ussd+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp2.bcmcsinfo+xml", NULL, NULL, NULL, true },
  { "application/vnd.3gpp2.sms", NULL, NULL, NULL, false },
  { "application/vnd.3gpp2.tcap", "tcap", "tcap", NULL, false },
  { "application/vnd.3lightssoftware.imagescal", NULL, NULL, NULL, false },
  { "application/vnd.3m.post-it-notes", "pwn", "pwn", NULL, false },
  { "application/vnd.accpac.simply.aso", "aso", "aso", NULL, false },
  { "application/vnd.accpac.simply.imp", "imp", "imp", NULL, false },
  { "application/vnd.acucobol", "acu", "acu", NULL, false },
  { "application/vnd.acucorp", "atc", "atc;acutc", NULL, false },
  { "application/vnd.adobe.air-application-installer-package+zip", "air", "air", NULL, false },
  { "application/vnd.adobe.flash.movie", NULL, NULL, NULL, false },
  { "application/vnd.adobe.formscentral.fcdt", "fcdt", "fcdt", NULL, false },
  { "application/vnd.adobe.fxp", "fxp", "fxp;fxpl", NULL, false },
  { "application/vnd.adobe.partial-upload", NULL, NULL, NULL, false },
  { "application/vnd.adobe.xdp+xml", "xdp", "xdp", NULL, true },
  { "application/vnd.adobe.xfdf", "xfdf", "xfdf", NULL, false },
  { "application/vnd.aether.imp", NULL, NULL, NULL, false },
  { "application/vnd.afpc.afplinedata", NULL, NULL, NULL, false },
  { "application/vnd.afpc.modca", NULL, NULL, NULL, false },
  { "application/vnd.ah-barcode", NULL, NULL, NULL, false },
  { "application/vnd.ahead.space", "ahead", "ahead", NULL, false },
  { "application/vnd.airzip.filesecure.azf", "azf", "azf", NULL, false },
  { "application/vnd.airzip.filesecure.azs", "azs", "azs", NULL, false },
  { "application/vnd.amadeus+json", NULL, NULL, NULL, true },
  { "application/vnd.amazon.ebook", "azw", "azw", NULL, false },
  { "application/vnd.amazon.mobi8-ebook", NULL, NULL, NULL, false },
  { "application/vnd.americandynamics.acc", "acc", "acc", NULL, false },
  { "application/vnd.amiga.ami", "ami", "ami", NULL, false },
  { "application/vnd.amundsen.maze+xml", NULL, NULL, NULL, true },
  { "application/vnd.android.package-archive", "apk", "apk", NULL, false },
  { "application/vnd.anki", NULL, NULL, NULL, false },
  { "application/vnd.anser-web-certificate-issue-initiation", "cii", "cii", NULL, false },
  { "application/vnd.anser-web-funds-transfer-initiation", "fti", "fti", NULL, false },
  { "application/vnd.antix.game-component", "atx", "atx", NULL, false },
  { "application/vnd.apache.thrift.binary", NULL, NULL, NULL, false },
  { "application/vnd.apache.thrift.compact", NULL, NULL, NULL, false },
  { "application/vnd.apache.thrift.json", NULL, NULL, NULL, false },
  { "application/vnd.api+json", NULL, NULL, NULL, true },
  { "application/vnd.apothekende.reservation+json", NULL, NULL, NULL, true },
  { "application/vnd.apple.installer+xml", "mpkg", "mpkg", NULL, true },
  { "application/vnd.apple.mpegurl", "m3u8", "m3u8", NULL, false },
  { "application/vnd.apple.pkpass", "pkpass", "pkpass", NULL, false },
  { "application/vnd.arastra.swi", NULL, NULL, NULL, false },
  { "application/vnd.aristanetworks.swi", "swi", "swi", NULL, false },
  { "application/vnd.artisan+json", NULL, NULL, NULL, true },
  { "application/vnd.artsquare", NULL, NULL, NULL, false },
  { "application/vnd.astraea-software.iota", "iota", "iota", NULL, false },
  { "application/vnd.audiograph", "aep", "aep", NULL, false },
  { "application/vnd.autopackage", NULL, NULL, NULL, false },
  { "application/vnd.avalon+json", NULL, NULL, NULL, true },
  { "application/vnd.avistar+xml", NULL, NULL, NULL, true },
  { "application/vnd.balsamiq.bmml+xml", NULL, NULL, NULL, true },
  { "application/vnd.balsamiq.bmpr", NULL, NULL, NULL, false },
  { "application/vnd.banana-accounting", NULL, NULL, NULL, false },
  { "application/vnd.bbf.usp.msg", NULL, NULL, NULL, false },
  { "application/vnd.bbf.usp.msg+json", NULL, NULL, NULL, true },
  { "application/vnd.bekitzur-stech+json", NULL, NULL, NULL, true },
  { "application/vnd.bint.med-content", NULL, NULL, NULL, false },
  { "application/vnd.biopax.rdf+xml", NULL, NULL, NULL, true },
  { "application/vnd.blink-idb-value-wrapper", NULL, NULL, NULL, false },
  { "application/vnd.blueice.multipass", "mpm", "mpm", NULL, false },
  { "application/vnd.bluetooth.ep.oob", NULL, NULL, NULL, false },
  { "application/vnd.bluetooth.le.oob", NULL, NULL, NULL, false },
  { "application/vnd.bmi", "bmi", "bmi", NULL, false },
  { "application/vnd.businessobjects", "rep", "rep", NULL, false },
  { "application/vnd.byu.uapi+json", NULL, NULL, NULL, true },
  { "application/vnd.cab-jscript", NULL, NULL, NULL, false },
  { "application/vnd.canon-cpdl", NULL, NULL, NULL, false },
  { "application/vnd.canon-lips", NULL, NULL, NULL, false },
  { "application/vnd.capasystems-pg+json", NULL, NULL, NULL, true },
  { "application/vnd.cendio.thinlinc.clientconf", NULL, NULL, NULL, false },
  { "application/vnd.century-systems.tcp_stream", NULL, NULL, NULL, false },
  { "application/vnd.chemdraw+xml", "cdxml", "cdxml", NULL, true },
  { "application/vnd.chess-pgn", NULL, NULL, NULL, false },
  { "application/vnd.chipnuts.karaoke-mmd", "mmd", "mmd", NULL, false },
  { "application/vnd.cinderella", "cdy", "cdy", NULL, false },
  { "application/vnd.cirpack.isdn-ext", NULL, NULL, NULL, false },
  { "application/vnd.citationstyles.style+xml", "csl", "csl", NULL, true },
  { "application/vnd.claymore", "cla", "cla", NULL, false },
  { "application/vnd.cloanto.rp9", "rp9", "rp9", NULL, false },
  { "application/vnd.clonk.c4group", "c4g", "c4g;c4d;c4f;c4p;c4u", NULL, false },
  { "application/vnd.cluetrust.cartomobile-config", "c11amc", "c11amc", NULL, false },
  { "application/vnd.cluetrust.cartomobile-config-pkg", "c11amz", "c11amz", NULL, false },
  { "application/vnd.coffeescript", NULL, NULL, NULL, false },
  { "application/vnd.collabio.xodocuments.document", NULL, NULL, NULL, false },
  { "application/vnd.collabio.xodocuments.document-template", NULL, NULL, NULL, false },
  { "application/vnd.collabio.xodocuments.presentation", NULL, NULL, NULL, false },
  { "application/vnd.collabio.xodocuments.presentation-template", NULL, NULL, NULL, false },
  { "application/vnd.collabio.xodocuments.spreadsheet", NULL, NULL, NULL, false },
  { "application/vnd.collabio.xodocuments.spreadsheet-template", NULL, NULL, NULL, false },
  { "application/vnd.collection+json", NULL, NULL, NULL, true },
  { "application/vnd.collection.doc+json", NULL, NULL, NULL, true },
  { "application/vnd.collection.next+json", NULL, NULL, NULL, true },
  { "application/vnd.comicbook+zip", NULL, NULL, NULL, false },
  { "application/vnd.comicbook-rar", NULL, NULL, NULL, false },
  { "application/vnd.commerce-battelle", NULL, NULL, NULL, false },
  { "application/vnd.commonspace", "csp", "csp", NULL, false },
  { "application/vnd.contact.cmsg", "cdbcmsg", "cdbcmsg", NULL, false },
  { "application/vnd.coreos.ignition+json", NULL, NULL, NULL, true },
  { "application/vnd.cosmocaller", "cmc", "cmc", NULL, false },
  { "application/vnd.crick.clicker", "clkx", "clkx", NULL, false },
  { "application/vnd.crick.clicker.keyboard", "clkk", "clkk", NULL, false },
  { "application/vnd.crick.clicker.palette", "clkp", "clkp", NULL, false },
  { "application/vnd.crick.clicker.template", "clkt", "clkt", NULL, false },
  { "application/vnd.crick.clicker.wordbank", "clkw", "clkw", NULL, false },
  { "application/vnd.criticaltools.wbs+xml", "wbs", "wbs", NULL, true },
  { "application/vnd.ctc-posml", "pml", "pml", NULL, false },
  { "application/vnd.ctct.ws+xml", NULL, NULL, NULL, true },
  { "application/vnd.cups-pdf", NULL, NULL, NULL, false },
  { "application/vnd.cups-postscript", NULL, NULL, NULL, false },
  { "application/vnd.cups-ppd", "ppd", "ppd", NULL, false },
  { "application/vnd.cups-raster", NULL, NULL, NULL, false },
  { "application/vnd.cups-raw", NULL, NULL, NULL, false },
  { "application/vnd.curl", NULL, NULL, NULL, false },
  { "application/vnd.curl.car", "car", "car", NULL, false },
  { "application/vnd.curl.pcurl", "pcurl", "pcurl", NULL, false },
  { "application/vnd.cyan.dean.root+xml", NULL, NULL, NULL, true },
  { "application/vnd.cybank", NULL, NULL, NULL, false },
  { "application/vnd.d2l.coursepackage1p0+zip", NULL, NULL, NULL, false },
  { "application/vnd.dart", "dart", "dart", NULL, true },
  { "application/vnd.data-vision.rdz", "rdz", "rdz", NULL, false },
  { "application/vnd.datapackage+json", NULL, NULL, NULL, true },
  { "application/vnd.dataresource+json", NULL, NULL, NULL, true },
  { "application/vnd.debian.binary-package", NULL, NULL, NULL, false },
  { "application/vnd.dece.data", "uvf", "uvf;uvvf;uvd;uvvd", NULL, false },
  { "application/vnd.dece.ttml+xml", "uvt", "uvt;uvvt", NULL, true },
  { "application/vnd.dece.unspecified", "uvx", "uvx;uvvx", NULL, false },
  { "application/vnd.dece.zip", "uvz", "uvz;uvvz", NULL, false },
  { "application/vnd.denovo.fcselayout-link", "fe_launch", "fe_launch", NULL, false },
  { "application/vnd.desmume-movie", NULL, NULL, NULL, false },
  { "application/vnd.desmume.movie", NULL, NULL, NULL, false },
  { "application/vnd.dir-bi.plate-dl-nosuffix", NULL, NULL, NULL, false },
  { "application/vnd.dm.delegation+xml", NULL, NULL, NULL, true },
  { "application/vnd.dna", "dna", "dna", NULL, false },
  { "application/vnd.document+json", NULL, NULL, NULL, true },
  { "application/vnd.dolby.mlp", "mlp", "mlp", NULL, false },
  { "application/vnd.dolby.mobile.1", NULL, NULL, NULL, false },
  { "application/vnd.dolby.mobile.2", NULL, NULL, NULL, false },
  { "application/vnd.doremir.scorecloud-binary-document", NULL, NULL, NULL, false },
  { "application/vnd.dpgraph", "dpg", "dpg", NULL, false },
  { "application/vnd.dreamfactory", "dfac", "dfac", NULL, false },
  { "application/vnd.drive+json", NULL, NULL, NULL, true },
  { "application/vnd.ds-keypoint", "kpxx", "kpxx", NULL, false },
  { "application/vnd.dtg.local", NULL, NULL, NULL, false },
  { "application/vnd.dtg.local.flash", NULL, NULL, NULL, false },
  { "application/vnd.dtg.local.html", NULL, NULL, NULL, false },
  { "application/vnd.dvb.ait", "ait", "ait", NULL, false },
  { "application/vnd.dvb.dvbj", NULL, NULL, NULL, false },
  { "application/vnd.dvb.esgcontainer", NULL, NULL, NULL, false },
  { "application/vnd.dvb.ipdcdftnotifaccess", NULL, NULL, NULL, false },
  { "application/vnd.dvb.ipdcesgaccess", NULL, NULL, NULL, false },
  { "application/vnd.dvb.ipdcesgaccess2", NULL, NULL, NULL, false },
  { "application/vnd.dvb.ipdcesgpdd", NULL, NULL, NULL, false },
  { "application/vnd.dvb.ipdcroaming", NULL, NULL, NULL, false },
  { "application/vnd.dvb.iptv.alfec-base", NULL, NULL, NULL, false },
  { "application/vnd.dvb.iptv.alfec-enhancement", NULL, NULL, NULL, false },
  { "application/vnd.dvb.notif-aggregate-root+xml", NULL, NULL, NULL, true },
  { "application/vnd.dvb.notif-container+xml", NULL, NULL, NULL, true },
  { "application/vnd.dvb.notif-generic+xml", NULL, NULL, NULL, true },
  { "application/vnd.dvb.notif-ia-msglist+xml", NULL, NULL, NULL, true },
  { "application/vnd.dvb.notif-ia-registration-request+xml", NULL, NULL, NULL, true },
  { "application/vnd.dvb.notif-ia-registration-response+xml", NULL, NULL, NULL, true },
  { "application/vnd.dvb.notif-init+xml", NULL, NULL, NULL, true },
  { "application/vnd.dvb.pfr", NULL, NULL, NULL, false },
  { "application/vnd.dvb.service", "svc", "svc", NULL, false },
  { "application/vnd.dxr", NULL, NULL, NULL, false },
  { "application/vnd.dynageo", "geo", "geo", NULL, false },
  { "application/vnd.dzr", NULL, NULL, NULL, false },
  { "application/vnd.easykaraoke.cdgdownload", NULL, NULL, NULL, false },
  { "application/vnd.ecdis-update", NULL, NULL, NULL, false },
  { "application/vnd.ecip.rlp", NULL, NULL, NULL, false },
  { "application/vnd.ecowin.chart", "mag", "mag", NULL, false },
  { "application/vnd.ecowin.filerequest", NULL, NULL, NULL, false },
  { "application/vnd.ecowin.fileupdate", NULL, NULL, NULL, false },
  { "application/vnd.ecowin.series", NULL, NULL, NULL, false },
  { "application/vnd.ecowin.seriesrequest", NULL, NULL, NULL, false },
  { "application/vnd.ecowin.seriesupdate", NULL, NULL, NULL, false },
  { "application/vnd.efi.img", NULL, NULL, NULL, false },
  { "application/vnd.efi.iso", NULL, NULL, NULL, false },
  { "application/vnd.emclient.accessrequest+xml", NULL, NULL, NULL, true },
  { "application/vnd.enliven", "nml", "nml", NULL, false },
  { "application/vnd.enphase.envoy", NULL, NULL, NULL, false },
  { "application/vnd.eprints.data+xml", NULL, NULL, NULL, true },
  { "application/vnd.epson.esf", "esf", "esf", NULL, false },
  { "application/vnd.epson.msf", "msf", "msf", NULL, false },
  { "application/vnd.epson.quickanime", "qam", "qam", NULL, false },
  { "application/vnd.epson.salt", "slt", "slt", NULL, false },
  { "application/vnd.epson.ssf", "ssf", "ssf", NULL, false },
  { "application/vnd.ericsson.quickcall", NULL, NULL, NULL, false },
  { "application/vnd.espass-espass+zip", NULL, NULL, NULL, false },
  { "application/vnd.eszigno3+xml", "es3", "es3;et3", NULL, true },
  { "application/vnd.etsi.aoc+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.asic-e+zip", NULL, NULL, NULL, false },
  { "application/vnd.etsi.asic-s+zip", NULL, NULL, NULL, false },
  { "application/vnd.etsi.cug+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvcommand+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvdiscovery+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvprofile+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvsad-bc+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvsad-cod+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvsad-npvr+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvservice+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvsync+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.iptvueprofile+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.mcid+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.mheg5", NULL, NULL, NULL, false },
  { "application/vnd.etsi.overload-control-policy-dataset+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.pstn+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.sci+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.simservs+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.timestamp-token", NULL, NULL, NULL, false },
  { "application/vnd.etsi.tsl+xml", NULL, NULL, NULL, true },
  { "application/vnd.etsi.tsl.der", NULL, NULL, NULL, false },
  { "application/vnd.eudora.data", NULL, NULL, NULL, false },
  { "application/vnd.evolv.ecig.profile", NULL, NULL, NULL, false },
  { "application/vnd.evolv.ecig.settings", NULL, NULL, NULL, false },
  { "application/vnd.evolv.ecig.theme", NULL, NULL, NULL, false },
  { "application/vnd.ezpix-album", "ez2", "ez2", NULL, false },
  { "application/vnd.ezpix-package", "ez3", "ez3", NULL, false },
  { "application/vnd.f-secure.mobile", NULL, NULL, NULL, false },
  { "application/vnd.fastcopy-disk-image", NULL, NULL, NULL, false },
  { "application/vnd.fdf", "fdf", "fdf", NULL, false },
  { "application/vnd.fdsn.mseed", "mseed", "mseed", NULL, false },
  { "application/vnd.fdsn.seed", "seed", "seed;dataless", NULL, false },
  { "application/vnd.ffsns", NULL, NULL, NULL, false },
  { "application/vnd.filmit.zfc", NULL, NULL, NULL, false },
  { "application/vnd.fints", NULL, NULL, NULL, false },
  { "application/vnd.firemonkeys.cloudcell", NULL, NULL, NULL, false },
  { "application/vnd.flographit", "gph", "gph", NULL, false },
  { "application/vnd.fluxtime.clip", "ftc", "ftc", NULL, false },
  { "application/vnd.font-fontforge-sfd", NULL, NULL, NULL, false },
  { "application/vnd.framemaker", "fm", "fm;frame;maker;book", NULL, false },
  { "application/vnd.frogans.fnc", "fnc", "fnc", NULL, false },
  { "application/vnd.frogans.ltf", "ltf", "ltf", NULL, false },
  { "application/vnd.fsc.weblaunch", "fsc", "fsc", NULL, false },
  { "application/vnd.fujitsu.oasys", "oas", "oas", NULL, false },
  { "application/vnd.fujitsu.oasys2", "oa2", "oa2", NULL, false },
  { "application/vnd.fujitsu.oasys3", "oa3", "oa3", NULL, false },
  { "application/vnd.fujitsu.oasysgp", "fg5", "fg5", NULL, false },
  { "application/vnd.fujitsu.oasysprs", "bh2", "bh2", NULL, false },
  { "application/vnd.fujixerox.art-ex", NULL, NULL, NULL, false },
  { "application/vnd.fujixerox.art4", NULL, NULL, NULL, false },
  { "application/vnd.fujixerox.ddd", "ddd", "ddd", NULL, false },
  { "application/vnd.fujixerox.docuworks", "xdw", "xdw", NULL, false },
  { "application/vnd.fujixerox.docuworks.binder", "xbd", "xbd", NULL, false },
  { "application/vnd.fujixerox.docuworks.container", NULL, NULL, NULL, false },
  { "application/vnd.fujixerox.hbpl", NULL, NULL, NULL, false },
  { "application/vnd.fut-misnet", NULL, NULL, NULL, false },
  { "application/vnd.fuzzysheet", "fzs", "fzs", NULL, false },
  { "application/vnd.genomatix.tuxedo", "txd", "txd", NULL, false },
  { "application/vnd.geo+json", NULL, NULL, NULL, true },
  { "application/vnd.geocube+xml", NULL, NULL, NULL, true },
  { "application/vnd.geogebra.file", "ggb", "ggb", NULL, false },
  { "application/vnd.geogebra.tool", "ggt", "ggt", NULL, false },
  { "application/vnd.geometry-explorer", "gex", "gex;gre", NULL, false },
  { "application/vnd.geonext", "gxt", "gxt", NULL, false },
  { "application/vnd.geoplan", "g2w", "g2w", NULL, false },
  { "application/vnd.geospace", "g3w", "g3w", NULL, false },
  { "application/vnd.gerber", NULL, NULL, NULL, false },
  { "application/vnd.globalplatform.card-content-mgt", NULL, NULL, NULL, false },
  { "application/vnd.globalplatform.card-content-mgt-response", NULL, NULL, NULL, false },
  { "application/vnd.gmx", "gmx", "gmx", NULL, false },
  { "application/vnd.google-apps.document", "gdoc", "gdoc", NULL, false },
  { "application/vnd.google-apps.presentation", "gslides", "gslides", NULL, false },
  { "application/vnd.google-apps.spreadsheet", "gsheet", "gsheet", NULL, false },
  { "application/vnd.google-earth.kml+xml", "kml", "kml", NULL, true },
  { "application/vnd.google-earth.kmz", "kmz", "kmz", NULL, false },
  { "application/vnd.gov.sk.e-form+xml", NULL, NULL, NULL, true },
  { "application/vnd.gov.sk.e-form+zip", NULL, NULL, NULL, false },
  { "application/vnd.gov.sk.xmldatacontainer+xml", NULL, NULL, NULL, true },
  { "application/vnd.grafeq", "gqf", "gqf;gqs", NULL, false },
  { "application/vnd.gridmp", NULL, NULL, NULL, false },
  { "application/vnd.groove-account", "gac", "gac", NULL, false },
  { "application/vnd.groove-help", "ghf", "ghf", NULL, false },
  { "application/vnd.groove-identity-message", "gim", "gim", NULL, false },
  { "application/vnd.groove-injector", "grv", "grv", NULL, false },
  { "application/vnd.groove-tool-message", "gtm", "gtm", NULL, false },
  { "application/vnd.groove-tool-template", "tpl", "tpl", NULL, false },
  { "application/vnd.groove-vcard", "vcg", "vcg", NULL, false },
  { "application/vnd.hal+json", NULL, NULL, NULL, true },
  { "application/vnd.hal+xml", "hal", "hal", NULL, true },
  { "application/vnd.handheld-entertainment+xml", "zmm", "zmm", NULL, true },
  { "application/vnd.hbci", "hbci", "hbci", NULL, false },
  { "application/vnd.hc+json", NULL, NULL, NULL, true },
  { "application/vnd.hcl-bireports", NULL, NULL, NULL, false },
  { "application/vnd.hdt", NULL, NULL, NULL, false },
  { "application/vnd.heroku+json", NULL, NULL, NULL, true },
  { "application/vnd.hhe.lesson-player", "les", "les", NULL, false },
  { "application/vnd.hp-hpgl", "hpgl", "hpgl", NULL, false },
  { "application/vnd.hp-hpid", "hpid", "hpid", NULL, false },
  { "application/vnd.hp-hps", "hps", "hps", NULL, false },
  { "application/vnd.hp-jlyt", "jlt", "jlt", NULL, false },
  { "application/vnd.hp-pcl", "pcl", "pcl", NULL, false },
  { "application/vnd.hp-pclxl", "pclxl", "pclxl", NULL, false },
  { "application/vnd.httphone", NULL, NULL, NULL, false },
  { "application/vnd.hydrostatix.sof-data", "sfd-hdstx", "sfd-hdstx", NULL, false },
  { "application/vnd.hyper+json", NULL, NULL, NULL, true },
  { "application/vnd.hyper-item+json", NULL, NULL, NULL, true },
  { "application/vnd.hyperdrive+json", NULL, NULL, NULL, true },
  { "application/vnd.hzn-3d-crossword", NULL, NULL, NULL, false },
  { "application/vnd.ibm.afplinedata", NULL, NULL, NULL, false },
  { "application/vnd.ibm.electronic-media", NULL, NULL, NULL, false },
  { "application/vnd.ibm.minipay", "mpy", "mpy", NULL, false },
  { "application/vnd.ibm.modcap", "afp", "afp;listafp;list3820", NULL, false },
  { "application/vnd.ibm.rights-management", "irm", "irm", NULL, false },
  { "application/vnd.ibm.secure-container", "sc", "sc", NULL, false },
  { "application/vnd.iccprofile", "icc", "icc;icm", NULL, false },
  { "application/vnd.ieee.1905", NULL, NULL, NULL, false },
  { "application/vnd.igloader", "igl", "igl", NULL, false },
  { "application/vnd.imagemeter.folder+zip", NULL, NULL, NULL, false },
  { "application/vnd.imagemeter.image+zip", NULL, NULL, NULL, false },
  { "application/vnd.immervision-ivp", "ivp", "ivp", NULL, false },
  { "application/vnd.immervision-ivu", "ivu", "ivu", NULL, false },
  { "application/vnd.ims.imsccv1p1", NULL, NULL, NULL, false },
  { "application/vnd.ims.imsccv1p2", NULL, NULL, NULL, false },
  { "application/vnd.ims.imsccv1p3", NULL, NULL, NULL, false },
  { "application/vnd.ims.lis.v2.result+json", NULL, NULL, NULL, true },
  { "application/vnd.ims.lti.v2.toolconsumerprofile+json", NULL, NULL, NULL, true },
  { "application/vnd.ims.lti.v2.toolproxy+json", NULL, NULL, NULL, true },
  { "application/vnd.ims.lti.v2.toolproxy.id+json", NULL, NULL, NULL, true },
  { "application/vnd.ims.lti.v2.toolsettings+json", NULL, NULL, NULL, true },
  { "application/vnd.ims.lti.v2.toolsettings.simple+json", NULL, NULL, NULL, true },
  { "application/vnd.informedcontrol.rms+xml", NULL, NULL, NULL, true },
  { "application/vnd.informix-visionary", NULL, NULL, NULL, false },
  { "application/vnd.infotech.project", NULL, NULL, NULL, false },
  { "application/vnd.infotech.project+xml", NULL, NULL, NULL, true },
  { "application/vnd.innopath.wamp.notification", NULL, NULL, NULL, false },
  { "application/vnd.insors.igm", "igm", "igm", NULL, false },
  { "application/vnd.intercon.formnet", "xpw", "xpw;xpx", NULL, false },
  { "application/vnd.intergeo", "i2g", "i2g", NULL, false },
  { "application/vnd.intertrust.digibox", NULL, NULL, NULL, false },
  { "application/vnd.intertrust.nncp", NULL, NULL, NULL, false },
  { "application/vnd.intu.qbo", "qbo", "qbo", NULL, false },
  { "application/vnd.intu.qfx", "qfx", "qfx", NULL, false },
  { "application/vnd.iptc.g2.catalogitem+xml", NULL, NULL, NULL, true },
  { "application/vnd.iptc.g2.conceptitem+xml", NULL, NULL, NULL, true },
  { "application/vnd.iptc.g2.knowledgeitem+xml", NULL, NULL, NULL, true },
  { "application/vnd.iptc.g2.newsitem+xml", NULL, NULL, NULL, true },
  { "application/vnd.iptc.g2.newsmessage+xml", NULL, NULL, NULL, true },
  { "application/vnd.iptc.g2.packageitem+xml", NULL, NULL, NULL, true },
  { "application/vnd.iptc.g2.planningitem+xml", NULL, NULL, NULL, true },
  { "application/vnd.ipunplugged.rcprofile", "rcprofile", "rcprofile", NULL, false },
  { "application/vnd.irepository.package+xml", "irp", "irp", NULL, true },
  { "application/vnd.is-xpr", "xpr", "xpr", NULL, false },
  { "application/vnd.isac.fcs", "fcs", "fcs", NULL, false },
  { "application/vnd.jam", "jam", "jam", NULL, false },
  { "application/vnd.japannet-directory-service", NULL, NULL, NULL, false },
  { "application/vnd.japannet-jpnstore-wakeup", NULL, NULL, NULL, false },
  { "application/vnd.japannet-payment-wakeup", NULL, NULL, NULL, false },
  { "application/vnd.japannet-registration", NULL, NULL, NULL, false },
  { "application/vnd.japannet-registration-wakeup", NULL, NULL, NULL, false },
  { "application/vnd.japannet-setstore-wakeup", NULL, NULL, NULL, false },
  { "application/vnd.japannet-verification", NULL, NULL, NULL, false },
  { "application/vnd.japannet-verification-wakeup", NULL, NULL, NULL, false },
  { "application/vnd.jcp.javame.midlet-rms", "rms", "rms", NULL, false },
  { "application/vnd.jisp", "jisp", "jisp", NULL, false },
  { "application/vnd.joost.joda-archive", "joda", "joda", NULL, false },
  { "application/vnd.jsk.isdn-ngn", NULL, NULL, NULL, false },
  { "application/vnd.kahootz", "ktz", "ktz;ktr", NULL, false },
  { "application/vnd.kde.karbon", "karbon", "karbon", NULL, false },
  { "application/vnd.kde.kchart", "chrt", "chrt", NULL, false },
  { "application/vnd.kde.kformula", "kfo", "kfo", NULL, false },
  { "application/vnd.kde.kivio", "flw", "flw", NULL, false },
  { "application/vnd.kde.kontour", "kon", "kon", NULL, false },
  { "application/vnd.kde.kpresenter", "kpr", "kpr;kpt", NULL, false },
  { "application/vnd.kde.kspread", "ksp", "ksp", NULL, false },
  { "application/vnd.kde.kword", "kwd", "kwd;kwt", NULL, false },
  { "application/vnd.kenameaapp", "htke", "htke", NULL, false },
  { "application/vnd.kidspiration", "kia", "kia", NULL, false },
  { "application/vnd.kinar", "kne", "kne;knp", NULL, false },
  { "application/vnd.koan", "skp", "skp;skd;skt;skm", NULL, false },
  { "application/vnd.kodak-descriptor", "sse", "sse", NULL, false },
  { "application/vnd.las.las+json", NULL, NULL, NULL, true },
  { "application/vnd.las.las+xml", "lasxml", "lasxml", NULL, true },
  { "application/vnd.leap+json", NULL, NULL, NULL, true },
  { "application/vnd.liberty-request+xml", NULL, NULL, NULL, true },
  { "application/vnd.llamagraphics.life-balance.desktop", "lbd", "lbd", NULL, false },
  { "application/vnd.llamagraphics.life-balance.exchange+xml", "lbe", "lbe", NULL, true },
  { "application/vnd.lotus-1-2-3", "123", "123", NULL, false },
  { "application/vnd.lotus-approach", "apr", "apr", NULL, false },
  { "application/vnd.lotus-freelance", "pre", "pre", NULL, false },
  { "application/vnd.lotus-notes", "nsf", "nsf", NULL, false },
  { "application/vnd.lotus-organizer", "org", "org", NULL, false },
  { "application/vnd.lotus-screencam", "scm", "scm", NULL, false },
  { "application/vnd.lotus-wordpro", "lwp", "lwp", NULL, false },
  { "application/vnd.macports.portpkg", "portpkg", "portpkg", NULL, false },
  { "application/vnd.mapbox-vector-tile", NULL, NULL, NULL, false },
  { "application/vnd.marlin.drm.actiontoken+xml", NULL, NULL, NULL, true },
  { "application/vnd.marlin.drm.conftoken+xml", NULL, NULL, NULL, true },
  { "application/vnd.marlin.drm.license+xml", NULL, NULL, NULL, true },
  { "application/vnd.marlin.drm.mdcf", NULL, NULL, NULL, false },
  { "application/vnd.mason+json", NULL, NULL, NULL, true },
  { "application/vnd.maxmind.maxmind-db", NULL, NULL, NULL, false },
  { "application/vnd.mcd", "mcd", "mcd", NULL, false },
  { "application/vnd.medcalcdata", "mc1", "mc1", NULL, false },
  { "application/vnd.mediastation.cdkey", "cdkey", "cdkey", NULL, false },
  { "application/vnd.meridian-slingshot", NULL, NULL, NULL, false },
  { "application/vnd.mfer", "mwf", "mwf", NULL, false },
  { "application/vnd.mfmp", "mfm", "mfm", NULL, false },
  { "application/vnd.micro+json", NULL, NULL, NULL, true },
  { "application/vnd.micrografx.flo", "flo", "flo", NULL, false },
  { "application/vnd.micrografx.igx", "igx", "igx", NULL, false },
  { "application/vnd.microsoft.portable-executable", NULL, NULL, NULL, false },
  { "application/vnd.microsoft.windows.thumbnail-cache", NULL, NULL, NULL, false },
  { "application/vnd.miele+json", NULL, NULL, NULL, true },
  { "application/vnd.mif", "mif", "mif", NULL, false },
  { "application/vnd.minisoft-hp3000-save", NULL, NULL, NULL, false },
  { "application/vnd.mitsubishi.misty-guard.trustweb", NULL, NULL, NULL, false },
  { "application/vnd.mobius.daf", "daf", "daf", NULL, false },
  { "application/vnd.mobius.dis", "dis", "dis", NULL, false },
  { "application/vnd.mobius.mbk", "mbk", "mbk", NULL, false },
  { "application/vnd.mobius.mqy", "mqy", "mqy", NULL, false },
  { "application/vnd.mobius.msl", "msl", "msl", NULL, false },
  { "application/vnd.mobius.plc", "plc", "plc", NULL, false },
  { "application/vnd.mobius.txf", "txf", "txf", NULL, false },
  { "application/vnd.mophun.application", "mpn", "mpn", NULL, false },
  { "application/vnd.mophun.certificate", "mpc", "mpc", NULL, false },
  { "application/vnd.motorola.flexsuite", NULL, NULL, NULL, false },
  { "application/vnd.motorola.flexsuite.adsi", NULL, NULL, NULL, false },
  { "application/vnd.motorola.flexsuite.fis", NULL, NULL, NULL, false },
  { "application/vnd.motorola.flexsuite.gotap", NULL, NULL, NULL, false },
  { "application/vnd.motorola.flexsuite.kmr", NULL, NULL, NULL, false },
  { "application/vnd.motorola.flexsuite.ttc", NULL, NULL, NULL, false },
  { "application/vnd.motorola.flexsuite.wem", NULL, NULL, NULL, false },
  { "application/vnd.motorola.iprm", NULL, NULL, NULL, false },
  { "application/vnd.mozilla.xul+xml", "xul", "xul", NULL, true },
  { "application/vnd.ms-3mfdocument", NULL, NULL, NULL, false },
  { "application/vnd.ms-artgalry", "cil", "cil", NULL, false },
  { "application/vnd.ms-asf", NULL, NULL, NULL, false },
  { "application/vnd.ms-cab-compressed", "cab", "cab", NULL, false },
  { "application/vnd.ms-color.iccprofile", NULL, NULL, NULL, false },
  { "application/vnd.ms-excel", "xls", "xls;xlm;xla;xlc;xlt;xlw", NULL, false },
  { "application/vnd.ms-excel.addin.macroenabled.12", "xlam", "xlam", NULL, false },
  { "application/vnd.ms-excel.sheet.binary.macroenabled.12", "xlsb", "xlsb", NULL, false },
  { "application/vnd.ms-excel.sheet.macroenabled.12", "xlsm", "xlsm", NULL, false },
  { "application/vnd.ms-excel.template.macroenabled.12", "xltm", "xltm", NULL, false },
  { "application/vnd.ms-fontobject", "eot", "eot", NULL, true },
  { "application/vnd.ms-htmlhelp", "chm", "chm", NULL, false },
  { "application/vnd.ms-ims", "ims", "ims", NULL, false },
  { "application/vnd.ms-lrm", "lrm", "lrm", NULL, false },
  { "application/vnd.ms-office.activex+xml", NULL, NULL, NULL, true },
  { "application/vnd.ms-officetheme", "thmx", "thmx", NULL, false },
  { "application/vnd.ms-opentype", NULL, NULL, NULL, true },
  { "application/vnd.ms-outlook", "msg", "msg", NULL, false },
  { "application/vnd.ms-package.obfuscated-opentype", NULL, NULL, NULL, false },
  { "application/vnd.ms-pki.seccat", "cat", "cat", NULL, false },
  { "application/vnd.ms-pki.stl", "stl", "stl", NULL, false },
  { "application/vnd.ms-playready.initiator+xml", NULL, NULL, NULL, true },
  { "application/vnd.ms-powerpoint", "ppt", "ppt;pps;pot", NULL, false },
  { "application/vnd.ms-powerpoint.addin.macroenabled.12", "ppam", "ppam", NULL, false },
  { "application/vnd.ms-powerpoint.presentation.macroenabled.12", "pptm", "pptm", NULL, false },
  { "application/vnd.ms-powerpoint.slide.macroenabled.12", "sldm", "sldm", NULL, false },
  { "application/vnd.ms-powerpoint.slideshow.macroenabled.12", "ppsm", "ppsm", NULL, false },
  { "application/vnd.ms-powerpoint.template.macroenabled.12", "potm", "potm", NULL, false },
  { "application/vnd.ms-printdevicecapabilities+xml", NULL, NULL, NULL, true },
  { "application/vnd.ms-printing.printticket+xml", NULL, NULL, NULL, true },
  { "application/vnd.ms-printschematicket+xml", NULL, NULL, NULL, true },
  { "application/vnd.ms-project", "mpp", "mpp;mpt", NULL, false },
  { "application/vnd.ms-tnef", NULL, NULL, NULL, false },
  { "application/vnd.ms-windows.devicepairing", NULL, NULL, NULL, false },
  { "application/vnd.ms-windows.nwprinting.oob", NULL, NULL, NULL, false },
  { "application/vnd.ms-windows.printerpairing", NULL, NULL, NULL, false },
  { "application/vnd.ms-windows.wsd.oob", NULL, NULL, NULL, false },
  { "application/vnd.ms-wmdrm.lic-chlg-req", NULL, NULL, NULL, false },
  { "application/vnd.ms-wmdrm.lic-resp", NULL, NULL, NULL, false },
  { "application/vnd.ms-wmdrm.meter-chlg-req", NULL, NULL, NULL, false },
  { "application/vnd.ms-wmdrm.meter-resp", NULL, NULL, NULL, false },
  { "application/vnd.ms-word.document.macroenabled.12", "docm", "docm", NULL, false },
  { "application/vnd.ms-word.template.macroenabled.12", "dotm", "dotm", NULL, false },
  { "application/vnd.ms-works", "wps", "wps;wks;wcm;wdb", NULL, false },
  { "application/vnd.ms-wpl", "wpl", "wpl", NULL, false },
  { "application/vnd.ms-xpsdocument", "xps", "xps", NULL, false },
  { "application/vnd.msa-disk-image", NULL, NULL, NULL, false },
  { "application/vnd.mseq", "mseq", "mseq", NULL, false },
  { "application/vnd.msign", NULL, NULL, NULL, false },
  { "application/vnd.multiad.creator", NULL, NULL, NULL, false },
  { "application/vnd.multiad.creator.cif", NULL, NULL, NULL, false },
  { "application/vnd.music-niff", NULL, NULL, NULL, false },
  { "application/vnd.musician", "mus", "mus", NULL, false },
  { "application/vnd.muvee.style", "msty", "msty", NULL, false },
  { "application/vnd.mynfc", "taglet", "taglet", NULL, false },
  { "application/vnd.ncd.control", NULL, NULL, NULL, false },
  { "application/vnd.ncd.reference", NULL, NULL, NULL, false },
  { "application/vnd.nearst.inv+json", NULL, NULL, NULL, true },
  { "application/vnd.nervana", NULL, NULL, NULL, false },
  { "application/vnd.netfpx", NULL, NULL, NULL, false },
  { "application/vnd.neurolanguage.nlu", "nlu", "nlu", NULL, false },
  { "application/vnd.nimn", NULL, NULL, NULL, false },
  { "application/vnd.nintendo.nitro.rom", NULL, NULL, NULL, false },
  { "application/vnd.nintendo.snes.rom", NULL, NULL, NULL, false },
  { "application/vnd.nitf", "ntf", "ntf;nitf", NULL, false },
  { "application/vnd.noblenet-directory", "nnd", "nnd", NULL, false },
  { "application/vnd.noblenet-sealer", "nns", "nns", NULL, false },
  { "application/vnd.noblenet-web", "nnw", "nnw", NULL, false },
  { "application/vnd.nokia.catalogs", NULL, NULL, NULL, false },
  { "application/vnd.nokia.conml+wbxml", NULL, NULL, NULL, false },
  { "application/vnd.nokia.conml+xml", NULL, NULL, NULL, true },
  { "application/vnd.nokia.iptv.config+xml", NULL, NULL, NULL, true },
  { "application/vnd.nokia.isds-radio-presets", NULL, NULL, NULL, false },
  { "application/vnd.nokia.landmark+wbxml", NULL, NULL, NULL, false },
  { "application/vnd.nokia.landmark+xml", NULL, NULL, NULL, true },
  { "application/vnd.nokia.landmarkcollection+xml", NULL, NULL, NULL, true },
  { "application/vnd.nokia.n-gage.ac+xml", NULL, NULL, NULL, true },
  { "application/vnd.nokia.n-gage.data", "ngdat", "ngdat", NULL, false },
  { "application/vnd.nokia.n-gage.symbian.install", "n-gage", "n-gage", NULL, false },
  { "application/vnd.nokia.ncd", NULL, NULL, NULL, false },
  { "application/vnd.nokia.pcd+wbxml", NULL, NULL, NULL, false },
  { "application/vnd.nokia.pcd+xml", NULL, NULL, NULL, true },
  { "application/vnd.nokia.radio-preset", "rpst", "rpst", NULL, false },
  { "application/vnd.nokia.radio-presets", "rpss", "rpss", NULL, false },
  { "application/vnd.novadigm.edm", "edm", "edm", NULL, false },
  { "application/vnd.novadigm.edx", "edx", "edx", NULL, false },
  { "application/vnd.novadigm.ext", "ext", "ext", NULL, false },
  { "application/vnd.ntt-local.content-share", NULL, NULL, NULL, false },
  { "application/vnd.ntt-local.file-transfer", NULL, NULL, NULL, false },
  { "application/vnd.ntt-local.ogw_remote-access", NULL, NULL, NULL, false },
  { "application/vnd.ntt-local.sip-ta_remote", NULL, NULL, NULL, false },
  { "application/vnd.ntt-local.sip-ta_tcp_stream", NULL, NULL, NULL, false },
  { "application/vnd.oasis.opendocument.chart", "odc", "odc", NULL, false },
  { "application/vnd.oasis.opendocument.chart-template", "otc", "otc", NULL, false },
  { "application/vnd.oasis.opendocument.database", "odb", "odb", NULL, false },
  { "application/vnd.oasis.opendocument.formula", "odf", "odf", NULL, false },
  { "application/vnd.oasis.opendocument.formula-template", "odft", "odft", NULL, false },
  { "application/vnd.oasis.opendocument.graphics", "odg", "odg", NULL, false },
  { "application/vnd.oasis.opendocument.graphics-template", "otg", "otg", NULL, false },
  { "application/vnd.oasis.opendocument.image", "odi", "odi", NULL, false },
  { "application/vnd.oasis.opendocument.image-template", "oti", "oti", NULL, false },
  { "application/vnd.oasis.opendocument.presentation", "odp", "odp", NULL, false },
  { "application/vnd.oasis.opendocument.presentation-template", "otp", "otp", NULL, false },
  { "application/vnd.oasis.opendocument.spreadsheet", "ods", "ods", NULL, false },
  { "application/vnd.oasis.opendocument.spreadsheet-template", "ots", "ots", NULL, false },
  { "application/vnd.oasis.opendocument.text", "odt", "odt", NULL, false },
  { "application/vnd.oasis.opendocument.text-master", "odm", "odm", NULL, false },
  { "application/vnd.oasis.opendocument.text-template", "ott", "ott", NULL, false },
  { "application/vnd.oasis.opendocument.text-web", "oth", "oth", NULL, false },
  { "application/vnd.obn", NULL, NULL, NULL, false },
  { "application/vnd.ocf+cbor", NULL, NULL, NULL, false },
  { "application/vnd.oftn.l10n+json", NULL, NULL, NULL, true },
  { "application/vnd.oipf.contentaccessdownload+xml", NULL, NULL, NULL, true },
  { "application/vnd.oipf.contentaccessstreaming+xml", NULL, NULL, NULL, true },
  { "application/vnd.oipf.cspg-hexbinary", NULL, NULL, NULL, false },
  { "application/vnd.oipf.dae.svg+xml", NULL, NULL, NULL, true },
  { "application/vnd.oipf.dae.xhtml+xml", NULL, NULL, NULL, true },
  { "application/vnd.oipf.mippvcontrolmessage+xml", NULL, NULL, NULL, true },
  { "application/vnd.oipf.pae.gem", NULL, NULL, NULL, false },
  { "application/vnd.oipf.spdiscovery+xml", NULL, NULL, NULL, true },
  { "application/vnd.oipf.spdlist+xml", NULL, NULL, NULL, true },
  { "application/vnd.oipf.ueprofile+xml", NULL, NULL, NULL, true },
  { "application/vnd.oipf.userprofile+xml", NULL, NULL, NULL, true },
  { "application/vnd.olpc-sugar", "xo", "xo", NULL, false },
  { "application/vnd.oma-scws-config", NULL, NULL, NULL, false },
  { "application/vnd.oma-scws-http-request", NULL, NULL, NULL, false },
  { "application/vnd.oma-scws-http-response", NULL, NULL, NULL, false },
  { "application/vnd.oma.bcast.associated-procedure-parameter+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.bcast.drm-trigger+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.bcast.imd+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.bcast.ltkm", NULL, NULL, NULL, false },
  { "application/vnd.oma.bcast.notification+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.bcast.provisioningtrigger", NULL, NULL, NULL, false },
  { "application/vnd.oma.bcast.sgboot", NULL, NULL, NULL, false },
  { "application/vnd.oma.bcast.sgdd+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.bcast.sgdu", NULL, NULL, NULL, false },
  { "application/vnd.oma.bcast.simple-symbol-container", NULL, NULL, NULL, false },
  { "application/vnd.oma.bcast.smartcard-trigger+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.bcast.sprov+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.bcast.stkm", NULL, NULL, NULL, false },
  { "application/vnd.oma.cab-address-book+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.cab-feature-handler+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.cab-pcc+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.cab-subs-invite+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.cab-user-prefs+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.dcd", NULL, NULL, NULL, false },
  { "application/vnd.oma.dcdc", NULL, NULL, NULL, false },
  { "application/vnd.oma.dd2+xml", "dd2", "dd2", NULL, true },
  { "application/vnd.oma.drm.risd+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.group-usage-list+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.lwm2m+json", NULL, NULL, NULL, true },
  { "application/vnd.oma.lwm2m+tlv", NULL, NULL, NULL, false },
  { "application/vnd.oma.pal+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.poc.detailed-progress-report+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.poc.final-report+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.poc.groups+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.poc.invocation-descriptor+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.poc.optimized-progress-report+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.push", NULL, NULL, NULL, false },
  { "application/vnd.oma.scidm.messages+xml", NULL, NULL, NULL, true },
  { "application/vnd.oma.xcap-directory+xml", NULL, NULL, NULL, true },
  { "application/vnd.omads-email+xml", NULL, NULL, NULL, true },
  { "application/vnd.omads-file+xml", NULL, NULL, NULL, true },
  { "application/vnd.omads-folder+xml", NULL, NULL, NULL, true },
  { "application/vnd.omaloc-supl-init", NULL, NULL, NULL, false },
  { "application/vnd.onepager", NULL, NULL, NULL, false },
  { "application/vnd.onepagertamp", NULL, NULL, NULL, false },
  { "application/vnd.onepagertamx", NULL, NULL, NULL, false },
  { "application/vnd.onepagertat", NULL, NULL, NULL, false },
  { "application/vnd.onepagertatp", NULL, NULL, NULL, false },
  { "application/vnd.onepagertatx", NULL, NULL, NULL, false },
  { "application/vnd.openblox.game+xml", NULL, NULL, NULL, true },
  { "application/vnd.openblox.game-binary", NULL, NULL, NULL, false },
  { "application/vnd.openeye.oeb", NULL, NULL, NULL, false },
  { "application/vnd.openofficeorg.extension", "oxt", "oxt", NULL, false },
  { "application/vnd.openstreetmap.data+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.custom-properties+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.customxmlproperties+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.drawing+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.drawingml.chart+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.drawingml.diagramcolors+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.drawingml.diagramdata+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.drawingml.diagramlayout+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.drawingml.diagramstyle+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.extended-properties+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.commentauthors+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.comments+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.handoutmaster+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.notesmaster+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.notesslide+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx", "pptx", NULL, false },
  { "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.presprops+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.slide", "sldx", "sldx", NULL, false },
  { "application/vnd.openxmlformats-officedocument.presentationml.slide+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.slidelayout+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.slidemaster+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.slideshow", "ppsx", "ppsx", NULL, false },
  { "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.slideupdateinfo+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.tablestyles+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.tags+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.template", "potx", "potx", NULL, false },
  { "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.presentationml.viewprops+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.calcchain+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.externallink+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcachedefinition+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotcacherecords+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.pivottable+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.querytable+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionheaders+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionlog+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedstrings+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx", "xlsx", NULL, false },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetmetadata+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.tablesinglecells+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.template", "xltx", "xltx", NULL, false },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.usernames+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.volatiledependencies+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.theme+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.themeoverride+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.vmldrawing", NULL, NULL, NULL, false },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx", "docx", NULL, false },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.fonttable+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.template", "dotx", "dotx", NULL, false },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-officedocument.wordprocessingml.websettings+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-package.core-properties+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml", NULL, NULL, NULL, true },
  { "application/vnd.openxmlformats-package.relationships+xml", NULL, NULL, NULL, true },
  { "application/vnd.oracle.resource+json", NULL, NULL, NULL, true },
  { "application/vnd.orange.indata", NULL, NULL, NULL, false },
  { "application/vnd.osa.netdeploy", NULL, NULL, NULL, false },
  { "application/vnd.osgeo.mapguide.package", "mgp", "mgp", NULL, false },
  { "application/vnd.osgi.bundle", NULL, NULL, NULL, false },
  { "application/vnd.osgi.dp", "dp", "dp", NULL, false },
  { "application/vnd.osgi.subsystem", "esa", "esa", NULL, false },
  { "application/vnd.otps.ct-kip+xml", NULL, NULL, NULL, true },
  { "application/vnd.oxli.countgraph", NULL, NULL, NULL, false },
  { "application/vnd.pagerduty+json", NULL, NULL, NULL, true },
  { "application/vnd.palm", "pdb", "pdb;pqa;oprc", NULL, false },
  { "application/vnd.panoply", NULL, NULL, NULL, false },
  { "application/vnd.paos+xml", NULL, NULL, NULL, true },
  { "application/vnd.paos.xml", NULL, NULL, NULL, false },
  { "application/vnd.patentdive", NULL, NULL, NULL, false },
  { "application/vnd.pawaafile", "paw", "paw", NULL, false },
  { "application/vnd.pcos", NULL, NULL, NULL, false },
  { "application/vnd.pg.format", "str", "str", NULL, false },
  { "application/vnd.pg.osasli", "ei6", "ei6", NULL, false },
  { "application/vnd.piaccess.application-licence", NULL, NULL, NULL, false },
  { "application/vnd.picsel", "efif", "efif", NULL, false },
  { "application/vnd.pmi.widget", "wg", "wg", NULL, false },
  { "application/vnd.poc.group-advertisement+xml", NULL, NULL, NULL, true },
  { "application/vnd.pocketlearn", "plf", "plf", NULL, false },
  { "application/vnd.powerbuilder6", "pbd", "pbd", NULL, false },
  { "application/vnd.powerbuilder6-s", NULL, NULL, NULL, false },
  { "application/vnd.powerbuilder7", NULL, NULL, NULL, false },
  { "application/vnd.powerbuilder7-s", NULL, NULL, NULL, false },
  { "application/vnd.powerbuilder75", NULL, NULL, NULL, false },
  { "application/vnd.powerbuilder75-s", NULL, NULL, NULL, false },
  { "application/vnd.preminet", NULL, NULL, NULL, false },
  { "application/vnd.previewsystems.box", "box", "box", NULL, false },
  { "application/vnd.proteus.magazine", "mgz", "mgz", NULL, false },
  { "application/vnd.psfs", NULL, NULL, NULL, false },
  { "application/vnd.publishare-delta-tree", "qps", "qps", NULL, false },
  { "application/vnd.pvi.ptid1", "ptid", "ptid", NULL, false },
  { "application/vnd.pwg-multiplexed", NULL, NULL, NULL, false },
  { "application/vnd.pwg-xhtml-print+xml", NULL, NULL, NULL, true },
  { "application/vnd.qualcomm.brew-app-res", NULL, NULL, NULL, false },
  { "application/vnd.quarantainenet", NULL, NULL, NULL, false },
  { "application/vnd.quark.quarkxpress", "qxd", "qxd;qxt;qwd;qwt;qxl;qxb", NULL, false },
  { "application/vnd.quobject-quoxdocument", NULL, NULL, NULL, false },
  { "application/vnd.radisys.moml+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-audit+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-audit-conf+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-audit-conn+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-audit-dialog+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-audit-stream+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-conf+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-dialog+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-dialog-base+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-dialog-fax-detect+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-dialog-fax-sendrecv+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-dialog-group+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-dialog-speech+xml", NULL, NULL, NULL, true },
  { "application/vnd.radisys.msml-dialog-transform+xml", NULL, NULL, NULL, true },
  { "application/vnd.rainstor.data", NULL, NULL, NULL, false },
  { "application/vnd.rapid", NULL, NULL, NULL, false },
  { "application/vnd.rar", NULL, NULL, NULL, false },
  { "application/vnd.realvnc.bed", "bed", "bed", NULL, false },
  { "application/vnd.recordare.musicxml", "mxl", "mxl", NULL, false },
  { "application/vnd.recordare.musicxml+xml", "musicxml", "musicxml", NULL, true },
  { "application/vnd.renlearn.rlprint", NULL, NULL, NULL, false },
  { "application/vnd.restful+json", NULL, NULL, NULL, true },
  { "application/vnd.rig.cryptonote", "cryptonote", "cryptonote", NULL, false },
  { "application/vnd.rim.cod", "cod", "cod", NULL, false },
  { "application/vnd.rn-realmedia", "rm", "rm", NULL, false },
  { "application/vnd.rn-realmedia-vbr", "rmvb", "rmvb", NULL, false },
  { "application/vnd.route66.link66+xml", "link66", "link66", NULL, true },
  { "application/vnd.rs-274x", NULL, NULL, NULL, false },
  { "application/vnd.ruckus.download", NULL, NULL, NULL, false },
  { "application/vnd.s3sms", NULL, NULL, NULL, false },
  { "application/vnd.sailingtracker.track", "st", "st", NULL, false },
  { "application/vnd.sbm.cid", NULL, NULL, NULL, false },
  { "application/vnd.sbm.mid2", NULL, NULL, NULL, false },
  { "application/vnd.scribus", NULL, NULL, NULL, false },
  { "application/vnd.sealed.3df", NULL, NULL, NULL, false },
  { "application/vnd.sealed.csf", NULL, NULL, NULL, false },
  { "application/vnd.sealed.doc", NULL, NULL, NULL, false },
  { "application/vnd.sealed.eml", NULL, NULL, NULL, false },
  { "application/vnd.sealed.mht", NULL, NULL, NULL, false },
  { "application/vnd.sealed.net", NULL, NULL, NULL, false },
  { "application/vnd.sealed.ppt", NULL, NULL, NULL, false },
  { "application/vnd.sealed.tiff", NULL, NULL, NULL, false },
  { "application/vnd.sealed.xls", NULL, NULL, NULL, false },
  { "application/vnd.sealedmedia.softseal.html", NULL, NULL, NULL, false },
  { "application/vnd.sealedmedia.softseal.pdf", NULL, NULL, NULL, false },
  { "application/vnd.seemail", "see", "see", NULL, false },
  { "application/vnd.sema", "sema", "sema", NULL, false },
  { "application/vnd.semd", "semd", "semd", NULL, false },
  { "application/vnd.semf", "semf", "semf", NULL, false },
  { "application/vnd.shana.informed.formdata", "ifm", "ifm", NULL, false },
  { "application/vnd.shana.informed.formtemplate", "itp", "itp", NULL, false },
  { "application/vnd.shana.informed.interchange", "iif", "iif", NULL, false },
  { "application/vnd.shana.informed.package", "ipk", "ipk", NULL, false },
  { "application/vnd.shootproof+json", NULL, NULL, NULL, true },
  { "application/vnd.sigrok.session", NULL, NULL, NULL, false },
  { "application/vnd.simtech-mindmapper", "twd", "twd;twds", NULL, false },
  { "application/vnd.siren+json", NULL, NULL, NULL, true },
  { "application/vnd.smaf", "mmf", "mmf", NULL, false },
  { "application/vnd.smart.notebook", NULL, NULL, NULL, false },
  { "application/vnd.smart.teacher", "teacher", "teacher", NULL, false },
  { "application/vnd.software602.filler.form+xml", NULL, NULL, NULL, true },
  { "application/vnd.software602.filler.form-xml-zip", NULL, NULL, NULL, false },
  { "application/vnd.solent.sdkm+xml", "sdkm", "sdkm;sdkd", NULL, true },
  { "application/vnd.spotfire.dxp", "dxp", "dxp", NULL, false },
  { "application/vnd.spotfire.sfs", "sfs", "sfs", NULL, false },
  { "application/vnd.sqlite3", NULL, NULL, NULL, false },
  { "application/vnd.sss-cod", NULL, NULL, NULL, false },
  { "application/vnd.sss-dtf", NULL, NULL, NULL, false },
  { "application/vnd.sss-ntf", NULL, NULL, NULL, false },
  { "application/vnd.stardivision.calc", "sdc", "sdc", NULL, false },
  { "application/vnd.stardivision.draw", "sda", "sda", NULL, false },
  { "application/vnd.stardivision.impress", "sdd", "sdd", NULL, false },
  { "application/vnd.stardivision.math", "smf", "smf", NULL, false },
  { "application/vnd.stardivision.writer", "sdw", "sdw;vor", NULL, false },
  { "application/vnd.stardivision.writer-global", "sgl", "sgl", NULL, false },
  { "application/vnd.stepmania.package", "smzip", "smzip", NULL, false },
  { "application/vnd.stepmania.stepchart", "sm", "sm", NULL, false },
  { "application/vnd.street-stream", NULL, NULL, NULL, false },
  { "application/vnd.sun.wadl+xml", "wadl", "wadl", NULL, true },
  { "application/vnd.sun.xml.calc", "sxc", "sxc", NULL, false },
  { "application/vnd.sun.xml.calc.template", "stc", "stc", NULL, false },
  { "application/vnd.sun.xml.draw", "sxd", "sxd", NULL, false },
  { "application/vnd.sun.xml.draw.template", "std", "std", NULL, false },
  { "application/vnd.sun.xml.impress", "sxi", "sxi", NULL, false },
  { "application/vnd.sun.xml.impress.template", "sti", "sti", NULL, false },
  { "application/vnd.sun.xml.math", "sxm", "sxm", NULL, false },
  { "application/vnd.sun.xml.writer", "sxw", "sxw", NULL, false },
  { "application/vnd.sun.xml.writer.global", "sxg", "sxg", NULL, false },
  { "application/vnd.sun.xml.writer.template", "stw", "stw", NULL, false },
  { "application/vnd.sus-calendar", "sus", "sus;susp", NULL, false },
  { "application/vnd.svd", "svd", "svd", NULL, false },
  { "application/vnd.swiftview-ics", NULL, NULL, NULL, false },
  { "application/vnd.symbian.install", "sis", "sis;sisx", NULL, false },
  { "application/vnd.syncml+xml", "xsm", "xsm", NULL, true },
  { "application/vnd.syncml.dm+wbxml", "bdm", "bdm", NULL, false },
  { "application/vnd.syncml.dm+xml", "xdm", "xdm", NULL, true },
  { "application/vnd.syncml.dm.notification", NULL, NULL, NULL, false },
  { "application/vnd.syncml.dmddf+wbxml", NULL, NULL, NULL, false },
  { "application/vnd.syncml.dmddf+xml", NULL, NULL, NULL, true },
  { "application/vnd.syncml.dmtnds+wbxml", NULL, NULL, NULL, false },
  { "application/vnd.syncml.dmtnds+xml", NULL, NULL, NULL, true },
  { "application/vnd.syncml.ds.notification", NULL, NULL, NULL, false },
  { "application/vnd.tableschema+json", NULL, NULL, NULL, true },
  { "application/vnd.tao.intent-module-archive", "tao", "tao", NULL, false },
  { "application/vnd.tcpdump.pcap", "pcap", "pcap;cap;dmp", NULL, false },
  { "application/vnd.think-cell.ppttc+json", NULL, NULL, NULL, true },
  { "application/vnd.tmd.mediaflex.api+xml", NULL, NULL, NULL, true },
  { "application/vnd.tml", NULL, NULL, NULL, false },
  { "application/vnd.tmobile-livetv", "tmo", "tmo", NULL, false },
  { "application/vnd.tri.onesource", NULL, NULL, NULL, false },
  { "application/vnd.trid.tpt", "tpt", "tpt", NULL, false },
  { "application/vnd.triscape.mxs", "mxs", "mxs", NULL, false },
  { "application/vnd.trueapp", "tra", "tra", NULL, false },
  { "application/vnd.truedoc", NULL, NULL, NULL, false },
  { "application/vnd.ubisoft.webplayer", NULL, NULL, NULL, false },
  { "application/vnd.ufdl", "ufd", "ufd;ufdl", NULL, false },
  { "application/vnd.uiq.theme", "utz", "utz", NULL, false },
  { "application/vnd.umajin", "umj", "umj", NULL, false },
  { "application/vnd.unity", "unityweb", "unityweb", NULL, false },
  { "application/vnd.uoml+xml", "uoml", "uoml", NULL, true },
  { "application/vnd.uplanet.alert", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.alert-wbxml", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.bearer-choice", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.bearer-choice-wbxml", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.cacheop", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.cacheop-wbxml", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.channel", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.channel-wbxml", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.list", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.list-wbxml", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.listcmd", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.listcmd-wbxml", NULL, NULL, NULL, false },
  { "application/vnd.uplanet.signal", NULL, NULL, NULL, false },
  { "application/vnd.uri-map", NULL, NULL, NULL, false },
  { "application/vnd.valve.source.material", NULL, NULL, NULL, false },
  { "application/vnd.vcx", "vcx", "vcx", NULL, false },
  { "application/vnd.vd-study", NULL, NULL, NULL, false },
  { "application/vnd.vectorworks", NULL, NULL, NULL, false },
  { "application/vnd.vel+json", NULL, NULL, NULL, true },
  { "application/vnd.verimatrix.vcas", NULL, NULL, NULL, false },
  { "application/vnd.vidsoft.vidconference", NULL, NULL, NULL, false },
  { "application/vnd.visio", "vsd", "vsd;vst;vss;vsw", NULL, false },
  { "application/vnd.visionary", "vis", "vis", NULL, false },
  { "application/vnd.vividence.scriptfile", NULL, NULL, NULL, false },
  { "application/vnd.vsf", "vsf", "vsf", NULL, false },
  { "application/vnd.wap.sic", NULL, NULL, NULL, false },
  { "application/vnd.wap.slc", NULL, NULL, NULL, false },
  { "application/vnd.wap.wbxml", "wbxml", "wbxml", NULL, false },
  { "application/vnd.wap.wmlc", "wmlc", "wmlc", NULL, false },
  { "application/vnd.wap.wmlscriptc", "wmlsc", "wmlsc", NULL, false },
  { "application/vnd.webturbo", "wtb", "wtb", NULL, false },
  { "application/vnd.wfa.p2p", NULL, NULL, NULL, false },
  { "application/vnd.wfa.wsc", NULL, NULL, NULL, false },
  { "application/vnd.windows.devicepairing", NULL, NULL, NULL, false },
  { "application/vnd.wmc", NULL, NULL, NULL, false },
  { "application/vnd.wmf.bootstrap", NULL, NULL, NULL, false },
  { "application/vnd.wolfram.mathematica", NULL, NULL, NULL, false },
  { "application/vnd.wolfram.mathematica.package", NULL, NULL, NULL, false },
  { "application/vnd.wolfram.player", "nbp", "nbp", NULL, false },
  { "application/vnd.wordperfect", "wpd", "wpd", NULL, false },
  { "application/vnd.wqd", "wqd", "wqd", NULL, false },
  { "application/vnd.wrq-hp3000-labelled", NULL, NULL, NULL, false },
  { "application/vnd.wt.stf", "stf", "stf", NULL, false },
  { "application/vnd.wv.csp+wbxml", NULL, NULL, NULL, false },
  { "application/vnd.wv.csp+xml", NULL, NULL, NULL, true },
  { "application/vnd.wv.ssp+xml", NULL, NULL, NULL, true },
  { "application/vnd.xacml+json", NULL, NULL, NULL, true },
  { "application/vnd.xara", "xar", "xar", NULL, false },
  { "application/vnd.xfdl", "xfdl", "xfdl", NULL, false },
  { "application/vnd.xfdl.webform", NULL, NULL, NULL, false },
  { "application/vnd.xmi+xml", NULL, NULL, NULL, true },
  { "application/vnd.xmpie.cpkg", NULL, NULL, NULL, false },
  { "application/vnd.xmpie.dpkg", NULL, NULL, NULL, false },
  { "application/vnd.xmpie.plan", NULL, NULL, NULL, false },
  { "application/vnd.xmpie.ppkg", NULL, NULL, NULL, false },
  { "application/vnd.xmpie.xlim", NULL, NULL, NULL, false },
  { "application/vnd.yamaha.hv-dic", "hvd", "hvd", NULL, false },
  { "application/vnd.yamaha.hv-script", "hvs", "hvs", NULL, false },
  { "application/vnd.yamaha.hv-voice", "hvp", "hvp", NULL, false },
  { "application/vnd.yamaha.openscoreformat", "osf", "osf", NULL, false },
  { "application/vnd.yamaha.openscoreformat.osfpvg+xml", "osfpvg", "osfpvg", NULL, true },
  { "application/vnd.yamaha.remote-setup", NULL, NULL, NULL, false },
  { "application/vnd.yamaha.smaf-audio", "saf", "saf", NULL, false },
  { "application/vnd.yamaha.smaf-phrase", "spf", "spf", NULL, false },
  { "application/vnd.yamaha.through-ngn", NULL, NULL, NULL, false },
  { "application/vnd.yamaha.tunnel-udpencap", NULL, NULL, NULL, false },
  { "application/vnd.yaoweme", NULL, NULL, NULL, false },
  { "application/vnd.yellowriver-custom-menu", "cmp", "cmp", NULL, false },
  { "application/vnd.youtube.yt", NULL, NULL, NULL, false },
  { "application/vnd.zul", "zir", "zir;zirz", NULL, false },
  { "application/vnd.zzazz.deck+xml", "zaz", "zaz", NULL, true },
  { "application/voicexml+xml", "vxml", "vxml", NULL, true },
  { "application/voucher-cms+json", NULL, NULL, NULL, true },
  { "application/vq-rtcpxr", NULL, NULL, NULL, false },
  { "application/wasm", "wasm", "wasm", NULL, true },
  { "application/watcherinfo+xml", NULL, NULL, NULL, true },
  { "application/webpush-options+json", NULL, NULL, NULL, true },
  { "application/whoispp-query", NULL, NULL, NULL, false },
  { "application/whoispp-response", NULL, NULL, NULL, false },
  { "application/widget", "wgt", "wgt", NULL, false },
  { "application/winhlp", "hlp", "hlp", NULL, false },
  { "application/wita", NULL, NULL, NULL, false },
  { "application/wordperfect5.1", NULL, NULL, NULL, false },
  { "application/wsdl+xml", "wsdl", "wsdl", NULL, true },
  { "application/wspolicy+xml", "wspolicy", "wspolicy", NULL, true },
  { "application/x-7z-compressed", "7z", "7z", NULL, false },
  { "application/x-abiword", "abw", "abw", NULL, false },
  { "application/x-ace-compressed", "ace", "ace", NULL, false },
  { "application/x-amf", NULL, NULL, NULL, false },
  { "application/x-apple-diskimage", "dmg", "dmg", NULL, false },
  { "application/x-arj", "arj", "arj", NULL, false },
  { "application/x-authorware-bin", "aab", "aab;x32;u32;vox", NULL, false },
  { "application/x-authorware-map", "aam", "aam", NULL, false },
  { "application/x-authorware-seg", "aas", "aas", NULL, false },
  { "application/x-bcpio", "bcpio", "bcpio", NULL, false },
  { "application/x-bdoc", "bdoc", "bdoc", NULL, false },
  { "application/x-bittorrent", "torrent", "torrent", NULL, false },
  { "application/x-blorb", "blb", "blb;blorb", NULL, false },
  { "application/x-bzip", "bz", "bz", NULL, false },
  { "application/x-bzip2", "bz2", "bz2;boz", NULL, false },
  { "application/x-cbr", "cbr", "cbr;cba;cbt;cbz;cb7", NULL, false },
  { "application/x-cdlink", "vcd", "vcd", NULL, false },
  { "application/x-cfs-compressed", "cfs", "cfs", NULL, false },
  { "application/x-chat", "chat", "chat", NULL, false },
  { "application/x-chess-pgn", "pgn", "pgn", NULL, false },
  { "application/x-chrome-extension", "crx", "crx", NULL, false },
  { "application/x-cocoa", "cco", "cco", NULL, false },
  { "application/x-compress", NULL, NULL, NULL, false },
  { "application/x-conference", "nsc", "nsc", NULL, false },
  { "application/x-cpio", "cpio", "cpio", NULL, false },
  { "application/x-csh", "csh", "csh", NULL, false },
  { "application/x-deb", NULL, NULL, NULL, false },
  { "application/x-debian-package", "deb", "deb;udeb", NULL, false },
  { "application/x-dgc-compressed", "dgc", "dgc", NULL, false },
  { "application/x-director", "dir", "dir;dcr;dxr;cst;cct;cxt;w3d;fgd;swa", NULL, false },
  { "application/x-doom", "wad", "wad", NULL, false },
  { "application/x-dtbncx+xml", "ncx", "ncx", NULL, true },
  { "application/x-dtbook+xml", "dtb", "dtb", NULL, true },
  { "application/x-dtbresource+xml", "res", "res", NULL, true },
  { "application/x-dvi", "dvi", "dvi", NULL, false },
  { "application/x-envoy", "evy", "evy", NULL, false },
  { "application/x-eva", "eva", "eva", NULL, false },
  { "application/x-font-bdf", "bdf", "bdf", NULL, false },
  { "application/x-font-dos", NULL, NULL, NULL, false },
  { "application/x-font-framemaker", NULL, NULL, NULL, false },
  { "application/x-font-ghostscript", "gsf", "gsf", NULL, false },
  { "application/x-font-libgrx", NULL, NULL, NULL, false },
  { "application/x-font-linux-psf", "psf", "psf", NULL, false },
  { "application/x-font-pcf", "pcf", "pcf", NULL, false },
  { "application/x-font-snf", "snf", "snf", NULL, false },
  { "application/x-font-speedo", NULL, NULL, NULL, false },
  { "application/x-font-sunos-news", NULL, NULL, NULL, false },
  { "application/x-font-type1", "pfa", "pfa;pfb;pfm;afm", NULL, false },
  { "application/x-font-vfont", NULL, NULL, NULL, false },
  { "application/x-freearc", "arc", "arc", NULL, false },
  { "application/x-futuresplash", "spl", "spl", NULL, false },
  { "application/x-gca-compressed", "gca", "gca", NULL, false },
  { "application/x-glulx", "ulx", "ulx", NULL, false },
  { "application/x-gnumeric", "gnumeric", "gnumeric", NULL, false },
  { "application/x-gramps-xml", "gramps", "gramps", NULL, false },
  { "application/x-gtar", "gtar", "gtar", NULL, false },
  { "application/x-gzip", NULL, NULL, NULL, false },
  { "application/x-hdf", "hdf", "hdf", NULL, false },
  { "application/x-httpd-php", "php", "php", NULL, true },
  { "application/x-install-instructions", "install", "install", NULL, false },
  { "application/x-iso9660-image", "iso", "iso", NULL, false },
  { "application/x-java-archive-diff", "jardiff", "jardiff", NULL, false },
  { "application/x-java-jnlp-file", "jnlp", "jnlp", NULL, false },
  { "application/x-javascript", NULL, NULL, NULL, true },
  { "application/x-latex", "latex", "latex", NULL, false },
  { "application/x-lua-bytecode", "luac", "luac", NULL, false },
  { "application/x-lzh-compressed", "lzh", "lzh;lha", NULL, false },
  { "application/x-makeself", "run", "run", NULL, false },
  { "application/x-mie", "mie", "mie", NULL, false },
  { "application/x-mobipocket-ebook", "prc", "prc;mobi", NULL, false },
  { "application/x-mpegurl", NULL, NULL, NULL, false },
  { "application/x-ms-application", "application", "application", NULL, false },
  { "application/x-ms-shortcut", "lnk", "lnk", NULL, false },
  { "application/x-ms-wmd", "wmd", "wmd", NULL, false },
  { "application/x-ms-wmz", "wmz", "wmz", NULL, false },
  { "application/x-ms-xbap", "xbap", "xbap", NULL, false },
  { "application/x-msaccess", "mdb", "mdb", NULL, false },
  { "application/x-msbinder", "obd", "obd", NULL, false },
  { "application/x-mscardfile", "crd", "crd", NULL, false },
  { "application/x-msclip", "clp", "clp", NULL, false },
  { "application/x-msdos-program", "exe", "exe", NULL, false },
  { "application/x-msdownload", "exe", "exe;dll;com;bat;msi", NULL, false },
  { "application/x-msmediaview", "mvb", "mvb;m13;m14", NULL, false },
  { "application/x-msmetafile", "wmf", "wmf;wmz;emf;emz", NULL, false },
  { "application/x-msmoney", "mny", "mny", NULL, false },
  { "application/x-mspublisher", "pub", "pub", NULL, false },
  { "application/x-msschedule", "scd", "scd", NULL, false },
  { "application/x-msterminal", "trm", "trm", NULL, false },
  { "application/x-mswrite", "wri", "wri", NULL, false },
  { "application/x-netcdf", "nc", "nc;cdf", NULL, false },
  { "application/x-ns-proxy-autoconfig", "pac", "pac", NULL, true },
  { "application/x-nzb", "nzb", "nzb", NULL, false },
  { "application/x-perl", "pl", "pl;pm", NULL, false },
  { "application/x-pilot", "prc", "prc;pdb", NULL, false },
  { "application/x-pkcs12", "p12", "p12;pfx", NULL, false },
  { "application/x-pkcs7-certificates", "p7b", "p7b;spc", NULL, false },
  { "application/x-pkcs7-certreqresp", "p7r", "p7r", NULL, false },
  { "application/x-rar-compressed", "rar", "rar", NULL, false },
  { "application/x-redhat-package-manager", "rpm", "rpm", NULL, false },
  { "application/x-research-info-systems", "ris", "ris", NULL, false },
  { "application/x-sea", "sea", "sea", NULL, false },
  { "application/x-sh", "sh", "sh", NULL, true },
  { "application/x-shar", "shar", "shar", NULL, false },
  { "application/x-shockwave-flash", "swf", "swf", NULL, false },
  { "application/x-silverlight-app", "xap", "xap", NULL, false },
  { "application/x-sql", "sql", "sql", NULL, false },
  { "application/x-stuffit", "sit", "sit", NULL, false },
  { "application/x-stuffitx", "sitx", "sitx", NULL, false },
  { "application/x-subrip", "srt", "srt", NULL, false },
  { "application/x-sv4cpio", "sv4cpio", "sv4cpio", NULL, false },
  { "application/x-sv4crc", "sv4crc", "sv4crc", NULL, false },
  { "application/x-t3vm-image", "t3", "t3", NULL, false },
  { "application/x-tads", "gam", "gam", NULL, false },
  { "application/x-tar", "tar", "tar", NULL, true },
  { "application/x-tcl", "tcl", "tcl;tk", NULL, false },
  { "application/x-tex", "tex", "tex", NULL, false },
  { "application/x-tex-tfm", "tfm", "tfm", NULL, false },
  { "application/x-texinfo", "texinfo", "texinfo;texi", NULL, false },
  { "application/x-tgif", "obj", "obj", NULL, false },
  { "application/x-ustar", "ustar", "ustar", NULL, false },
  { "application/x-virtualbox-hdd", "hdd", "hdd", NULL, true },
  { "application/x-virtualbox-ova", "ova", "ova", NULL, true },
  { "application/x-virtualbox-ovf", "ovf", "ovf", NULL, true },
  { "application/x-virtualbox-vbox", "vbox", "vbox", NULL, true },
  { "application/x-virtualbox-vbox-extpack", "vbox-extpack", "vbox-extpack", NULL, false },
  { "application/x-virtualbox-vdi", "vdi", "vdi", NULL, true },
  { "application/x-virtualbox-vhd", "vhd", "vhd", NULL, true },
  { "application/x-virtualbox-vmdk", "vmdk", "vmdk", NULL, true },
  { "application/x-wais-source", "src", "src", NULL, false },
  { "application/x-web-app-manifest+json", "webapp", "webapp", NULL, true },
  { "application/x-www-form-urlencoded", NULL, NULL, NULL, true },
  { "application/x-x509-ca-cert", "der", "der;crt;pem", NULL, false },
  { "application/x-xfig", "fig", "fig", NULL, false },
  { "application/x-xliff+xml", "xlf", "xlf", NULL, true },
  { "application/x-xpinstall", "xpi", "xpi", NULL, false },
  { "application/x-xz", "xz", "xz", NULL, false },
  { "application/x-zmachine", "z1", "z1;z2;z3;z4;z5;z6;z7;z8", NULL, false },
  { "application/x400-bp", NULL, NULL, NULL, false },
  { "application/xacml+xml", NULL, NULL, NULL, true },
  { "application/xaml+xml", "xaml", "xaml", NULL, true },
  { "application/xcap-att+xml", NULL, NULL, NULL, true },
  { "application/xcap-caps+xml", NULL, NULL, NULL, true },
  { "application/xcap-diff+xml", "xdf", "xdf", NULL, true },
  { "application/xcap-el+xml", NULL, NULL, NULL, true },
  { "application/xcap-error+xml", NULL, NULL, NULL, true },
  { "application/xcap-ns+xml", NULL, NULL, NULL, true },
  { "application/xcon-conference-info+xml", NULL, NULL, NULL, true },
  { "application/xcon-conference-info-diff+xml", NULL, NULL, NULL, true },
  { "application/xenc+xml", "xenc", "xenc", NULL, true },
  { "application/xhtml+xml", "xhtml", "xhtml;xht", NULL, true },
  { "application/xhtml-voice+xml", NULL, NULL, NULL, true },
  { "application/xliff+xml", NULL, NULL, NULL, true },
  { "application/xml", "xml", "xml;xsl;xsd;rng", NULL, true },
  { "application/xml-dtd", "dtd", "dtd", NULL, true },
  { "application/xml-external-parsed-entity", NULL, NULL, NULL, false },
  { "application/xml-patch+xml", NULL, NULL, NULL, true },
  { "application/xmpp+xml", NULL, NULL, NULL, true },
  { "application/xop+xml", "xop", "xop", NULL, true },
  { "application/xproc+xml", "xpl", "xpl", NULL, true },
  { "application/xslt+xml", "xslt", "xslt", NULL, true },
  { "application/xspf+xml", "xspf", "xspf", NULL, true },
  { "application/xv+xml", "mxml", "mxml;xhvml;xvml;xvm", NULL, true },
  { "application/yang", "yang", "yang", NULL, false },
  { "application/yang-data+json", NULL, NULL, NULL, true },
  { "application/yang-data+xml", NULL, NULL, NULL, true },
  { "application/yang-patch+json", NULL, NULL, NULL, true },
  { "application/yang-patch+xml", NULL, NULL, NULL, true },
  { "application/yin+xml", "yin", "yin", NULL, true },
  { "application/zip", "zip", "zip", NULL, false },
  { "application/zlib", NULL, NULL, NULL, false },
  { "audio/1d-interleaved-parityfec", NULL, NULL, NULL, false },
  { "audio/32kadpcm", NULL, NULL, NULL, false },
  { "audio/3gpp", "3gpp", "3gpp", NULL, false },
  { "audio/3gpp2", NULL, NULL, NULL, false },
  { "audio/aac", NULL, NULL, NULL, false },
  { "audio/ac3", NULL, NULL, NULL, false },
  { "audio/adpcm", "adp", "adp", NULL, false },
  { "audio/amr", NULL, NULL, NULL, false },
  { "audio/amr-wb", NULL, NULL, NULL, false },
  { "audio/amr-wb+", NULL, NULL, NULL, false },
  { "audio/aptx", NULL, NULL, NULL, false },
  { "audio/asc", NULL, NULL, NULL, false },
  { "audio/atrac-advanced-lossless", NULL, NULL, NULL, false },
  { "audio/atrac-x", NULL, NULL, NULL, false },
  { "audio/atrac3", NULL, NULL, NULL, false },
  { "audio/basic", "au", "au;snd", NULL, false },
  { "audio/bv16", NULL, NULL, NULL, false },
  { "audio/bv32", NULL, NULL, NULL, false },
  { "audio/clearmode", NULL, NULL, NULL, false },
  { "audio/cn", NULL, NULL, NULL, false },
  { "audio/dat12", NULL, NULL, NULL, false },
  { "audio/dls", NULL, NULL, NULL, false },
  { "audio/dsr-es201108", NULL, NULL, NULL, false },
  { "audio/dsr-es202050", NULL, NULL, NULL, false },
  { "audio/dsr-es202211", NULL, NULL, NULL, false },
  { "audio/dsr-es202212", NULL, NULL, NULL, false },
  { "audio/dv", NULL, NULL, NULL, false },
  { "audio/dvi4", NULL, NULL, NULL, false },
  { "audio/eac3", NULL, NULL, NULL, false },
  { "audio/encaprtp", NULL, NULL, NULL, false },
  { "audio/evrc", NULL, NULL, NULL, false },
  { "audio/evrc-qcp", NULL, NULL, NULL, false },
  { "audio/evrc0", NULL, NULL, NULL, false },
  { "audio/evrc1", NULL, NULL, NULL, false },
  { "audio/evrcb", NULL, NULL, NULL, false },
  { "audio/evrcb0", NULL, NULL, NULL, false },
  { "audio/evrcb1", NULL, NULL, NULL, false },
  { "audio/evrcnw", NULL, NULL, NULL, false },
  { "audio/evrcnw0", NULL, NULL, NULL, false },
  { "audio/evrcnw1", NULL, NULL, NULL, false },
  { "audio/evrcwb", NULL, NULL, NULL, false },
  { "audio/evrcwb0", NULL, NULL, NULL, false },
  { "audio/evrcwb1", NULL, NULL, NULL, false },
  { "audio/evs", NULL, NULL, NULL, false },
  { "audio/fwdred", NULL, NULL, NULL, false },
  { "audio/g711-0", NULL, NULL, NULL, false },
  { "audio/g719", NULL, NULL, NULL, false },
  { "audio/g722", NULL, NULL, NULL, false },
  { "audio/g7221", NULL, NULL, NULL, false },
  { "audio/g723", NULL, NULL, NULL, false },
  { "audio/g726-16", NULL, NULL, NULL, false },
  { "audio/g726-24", NULL, NULL, NULL, false },
  { "audio/g726-32", NULL, NULL, NULL, false },
  { "audio/g726-40", NULL, NULL, NULL, false },
  { "audio/g728", NULL, NULL, NULL, false },
  { "audio/g729", NULL, NULL, NULL, false },
  { "audio/g7291", NULL, NULL, NULL, false },
  { "audio/g729d", NULL, NULL, NULL, false },
  { "audio/g729e", NULL, NULL, NULL, false },
  { "audio/gsm", NULL, NULL, NULL, false },
  { "audio/gsm-efr", NULL, NULL, NULL, false },
  { "audio/gsm-hr-08", NULL, NULL, NULL, false },
  { "audio/ilbc", NULL, NULL, NULL, false },
  { "audio/ip-mr_v2.5", NULL, NULL, NULL, false },
  { "audio/isac", NULL, NULL, NULL, false },
  { "audio/l16", NULL, NULL, NULL, false },
  { "audio/l20", NULL, NULL, NULL, false },
  { "audio/l24", NULL, NULL, NULL, false },
  { "audio/l8", NULL, NULL, NULL, false },
  { "audio/lpc", NULL, NULL, NULL, false },
  { "audio/melp", NULL, NULL, NULL, false },
  { "audio/melp1200", NULL, NULL, NULL, false },
  { "audio/melp2400", NULL, NULL, NULL, false },
  { "audio/melp600", NULL, NULL, NULL, false },
  { "audio/midi", "mid", "mid;midi;kar;rmi", NULL, false },
  { "audio/mobile-xmf", NULL, NULL, NULL, false },
  { "audio/mp3", "mp3", "mp3", NULL, false },
  { "audio/mp4", "m4a", "m4a;mp4a", NULL, false },
  { "audio/mp4a-latm", NULL, NULL, NULL, false },
  { "audio/mpa", NULL, NULL, NULL, false },
  { "audio/mpa-robust", NULL, NULL, NULL, false },
  { "audio/mpeg", "mpga", "mpga;mp2;mp2a;mp3;m2a;m3a", NULL, false },
  { "audio/mpeg4-generic", NULL, NULL, NULL, false },
  { "audio/musepack", NULL, NULL, NULL, false },
  { "audio/ogg", "oga", "oga;ogg;spx", NULL, false },
  { "audio/opus", NULL, NULL, NULL, false },
  { "audio/parityfec", NULL, NULL, NULL, false },
  { "audio/pcma", NULL, NULL, NULL, false },
  { "audio/pcma-wb", NULL, NULL, NULL, false },
  { "audio/pcmu", NULL, NULL, NULL, false },
  { "audio/pcmu-wb", NULL, NULL, NULL, false },
  { "audio/prs.sid", NULL, NULL, NULL, false },
  { "audio/qcelp", NULL, NULL, NULL, false },
  { "audio/raptorfec", NULL, NULL, NULL, false },
  { "audio/red", NULL, NULL, NULL, false },
  { "audio/rtp-enc-aescm128", NULL, NULL, NULL, false },
  { "audio/rtp-midi", NULL, NULL, NULL, false },
  { "audio/rtploopback", NULL, NULL, NULL, false },
  { "audio/rtx", NULL, NULL, NULL, false },
  { "audio/s3m", "s3m", "s3m", NULL, false },
  { "audio/silk", "sil", "sil", NULL, false },
  { "audio/smv", NULL, NULL, NULL, false },
  { "audio/smv-qcp", NULL, NULL, NULL, false },
  { "audio/smv0", NULL, NULL, NULL, false },
  { "audio/sp-midi", NULL, NULL, NULL, false },
  { "audio/speex", NULL, NULL, NULL, false },
  { "audio/t140c", NULL, NULL, NULL, false },
  { "audio/t38", NULL, NULL, NULL, false },
  { "audio/telephone-event", NULL, NULL, NULL, false },
  { "audio/tone", NULL, NULL, NULL, false },
  { "audio/uemclip", NULL, NULL, NULL, false },
  { "audio/ulpfec", NULL, NULL, NULL, false },
  { "audio/usac", NULL, NULL, NULL, false },
  { "audio/vdvi", NULL, NULL, NULL, false },
  { "audio/vmr-wb", NULL, NULL, NULL, false },
  { "audio/vnd.3gpp.iufp", NULL, NULL, NULL, false },
  { "audio/vnd.4sb", NULL, NULL, NULL, false },
  { "audio/vnd.audiokoz", NULL, NULL, NULL, false },
  { "audio/vnd.celp", NULL, NULL, NULL, false },
  { "audio/vnd.cisco.nse", NULL, NULL, NULL, false },
  { "audio/vnd.cmles.radio-events", NULL, NULL, NULL, false },
  { "audio/vnd.cns.anp1", NULL, NULL, NULL, false },
  { "audio/vnd.cns.inf1", NULL, NULL, NULL, false },
  { "audio/vnd.dece.audio", "uva", "uva;uvva", NULL, false },
  { "audio/vnd.digital-winds", "eol", "eol", NULL, false },
  { "audio/vnd.dlna.adts", NULL, NULL, NULL, false },
  { "audio/vnd.dolby.heaac.1", NULL, NULL, NULL, false },
  { "audio/vnd.dolby.heaac.2", NULL, NULL, NULL, false },
  { "audio/vnd.dolby.mlp", NULL, NULL, NULL, false },
  { "audio/vnd.dolby.mps", NULL, NULL, NULL, false },
  { "audio/vnd.dolby.pl2", NULL, NULL, NULL, false },
  { "audio/vnd.dolby.pl2x", NULL, NULL, NULL, false },
  { "audio/vnd.dolby.pl2z", NULL, NULL, NULL, false },
  { "audio/vnd.dolby.pulse.1", NULL, NULL, NULL, false },
  { "audio/vnd.dra", "dra", "dra", NULL, false },
  { "audio/vnd.dts", "dts", "dts", NULL, false },
  { "audio/vnd.dts.hd", "dtshd", "dtshd", NULL, false },
  { "audio/vnd.dvb.file", NULL, NULL, NULL, false },
  { "audio/vnd.everad.plj", NULL, NULL, NULL, false },
  { "audio/vnd.hns.audio", NULL, NULL, NULL, false },
  { "audio/vnd.lucent.voice", "lvp", "lvp", NULL, false },
  { "audio/vnd.ms-playready.media.pya", "pya", "pya", NULL, false },
  { "audio/vnd.nokia.mobile-xmf", NULL, NULL, NULL, false },
  { "audio/vnd.nortel.vbk", NULL, NULL, NULL, false },
  { "audio/vnd.nuera.ecelp4800", "ecelp4800", "ecelp4800", NULL, false },
  { "audio/vnd.nuera.ecelp7470", "ecelp7470", "ecelp7470", NULL, false },
  { "audio/vnd.nuera.ecelp9600", "ecelp9600", "ecelp9600", NULL, false },
  { "audio/vnd.octel.sbc", NULL, NULL, NULL, false },
  { "audio/vnd.presonus.multitrack", NULL, NULL, NULL, false },
  { "audio/vnd.qcelp", NULL, NULL, NULL, false },
  { "audio/vnd.rhetorex.32kadpcm", NULL, NULL, NULL, false },
  { "audio/vnd.rip", "rip", "rip", NULL, false },
  { "audio/vnd.rn-realaudio", NULL, NULL, NULL, false },
  { "audio/vnd.sealedmedia.softseal.mpeg", NULL, NULL, NULL, false },
  { "audio/vnd.vmx.cvsd", NULL, NULL, NULL, false },
  { "audio/vnd.wave", NULL, NULL, NULL, false },
  { "audio/vorbis", NULL, NULL, NULL, false },
  { "audio/vorbis-config", NULL, NULL, NULL, false },
  { "audio/wav", "wav", "wav", NULL, false },
  { "audio/wave", "wav", "wav", NULL, false },
  { "audio/webm", "weba", "weba", NULL, false },
  { "audio/x-aac", "aac", "aac", NULL, false },
  { "audio/x-aiff", "aif", "aif;aiff;aifc", NULL, false },
  { "audio/x-caf", "caf", "caf", NULL, false },
  { "audio/x-flac", "flac", "flac", NULL, false },
  { "audio/x-m4a", "m4a", "m4a", NULL, false },
  { "audio/x-matroska", "mka", "mka", NULL, false },
  { "audio/x-mpegurl", "m3u", "m3u", NULL, false },
  { "audio/x-ms-wax", "wax", "wax", NULL, false },
  { "audio/x-ms-wma", "wma", "wma", NULL, false },
  { "audio/x-pn-realaudio", "ram", "ram;ra", NULL, false },
  { "audio/x-pn-realaudio-plugin", "rmp", "rmp", NULL, false },
  { "audio/x-realaudio", "ra", "ra", NULL, false },
  { "audio/x-tta", NULL, NULL, NULL, false },
  { "audio/x-wav", "wav", "wav", NULL, false },
  { "audio/xm", "xm", "xm", NULL, false },
  { "chemical/x-cdx", "cdx", "cdx", NULL, false },
  { "chemical/x-cif", "cif", "cif", NULL, false },
  { "chemical/x-cmdf", "cmdf", "cmdf", NULL, false },
  { "chemical/x-cml", "cml", "cml", NULL, false },
  { "chemical/x-csml", "csml", "csml", NULL, false },
  { "chemical/x-pdb", NULL, NULL, NULL, false },
  { "chemical/x-xyz", "xyz", "xyz", NULL, false },
  { "font/collection", "ttc", "ttc", NULL, false },
  { "font/otf", "otf", "otf", NULL, true },
  { "font/sfnt", NULL, NULL, NULL, false },
  { "font/ttf", "ttf", "ttf", NULL, false },
  { "font/woff", "woff", "woff", NULL, false },
  { "font/woff2", "woff2", "woff2", NULL, false },
  { "image/aces", NULL, NULL, NULL, false },
  { "image/apng", "apng", "apng", NULL, false },
  { "image/bmp", "bmp", "bmp", NULL, true },
  { "image/cgm", "cgm", "cgm", NULL, false },
  { "image/dicom-rle", NULL, NULL, NULL, false },
  { "image/emf", NULL, NULL, NULL, false },
  { "image/fits", NULL, NULL, NULL, false },
  { "image/g3fax", "g3", "g3", NULL, false },
  { "image/gif", "gif", "gif", NULL, false },
  { "image/ief", "ief", "ief", NULL, false },
  { "image/jls", NULL, NULL, NULL, false },
  { "image/jp2", "jp2", "jp2;jpg2", NULL, false },
  { "image/jpeg", "jpeg", "jpeg;jpg;jpe", NULL, false },
  { "image/jpm", "jpm", "jpm", NULL, false },
  { "image/jpx", "jpx", "jpx;jpf", NULL, false },
  { "image/ktx", "ktx", "ktx", NULL, false },
  { "image/naplps", NULL, NULL, NULL, false },
  { "image/pjpeg", NULL, NULL, NULL, false },
  { "image/png", "png", "png", NULL, false },
  { "image/prs.btif", "btif", "btif", NULL, false },
  { "image/prs.pti", NULL, NULL, NULL, false },
  { "image/pwg-raster", NULL, NULL, NULL, false },
  { "image/sgi", "sgi", "sgi", NULL, false },
  { "image/svg+xml", "svg", "svg;svgz", NULL, true },
  { "image/t38", NULL, NULL, NULL, false },
  { "image/tiff", "tiff", "tiff;tif", NULL, false },
  { "image/tiff-fx", NULL, NULL, NULL, false },
  { "image/vnd.adobe.photoshop", "psd", "psd", NULL, true },
  { "image/vnd.airzip.accelerator.azv", NULL, NULL, NULL, false },
  { "image/vnd.cns.inf2", NULL, NULL, NULL, false },
  { "image/vnd.dece.graphic", "uvi", "uvi;uvvi;uvg;uvvg", NULL, false },
  { "image/vnd.djvu", "djvu", "djvu;djv", NULL, false },
  { "image/vnd.dvb.subtitle", "sub", "sub", NULL, false },
  { "image/vnd.dwg", "dwg", "dwg", NULL, false },
  { "image/vnd.dxf", "dxf", "dxf", NULL, false },
  { "image/vnd.fastbidsheet", "fbs", "fbs", NULL, false },
  { "image/vnd.fpx", "fpx", "fpx", NULL, false },
  { "image/vnd.fst", "fst", "fst", NULL, false },
  { "image/vnd.fujixerox.edmics-mmr", "mmr", "mmr", NULL, false },
  { "image/vnd.fujixerox.edmics-rlc", "rlc", "rlc", NULL, false },
  { "image/vnd.globalgraphics.pgb", NULL, NULL, NULL, false },
  { "image/vnd.microsoft.icon", NULL, NULL, NULL, false },
  { "image/vnd.mix", NULL, NULL, NULL, false },
  { "image/vnd.mozilla.apng", NULL, NULL, NULL, false },
  { "image/vnd.ms-modi", "mdi", "mdi", NULL, false },
  { "image/vnd.ms-photo", "wdp", "wdp", NULL, false },
  { "image/vnd.net-fpx", "npx", "npx", NULL, false },
  { "image/vnd.radiance", NULL, NULL, NULL, false },
  { "image/vnd.sealed.png", NULL, NULL, NULL, false },
  { "image/vnd.sealedmedia.softseal.gif", NULL, NULL, NULL, false },
  { "image/vnd.sealedmedia.softseal.jpg", NULL, NULL, NULL, false },
  { "image/vnd.svf", NULL, NULL, NULL, false },
  { "image/vnd.tencent.tap", NULL, NULL, NULL, false },
  { "image/vnd.valve.source.texture", NULL, NULL, NULL, false },
  { "image/vnd.wap.wbmp", "wbmp", "wbmp", NULL, false },
  { "image/vnd.xiff", "xif", "xif", NULL, false },
  { "image/vnd.zbrush.pcx", NULL, NULL, NULL, false },
  { "image/webp", "webp", "webp", NULL, false },
  { "image/wmf", NULL, NULL, NULL, false },
  { "image/x-3ds", "3ds", "3ds", NULL, false },
  { "image/x-cmu-raster", "ras", "ras", NULL, false },
  { "image/x-cmx", "cmx", "cmx", NULL, false },
  { "image/x-freehand", "fh", "fh;fhc;fh4;fh5;fh7", NULL, false },
  { "image/x-icon", "ico", "ico", NULL, true },
  { "image/x-jng", "jng", "jng", NULL, false },
  { "image/x-mrsid-image", "sid", "sid", NULL, false },
  { "image/x-ms-bmp", "bmp", "bmp", NULL, true },
  { "image/x-pcx", "pcx", "pcx", NULL, false },
  { "image/x-pict", "pic", "pic;pct", NULL, false },
  { "image/x-portable-anymap", "pnm", "pnm", NULL, false },
  { "image/x-portable-bitmap", "pbm", "pbm", NULL, false },
  { "image/x-portable-graymap", "pgm", "pgm", NULL, false },
  { "image/x-portable-pixmap", "ppm", "ppm", NULL, false },
  { "image/x-rgb", "rgb", "rgb", NULL, false },
  { "image/x-tga", "tga", "tga", NULL, false },
  { "image/x-xbitmap", "xbm", "xbm", NULL, false },
  { "image/x-xcf", NULL, NULL, NULL, false },
  { "image/x-xpixmap", "xpm", "xpm", NULL, false },
  { "image/x-xwindowdump", "xwd", "xwd", NULL, false },
  { "message/cpim", NULL, NULL, NULL, false },
  { "message/delivery-status", NULL, NULL, NULL, false },
  { "message/disposition-notification", "disposition-notification", "disposition-notification", NULL, false },
  { "message/external-body", NULL, NULL, NULL, false },
  { "message/feedback-report", NULL, NULL, NULL, false },
  { "message/global", "u8msg", "u8msg", NULL, false },
  { "message/global-delivery-status", "u8dsn", "u8dsn", NULL, false },
  { "message/global-disposition-notification", "u8mdn", "u8mdn", NULL, false },
  { "message/global-headers", "u8hdr", "u8hdr", NULL, false },
  { "message/http", NULL, NULL, NULL, false },
  { "message/imdn+xml", NULL, NULL, NULL, true },
  { "message/news", NULL, NULL, NULL, false },
  { "message/partial", NULL, NULL, NULL, false },
  { "message/rfc822", "eml", "eml;mime", NULL, true },
  { "message/s-http", NULL, NULL, NULL, false },
  { "message/sip", NULL, NULL, NULL, false },
  { "message/sipfrag", NULL, NULL, NULL, false },
  { "message/tracking-status", NULL, NULL, NULL, false },
  { "message/vnd.si.simp", NULL, NULL, NULL, false },
  { "message/vnd.wfa.wsc", "wsc", "wsc", NULL, false },
  { "model/3mf", NULL, NULL, NULL, false },
  { "model/gltf+json", "gltf", "gltf", NULL, true },
  { "model/gltf-binary", "glb", "glb", NULL, true },
  { "model/iges", "igs", "igs;iges", NULL, false },
  { "model/mesh", "msh", "msh;mesh;silo", NULL, false },
  { "model/stl", NULL, NULL, NULL, false },
  { "model/vnd.collada+xml", "dae", "dae", NULL, true },
  { "model/vnd.dwf", "dwf", "dwf", NULL, false },
  { "model/vnd.flatland.3dml", NULL, NULL, NULL, false },
  { "model/vnd.gdl", "gdl", "gdl", NULL, false },
  { "model/vnd.gs-gdl", NULL, NULL, NULL, false },
  { "model/vnd.gs.gdl", NULL, NULL, NULL, false },
  { "model/vnd.gtw", "gtw", "gtw", NULL, false },
  { "model/vnd.moml+xml", NULL, NULL, NULL, true },
  { "model/vnd.mts", "mts", "mts", NULL, false },
  { "model/vnd.opengex", NULL, NULL, NULL, false },
  { "model/vnd.parasolid.transmit.binary", NULL, NULL, NULL, false },
  { "model/vnd.parasolid.transmit.text", NULL, NULL, NULL, false },
  { "model/vnd.rosette.annotated-data-model", NULL, NULL, NULL, false },
  { "model/vnd.usdz+zip", NULL, NULL, NULL, false },
  { "model/vnd.valve.source.compiled-map", NULL, NULL, NULL, false },
  { "model/vnd.vtu", "vtu", "vtu", NULL, false },
  { "model/vrml", "wrl", "wrl;vrml", NULL, false },
  { "model/x3d+binary", "x3db", "x3db;x3dbz", NULL, false },
  { "model/x3d+fastinfoset", NULL, NULL, NULL, false },
  { "model/x3d+vrml", "x3dv", "x3dv;x3dvz", NULL, false },
  { "model/x3d+xml", "x3d", "x3d;x3dz", NULL, true },
  { "model/x3d-vrml", NULL, NULL, NULL, false },
  { "multipart/alternative", NULL, NULL, NULL, false },
  { "multipart/appledouble", NULL, NULL, NULL, false },
  { "multipart/byteranges", NULL, NULL, NULL, false },
  { "multipart/digest", NULL, NULL, NULL, false },
  { "multipart/encrypted", NULL, NULL, NULL, false },
  { "multipart/form-data", NULL, NULL, NULL, false },
  { "multipart/header-set", NULL, NULL, NULL, false },
  { "multipart/mixed", NULL, NULL, NULL, false },
  { "multipart/multilingual", NULL, NULL, NULL, false },
  { "multipart/parallel", NULL, NULL, NULL, false },
  { "multipart/related", NULL, NULL, NULL, false },
  { "multipart/report", NULL, NULL, NULL, false },
  { "multipart/signed", NULL, NULL, NULL, false },
  { "multipart/vnd.bint.med-plus", NULL, NULL, NULL, false },
  { "multipart/voice-message", NULL, NULL, NULL, false },
  { "multipart/x-mixed-replace", NULL, NULL, NULL, false },
  { "text/1d-interleaved-parityfec", NULL, NULL, NULL, false },
  { "text/cache-manifest", "appcache", "appcache;manifest", NULL, true },
  { "text/calendar", "ics", "ics;ifb", NULL, false },
  { "text/calender", NULL, NULL, NULL, true },
  { "text/cmd", NULL, NULL, NULL, true },
  { "text/coffeescript", "coffee", "coffee;litcoffee", NULL, false },
  { "text/css", "css", "css", "UTF-8", true },
  { "text/csv", "csv", "csv", NULL, true },
  { "text/csv-schema", NULL, NULL, NULL, false },
  { "text/directory", NULL, NULL, NULL, false },
  { "text/dns", NULL, NULL, NULL, false },
  { "text/ecmascript", NULL, NULL, NULL, false },
  { "text/encaprtp", NULL, NULL, NULL, false },
  { "text/enriched", NULL, NULL, NULL, false },
  { "text/fwdred", NULL, NULL, NULL, false },
  { "text/grammar-ref-list", NULL, NULL, NULL, false },
  { "text/html", "html", "html;htm;shtml", NULL, true },
  { "text/jade", "jade", "jade", NULL, false },
  { "text/javascript", NULL, NULL, NULL, true },
  { "text/jcr-cnd", NULL, NULL, NULL, false },
  { "text/jsx", "jsx", "jsx", NULL, true },
  { "text/less", "less", "less", NULL, false },
  { "text/markdown", "markdown", "markdown;md", NULL, true },
  { "text/mathml", "mml", "mml", NULL, false },
  { "text/mizar", NULL, NULL, NULL, false },
  { "text/n3", "n3", "n3", NULL, true },
  { "text/parameters", NULL, NULL, NULL, false },
  { "text/parityfec", NULL, NULL, NULL, false },
  { "text/plain", "txt", "txt;text;conf;def;list;log;in;ini", NULL, true },
  { "text/provenance-notation", NULL, NULL, NULL, false },
  { "text/prs.fallenstein.rst", NULL, NULL, NULL, false },
  { "text/prs.lines.tag", "dsc", "dsc", NULL, false },
  { "text/prs.prop.logic", NULL, NULL, NULL, false },
  { "text/raptorfec", NULL, NULL, NULL, false },
  { "text/red", NULL, NULL, NULL, false },
  { "text/rfc822-headers", NULL, NULL, NULL, false },
  { "text/richtext", "rtx", "rtx", NULL, true },
  { "text/rtf", "rtf", "rtf", NULL, true },
  { "text/rtp-enc-aescm128", NULL, NULL, NULL, false },
  { "text/rtploopback", NULL, NULL, NULL, false },
  { "text/rtx", NULL, NULL, NULL, false },
  { "text/sgml", "sgml", "sgml;sgm", NULL, false },
  { "text/shex", "shex", "shex", NULL, false },
  { "text/slim", "slim", "slim;slm", NULL, false },
  { "text/strings", NULL, NULL, NULL, false },
  { "text/stylus", "stylus", "stylus;styl", NULL, false },
  { "text/t140", NULL, NULL, NULL, false },
  { "text/tab-separated-values", "tsv", "tsv", NULL, true },
  { "text/troff", "t", "t;tr;roff;man;me;ms", NULL, false },
  { "text/turtle", "ttl", "ttl", "UTF-8", false },
  { "text/ulpfec", NULL, NULL, NULL, false },
  { "text/uri-list", "uri", "uri;uris;urls", NULL, true },
  { "text/vcard", "vcard", "vcard", NULL, true },
  { "text/vnd.a", NULL, NULL, NULL, false },
  { "text/vnd.abc", NULL, NULL, NULL, false },
  { "text/vnd.ascii-art", NULL, NULL, NULL, false },
  { "text/vnd.curl", "curl", "curl", NULL, false },
  { "text/vnd.curl.dcurl", "dcurl", "dcurl", NULL, false },
  { "text/vnd.curl.mcurl", "mcurl", "mcurl", NULL, false },
  { "text/vnd.curl.scurl", "scurl", "scurl", NULL, false },
  { "text/vnd.debian.copyright", NULL, NULL, NULL, false },
  { "text/vnd.dmclientscript", NULL, NULL, NULL, false },
  { "text/vnd.dvb.subtitle", "sub", "sub", NULL, false },
  { "text/vnd.esmertec.theme-descriptor", NULL, NULL, NULL, false },
  { "text/vnd.fly", "fly", "fly", NULL, false },
  { "text/vnd.fmi.flexstor", "flx", "flx", NULL, false },
  { "text/vnd.gml", NULL, NULL, NULL, false },
  { "text/vnd.graphviz", "gv", "gv", NULL, false },
  { "text/vnd.hgl", NULL, NULL, NULL, false },
  { "text/vnd.in3d.3dml", "3dml", "3dml", NULL, false },
  { "text/vnd.in3d.spot", "spot", "spot", NULL, false },
  { "text/vnd.iptc.newsml", NULL, NULL, NULL, false },
  { "text/vnd.iptc.nitf", NULL, NULL, NULL, false },
  { "text/vnd.latex-z", NULL, NULL, NULL, false },
  { "text/vnd.motorola.reflex", NULL, NULL, NULL, false },
  { "text/vnd.ms-mediapackage", NULL, NULL, NULL, false },
  { "text/vnd.net2phone.commcenter.command", NULL, NULL, NULL, false },
  { "text/vnd.radisys.msml-basic-layout", NULL, NULL, NULL, false },
  { "text/vnd.si.uricatalogue", NULL, NULL, NULL, false },
  { "text/vnd.sun.j2me.app-descriptor", "jad", "jad", NULL, false },
  { "text/vnd.trolltech.linguist", NULL, NULL, NULL, false },
  { "text/vnd.wap.si", NULL, NULL, NULL, false },
  { "text/vnd.wap.sl", NULL, NULL, NULL, false },
  { "text/vnd.wap.wml", "wml", "wml", NULL, false },
  { "text/vnd.wap.wmlscript", "wmls", "wmls", NULL, false },
  { "text/vtt", "vtt", "vtt", "UTF-8", true },
  { "text/x-asm", "s", "s;asm", NULL, false },
  { "text/x-c", "c", "c;cc;cxx;cpp;h;hh;dic", NULL, false },
  { "text/x-component", "htc", "htc", NULL, false },
  { "text/x-fortran", "f", "f;for;f77;f90", NULL, false },
  { "text/x-gwt-rpc", NULL, NULL, NULL, true },
  { "text/x-handlebars-template", "hbs", "hbs", NULL, false },
  { "text/x-java-source", "java", "java", NULL, false },
  { "text/x-jquery-tmpl", NULL, NULL, NULL, true },
  { "text/x-lua", "lua", "lua", NULL, false },
  { "text/x-markdown", "mkd", "mkd", NULL, true },
  { "text/x-nfo", "nfo", "nfo", NULL, false },
  { "text/x-opml", "opml", "opml", NULL, false },
  { "text/x-org", "org", "org", NULL, true },
  { "text/x-pascal", "p", "p;pas", NULL, false },
  { "text/x-processing", "pde", "pde", NULL, true },
  { "text/x-sass", "sass", "sass", NULL, false },
  { "text/x-scss", "scss", "scss", NULL, false },
  { "text/x-setext", "etx", "etx", NULL, false },
  { "text/x-sfv", "sfv", "sfv", NULL, false },
  { "text/x-suse-ymp", "ymp", "ymp", NULL, true },
  { "text/x-uuencode", "uu", "uu", NULL, false },
  { "text/x-vcalendar", "vcs", "vcs", NULL, false },
  { "text/x-vcard", "vcf", "vcf", NULL, false },
  { "text/xml", "xml", "xml", NULL, true },
  { "text/xml-external-parsed-entity", NULL, NULL, NULL, false },
  { "text/yaml", "yaml", "yaml;yml", NULL, false },
  { "video/1d-interleaved-parityfec", NULL, NULL, NULL, false },
  { "video/3gpp", "3gp", "3gp;3gpp", NULL, false },
  { "video/3gpp-tt", NULL, NULL, NULL, false },
  { "video/3gpp2", "3g2", "3g2", NULL, false },
  { "video/bmpeg", NULL, NULL, NULL, false },
  { "video/bt656", NULL, NULL, NULL, false },
  { "video/celb", NULL, NULL, NULL, false },
  { "video/dv", NULL, NULL, NULL, false },
  { "video/encaprtp", NULL, NULL, NULL, false },
  { "video/h261", "h261", "h261", NULL, false },
  { "video/h263", "h263", "h263", NULL, false },
  { "video/h263-1998", NULL, NULL, NULL, false },
  { "video/h263-2000", NULL, NULL, NULL, false },
  { "video/h264", "h264", "h264", NULL, false },
  { "video/h264-rcdo", NULL, NULL, NULL, false },
  { "video/h264-svc", NULL, NULL, NULL, false },
  { "video/h265", NULL, NULL, NULL, false },
  { "video/iso.segment", NULL, NULL, NULL, false },
  { "video/jpeg", "jpgv", "jpgv", NULL, false },
  { "video/jpeg2000", NULL, NULL, NULL, false },
  { "video/jpm", "jpm", "jpm;jpgm", NULL, false },
  { "video/mj2", "mj2", "mj2;mjp2", NULL, false },
  { "video/mp1s", NULL, NULL, NULL, false },
  { "video/mp2p", NULL, NULL, NULL, false },
  { "video/mp2t", "ts", "ts", NULL, false },
  { "video/mp4", "mp4", "mp4;mp4v;mpg4", NULL, false },
  { "video/mp4v-es", NULL, NULL, NULL, false },
  { "video/mpeg", "mpeg", "mpeg;mpg;mpe;m1v;m2v", NULL, false },
  { "video/mpeg4-generic", NULL, NULL, NULL, false },
  { "video/mpv", NULL, NULL, NULL, false },
  { "video/nv", NULL, NULL, NULL, false },
  { "video/ogg", "ogv", "ogv", NULL, false },
  { "video/parityfec", NULL, NULL, NULL, false },
  { "video/pointer", NULL, NULL, NULL, false },
  { "video/quicktime", "qt", "qt;mov", NULL, false },
  { "video/raptorfec", NULL, NULL, NULL, false },
  { "video/raw", NULL, NULL, NULL, false },
  { "video/rtp-enc-aescm128", NULL, NULL, NULL, false },
  { "video/rtploopback", NULL, NULL, NULL, false },
  { "video/rtx", NULL, NULL, NULL, false },
  { "video/smpte291", NULL, NULL, NULL, false },
  { "video/smpte292m", NULL, NULL, NULL, false },
  { "video/ulpfec", NULL, NULL, NULL, false },
  { "video/vc1", NULL, NULL, NULL, false },
  { "video/vnd.cctv", NULL, NULL, NULL, false },
  { "video/vnd.dece.hd", "uvh", "uvh;uvvh", NULL, false },
  { "video/vnd.dece.mobile", "uvm", "uvm;uvvm", NULL, false },
  { "video/vnd.dece.mp4", NULL, NULL, NULL, false },
  { "video/vnd.dece.pd", "uvp", "uvp;uvvp", NULL, false },
  { "video/vnd.dece.sd", "uvs", "uvs;uvvs", NULL, false },
  { "video/vnd.dece.video", "uvv", "uvv;uvvv", NULL, false },
  { "video/vnd.directv.mpeg", NULL, NULL, NULL, false },
  { "video/vnd.directv.mpeg-tts", NULL, NULL, NULL, false },
  { "video/vnd.dlna.mpeg-tts", NULL, NULL, NULL, false },
  { "video/vnd.dvb.file", "dvb", "dvb", NULL, false },
  { "video/vnd.fvt", "fvt", "fvt", NULL, false },
  { "video/vnd.hns.video", NULL, NULL, NULL, false },
  { "video/vnd.iptvforum.1dparityfec-1010", NULL, NULL, NULL, false },
  { "video/vnd.iptvforum.1dparityfec-2005", NULL, NULL, NULL, false },
  { "video/vnd.iptvforum.2dparityfec-1010", NULL, NULL, NULL, false },
  { "video/vnd.iptvforum.2dparityfec-2005", NULL, NULL, NULL, false },
  { "video/vnd.iptvforum.ttsavc", NULL, NULL, NULL, false },
  { "video/vnd.iptvforum.ttsmpeg2", NULL, NULL, NULL, false },
  { "video/vnd.motorola.video", NULL, NULL, NULL, false },
  { "video/vnd.motorola.videop", NULL, NULL, NULL, false },
  { "video/vnd.mpegurl", "mxu", "mxu;m4u", NULL, false },
  { "video/vnd.ms-playready.media.pyv", "pyv", "pyv", NULL, false },
  { "video/vnd.nokia.interleaved-multimedia", NULL, NULL, NULL, false },
  { "video/vnd.nokia.mp4vr", NULL, NULL, NULL, false },
  { "video/vnd.nokia.videovoip", NULL, NULL, NULL, false },
  { "video/vnd.objectvideo", NULL, NULL, NULL, false },
  { "video/vnd.radgamettools.bink", NULL, NULL, NULL, false },
  { "video/vnd.radgamettools.smacker", NULL, NULL, NULL, false },
  { "video/vnd.sealed.mpeg1", NULL, NULL, NULL, false },
  { "video/vnd.sealed.mpeg4", NULL, NULL, NULL, false },
  { "video/vnd.sealed.swf", NULL, NULL, NULL, false },
  { "video/vnd.sealedmedia.softseal.mov", NULL, NULL, NULL, false },
  { "video/vnd.uvvu.mp4", "uvu", "uvu;uvvu", NULL, false },
  { "video/vnd.vivo", "viv", "viv", NULL, false },
  { "video/vp8", NULL, NULL, NULL, false },
  { "video/webm", "webm", "webm", NULL, false },
  { "video/x-f4v", "f4v", "f4v", NULL, false },
  { "video/x-fli", "fli", "fli", NULL, false },
  { "video/x-flv", "flv", "flv", NULL, false },
  { "video/x-m4v", "m4v", "m4v", NULL, false },
  { "video/x-matroska", "mkv", "mkv;mk3d;mks", NULL, false },
  { "video/x-mng", "mng", "mng", NULL, false },
  { "video/x-ms-asf", "asf", "asf;asx", NULL, false },
  { "video/x-ms-vob", "vob", "vob", NULL, false },
  { "video/x-ms-wm", "wm", "wm", NULL, false },
  { "video/x-ms-wmv", "wmv", "wmv", NULL, false },
  { "video/x-ms-wmx", "wmx", "wmx", NULL, false },
  { "video/x-ms-wvx", "wvx", "wvx", NULL, false },
  { "video/x-msvideo", "avi", "avi", NULL, false },
  { "video/x-sgi-movie", "movie", "movie", NULL, false },
  { "video/x-smv", "smv", "smv", NULL, false },
  { "x-conference/x-cooltalk", "ice", "ice", NULL, false },
  { "x-shader/x-fragment", NULL, NULL, NULL, true },
  { "x-shader/x-vertex", NULL, NULL, NULL, true }
};

// sorted extension to mime type mapping
static mime_ext_t mime_extensions[] = {
  { "123", 796 },
  { "3dml", 1913 },
  { "3ds", 1760 },
  { "3g2", 1959 },
  { "3gp", 1957 },
  { "3gpp", 1514 },
  { "3gpp", 1957 },
  { "7z", 1342 },
  { "aab", 1348 },
  { "aac", 1673 },
  { "aam", 1349 },
  { "aas", 1350 },
  { "abw", 1343 },
  { "ac", 262 },
  { "acc", 444 },
  { "ace", 1344 },
  { "acu", 425 },
  { "acutc", 426 },
  { "adp", 1518 },
  { "aep", 465 },
  { "afm", 1389 },
  { "afp", 721 },
  { "ahead", 438 },
  { "ai", 269 },
  { "aif", 1674 },
  { "aifc", 1674 },
  { "aiff", 1674 },
  { "air", 427 },
  { "ait", 564 },
  { "ami", 445 },
  { "apk", 447 },
  { "apng", 1702 },
  { "appcache", 1845 },
  { "application", 1413 },
  { "apr", 797 },
  { "arc", 1391 },
  { "arj", 1347 },
  { "asc", 252 },
  { "asf", 2043 },
  { "asm", 1930 },
  { "aso", 423 },
  { "asx", 2043 },
  { "atc", 426 },
  { "atom", 22 },
  { "atomcat", 23 },
  { "atomsvc", 26 },
  { "atx", 451 },
  { "au", 1527 },
  { "avi", 2049 },
  { "aw", 19 },
  { "azf", 439 },
  { "azs", 440 },
  { "azw", 442 },
  { "bat", 1423 },
  { "bcpio", 1351 },
  { "bdf", 1379 },
  { "bdm", 1231 },
  { "bdoc", 1352 },
  { "bdoc", 31 },
  { "bed", 1154 },
  { "bh2", 657 },
  { "bin", 236 },
  { "blb", 1354 },
  { "blorb", 1354 },
  { "bmi", 481 },
  { "bmp", 1767 },
  { "bmp", 1703 },
  { "book", 649 },
  { "box", 1125 },
  { "boz", 1356 },
  { "bpk", 236 },
  { "btif", 1720 },
  { "buffer", 236 },
  { "bz", 1355 },
  { "bz2", 1356 },
  { "c", 1931 },
  { "c11amc", 499 },
  { "c11amz", 500 },
  { "c4d", 498 },
  { "c4f", 498 },
  { "c4g", 498 },
  { "c4p", 498 },
  { "c4u", 498 },
  { "cab", 847 },
  { "caf", 1675 },
  { "cap", 1241 },
  { "car", 532 },
  { "cat", 863 },
  { "cb7", 1357 },
  { "cba", 1357 },
  { "cbr", 1357 },
  { "cbt", 1357 },
  { "cbz", 1357 },
  { "cc", 1931 },
  { "cco", 1363 },
  { "cct", 1371 },
  { "ccxml", 40 },
  { "cdbcmsg", 515 },
  { "cdf", 1431 },
  { "cdkey", 813 },
  { "cdmia", 42 },
  { "cdmic", 43 },
  { "cdmid", 44 },
  { "cdmio", 45 },
  { "cdmiq", 46 },
  { "cdx", 1688 },
  { "cdxml", 490 },
  { "cdy", 493 },
  { "cer", 263 },
  { "cfs", 1359 },
  { "cgm", 1704 },
  { "chat", 1360 },
  { "chm", 855 },
  { "chrt", 778 },
  { "cif", 1689 },
  { "cii", 449 },
  { "cil", 845 },
  { "cla", 496 },
  { "class", 152 },
  { "clkk", 519 },
  { "clkp", 520 },
  { "clkt", 521 },
  { "clkw", 522 },
  { "clkx", 518 },
  { "clp", 1421 },
  { "cmc", 517 },
  { "cmdf", 1690 },
  { "cml", 1691 },
  { "cmp", 1324 },
  { "cmx", 1762 },
  { "cod", 1160 },
  { "coffee", 1849 },
  { "com", 1423 },
  { "conf", 1872 },
  { "cpio", 1366 },
  { "cpp", 1931 },
  { "cpt", 176 },
  { "crd", 1420 },
  { "crl", 264 },
  { "crt", 1473 },
  { "crx", 1362 },
  { "cryptonote", 1159 },
  { "csh", 1367 },
  { "csl", 495 },
  { "csml", 1692 },
  { "csp", 514 },
  { "css", 1850 },
  { "cst", 1371 },
  { "csv", 1851 },
  { "cu", 67 },
  { "curl", 1900 },
  { "cww", 275 },
  { "cxt", 1371 },
  { "cxx", 1931 },
  { "dae", 1806 },
  { "daf", 826 },
  { "dart", 537 },
  { "dataless", 641 },
  { "davmount", 73 },
  { "dbk", 85 },
  { "dcr", 1371 },
  { "dcurl", 1901 },
  { "dd2", 991 },
  { "ddd", 660 },
  { "deb", 1369 },
  { "deb", 236 },
  { "def", 1872 },
  { "deploy", 236 },
  { "der", 1473 },
  { "dfac", 558 },
  { "dgc", 1370 },
  { "dic", 1931 },
  { "dir", 1371 },
  { "dis", 827 },
  { "disposition-notification", 1782 },
  { "dist", 236 },
  { "distz", 236 },
  { "djv", 1732 },
  { "djvu", 1732 },
  { "dll", 236 },
  { "dll", 1423 },
  { "dmg", 236 },
  { "dmg", 1346 },
  { "dmp", 1241 },
  { "dms", 236 },
  { "dna", 551 },
  { "doc", 222 },
  { "docm", 885 },
  { "docx", 1078 },
  { "dot", 222 },
  { "dotm", 886 },
  { "dotx", 1088 },
  { "dp", 1099 },
  { "dpg", 557 },
  { "dra", 1646 },
  { "dsc", 1875 },
  { "dssc", 87 },
  { "dtb", 1374 },
  { "dtd", 1495 },
  { "dts", 1647 },
  { "dtshd", 1648 },
  { "dump", 236 },
  { "dvb", 2010 },
  { "dvi", 1376 },
  { "dwf", 1807 },
  { "dwg", 1734 },
  { "dxf", 1735 },
  { "dxp", 1200 },
  { "dxr", 1371 },
  { "ear", 150 },
  { "ecelp4800", 1656 },
  { "ecelp7470", 1657 },
  { "ecelp9600", 1658 },
  { "ecma", 90 },
  { "edm", 928 },
  { "edx", 929 },
  { "efif", 1114 },
  { "ei6", 1112 },
  { "elc", 236 },
  { "emf", 1425 },
  { "eml", 1793 },
  { "emma", 103 },
  { "emz", 1425 },
  { "eol", 1636 },
  { "eot", 854 },
  { "eps", 269 },
  { "epub", 107 },
  { "es", 90 },
  { "es3", 608 },
  { "esa", 1100 },
  { "esf", 601 },
  { "et3", 608 },
  { "etx", 1947 },
  { "eva", 1378 },
  { "evy", 1377 },
  { "exe", 1423 },
  { "exe", 236 },
  { "exe", 1422 },
  { "exi", 109 },
  { "ext", 930 },
  { "ez", 17 },
  { "ez2", 635 },
  { "ez3", 636 },
  { "f", 1933 },
  { "f4v", 2037 },
  { "f77", 1933 },
  { "f90", 1933 },
  { "fbs", 1736 },
  { "fcdt", 429 },
  { "fcs", 762 },
  { "fdf", 639 },
  { "fe_launch", 546 },
  { "fg5", 656 },
  { "fgd", 1371 },
  { "fh", 1763 },
  { "fh4", 1763 },
  { "fh5", 1763 },
  { "fh7", 1763 },
  { "fhc", 1763 },
  { "fig", 1474 },
  { "flac", 1676 },
  { "fli", 2038 },
  { "flo", 818 },
  { "flv", 2039 },
  { "flw", 780 },
  { "flx", 1909 },
  { "fly", 1908 },
  { "fm", 649 },
  { "fnc", 650 },
  { "for", 1933 },
  { "fpx", 1737 },
  { "frame", 649 },
  { "fsc", 652 },
  { "fst", 1738 },
  { "ftc", 647 },
  { "fti", 450 },
  { "fvt", 2011 },
  { "fxp", 430 },
  { "fxpl", 430 },
  { "fzs", 666 },
  { "g2w", 674 },
  { "g3", 1708 },
  { "g3w", 675 },
  { "gac", 690 },
  { "gam", 1454 },
  { "gbr", 300 },
  { "gca", 1393 },
  { "gdl", 1809 },
  { "gdoc", 680 },
  { "geo", 584 },
  { "geojson", 121 },
  { "gex", 672 },
  { "ggb", 670 },
  { "ggt", 671 },
  { "ghf", 691 },
  { "gif", 1709 },
  { "gim", 692 },
  { "glb", 1802 },
  { "gltf", 1801 },
  { "gml", 125 },
  { "gmx", 679 },
  { "gnumeric", 1395 },
  { "gph", 646 },
  { "gpx", 126 },
  { "gqf", 688 },
  { "gqs", 688 },
  { "gram", 353 },
  { "gramps", 1396 },
  { "gre", 672 },
  { "grv", 693 },
  { "grxml", 354 },
  { "gsf", 1382 },
  { "gsheet", 682 },
  { "gslides", 681 },
  { "gtar", 1397 },
  { "gtm", 694 },
  { "gtw", 1812 },
  { "gv", 1911 },
  { "gxf", 127 },
  { "gxt", 673 },
  { "gz", 128 },
  { "h", 1931 },
  { "h261", 1965 },
  { "h263", 1966 },
  { "h264", 1969 },
  { "hal", 698 },
  { "hbci", 700 },
  { "hbs", 1935 },
  { "hdd", 1462 },
  { "hdf", 1399 },
  { "hh", 1931 },
  { "hjson", 131 },
  { "hlp", 1337 },
  { "hpgl", 706 },
  { "hpid", 707 },
  { "hps", 708 },
  { "hqx", 175 },
  { "htc", 1932 },
  { "htke", 785 },
  { "htm", 1860 },
  { "html", 1860 },
  { "hvd", 1313 },
  { "hvp", 1315 },
  { "hvs", 1314 },
  { "i2g", 747 },
  { "icc", 724 },
  { "ice", 2052 },
  { "icm", 724 },
  { "ico", 1764 },
  { "ics", 1846 },
  { "ief", 1710 },
  { "ifb", 1846 },
  { "ifm", 1186 },
  { "iges", 1803 },
  { "igl", 726 },
  { "igm", 745 },
  { "igs", 1803 },
  { "igx", 819 },
  { "iif", 1188 },
  { "img", 236 },
  { "imp", 424 },
  { "ims", 856 },
  { "in", 1872 },
  { "ini", 1872 },
  { "ink", 144 },
  { "inkml", 144 },
  { "install", 1401 },
  { "iota", 464 },
  { "ipfix", 146 },
  { "ipk", 1189 },
  { "irm", 722 },
  { "irp", 760 },
  { "iso", 236 },
  { "iso", 1402 },
  { "itp", 1187 },
  { "ivp", 729 },
  { "ivu", 730 },
  { "jad", 1923 },
  { "jade", 1861 },
  { "jam", 763 },
  { "jar", 150 },
  { "jardiff", 1403 },
  { "java", 1936 },
  { "jisp", 773 },
  { "jlt", 709 },
  { "jng", 1765 },
  { "jnlp", 1404 },
  { "joda", 774 },
  { "jp2", 1712 },
  { "jpe", 1713 },
  { "jpeg", 1713 },
  { "jpf", 1715 },
  { "jpg", 1713 },
  { "jpg2", 1712 },
  { "jpgm", 1976 },
  { "jpgv", 1974 },
  { "jpm", 1714 },
  { "jpm", 1976 },
  { "jpx", 1715 },
  { "js", 153 },
  { "json", 158 },
  { "json5", 161 },
  { "jsonld", 168 },
  { "jsonml", 162 },
  { "jsx", 1864 },
  { "kar", 1586 },
  { "karbon", 777 },
  { "kfo", 779 },
  { "kia", 786 },
  { "kml", 683 },
  { "kmz", 684 },
  { "kne", 787 },
  { "knp", 787 },
  { "kon", 781 },
  { "kpr", 782 },
  { "kpt", 782 },
  { "kpxx", 560 },
  { "ksp", 783 },
  { "ktr", 776 },
  { "ktx", 1716 },
  { "ktz", 776 },
  { "kwd", 784 },
  { "kwt", 784 },
  { "lasxml", 791 },
  { "latex", 1406 },
  { "lbd", 794 },
  { "lbe", 795 },
  { "les", 705 },
  { "less", 1865 },
  { "lha", 1408 },
  { "link66", 1163 },
  { "list", 1872 },
  { "list3820", 721 },
  { "listafp", 721 },
  { "litcoffee", 1849 },
  { "lnk", 1414 },
  { "log", 1872 },
  { "lostxml", 172 },
  { "lrf", 236 },
  { "lrm", 857 },
  { "ltf", 651 },
  { "lua", 1938 },
  { "luac", 1407 },
  { "lvp", 1652 },
  { "lwp", 802 },
  { "lzh", 1408 },
  { "m13", 1424 },
  { "m14", 1424 },
  { "m1v", 1983 },
  { "m21", 213 },
  { "m2a", 1593 },
  { "m2v", 1983 },
  { "m3a", 1593 },
  { "m3u", 1679 },
  { "m3u8", 458 },
  { "m4a", 1677 },
  { "m4a", 1589 },
  { "m4p", 214 },
  { "m4u", 2021 },
  { "m4v", 2040 },
  { "ma", 182 },
  { "mads", 178 },
  { "mag", 589 },
  { "maker", 649 },
  { "man", 1892 },
  { "manifest", 1845 },
  { "map", 158 },
  { "mar", 236 },
  { "markdown", 1866 },
  { "mathml", 183 },
  { "mb", 182 },
  { "mbk", 828 },
  { "mbox", 197 },
  { "mc1", 812 },
  { "mcd", 811 },
  { "mcurl", 1902 },
  { "md", 1866 },
  { "mdb", 1418 },
  { "mdi", 1745 },
  { "me", 1892 },
  { "mesh", 1804 },
  { "meta4", 203 },
  { "metalink", 202 },
  { "mets", 204 },
  { "mfm", 816 },
  { "mft", 301 },
  { "mgp", 1097 },
  { "mgz", 1126 },
  { "mid", 1586 },
  { "midi", 1586 },
  { "mie", 1410 },
  { "mif", 823 },
  { "mime", 1793 },
  { "mj2", 1977 },
  { "mjp2", 1977 },
  { "mjs", 153 },
  { "mk3d", 2041 },
  { "mka", 1678 },
  { "mkd", 1939 },
  { "mks", 2041 },
  { "mkv", 2041 },
  { "mlp", 553 },
  { "mmd", 492 },
  { "mmf", 1194 },
  { "mml", 1867 },
  { "mmr", 1739 },
  { "mng", 2042 },
  { "mny", 1426 },
  { "mobi", 1411 },
  { "mods", 208 },
  { "mov", 1990 },
  { "movie", 2050 },
  { "mp2", 1593 },
  { "mp21", 213 },
  { "mp2a", 1593 },
  { "mp3", 1588 },
  { "mp3", 1593 },
  { "mp4", 1981 },
  { "mp4a", 1589 },
  { "mp4s", 214 },
  { "mp4v", 1981 },
  { "mpc", 834 },
  { "mpd", 71 },
  { "mpe", 1983 },
  { "mpeg", 1983 },
  { "mpg", 1983 },
  { "mpg4", 1981 },
  { "mpga", 1593 },
  { "mpkg", 457 },
  { "mpm", 478 },
  { "mpn", 833 },
  { "mpp", 875 },
  { "mpt", 875 },
  { "mpy", 720 },
  { "mqy", 829 },
  { "mrc", 180 },
  { "mrcx", 181 },
  { "ms", 1892 },
  { "mscml", 200 },
  { "mseed", 640 },
  { "mseq", 891 },
  { "msf", 602 },
  { "msg", 861 },
  { "msh", 1804 },
  { "msi", 236 },
  { "msi", 1423 },
  { "msl", 830 },
  { "msm", 236 },
  { "msp", 236 },
  { "msty", 897 },
  { "mts", 1814 },
  { "mus", 896 },
  { "musicxml", 1156 },
  { "mvb", 1424 },
  { "mwf", 815 },
  { "mxf", 224 },
  { "mxl", 1155 },
  { "mxml", 1503 },
  { "mxs", 1248 },
  { "mxu", 2021 },
  { "n-gage", 922 },
  { "n3", 1869 },
  { "nb", 182 },
  { "nbp", 1295 },
  { "nc", 1431 },
  { "ncx", 1373 },
  { "nfo", 1940 },
  { "ngdat", 921 },
  { "nitf", 908 },
  { "nlu", 904 },
  { "nml", 598 },
  { "nnd", 909 },
  { "nns", 910 },
  { "nnw", 911 },
  { "npx", 1747 },
  { "nsc", 1365 },
  { "nsf", 799 },
  { "ntf", 908 },
  { "nzb", 1433 },
  { "oa2", 654 },
  { "oa3", 655 },
  { "oas", 653 },
  { "obd", 1419 },
  { "obj", 1460 },
  { "oda", 237 },
  { "odb", 938 },
  { "odc", 936 },
  { "odf", 939 },
  { "odft", 940 },
  { "odg", 941 },
  { "odi", 943 },
  { "odm", 950 },
  { "odp", 945 },
  { "ods", 947 },
  { "odt", 949 },
  { "oga", 1596 },
  { "ogg", 1596 },
  { "ogv", 1987 },
  { "ogx", 240 },
  { "omdoc", 241 },
  { "onepkg", 242 },
  { "onetmp", 242 },
  { "onetoc", 242 },
  { "onetoc2", 242 },
  { "opf", 239 },
  { "opml", 1941 },
  { "oprc", 1104 },
  { "org", 800 },
  { "org", 1942 },
  { "osf", 1316 },
  { "osfpvg", 1317 },
  { "otc", 937 },
  { "otf", 1696 },
  { "otg", 942 },
  { "oth", 952 },
  { "oti", 944 },
  { "otp", 946 },
  { "ots", 948 },
  { "ott", 951 },
  { "ova", 1463 },
  { "ovf", 1464 },
  { "owl", 286 },
  { "oxps", 243 },
  { "oxt", 1018 },
  { "p", 1943 },
  { "p10", 256 },
  { "p12", 1436 },
  { "p7b", 1437 },
  { "p7c", 258 },
  { "p7m", 258 },
  { "p7r", 1438 },
  { "p7s", 259 },
  { "p8", 260 },
  { "pac", 1432 },
  { "pas", 1943 },
  { "paw", 1109 },
  { "pbd", 1118 },
  { "pbm", 1771 },
  { "pcap", 1241 },
  { "pcf", 1385 },
  { "pcl", 710 },
  { "pclxl", 711 },
  { "pct", 1769 },
  { "pcurl", 533 },
  { "pcx", 1768 },
  { "pdb", 1435 },
  { "pdb", 1104 },
  { "pde", 1944 },
  { "pdf", 248 },
  { "pem", 1473 },
  { "pfa", 1389 },
  { "pfb", 1389 },
  { "pfm", 1389 },
  { "pfr", 118 },
  { "pfx", 1436 },
  { "pgm", 1772 },
  { "pgn", 1361 },
  { "pgp", 250 },
  { "php", 1400 },
  { "pic", 1769 },
  { "pkg", 236 },
  { "pki", 266 },
  { "pkipath", 265 },
  { "pkpass", 459 },
  { "pl", 1434 },
  { "plb", 409 },
  { "plc", 831 },
  { "plf", 1117 },
  { "pls", 267 },
  { "pm", 1434 },
  { "pml", 524 },
  { "png", 1719 },
  { "pnm", 1770 },
  { "portpkg", 803 },
  { "pot", 866 },
  { "potm", 871 },
  { "potx", 1047 },
  { "ppam", 867 },
  { "ppd", 528 },
  { "ppm", 1773 },
  { "pps", 866 },
  { "ppsm", 870 },
  { "ppsx", 1042 },
  { "ppt", 866 },
  { "pptm", 868 },
  { "pptx", 1035 },
  { "pqa", 1104 },
  { "prc", 1411 },
  { "prc", 1435 },
  { "pre", 798 },
  { "prf", 253 },
  { "ps", 269 },
  { "psb", 410 },
  { "psd", 1728 },
  { "psf", 1384 },
  { "pskcxml", 281 },
  { "ptid", 1129 },
  { "pub", 1427 },
  { "pvb", 411 },
  { "pwn", 422 },
  { "pya", 1653 },
  { "pyv", 2022 },
  { "qam", 603 },
  { "qbo", 750 },
  { "qfx", 751 },
  { "qps", 1128 },
  { "qt", 1990 },
  { "qwd", 1134 },
  { "qwt", 1134 },
  { "qxb", 1134 },
  { "qxd", 1134 },
  { "qxl", 1134 },
  { "qxt", 1134 },
  { "ra", 1684 },
  { "ra", 1682 },
  { "ram", 1682 },
  { "raml", 283 },
  { "rar", 1439 },
  { "ras", 1761 },
  { "rcprofile", 759 },
  { "rdf", 286 },
  { "rdz", 538 },
  { "rep", 482 },
  { "res", 1375 },
  { "rgb", 1774 },
  { "rif", 287 },
  { "rip", 1663 },
  { "ris", 1441 },
  { "rl", 291 },
  { "rlc", 1740 },
  { "rld", 292 },
  { "rm", 1161 },
  { "rmi", 1586 },
  { "rmp", 1683 },
  { "rms", 772 },
  { "rmvb", 1162 },
  { "rnc", 288 },
  { "rng", 1494 },
  { "roa", 303 },
  { "roff", 1892 },
  { "rp9", 497 },
  { "rpm", 1440 },
  { "rpss", 927 },
  { "rpst", 926 },
  { "rq", 349 },
  { "rs", 296 },
  { "rsd", 305 },
  { "rss", 306 },
  { "rtf", 1881 },
  { "rtf", 307 },
  { "rtx", 1880 },
  { "run", 1409 },
  { "s", 1930 },
  { "s3m", 1611 },
  { "saf", 1319 },
  { "sass", 1945 },
  { "sbml", 312 },
  { "sc", 723 },
  { "scd", 1428 },
  { "scm", 801 },
  { "scq", 315 },
  { "scs", 316 },
  { "scss", 1946 },
  { "scurl", 1903 },
  { "sda", 1207 },
  { "sdc", 1206 },
  { "sdd", 1208 },
  { "sdkd", 1199 },
  { "sdkm", 1199 },
  { "sdp", 319 },
  { "sdw", 1210 },
  { "sea", 1442 },
  { "see", 1182 },
  { "seed", 641 },
  { "sema", 1183 },
  { "semd", 1184 },
  { "semf", 1185 },
  { "ser", 151 },
  { "setpay", 333 },
  { "setreg", 335 },
  { "sfd-hdstx", 713 },
  { "sfs", 1201 },
  { "sfv", 1948 },
  { "sgi", 1723 },
  { "sgl", 1211 },
  { "sgm", 1885 },
  { "sgml", 1885 },
  { "sh", 1443 },
  { "shar", 1444 },
  { "shex", 1886 },
  { "shf", 338 },
  { "shtml", 1860 },
  { "sid", 1766 },
  { "sig", 252 },
  { "sil", 1612 },
  { "silo", 1804 },
  { "sis", 1229 },
  { "sisx", 1229 },
  { "sit", 1448 },
  { "sitx", 1449 },
  { "skd", 788 },
  { "skm", 788 },
  { "skp", 788 },
  { "skt", 788 },
  { "sldm", 869 },
  { "sldx", 1038 },
  { "slim", 1887 },
  { "slm", 1887 },
  { "slt", 604 },
  { "sm", 1213 },
  { "smf", 1209 },
  { "smi", 345 },
  { "smil", 345 },
  { "smv", 2051 },
  { "smzip", 1212 },
  { "snd", 1527 },
  { "snf", 1386 },
  { "so", 236 },
  { "spc", 1437 },
  { "spf", 1320 },
  { "spl", 1392 },
  { "spot", 1914 },
  { "spp", 318 },
  { "spq", 317 },
  { "spx", 1596 },
  { "sql", 1447 },
  { "src", 1470 },
  { "srt", 1450 },
  { "sru", 355 },
  { "srx", 350 },
  { "ssdl", 356 },
  { "sse", 789 },
  { "ssf", 605 },
  { "ssml", 357 },
  { "st", 1167 },
  { "stc", 1217 },
  { "std", 1219 },
  { "stf", 1299 },
  { "sti", 1221 },
  { "stk", 133 },
  { "stl", 864 },
  { "str", 1111 },
  { "stw", 1225 },
  { "styl", 1889 },
  { "stylus", 1889 },
  { "sub", 1906 },
  { "sub", 1733 },
  { "sus", 1226 },
  { "susp", 1226 },
  { "sv4cpio", 1451 },
  { "sv4crc", 1452 },
  { "svc", 582 },
  { "svd", 1227 },
  { "svg", 1724 },
  { "svgz", 1724 },
  { "swa", 1371 },
  { "swf", 1445 },
  { "swi", 461 },
  { "sxc", 1216 },
  { "sxd", 1218 },
  { "sxg", 1224 },
  { "sxi", 1220 },
  { "sxm", 1222 },
  { "sxw", 1223 },
  { "t", 1892 },
  { "t3", 1453 },
  { "taglet", 898 },
  { "tao", 1240 },
  { "tar", 1455 },
  { "tcap", 420 },
  { "tcl", 1456 },
  { "teacher", 1196 },
  { "tei", 372 },
  { "teicorpus", 372 },
  { "tex", 1457 },
  { "texi", 1459 },
  { "texinfo", 1459 },
  { "text", 1872 },
  { "tfi", 373 },
  { "tfm", 1458 },
  { "tga", 1775 },
  { "thmx", 859 },
  { "tif", 1726 },
  { "tiff", 1726 },
  { "tk", 1456 },
  { "tmo", 1245 },
  { "torrent", 1353 },
  { "tpl", 695 },
  { "tpt", 1247 },
  { "tr", 1892 },
  { "tra", 1249 },
  { "trm", 1429 },
  { "ts", 1980 },
  { "tsd", 376 },
  { "tsv", 1891 },
  { "ttc", 1695 },
  { "ttf", 1698 },
  { "ttl", 1893 },
  { "twd", 1192 },
  { "twds", 1192 },
  { "txd", 667 },
  { "txf", 832 },
  { "txt", 1872 },
  { "u32", 1348 },
  { "u8dsn", 1786 },
  { "u8hdr", 1788 },
  { "u8mdn", 1787 },
  { "u8msg", 1785 },
  { "udeb", 1369 },
  { "ufd", 1252 },
  { "ufdl", 1252 },
  { "ulx", 1394 },
  { "umj", 1254 },
  { "unityweb", 1255 },
  { "uoml", 1256 },
  { "uri", 1895 },
  { "uris", 1895 },
  { "urls", 1895 },
  { "ustar", 1461 },
  { "utz", 1253 },
  { "uu", 1950 },
  { "uva", 1635 },
  { "uvd", 542 },
  { "uvf", 542 },
  { "uvg", 1731 },
  { "uvh", 2001 },
  { "uvi", 1731 },
  { "uvm", 2002 },
  { "uvp", 2004 },
  { "uvs", 2005 },
  { "uvt", 543 },
  { "uvu", 2033 },
  { "uvv", 2006 },
  { "uvva", 1635 },
  { "uvvd", 542 },
  { "uvvf", 542 },
  { "uvvg", 1731 },
  { "uvvh", 2001 },
  { "uvvi", 1731 },
  { "uvvm", 2002 },
  { "uvvp", 2004 },
  { "uvvs", 2005 },
  { "uvvt", 543 },
  { "uvvu", 2033 },
  { "uvvv", 2006 },
  { "uvvx", 544 },
  { "uvvz", 545 },
  { "uvx", 544 },
  { "uvz", 545 },
  { "vbox", 1465 },
  { "vbox-extpack", 1466 },
  { "vcard", 1896 },
  { "vcd", 1358 },
  { "vcf", 1952 },
  { "vcg", 696 },
  { "vcs", 1951 },
  { "vcx", 1272 },
  { "vdi", 1467 },
  { "vhd", 1468 },
  { "vis", 1279 },
  { "viv", 2034 },
  { "vmdk", 1469 },
  { "vob", 2044 },
  { "vor", 1210 },
  { "vox", 1348 },
  { "vrml", 1822 },
  { "vsd", 1278 },
  { "vsf", 1281 },
  { "vss", 1278 },
  { "vst", 1278 },
  { "vsw", 1278 },
  { "vtt", 1929 },
  { "vtu", 1821 },
  { "vxml", 1328 },
  { "w3d", 1371 },
  { "wad", 1372 },
  { "wadl", 1215 },
  { "war", 150 },
  { "wasm", 1331 },
  { "wav", 1671 },
  { "wav", 1670 },
  { "wav", 1686 },
  { "wax", 1680 },
  { "wbmp", 1755 },
  { "wbs", 523 },
  { "wbxml", 1284 },
  { "wcm", 887 },
  { "wdb", 887 },
  { "wdp", 1746 },
  { "weba", 1672 },
  { "webapp", 1471 },
  { "webm", 2036 },
  { "webmanifest", 179 },
  { "webp", 1758 },
  { "wg", 1115 },
  { "wgt", 1336 },
  { "wks", 887 },
  { "wm", 2045 },
  { "wma", 1681 },
  { "wmd", 1415 },
  { "wmf", 1425 },
  { "wml", 1927 },
  { "wmlc", 1285 },
  { "wmls", 1928 },
  { "wmlsc", 1286 },
  { "wmv", 2046 },
  { "wmx", 2047 },
  { "wmz", 1425 },
  { "wmz", 1416 },
  { "woff", 1699 },
  { "woff2", 1700 },
  { "wpd", 1296 },
  { "wpl", 888 },
  { "wps", 887 },
  { "wqd", 1297 },
  { "wri", 1430 },
  { "wrl", 1822 },
  { "wsc", 1799 },
  { "wsdl", 1340 },
  { "wspolicy", 1341 },
  { "wtb", 1287 },
  { "wvx", 2048 },
  { "x32", 1348 },
  { "x3d", 1826 },
  { "x3db", 1823 },
  { "x3dbz", 1823 },
  { "x3dv", 1825 },
  { "x3dvz", 1825 },
  { "x3dz", 1826 },
  { "xaml", 1481 },
  { "xap", 1446 },
  { "xar", 1304 },
  { "xbap", 1417 },
  { "xbd", 662 },
  { "xbm", 1776 },
  { "xdf", 1484 },
  { "xdm", 1232 },
  { "xdp", 432 },
  { "xdssc", 88 },
  { "xdw", 661 },
  { "xenc", 1490 },
  { "xer", 247 },
  { "xfdf", 433 },
  { "xfdl", 1305 },
  { "xht", 1491 },
  { "xhtml", 1491 },
  { "xhvml", 1503 },
  { "xif", 1756 },
  { "xla", 849 },
  { "xlam", 850 },
  { "xlc", 849 },
  { "xlf", 1475 },
  { "xlm", 849 },
  { "xls", 849 },
  { "xlsb", 851 },
  { "xlsm", 852 },
  { "xlsx", 1063 },
  { "xlt", 849 },
  { "xltm", 853 },
  { "xltx", 1069 },
  { "xlw", 849 },
  { "xm", 1687 },
  { "xml", 1494 },
  { "xml", 1953 },
  { "xo", 967 },
  { "xop", 1499 },
  { "xpi", 1476 },
  { "xpl", 1500 },
  { "xpm", 1778 },
  { "xpr", 761 },
  { "xps", 889 },
  { "xpw", 746 },
  { "xpx", 746 },
  { "xsd", 1494 },
  { "xsl", 1494 },
  { "xslt", 1501 },
  { "xsm", 1230 },
  { "xspf", 1502 },
  { "xul", 843 },
  { "xvm", 1503 },
  { "xvml", 1503 },
  { "xwd", 1779 },
  { "xyz", 1694 },
  { "xz", 1477 },
  { "yaml", 1955 },
  { "yang", 1504 },
  { "yin", 1509 },
  { "yml", 1955 },
  { "ymp", 1949 },
  { "z1", 1478 },
  { "z2", 1478 },
  { "z3", 1478 },
  { "z4", 1478 },
  { "z5", 1478 },
  { "z6", 1478 },
  { "z7", 1478 },
  { "z8", 1478 },
  { "zaz", 1327 },
  { "zip", 1510 },
  { "zir", 1326 },
  { "zirz", 1326 },
  { "zmm", 699 }
};


<|start_filename|>docs/api/group__effect_structlh__exception.js<|end_filename|>
var group__effect_structlh__exception =
[
    [ "_is_alloced", "group__effect.html#a42489ecd1468adadd6bd9c8d6fc7850e", null ],
    [ "code", "group__effect.html#a2f415f8397a6e3e497814e50fbb3eb35", null ],
    [ "data", "group__effect.html#a9a780c96e58c551826cb9d7055e0e939", null ],
    [ "msg", "group__effect.html#a400106a97a1ab0617f39aa7a4e8618b0", null ]
];

<|start_filename|>docs/api/group__buffers.js<|end_filename|>
var group__buffers =
[
    [ "uv_buf_t", "group__buffers.html#structuv__buf__t", [
      [ "base", "group__buffers.html#a728c18d77668f4691201c4937133d7f0", null ],
      [ "len", "group__buffers.html#a1f258533d380bfb77f226e03f590388d", null ]
    ] ],
    [ "using_buf", "group__buffers.html#gac1eb74560e4589e704bebc105d6343d3", null ],
    [ "using_buf_on_abort_free", "group__buffers.html#ga66f1fa47b02d2868d948e319d7855a03", null ],
    [ "using_buf_owned", "group__buffers.html#ga3e5bc4c460db62160c8642b8e1d65c1b", null ],
    [ "nodec_buf", "group__buffers.html#ga22990fc91a4a3b03f60099ba6fc5a5d9", null ],
    [ "nodec_buf_alloc", "group__buffers.html#gadb94c9a2e1e2f18ceae226d1f3bc72f6", null ],
    [ "nodec_buf_append_into", "group__buffers.html#ga8e4c0afa16e893dfe6a851908b96e778", null ],
    [ "nodec_buf_ensure", "group__buffers.html#ga9eb9003e53b6fa855d1975675b21890f", null ],
    [ "nodec_buf_ensure_ex", "group__buffers.html#ga63dcd6d892c8adce49bb1fff03fcb454", null ],
    [ "nodec_buf_fit", "group__buffers.html#ga548a678be8d8b8c799ca20053c24df2e", null ],
    [ "nodec_buf_free", "group__buffers.html#ga6fe3f3295663abdcee81a3834ee24133", null ],
    [ "nodec_buf_is_null", "group__buffers.html#gace6138cbc07d9e625f7fe5dd23f14a53", null ],
    [ "nodec_buf_null", "group__buffers.html#gae3f6cebaabd694055d98c5728f55fabe", null ],
    [ "nodec_buf_realloc", "group__buffers.html#ga9dea20d1574c550161572de9f9aba286", null ],
    [ "nodec_buf_str", "group__buffers.html#ga58ab5cad603fb793560fd927dfbc9989", null ],
    [ "nodec_buf_strdup", "group__buffers.html#ga550d82d8a29f8b1a05766451dd4e3737", null ],
    [ "nodec_bufref_free", "group__buffers.html#gaa6ccecc9f6261de344980239606f436f", null ],
    [ "nodec_bufref_freev", "group__buffers.html#ga91c4241a67c3eee14fd4f9ee8d94def0", null ],
    [ "nodec_bufref_nofreev", "group__buffers.html#ga5f1b0e1ca03687d24bd982c34f8b4bd4", null ]
];

<|start_filename|>docs/api/search/groups_8.js<|end_filename|>
var searchData=
[
  ['linear_20handlers',['Linear Handlers',['../group__effect__linear.html',1,'']]],
  ['loggin',['Loggin',['../group__log.html',1,'']]],
  ['low_20level_20tcp_20connections',['Low level TCP Connections',['../group__tcpx.html',1,'']]]
];


<|start_filename|>examples/ex-fs-search.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include <nodec.h>
#include "examples.h"


/*-----------------------------------------------------------------
  
-----------------------------------------------------------------*/


void ex_fs_search() {
  nodec_log_debug("hi there");
}


<|start_filename|>docs/api/search/variables_a.js<|end_filename|>
var searchData=
[
  ['max_5finterleaving',['max_interleaving',['../group__tcp.html#ace7d6ad760cdb393909b6b4496929d4d',1,'tcp_server_config_t']]],
  ['msg',['msg',['../group__effect__exn.html#a400106a97a1ab0617f39aa7a4e8618b0',1,'lh_exception']]]
];


<|start_filename|>docs/api/group__effect_structlh__operation.js<|end_filename|>
var group__effect_structlh__operation =
[
    [ "opfun", "group__effect.html#a0ccd260a5593683d8343aa0d9a75d7d7", null ],
    [ "opkind", "group__effect.html#a596d55fa9ca51ec0602b60b37e818453", null ],
    [ "optag", "group__effect.html#ad8b0fc0385aecd9216ec6df9df996539", null ]
];

<|start_filename|>docs/api/group__nodec__fs_structuv__timespec__t.js<|end_filename|>
var group__nodec__fs_structuv__timespec__t =
[
    [ "tv_nsec", "group__nodec__fs.html#af1552c618f27286c9734be801d0ecf3f", null ],
    [ "tv_sec", "group__nodec__fs.html#a6c1e6a2cd2d9f10dc4df27ab3a79bc9d", null ]
];

<|start_filename|>examples/web/styles/main.css<|end_filename|>

body {
  font-family: Nunito, Segoe UI, sans-serif;
  padding-left: 1ex;
  width: 100%;
}


<|start_filename|>test/main.c<|end_filename|>

#include <stdio.h>
#include <nodec.h>

/*-----------------------------------------------------------------
  Test files
-----------------------------------------------------------------*/
static void test_stat() {
  const char* path = "cenv.h";
  printf("stat file %s\n", path);
  uv_stat_t stat = async_fs_stat(path);
  printf("file %s last access time: %li\n", path, stat.st_atim.tv_sec);
}

static void test_fileread() {
  const char* path = "cenv.h";
  printf("opening file: %s\n", path);
  char* contents = async_fs_read_from(path);
  {using_free(contents) {
    printf("read %zi bytes from %s:\n...\n", strlen(contents), path);
  }}
}

static void test_files() {
  test_stat();
  test_fileread();
}

/*-----------------------------------------------------------------
  Test interleave
-----------------------------------------------------------------*/

lh_value test_statx(lh_value arg) {
  test_stat();
  return lh_value_null;
}
lh_value test_filereadx(lh_value arg) {
  test_fileread();
  return lh_value_null;
}
lh_value test_filereads(lh_value arg) {
  printf("test filereads\n");
  lh_actionfun* actions[2] = { &test_filereadx, &test_statx };
  async_interleave(2, actions, NULL);
  return lh_value_null;
}

static void test_interleave() {
  lh_actionfun* actions[3] = { &test_filereadx, &test_statx, &test_filereads };
  async_interleave(3, actions, NULL);
}


/*-----------------------------------------------------------------
  Test cancel
-----------------------------------------------------------------*/

static lh_value test_cancel1(lh_value arg) {
  printf("starting work...\n");
  test_interleave();
  printf("and waiting a bit.. (1s)\n");
  async_wait(1000);
  printf("done work\n");
}

static void test_cancel_timeout(uint64_t timeout) {
  bool timedout = false;
  lh_value res = async_timeout(&test_cancel1, lh_value_null, timeout, &timedout);
  if (timedout) {
    printf("timed out\n");
  }
  else {
    printf("finished with: %i\n", lh_int_value(res));
  }
}

static void test_cancel() {
  test_cancel_timeout(1000);
  test_cancel_timeout(1500);
}


/*-----------------------------------------------------------------
  TCP
-----------------------------------------------------------------*/

const char* response_headers =
"HTTP/1.1 200 OK\r\n"
"Server : NodeC/0.1 (windows-x64)\r\n"
"Content-Type : text/html; charset=utf-8\r\n"
"Connection : Closed\r\n";

const char* response_body =
"<!DOCTYPE html>"
"<html>\n"
"<head>\n"
"  <meta charset=\"utf-8\">\n"
"</head>\n"
"<body>\n"
"  <h1>Hello NodeC World!</h1>\n"
"</body>\n"
"</html>\n";



static void test_http_serve() {
  int strand_id = http_strand_id();
  // input
#ifndef NDEBUG
  fprintf(stderr, "strand %i request, url: %s, content length: %llu\n", strand_id, http_req_url(), (unsigned long long)http_req_content_length());
  http_req_print();
#endif
  // work
  //printf("waiting %i secs...\n", 2); 
  //async_wait(1000);
  //check_uverr(UV_EADDRINUSE);

  http_static_config_t config = http_static_default_config();
  config.use_last_modified = true;
  config.use_etag = false;
  //config.gzip_min_size = SIZE_MAX;
  //config.read_buf_size = 1024;
  http_serve_static("../../../nodec-bench/web"
    , &config);

  // response
  /*
  const char* accept = http_req_header("Accept");
  if (accept != NULL && strstr(accept, "text/html")) {
    http_resp_send_body_str(HTTP_STATUS_OK, response_body, "text/html");
  }
  else {
    http_resp_send_ok();
  }
  */
  //printf("request handled\n\n\n");
}

static void test_tcp() {
  //tcp_server_config_t config = tcp_server_config();
  //config.max_interleaving = 500;
  const char* host = "127.0.0.1:8080";
  printf("serving at: %s\n", host);
  async_http_server_at(host, NULL, &test_http_serve);
}

static void test_https() {
  //tcp_server_config_t config = tcp_server_config();
  //config.max_interleaving = 500;
  const char* host = "127.0.0.1:443";
  printf("serving at: %s\n", host);
  nodec_ssl_config_t* ssl_config = nodec_ssl_config_server_from("../../../nodec-bench/nodec.crt",
    "../../../nodec-bench/nodec.key", "NodeC");
  {using_ssl_config(ssl_config) {
    async_https_server_at(host, NULL, ssl_config, &test_http_serve);
  }}
}



static void wait_tty() {
  async_tty_write("press enter to quit the server...");
  const char* s = async_tty_readline();
  nodec_free(s);
  async_tty_write("canceling server...");

}

static void test_tcp_tty() {
  async_firstof(&test_tcp, &wait_tty);
  // printf( first ? "http server exited\n" : "http server was terminated by the user\n");
}



/*-----------------------------------------------------------------
  TTY
-----------------------------------------------------------------*/

static void test_tty() {
  async_tty_write("\033[41;37m" "what is your name?" "\033[0m" " ");
  const char* s = async_tty_readline();
  {using_free(s) {
    async_tty_printf("I got: %s\n", s);
  }}
  async_tty_write("\033[41;37m" "and your age?" "\033[0m" " ");
  s = async_tty_readline();
  {using_free(s) {
    async_tty_printf("Now I got: %s\n", s);
  }}
}

/*-----------------------------------------------------------------
Test scandir
-----------------------------------------------------------------*/

void test_scandir() {
  nodec_scandir_t* scan = async_fs_scandir(".");
  {using_fs_scandir(scan) {
    uv_dirent_t dirent;
    while (async_fs_scandir_next(scan, &dirent)) {
      printf("entry %i: %s\n", dirent.type, dirent.name);
    }
  }}
}

/*-----------------------------------------------------------------
Test dns
-----------------------------------------------------------------*/

void test_dns() {
  struct addrinfo* info = async_getaddrinfo("iana.org", NULL, NULL);
  {using_addrinfo(info) {
    for (struct addrinfo* current = info; current != NULL; current = current->ai_next) {
      char sockname[128];
      nodec_sockname(current->ai_addr, sockname, sizeof(sockname));
      char* host = NULL;
      async_getnameinfo(current->ai_addr, 0, &host, NULL);
      {using_free(host) {
        printf("info: protocol %i at %s, reverse host: %s\n", current->ai_protocol, sockname, host);
      }}
    }
  }}
}

/*-----------------------------------------------------------------
  Test connect
-----------------------------------------------------------------*/
const char* http_request =
"GET / HTTP/1.1\r\n"
"Host: www.bing.com\r\n"
"Connection: close\r\n"
"\r\n";

lh_value test_connection(http_in_t* in, http_out_t* out, lh_value arg) {
  http_out_add_header(out, "Connection", "close");
  http_out_add_header(out, "Accept-Encoding", "gzip");
  http_out_send_request(out, HTTP_GET, "/");
  async_http_in_read_headers(in); // wait for response
  printf("received, status: %i, content length: %llu\n", http_in_status(in), (unsigned long long)http_in_content_length(in));
  http_in_status_print(in);
  return lh_value_null;
}

void test_connect() {
  async_http_connect("www.bing.com", test_connection, lh_value_null);
}

/*-----------------------------------------------------------------
  Client Test
-----------------------------------------------------------------*/
const char* http_request_parts[] = {
  "GET / HTTP/1.1\r\n",
  "Host: 127.0.0.1\r\n",
  "Connection: close\r\n",
  "\r\n",
  NULL
};

void test_as_client() {
  nodec_bstream_t* conn = async_tcp_connect("127.0.0.1:8080");
  {using_bstream(conn) {
    const char* s;
    for (size_t i = 0; (s = http_request_parts[i]) != NULL; i++) {
      printf("write: %s\n", s);
      async_write(as_stream(conn), s);
      async_wait(250);
    }
    printf("await response...\n");
    char* body = async_read_all(conn, 32 * NODEC_MB);
    {using_free(body) {
      printf("received:\n%s", body);
    }}
  }}
}

/*-----------------------------------------------------------------
 test url parsing
-----------------------------------------------------------------*/

static void url_print(const char* urlstr) {
  nodec_url_t* url = nodec_parse_url(urlstr);
  {using_url(url) {
    printf("url: %s\n schema: %s\n userinfo: %s\n host: %s\n port: %u\n path: %s\n query: %s\n fragment: %s\n\n",
      urlstr,
      nodec_url_schema(url), nodec_url_userinfo(url), nodec_url_host(url),
      nodec_url_port(url),
      nodec_url_path(url), nodec_url_query(url), nodec_url_fragment(url)
    );
  }}
}

static void host_url_print(const char* urlstr) {
  nodec_url_t* url = nodec_parse_host(urlstr);
  {using_url(url) {
    printf("url: %s\n host: %s\n port: %u\n\n",
      urlstr, nodec_url_host(url), nodec_url_port(url)
    );
  }}
}

static void test_url() {
  url_print("http://daan@www.bing.com:72/foo?x=10;y=3#locallink");
  url_print("https://bing.com:8080");
  url_print("http://127.0.0.1");
  host_url_print("localhost:8080");
  host_url_print("my.server.com:80");
  host_url_print("127.0.0.1:80");
  host_url_print("[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443");
  //host_url_print("http://127.0.0.1"); // invalid
  //host_url_print("127.0.0.1");        // invalid
}

/*-----------------------------------------------------------------
  Main
-----------------------------------------------------------------*/

static void entry() {
  //void test_http();
  printf("in the main loop\n");
  //test_files();
  //test_interleave();
  //test_cancel();
  //test_tcp();
  test_tty();
  //test_scandir();
  //test_dns();
  //test_http();
  //test_as_client();
  //test_connect();
  //test_tcp_tty();
  //test_url();
  //test_https();
}

int main() {
  async_main(entry);
  return 0;
}

<|start_filename|>docs/api/group__effect__handlers_structlh__handlerdef.js<|end_filename|>
var group__effect__handlers_structlh__handlerdef =
[
    [ "effect", "group__effect__handlers.html#a08413ea484f015bb79d395c349eb1a29", null ],
    [ "local_acquire", "group__effect__handlers.html#a197b2abb85288eda9fc73dddb02213c9", null ],
    [ "local_release", "group__effect__handlers.html#ac03ed465c68a46c167612e712160fd61", null ],
    [ "operations", "group__effect__handlers.html#a8588e38ec7973da55f90d239f7ec854f", null ],
    [ "resultfun", "group__effect__handlers.html#afe5be74288279eb0ec913ca483942ddd", null ]
];

<|start_filename|>docs/api/search/groups_9.js<|end_filename|>
var searchData=
[
  ['memory_20allocation_2e',['Memory Allocation.',['../group__nodec__alloc.html',1,'']]],
  ['mime_20types_2e',['MIME Types.',['../group__nodec__mime.html',1,'']]]
];


<|start_filename|>docs/api/search/all_0.js<|end_filename|>
var searchData=
[
  ['_5fis_5falloced',['_is_alloced',['../group__effect__exn.html#a42489ecd1468adadd6bd9c8d6fc7850e',1,'lh_exception']]]
];


<|start_filename|>src/http.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>



/*-----------------------------------------------------------------
  HTTP errors|
-----------------------------------------------------------------*/
const char* http_error_headers =
"HTTP/1.1 %i %s\r\n"
"Server : NodeC\r\n"
"Content-Length : %i\r\n"
"Content-Type : text/html; charset=utf-8\r\n"
"Connection : Closed\r\n"
"\r\n";

const char* http_error_body =
"<!DOCTYPE html>"
"<html>\n"
"<head>\n"
"  <meta charset=\"utf-8\">\n"
"</head>\n"
"<body>\n"
"  <p>Error %i (%s)%s%s.</p>\n"
"</body>\n"
"</html>\n";



const char* nodec_http_status_str(http_status_t code) {
  return http_status_str(code);
}

const char* nodec_http_method_str(http_method_t method) {
  return http_method_str(method);
}


static void async_write_http_err(uv_stream_t* client, http_status_t code, const char* msg) {
  const char* reason = nodec_http_status_str(code);
  char body[256];
  snprintf(body, 255, http_error_body, code, reason, (msg == NULL ? "" : ": "), (msg == NULL ? "" : msg));
  body[255] = 0;
  char headers[256];
  snprintf(headers, 255, http_error_headers, code, reason, strlen(body));
  headers[255] = 0;
  fprintf(stderr, "HTTP error: %i (%s): %s\n\n", code, reason, (msg == NULL ? "" : msg));
  if (!uv_is_closing((uv_handle_t*)client)) {
    // don't raise exceptions..
    uv_buf_t bufs[2] = { nodec_buf_str(headers), nodec_buf_str(body) };
    asyncx_uv_write_bufs(client, bufs, 2);
  }
}

void throw_http_err_str(http_status_t status, const char* msg) {
  lh_throw_str(UV_EHTTP - status, msg);
}

void throw_http_err_strdup(http_status_t status, const char* msg) {
  lh_throw_strdup(UV_EHTTP - status, msg);
}

void throw_http_err(http_status_t status) {
  throw_http_err_str(status, NULL);
}

lh_value async_write_http_exnv(lh_value exnv) {
  lh_exception* exn = (lh_exception*)lh_ptr_value(exnv);
  if (exn == NULL || exn->data == NULL) return lh_value_null;
  uv_stream_t* client = exn->data;
  http_status_t status = 500;
  if (exn->code < UV_EHTTP && exn->code >(UV_EHTTP - 600)) {
    status = -(exn->code - UV_EHTTP);
  }
  async_write_http_err(client, status, exn->msg);
  return lh_value_null;
}





<|start_filename|>docs/api/search/all_2.js<|end_filename|>
var searchData=
[
  ['backlog',['backlog',['../group__tcp.html#a64ed27e28dbbddbcf1fe1fed3538014b',1,'tcp_server_config_t']]],
  ['base',['base',['../group__buffers.html#a728c18d77668f4691201c4937133d7f0',1,'uv_buf_t']]],
  ['buffer_27s',['Buffer&apos;s',['../group__buffers.html',1,'']]]
];


<|start_filename|>src/channel.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h> 

/*-----------------------------------------------------------------
    Channels
-----------------------------------------------------------------*/
typedef struct _channel_elem {
  lh_value data;
  lh_value arg;
  int      err;
} channel_elem;

typedef void (channel_listener_fun)(lh_value arg, channel_elem result);
typedef struct _channel_listener {
  channel_listener_fun* fun;
  lh_value              arg;
} channel_listener;

struct _channel_t {
  // listeners are a stack, so the last listeners
  // gets to handle first
  channel_listener* listeners;
  volatile ssize_t  lcount;
  volatile ssize_t  lsize;

  // the queue is a true queue
  channel_elem*     queue;
  ssize_t           qcount;
  ssize_t           qhead; // start index, wrap around
  ssize_t           qsize;
  ssize_t           qmax;

  // release
  lh_releasefun* release_fun;
  lh_value       release_arg;
  channel_release_elem_fun* release_elem;
};

channel_t* channel_alloc(ssize_t queue_max) {
  return channel_alloc_ex(queue_max, NULL, lh_value_null, NULL);
}

channel_t* channel_alloc_ex( ssize_t queue_max, lh_releasefun* release_fun, lh_value release_arg, 
                          channel_release_elem_fun* release_elem ) 
{
  channel_t* channel = nodec_alloc(channel_t);
  channel->listeners = NULL;
  channel->lcount = 0;
  channel->lsize  = 0;
  channel->queue  = NULL;
  channel->qsize  = 0;
  channel->qcount = 0;
  channel->qhead  = 0;
  channel->qmax = (queue_max < 0 ? 1024*1024 : queue_max);
  channel->release_fun = release_fun;
  channel->release_arg = release_arg;
  channel->release_elem = release_elem;
  return channel;
}

void channel_free(channel_t* channel) {
  if (channel->queue != NULL) {
    if (channel->release_elem != NULL) {
      for (ssize_t i = 0; i < channel->qcount; i++) {
        ssize_t idx = i + channel->qhead;
        if (idx >= channel->qsize) idx = idx - channel->qsize;
        channel_elem* elem = &channel->queue[idx];
        channel->release_elem(elem->data, elem->arg, elem->err);
      }
    }
    nodec_free(channel->queue);
    channel->queue = NULL;
    channel->qcount = channel->qhead= channel->qsize = 0;
  }
  if (channel->listeners != NULL) {
    channel_elem cancel = { lh_value_null,lh_value_null,UV_ECANCELED };
    for (ssize_t i = 0; i < channel->lcount; i++) {
      channel_listener* l = &channel->listeners[i];
      l->fun(l->arg, cancel);
    }
    nodec_free(channel->listeners);
    channel->listeners = NULL;
    channel->lcount = channel->lsize = 0;
  }
  if (channel->release_fun != NULL) {
    channel->release_fun(channel->release_arg);
  }
  nodec_free(channel);
}

void channel_freev(lh_value vchannel) {
  channel_free(lh_ptr_value(vchannel));
}

bool  channel_is_full(channel_t* channel) {
  return (channel->lcount <= 0 && channel->qcount >= channel->qmax);
}

uv_errno_t channel_emit(channel_t* channel, lh_value data, lh_value arg, int err) {
  channel_elem elem = { data, arg, err };
  if (channel->lcount > 0) {
    // a listener, serve immediately
    // LIFO: good for web services but should perhaps be a parameter?
    // channel->lcount--;
    const channel_listener* l = &channel->listeners[channel->lcount-1];
    l->fun(l->arg, elem);  // listeners remove themselves from the channel (due to cancelation support)
    return 0;
  }
  else if (channel->qcount >= channel->qmax) {
    return UV_ENOSPC;
  }
  else {
    // otherwise queue it (FIFO buffer)
    if (channel->qcount >= channel->qsize) {
      ssize_t newsize = (channel->qsize > 0 ? 2 * channel->qsize : 2);
      channel_elem* newqueue = (channel_elem*)(channel->queue == NULL ? nodecx_malloc(newsize * sizeof(channel_elem))
        : nodecx_realloc(channel->queue, newsize * sizeof(channel_elem)));
      if (newqueue == NULL) return UV_ENOMEM;
      channel->queue = newqueue;
      channel->qsize = newsize;
    }
    ssize_t idx = (channel->qhead + channel->qcount);
    if (idx>=channel->qsize) idx = idx - channel->qsize;
    channel->queue[idx] = elem;
    channel->qcount++;
    return 0;
  }
}



typedef struct _uv_channel_req_t {
  uv_req_t req;  // must be the first element!
  channel_elem elem;
} uv_channel_req_t;

static void _channel_req_listener_fun(lh_value arg, channel_elem elem) {
  uv_channel_req_t* req = (uv_channel_req_t*)lh_ptr_value(arg);
  req->elem = elem;
  async_req_resume(&req->req, 0 /* error for our channel */);
}


static channel_elem channel_receive_ex(channel_t* channel, bool nocancel) {
  channel_elem result;
  nodec_zero(channel_elem, &result);
  if (channel->qcount>0) {
    // take top of the queue and continue
    result = channel->queue[channel->qhead];
    channel->qcount--;
    channel->qhead++;
    if (channel->qhead >= channel->qsize) channel->qhead = 0;
  }
  else {
    // await the next emit
    uv_channel_req_t* req = nodec_zero_alloc_n(1, uv_channel_req_t);
    {using_free(req){ // always free our request
      if (channel->lcount >= channel->lsize) {
        ssize_t newsize = (channel->lsize > 0 ? 2 * channel->lsize : 2);
        channel->listeners = (channel->listeners == NULL ? nodec_alloc_n(newsize, channel_listener)
          : (channel_listener*)nodec_realloc(channel->listeners, newsize*sizeof(channel_listener)));
        channel->lsize = newsize;
      }
      channel->listeners[channel->lcount].arg = lh_value_ptr(req);
      channel->listeners[channel->lcount].fun = &_channel_req_listener_fun;
      channel->lcount++;
      // and await our request 
      uv_errno_t err;
      if (nocancel) {
        err = asyncx_nocancel_await(&req->req);
      } 
      else {
        err = asyncxx_await(&req->req,0,NULL);
      }
      // remove ourselves; we must do it here instead of in emit due to cancelations
      bool removed = false;
      for (ssize_t i = channel->lcount-1; i >= 0; i--) { // backward for efficiency
        if (channel->listeners[i].arg == lh_value_ptr(req)) {
          // found
          removed = true;
          channel->lcount--;
          if (i < channel->lcount) {
            // move if it wasnt the last one (can happen on cancelation)
            memmove(&channel->listeners[i], &channel->listeners[i + 1], (channel->lcount - i) * sizeof(channel_listener));
          }
          break;
        }
      }
      assert(removed);
      // check errors
      nodec_check(err);
      // and return the result
      result = req->elem;
    }}
  }
  return result;
}

int channel_receive(channel_t* channel, lh_value* data, lh_value* arg ) {
  channel_elem elem = channel_receive_ex(channel, false);
  if (data != NULL) *data = elem.data;
  if (arg != NULL) *arg = elem.arg;
  return elem.err;
}

int channel_receive_nocancel(channel_t* channel, lh_value* data, lh_value* arg) {
  channel_elem elem = channel_receive_ex(channel, true);
  if (data != NULL) *data = elem.data;
  if (arg != NULL) *arg = elem.arg;
  return elem.err;

}


<|start_filename|>docs/api/search/variables_10.js<|end_filename|>
var searchData=
[
  ['use_5fetag',['use_etag',['../group__nodec__static.html#ab77a0887c2beea113ae3a647bd5051ed',1,'http_static_config_t']]],
  ['use_5flast_5fmodified',['use_last_modified',['../group__nodec__static.html#a28d756b30046e7e7ee6b88d4dfe3dcce',1,'http_static_config_t']]]
];


<|start_filename|>docs/api/navtreeindex0.js<|end_filename|>
var NAVTREEINDEX0 =
{
"annotated.html":[1,0],
"classes.html":[1,1],
"functions.html":[1,2,0],
"functions_vars.html":[1,2,1],
"group__buffers.html":[0,0],
"group__buffers.html#a1f258533d380bfb77f226e03f590388d":[0,0,0,1],
"group__buffers.html#a728c18d77668f4691201c4937133d7f0":[0,0,0,0],
"group__buffers.html#ga22990fc91a4a3b03f60099ba6fc5a5d9":[0,0,4],
"group__buffers.html#ga3e5bc4c460db62160c8642b8e1d65c1b":[0,0,3],
"group__buffers.html#ga548a678be8d8b8c799ca20053c24df2e":[0,0,9],
"group__buffers.html#ga550d82d8a29f8b1a05766451dd4e3737":[0,0,15],
"group__buffers.html#ga58ab5cad603fb793560fd927dfbc9989":[0,0,14],
"group__buffers.html#ga5f1b0e1ca03687d24bd982c34f8b4bd4":[0,0,18],
"group__buffers.html#ga63dcd6d892c8adce49bb1fff03fcb454":[0,0,8],
"group__buffers.html#ga66f1fa47b02d2868d948e319d7855a03":[0,0,2],
"group__buffers.html#ga6fe3f3295663abdcee81a3834ee24133":[0,0,10],
"group__buffers.html#ga8e4c0afa16e893dfe6a851908b96e778":[0,0,6],
"group__buffers.html#ga91c4241a67c3eee14fd4f9ee8d94def0":[0,0,17],
"group__buffers.html#ga9dea20d1574c550161572de9f9aba286":[0,0,13],
"group__buffers.html#ga9eb9003e53b6fa855d1975675b21890f":[0,0,7],
"group__buffers.html#gaa6ccecc9f6261de344980239606f436f":[0,0,16],
"group__buffers.html#gac1eb74560e4589e704bebc105d6343d3":[0,0,1],
"group__buffers.html#gace6138cbc07d9e625f7fe5dd23f14a53":[0,0,11],
"group__buffers.html#gadb94c9a2e1e2f18ceae226d1f3bc72f6":[0,0,5],
"group__buffers.html#gae3f6cebaabd694055d98c5728f55fabe":[0,0,12],
"group__buffers.html#structuv__buf__t":[0,0,0],
"group__effect.html":[0,13],
"group__effect__debug.html":[0,13,2],
"group__effect__debug.html#ga2640fed6ccd08b9f0c14b3bff1808413":[0,13,2,12],
"group__effect__debug.html#ga56ae0b80ee63d88b8e547974f8f790dd":[0,13,2,13],
"group__effect__debug.html#ga647046150553dd8b90064a8f2b94c9f9":[0,13,2,4],
"group__effect__debug.html#ga72b06ce360f76629e77a23da18b71f49":[0,13,2,9],
"group__effect__debug.html#ga92212c836a97fbe9678c97093bded2c8":[0,13,2,5],
"group__effect__debug.html#ga97cb6281b08f6507c8a2be612ec662b6":[0,13,2,15],
"group__effect__debug.html#gaaa958e853fd8e039d2bd95a3589d05d8":[0,13,2,1],
"group__effect__debug.html#gabd736ee0a830b88c25a6e577e1afc273":[0,13,2,2],
"group__effect__debug.html#gacc1e33676af3f1835024a9741a1f1318":[0,13,2,6],
"group__effect__debug.html#gacd4f8378c32e897364593e1b881c9eb0":[0,13,2,14],
"group__effect__debug.html#gad5238b41612231e43d6251e46a3ff97f":[0,13,2,11],
"group__effect__debug.html#gad677b9f6ba5b92dac0729c3aa474f786":[0,13,2,10],
"group__effect__debug.html#gad6a1f6b1a5962f31b8a3d00fc134f190":[0,13,2,3],
"group__effect__debug.html#gae07f635b592d37224a693c4b96c3ca8b":[0,13,2,0],
"group__effect__debug.html#gaf2f2e9675af64eb749d5927d3208ea0e":[0,13,2,7],
"group__effect__debug.html#gafd04836042b1a8668b04523991933221":[0,13,2,8],
"group__effect__def.html":[0,13,3],
"group__effect__exn.html":[0,13,5],
"group__effect__exn.html#a2f415f8397a6e3e497814e50fbb3eb35":[0,13,5,0,1],
"group__effect__exn.html#a400106a97a1ab0617f39aa7a4e8618b0":[0,13,5,0,3],
"group__effect__exn.html#a42489ecd1468adadd6bd9c8d6fc7850e":[0,13,5,0,0],
"group__effect__exn.html#a9a780c96e58c551826cb9d7055e0e939":[0,13,5,0,2],
"group__effect__exn.html#ga0e7009f79fe092da3a57d5fc75fcf162":[0,13,5,6],
"group__effect__exn.html#ga1383c391fc3f472b7e80c5ab48036cd3":[0,13,5,8],
"group__effect__exn.html#ga2df9acd0863570798216432d189093c6":[0,13,5,1],
"group__effect__exn.html#ga777f696b7d834b82d13aada6f35f4187":[0,13,5,9],
"group__effect__exn.html#gab7825b03a6de262e4477626f8a4eba9b":[0,13,5,4],
"group__effect__exn.html#gabec7bf8798d2386b437e9d069334dcc5":[0,13,5,2],
"group__effect__exn.html#gad10ff668cc67eba82069135e11fd0a22":[0,13,5,5],
"group__effect__exn.html#gade0dd921fe6097b4c848a2e9d60efd9f":[0,13,5,3],
"group__effect__exn.html#gafe25913497ef882eb7a2a5b4c501ce32":[0,13,5,7],
"group__effect__exn.html#structlh__exception":[0,13,5,0],
"group__effect__handlers.html":[0,13,1],
"group__effect__handlers.html#a08413ea484f015bb79d395c349eb1a29":[0,13,1,2,0],
"group__effect__handlers.html#a0ccd260a5593683d8343aa0d9a75d7d7":[0,13,1,1,0],
"group__effect__handlers.html#a197b2abb85288eda9fc73dddb02213c9":[0,13,1,2,1],
"group__effect__handlers.html#a2dd60935d436953098e73cbdd77fdcd2":[0,13,1,3,1],
"group__effect__handlers.html#a596d55fa9ca51ec0602b60b37e818453":[0,13,1,1,1],
"group__effect__handlers.html#a8588e38ec7973da55f90d239f7ec854f":[0,13,1,2,3],
"group__effect__handlers.html#aaba7e2907daa4422cd4f47e91e1b171d":[0,13,1,0,0],
"group__effect__handlers.html#ab797acdcd17ed8d49fd79980409be48b":[0,13,1,3,0],
"group__effect__handlers.html#ac03ed465c68a46c167612e712160fd61":[0,13,1,2,2],
"group__effect__handlers.html#ad756a8e13a174bcb47041076d8611593":[0,13,1,0,1],
"group__effect__handlers.html#ad8b0fc0385aecd9216ec6df9df996539":[0,13,1,1,2],
"group__effect__handlers.html#afe5be74288279eb0ec913ca483942ddd":[0,13,1,2,4],
"group__effect__handlers.html#ga12164effe25a00e7496809a243bfc627":[0,13,1,19],
"group__effect__handlers.html#ga12630c5bdf2cb1592a59576ae1ed2190":[0,13,1,21],
"group__effect__handlers.html#ga246cdf54dc0c6c8ce94f3c20f80e2b0a":[0,13,1,8],
"group__effect__handlers.html#ga34234a90a032e52d31f6315ea31e715d":[0,13,1,25],
"group__effect__handlers.html#ga389460200d7dc3d8b5e8b9deb83f1efe":[0,13,1,9],
"group__effect__handlers.html#ga450ac4de67ba22cd422942c22973a994":[0,13,1,17],
"group__effect__handlers.html#ga4607747cefd19c1a58490d0d2b337d03":[0,13,1,18],
"group__effect__handlers.html#ga5eec69ac638f5046561917fd11edd9ec":[0,13,1,23],
"group__effect__handlers.html#ga6c517e0f68c9e482e3dc92cf9c4c53f5":[0,13,1,6],
"group__effect__handlers.html#ga86b49751042776deb9582fefb843052a":[0,13,1,26],
"group__effect__handlers.html#ga8ca9fef815d0ad701f17b8f499c47645":[0,13,1,24],
"group__effect__handlers.html#ga9415634aefb671906b8a37698079e36f":[0,13,1,5],
"group__effect__handlers.html#gaa1b51c33da7448e42829ec71678631fc":[0,13,1,14],
"group__effect__handlers.html#gaad5d615b9d98a63df083512caa78d78a":[0,13,1,12],
"group__effect__handlers.html#gab351a98efa1a6078343a93bec2eed9f4":[0,13,1,22],
"group__effect__handlers.html#gab8f54bf97bbcd7973b2d54331d21a5f9":[0,13,1,20],
"group__effect__handlers.html#gac1320c2b79475b7e3fe89e2f7a0a16f5":[0,13,1,7],
"group__effect__handlers.html#gac283305e0f1f5581764aa8d77b36266d":[0,13,1,4],
"group__effect__handlers.html#gac73de5bfdb8644f425dec0ca4782a50e":[0,13,1,15],
"group__effect__handlers.html#gace940505f4ef9f09e2aeeca789c5fdea":[0,13,1,13],
"group__effect__handlers.html#gaf06dcf1847501605b52051581f50cb8d":[0,13,1,10],
"group__effect__handlers.html#gaf14082b6131de8920bdd6a6dc86f1096":[0,13,1,16],
"group__effect__handlers.html#gaf79f5f56c6360a3ca0115773b1a21bec":[0,13,1,11],
"group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca22aca2bf964620d72e447d8bcd116d3d":[0,13,1,14,3],
"group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca2cdd41fc10e2c645450aac1310941b06":[0,13,1,14,0],
"group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca46b8f876817791c1ae49735d9cf2cc9b":[0,13,1,14,7],
"group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca4aa95af9b879aeff29642da0c1fcf16e":[0,13,1,14,2],
"group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca680485ac044b9af0ca4d0284839be233":[0,13,1,14,5],
"group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca95b23feba31938ac3c29de4542342718":[0,13,1,14,1],
"group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fcaa4f5a6c62b5c8780ed1a9256b2f2332f":[0,13,1,14,6],
"group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fcab8ab449e5f433cd8a4bc77e2269ebb00":[0,13,1,14,4],
"group__effect__handlers.html#structlh__handlerdef":[0,13,1,2],
"group__effect__handlers.html#structlh__operation":[0,13,1,1],
"group__effect__handlers.html#structlh__optag__":[0,13,1,0],
"group__effect__handlers.html#structyieldargs":[0,13,1,3],
"group__effect__implicit.html":[0,13,6],
"group__effect__implicit.html#ga40e8799dc7d3bbaaca7fe49dc134fbe5":[0,13,6,0],
"group__effect__implicit.html#gac94bad95bb8fcb34dcc29f46564bf750":[0,13,6,1],
"group__effect__implicit.html#gad6572acb8b04d7b33ea661035fb0d8d2":[0,13,6,2],
"group__effect__implicit.html#gaea88b12f476f8d029fe6043febecae19":[0,13,6,3],
"group__effect__linear.html":[0,13,4],
"group__effect__value.html":[0,13,0],
"group__effect__value.html#ga11799a8a77ce5e6e76fcc04ea346973b":[0,13,0,1],
"group__effect__value.html#ga18d15281353b7e38984b0ef2e36abb83":[0,13,0,23],
"group__effect__value.html#ga1f24f3a2029820ca75484a67853370a3":[0,13,0,22],
"group__effect__value.html#ga31b1c44a920420d7f3f85fdda1ef6db2":[0,13,0,3],
"group__effect__value.html#ga336a950128d6d89c85eb2a8b599c8bf2":[0,13,0,2],
"group__effect__value.html#ga540551bfce3a27024092a782553ceddf":[0,13,0,21],
"group__effect__value.html#ga5734aa37e6622d1b505a0173e2545ba8":[0,13,0,18],
"group__effect__value.html#ga58b182dd21feb550d40436ef31d09614":[0,13,0,24],
"group__effect__value.html#ga5a08b630153ff4e9c2120d0bb2debd89":[0,13,0,19],
"group__effect__value.html#ga6adf34d555ad2d09666254d3d923189b":[0,13,0,4],
"group__effect__value.html#ga6f614d89d8e6565a50317139cca21db3":[0,13,0,20],
"group__effect__value.html#ga7b4439b7ec3223634778002ea318acea":[0,13,0,25],
"group__effect__value.html#ga7b9e682e24d41d0203c1156900398d39":[0,13,0,14],
"group__effect__value.html#ga7f44e7912be4eabc993a976069056b79":[0,13,0,9],
"group__effect__value.html#ga80a6a2b55e0d45bbe2171f396bdc4af0":[0,13,0,17],
"group__effect__value.html#ga817d2d4a6080a05db53ef154a6218205":[0,13,0,8],
"group__effect__value.html#ga9ee5f894ccf5ffdd91b8687e5d16b946":[0,13,0,0],
"group__effect__value.html#gaa42c543224030e5080188cb11b5b27bb":[0,13,0,11],
"group__effect__value.html#gaada7a7ceaab9a482320d4cfc904a4da6":[0,13,0,26],
"group__effect__value.html#gab17d96897899981289f52af69fb53889":[0,13,0,16],
"group__effect__value.html#gab30af45b5f41cca5a1287d2bfa1d39a5":[0,13,0,7],
"group__effect__value.html#gab455eed48ff144c3b8ab0977d8838391":[0,13,0,15],
"group__effect__value.html#gabe964e091840d37f8cb46a747452cee5":[0,13,0,6],
"group__effect__value.html#gac069dce2efd9a6972e0e1467564a1acb":[0,13,0,13],
"group__effect__value.html#gac14a78aec4be59be7a920507f0e61a32":[0,13,0,10],
"group__effect__value.html#gac9ebc79475233b3cdc4d8468b05b78aa":[0,13,0,5],
"group__effect__value.html#gacdfc1731e309c33e055ac88b459bdcad":[0,13,0,12],
"group__http.html":[0,9],
"group__http__in__out.html":[0,9,1],
"group__http__in__out.html#ga0a71fdfb195b7fe66eadd442e0fbd4e5":[0,9,1,5],
"group__http__in__out.html#ga65fe57df6004b40a189295c418a68273":[0,9,1,3],
"group__http__in__out.html#ga71b82996867f08b9f7aaed6d10f507bf":[0,9,1,2],
"group__http__in__out.html#ga9c23d2a40e3792bf162f3a3e7bcb40df":[0,9,1,1],
"group__http__in__out.html#gaa17e8de265c190884d433e8ef91e3ad4":[0,9,1,4],
"group__http__in__out.html#gaa8e34fdd12cb1290da994deab9d64594":[0,9,1,0],
"group__http__req__resp.html":[0,9,2],
"group__http__req__resp.html#ga19397cedfdd8a668cf4956891fce3e76":[0,9,2,14],
"group__http__req__resp.html#ga1bbbf2a9c6763cc8abd61c64e3d56be9":[0,9,2,7],
"group__http__req__resp.html#ga2e5c04d158afc7ab3575ba3ddf5c98a0":[0,9,2,19],
"group__http__req__resp.html#ga453c19b95ff4853887cc38eab789dc0d":[0,9,2,11],
"group__http__req__resp.html#ga5639e997c65e093372f3f369decdabbb":[0,9,2,4],
"group__http__req__resp.html#ga5a4e7beef65369fe9dd0af4e950e0296":[0,9,2,6],
"group__http__req__resp.html#ga6fbdd916c136f477bbc3b45402907241":[0,9,2,2],
"group__http__req__resp.html#ga79ccdc504575e50c2c7f7898f9f4e098":[0,9,2,9],
"group__http__req__resp.html#ga90c24eeea159e619c53f08c349930434":[0,9,2,0],
"group__http__req__resp.html#ga9299a8391a97562030e97d19dec8e611":[0,9,2,17],
"group__http__req__resp.html#gaa071ab8ae8e6548f41f3945a3a8944ee":[0,9,2,8],
"group__http__req__resp.html#gab2f0f9c13b27e4d3960a40fa4123fa49":[0,9,2,1],
"group__http__req__resp.html#gab3b9ae435e3ff47e65c120849637570e":[0,9,2,5],
"group__http__req__resp.html#gab7bbb21ed41c9c4adc79aef33f08ddca":[0,9,2,15],
"group__http__req__resp.html#gabbd2e133173ef6dcbb5ae9f0dd2b8888":[0,9,2,10],
"group__http__req__resp.html#gac25ef2db063b5b4f33e4fb3042137dab":[0,9,2,3],
"group__http__req__resp.html#gac4b657f50f7b004fdc226c7aad4cef5c":[0,9,2,18],
"group__http__req__resp.html#gac7cf19d0f1a0d2704f8882b218bdfeec":[0,9,2,16],
"group__http__req__resp.html#gad6f9bebbcdf5329df19b69d0ff915e12":[0,9,2,13],
"group__http__req__resp.html#gaf6c636b98224eb09dfb01bb1779ec08f":[0,9,2,20],
"group__http__req__resp.html#gafcc54624869b5d7c85b79843901fefd7":[0,9,2,12],
"group__http__setup.html":[0,9,0],
"group__http__setup.html#ga2fd930fba66817097e73ba9b75800eb2":[0,9,0,0],
"group__http__setup.html#gaf5ffe69ea1b5908893c7eddf74c8151b":[0,9,0,1],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a12eca1105638a830128e9dc989318f92":[0,9,0,0,13],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a20ba2a33b5833d9a16cca5a1ba9de8e3":[0,9,0,0,24],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a2415577115dd0cd5d5a5aa62d52e1512":[0,9,0,0,4],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a2bf319204b9298444ae66023a78ba9b4":[0,9,0,0,21],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a2cc45c2a2e2dcce009d34ac124a0c80f":[0,9,0,0,30],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a327dcf61f2241a23b809ffda50af1e9b":[0,9,0,0,16],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a36428da6d444779444f9944ee50ff2a5":[0,9,0,0,29],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a39f145838eada9b10c689e4a2b154ccf":[0,9,0,0,25],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a3d253975870f94c93f049575afaff83d":[0,9,0,0,12],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a427575ab776ed70f0aa76667affc3942":[0,9,0,0,0],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a4851e8dca997a9b41099f2d8f14be069":[0,9,0,0,23],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a4ad23c3d2f21f7502ba058ef89518166":[0,9,0,0,1],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a544d35cc7e95e6a0be97122ab744f2bd":[0,9,0,0,20],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a59dcf849dc60543ed33bdb9ccd476daf":[0,9,0,0,26],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a658daa8d3d7dbc917e2473eeb9a8590a":[0,9,0,0,18],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a73feb27a5ac46d923d5c636f797b3e76":[0,9,0,0,5],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a81202ae84a8fc30e687bc5059958c477":[0,9,0,0,15],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a850ffd3aeeb1d55b124aa6d1f990b3a6":[0,9,0,0,10],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a86aa5540a666a38528d28bd7591cb251":[0,9,0,0,6],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a86af7b9dfab558c6d7e77788b0bf9c29":[0,9,0,0,31],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a930cc3d0451fc715afb07adb2267198f":[0,9,0,0,27],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2a9a8d347e6e7127432058b90466f6a2fd":[0,9,0,0,17],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2aa7119ecfb8aece4bbfb7a261331abddf":[0,9,0,0,9],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2aa763023b51da2b3618f24cbf994b34bf":[0,9,0,0,3],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2ab547cb50f6792b41c3e7d52ee0f8e442":[0,9,0,0,2],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2ab5d284c93074084b1782b6b4f0c84cac":[0,9,0,0,14],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2ab7485b3ada0c697c057054e785b443d4":[0,9,0,0,32],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2ab76b7cdc0a589106ab2be8e068ae9e8c":[0,9,0,0,19],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2abcc115ec0192f6a56fd7ca8564c9e30e":[0,9,0,0,22],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2ad284635f2909fb5b9640928cb0e705c7":[0,9,0,0,11],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2adb9611ac2f3fbaa16b21eddc40791b7c":[0,9,0,0,8],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2ae1345469c9ff83a10676dd96e7acf0dc":[0,9,0,0,28],
"group__http__setup.html#gga2fd930fba66817097e73ba9b75800eb2aeb1f77f6a1e09403704128086deecf6c":[0,9,0,0,7],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba033ec26aabe2b7ece963fd3e43d0d064":[0,9,0,1,35],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba08107f6b0e1d7c9e2ca100700cc7200f":[0,9,0,1,38],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba145570ed1178d3d90ad9b7652fea83cf":[0,9,0,1,36],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba1558c42d80f54def5f3277dc879d2844":[0,9,0,1,27],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba2876de0bf7f4302883626399b868b49b":[0,9,0,1,55],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba299a2d262210540b593420fe89e01b32":[0,9,0,1,48],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba36b5bcf2059ae3c84a47e080822239c7":[0,9,0,1,29],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba3e8907f1d75b7b2f41a1e3015a212d2e":[0,9,0,1,39],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba419c919f74b88d18803358141ab9471c":[0,9,0,1,24],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba49cf9c4c184f9e4d265ceae249e92477":[0,9,0,1,21],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba4edd1153b1090c6b04f41a9728184c69":[0,9,0,1,2],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba536b8b672182e77d9dc33e0e3dd4526e":[0,9,0,1,28],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba5398c5b8f1a8d3b7656994638dba2ad5":[0,9,0,1,37],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba53df069872b37830e4296f32e7ec20d8":[0,9,0,1,15],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba571d6791189cba33c31e0a6de346abc9":[0,9,0,1,46],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba5b5f6b48a150926864f66aa8882708df":[0,9,0,1,10],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba5ce4ed593a2ebd6f7fb3cd86c8ab5fb8":[0,9,0,1,43],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba5db103eeafcd3c0b245f42065a8a096f":[0,9,0,1,56],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba63eb71a406e943d4634c357d60dd96df":[0,9,0,1,26],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba67278d96cfa0eb507535b94338810d65":[0,9,0,1,31],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba67de2c448cc952048485e4261a5dab19":[0,9,0,1,17],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba6964f9591ba7284dc4bd388d40c106a9":[0,9,0,1,30],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba6bc23fb7aebd0a4c1e362098ba8447aa":[0,9,0,1,47],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba70de6a18224da6df336ce0c4be7d52e7":[0,9,0,1,19],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba72431bee6eae3e12040af717a0af2df1":[0,9,0,1,8],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba892176fbdf3f79e2c888f677ea34d451":[0,9,0,1,45],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba95022d17916c4aa66324379ecdd53247":[0,9,0,1,5],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba9632802fcd318d1676be7589e6004e96":[0,9,0,1,14],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba9ddf391ade5190c8d685d998ef35e604":[0,9,0,1,44],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151ba9f989de7a400bb10b4a75613504bd889":[0,9,0,1,57],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151baa55ceddb5bb2104bfcfefb16995192a4":[0,9,0,1,9],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151baa6e28f289443c753660cddc9b4ce28a1":[0,9,0,1,41],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151baa9a59ef7151e4a9944519c2a7b5a4193":[0,9,0,1,4],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151baadd5b6f83dd05c6de2ef12de1dd006cd":[0,9,0,1,12],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bab13f90697368bfecc05dc5a8b18fc87c":[0,9,0,1,18],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bab355dd546e62b1478fe3ef94b554f75c":[0,9,0,1,51],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bab42dfcbd67b4e66096e3a8e924b6d6c9":[0,9,0,1,32],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bab8996a5af1f75f21bbe7a7c9ab3a499d":[0,9,0,1,6],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151babc076cee0aac4b407618e7c0bb916138":[0,9,0,1,34],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151babdff771301c12ef6b9ae9b7b5b87c30d":[0,9,0,1,1],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bac0f841f58eb31779ca76bb3cfb600267":[0,9,0,1,20],
"group__http__setup.html#ggaf5ffe69ea1b5908893c7eddf74c8151bac268c2f9a1c531f403403a99da6a80cd":[0,9,0,1,42]
};


<|start_filename|>docs/api/search/variables_c.js<|end_filename|>
var searchData=
[
  ['operations',['operations',['../group__effect__handlers.html#a8588e38ec7973da55f90d239f7ec854f',1,'lh_handlerdef']]],
  ['opfun',['opfun',['../group__effect__handlers.html#a0ccd260a5593683d8343aa0d9a75d7d7',1,'lh_operation']]],
  ['opkind',['opkind',['../group__effect__handlers.html#a596d55fa9ca51ec0602b60b37e818453',1,'lh_operation']]],
  ['optag',['optag',['../group__effect__handlers.html#ad8b0fc0385aecd9216ec6df9df996539',1,'lh_operation']]]
];


<|start_filename|>docs/api/group__streams.js<|end_filename|>
var group__streams =
[
    [ "using_bstream", "group__streams.html#ga3371300a9eea0177e792b08bb5b3bf3d", null ],
    [ "using_stream", "group__streams.html#gaf8015d9228ada99910bc414d748536e8", null ],
    [ "nodec_bstream_t", "group__streams.html#gadaf4d5e2ac9a12cc14da8fa006d85916", null ],
    [ "nodec_stream_t", "group__streams.html#gaee920c8bd173ce7bf3b57f42cbcf3f5f", null ],
    [ "uv_stream_t", "group__streams.html#gaa1b461b8603be0c65dbf1e6037b01668", null ],
    [ "as_stream", "group__streams.html#ga5086fb89c05a1dc0ecf923af3e953403", null ],
    [ "async_printf", "group__streams.html#ga0d8f8fb4145849530ac2e9c7cdef8ec5", null ],
    [ "async_read", "group__streams.html#ga1285146007f649dc4afab2d4c97ed0a8", null ],
    [ "async_read_all", "group__streams.html#ga8406e8a53fce782cb90557358c6290c3", null ],
    [ "async_read_buf", "group__streams.html#ga3c8beb53a775b37e696fdf5e8e31d6d6", null ],
    [ "async_read_buf_all", "group__streams.html#ga353b56dd825e99a056c7cd6ebfd1635a", null ],
    [ "async_read_buf_including", "group__streams.html#gabdf6ced2f262d5a4285465e347af1d79", null ],
    [ "async_read_buf_line", "group__streams.html#ga7691ad253e2f25057450847e697fc210", null ],
    [ "async_read_buf_upto", "group__streams.html#ga944deab13d009fe3b6b94a8c7c4943b9", null ],
    [ "async_read_bufx", "group__streams.html#ga756ccec235ed35716f4c725a948a7943", null ],
    [ "async_read_into", "group__streams.html#gae0d28422e33ff8ba269b4982ec7160aa", null ],
    [ "async_read_line", "group__streams.html#gaf1b8928176f1a3e88f2683c66324bab0", null ],
    [ "async_shutdown", "group__streams.html#ga530b49b85b811a2377209a55b2e670cb", null ],
    [ "async_vprintf", "group__streams.html#ga4f53610d2e3294ec02084a8c618828aa", null ],
    [ "async_write", "group__streams.html#ga333329e70821cc682fdeaa333f210260", null ],
    [ "async_write_buf", "group__streams.html#ga025bb7d59c9dae3b4899e0874d08dc68", null ],
    [ "async_write_bufs", "group__streams.html#ga254f878556059c35448ad927ab982f36", null ],
    [ "nodec_bstream_alloc", "group__streams.html#ga3402e9960bf043d2d361225e18483645", null ],
    [ "nodec_bstream_alloc_on", "group__streams.html#gaa1bf780a5ccea514dc1592f8a0eccb58", null ],
    [ "nodec_bstream_alloc_read", "group__streams.html#gac952f2f3a280250a382a36419f7b16da", null ],
    [ "nodec_bstream_alloc_read_ex", "group__streams.html#ga46bab5d4f8f6d45e6eb3587b796683a2", null ],
    [ "nodec_pushback_buf", "group__streams.html#gaa2323dea0f12493ae97032fe7e6dcd55", null ],
    [ "nodec_stream_free", "group__streams.html#ga7a87a30d292c709502440b0d95b9cb0b", null ],
    [ "nodec_stream_freev", "group__streams.html#ga29902a3b1e06b6d1b87e11b12bfbb11a", null ],
    [ "nodec_zstream_alloc", "group__streams.html#gaac0281d255b7277db65d1e3e30d550fa", null ],
    [ "nodec_zstream_alloc_ex", "group__streams.html#ga8b99efcef9771afc6fad163fbd51d00b", null ]
];

<|start_filename|>docs/api/group__nodec__ip.js<|end_filename|>
var group__nodec__ip =
[
    [ "define_ip4_addr", "group__nodec__ip.html#ga66bb31de758bc23d65745d0dd68a7858", null ],
    [ "define_ip6_addr", "group__nodec__ip.html#ga6c2bfc89980866ce4b3abc76128ee31a", null ]
];

<|start_filename|>docs/api/structlh__handlerdef.js<|end_filename|>
var structlh__handlerdef =
[
    [ "effect", "structlh__handlerdef.html#a08413ea484f015bb79d395c349eb1a29", null ],
    [ "local_acquire", "structlh__handlerdef.html#a197b2abb85288eda9fc73dddb02213c9", null ],
    [ "local_release", "structlh__handlerdef.html#ac03ed465c68a46c167612e712160fd61", null ],
    [ "operations", "structlh__handlerdef.html#a8588e38ec7973da55f90d239f7ec854f", null ],
    [ "resultfun", "structlh__handlerdef.html#afe5be74288279eb0ec913ca483942ddd", null ]
];

<|start_filename|>docs/api/group__effect__handlers.js<|end_filename|>
var group__effect__handlers =
[
    [ "lh_optag_", "group__effect__handlers.html#structlh__optag__", [
      [ "effect", "group__effect__handlers.html#aaba7e2907daa4422cd4f47e91e1b171d", null ],
      [ "opidx", "group__effect__handlers.html#ad756a8e13a174bcb47041076d8611593", null ]
    ] ],
    [ "lh_operation", "group__effect__handlers.html#structlh__operation", [
      [ "opfun", "group__effect__handlers.html#a0ccd260a5593683d8343aa0d9a75d7d7", null ],
      [ "opkind", "group__effect__handlers.html#a596d55fa9ca51ec0602b60b37e818453", null ],
      [ "optag", "group__effect__handlers.html#ad8b0fc0385aecd9216ec6df9df996539", null ]
    ] ],
    [ "lh_handlerdef", "group__effect__handlers.html#structlh__handlerdef", [
      [ "effect", "group__effect__handlers.html#a08413ea484f015bb79d395c349eb1a29", null ],
      [ "local_acquire", "group__effect__handlers.html#a197b2abb85288eda9fc73dddb02213c9", null ],
      [ "local_release", "group__effect__handlers.html#ac03ed465c68a46c167612e712160fd61", null ],
      [ "operations", "group__effect__handlers.html#a8588e38ec7973da55f90d239f7ec854f", null ],
      [ "resultfun", "group__effect__handlers.html#afe5be74288279eb0ec913ca483942ddd", null ]
    ] ],
    [ "yieldargs", "group__effect__handlers.html#structyieldargs", [
      [ "argcount", "group__effect__handlers.html#ab797acdcd17ed8d49fd79980409be48b", null ],
      [ "args", "group__effect__handlers.html#a2dd60935d436953098e73cbdd77fdcd2", null ]
    ] ],
    [ "lh_cstack_ptr_value", "group__effect__handlers.html#gac283305e0f1f5581764aa8d77b36266d", null ],
    [ "lh_effect_null", "group__effect__handlers.html#ga9415634aefb671906b8a37698079e36f", null ],
    [ "lh_op_null", "group__effect__handlers.html#ga6c517e0f68c9e482e3dc92cf9c4c53f5", null ],
    [ "lh_value_cstack_ptr", "group__effect__handlers.html#gac1320c2b79475b7e3fe89e2f7a0a16f5", null ],
    [ "lh_value_yieldargs", "group__effect__handlers.html#ga246cdf54dc0c6c8ce94f3c20f80e2b0a", null ],
    [ "lh_yieldargs_value", "group__effect__handlers.html#ga389460200d7dc3d8b5e8b9deb83f1efe", null ],
    [ "lh_effect", "group__effect__handlers.html#gaf06dcf1847501605b52051581f50cb8d", null ],
    [ "lh_opfun", "group__effect__handlers.html#gaf79f5f56c6360a3ca0115773b1a21bec", null ],
    [ "lh_optag", "group__effect__handlers.html#gaad5d615b9d98a63df083512caa78d78a", null ],
    [ "lh_resume", "group__effect__handlers.html#gace940505f4ef9f09e2aeeca789c5fdea", null ],
    [ "lh_opkind", "group__effect__handlers.html#gaa1b51c33da7448e42829ec71678631fc", [
      [ "LH_OP_NULL", "group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca2cdd41fc10e2c645450aac1310941b06", null ],
      [ "LH_OP_FORWARD", "group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca95b23feba31938ac3c29de4542342718", null ],
      [ "LH_OP_NORESUMEX", "group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca4aa95af9b879aeff29642da0c1fcf16e", null ],
      [ "LH_OP_NORESUME", "group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca22aca2bf964620d72e447d8bcd116d3d", null ],
      [ "LH_OP_TAIL_NOOP", "group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fcab8ab449e5f433cd8a4bc77e2269ebb00", null ],
      [ "LH_OP_TAIL", "group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca680485ac044b9af0ca4d0284839be233", null ],
      [ "LH_OP_SCOPED", "group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fcaa4f5a6c62b5c8780ed1a9256b2f2332f", null ],
      [ "LH_OP_GENERAL", "group__effect__handlers.html#ggaa1b51c33da7448e42829ec71678631fca46b8f876817791c1ae49735d9cf2cc9b", null ]
    ] ],
    [ "lh_call_resume", "group__effect__handlers.html#gac73de5bfdb8644f425dec0ca4782a50e", null ],
    [ "lh_cstack_ptr", "group__effect__handlers.html#gaf14082b6131de8920bdd6a6dc86f1096", null ],
    [ "lh_effect_name", "group__effect__handlers.html#ga450ac4de67ba22cd422942c22973a994", null ],
    [ "lh_handle", "group__effect__handlers.html#ga4607747cefd19c1a58490d0d2b337d03", null ],
    [ "lh_optag_name", "group__effect__handlers.html#ga12164effe25a00e7496809a243bfc627", null ],
    [ "lh_release", "group__effect__handlers.html#gab8f54bf97bbcd7973b2d54331d21a5f9", null ],
    [ "lh_release_resume", "group__effect__handlers.html#ga12630c5bdf2cb1592a59576ae1ed2190", null ],
    [ "lh_scoped_resume", "group__effect__handlers.html#gab351a98efa1a6078343a93bec2eed9f4", null ],
    [ "lh_tail_resume", "group__effect__handlers.html#ga5eec69ac638f5046561917fd11edd9ec", null ],
    [ "lh_yield", "group__effect__handlers.html#ga8ca9fef815d0ad701f17b8f499c47645", null ],
    [ "lh_yield_local", "group__effect__handlers.html#ga34234a90a032e52d31f6315ea31e715d", null ],
    [ "lh_yieldN", "group__effect__handlers.html#ga86b49751042776deb9582fefb843052a", null ]
];

<|start_filename|>docs/api/group__nodec__alloc.js<|end_filename|>
var group__nodec__alloc =
[
    [ "nodec_alloc", "group__nodec__alloc.html#ga93ffdec72fa29b5f091e428cbd4245a9", null ],
    [ "using_alloc", "group__nodec__alloc.html#ga0d6776c7ec091d6230510370d65be21e", null ],
    [ "using_free", "group__nodec__alloc.html#ga12562044b20322d894cfe686eef25c15", null ]
];

<|start_filename|>docs/api/search/all_13.js<|end_filename|>
var searchData=
[
  ['w_5fok',['W_OK',['../group__nodec__fs.html#gae5acf4043c0903cda7436b108e29e8e6',1,'nodec-extra-doc.h']]]
];


<|start_filename|>docs/api/search/variables_8.js<|end_filename|>
var searchData=
[
  ['implicit_5fexts',['implicit_exts',['../group__nodec__static.html#a35e38162a485696d0259c3789b690f1d',1,'http_static_config_t']]],
  ['implicit_5findex',['implicit_index',['../group__nodec__static.html#a6b2390b3780a8912fb6d571aa7a08ba6',1,'http_static_config_t']]]
];


<|start_filename|>examples/ex-https-server-static.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include <nodec.h>
#include "examples.h"

/*-----------------------------------------------------------------
  Example of a static HTTPS server
-----------------------------------------------------------------*/

static void https_serve() {
#ifndef NDEBUG
  fprintf(stderr, "\nstrand %i: secure request url: %s, content length: %llu\n", http_strand_id(), http_req_url(), (unsigned long long)http_req_content_length());
  http_req_print();
#endif
  http_serve_static("./examples/web", NULL);
}

static void https_server() {
  const char* host = "127.0.0.1:8443";
  fprintf(stderr, "securely serving at: %s\n", host);
  nodec_ssl_config_t* ssl_config = nodec_ssl_config_server_from(
    "./examples/data/nodec.crt", "./examples/data/nodec.key", "NodeC");
  {using_ssl_config(ssl_config) {
    async_https_server_at(host, NULL, ssl_config, &https_serve);
  }}
}

void ex_https_server_static() {
  async_stop_on_enter(&https_server);
}


<|start_filename|>docs/api/group__http__in__out.js<|end_filename|>
var group__http__in__out =
[
    [ "NODEC_CHUNKED", "group__http__in__out.html#gaa8e34fdd12cb1290da994deab9d64594", null ],
    [ "http_in_t", "group__http__in__out.html#ga9c23d2a40e3792bf162f3a3e7bcb40df", null ],
    [ "http_out_t", "group__http__in__out.html#ga71b82996867f08b9f7aaed6d10f507bf", null ],
    [ "async_http_in_read_body", "group__http__in__out.html#ga65fe57df6004b40a189295c418a68273", null ],
    [ "http_in_body", "group__http__in__out.html#gaa17e8de265c190884d433e8ef91e3ad4", null ],
    [ "http_in_status_print", "group__http__in__out.html#ga0a71fdfb195b7fe66eadd442e0fbd4e5", null ]
];

<|start_filename|>docs/api/search/groups_5.js<|end_filename|>
var searchData=
[
  ['generic_20values',['Generic Values',['../group__effect__value.html',1,'']]]
];


<|start_filename|>docs/api/search/all_b.js<|end_filename|>
var searchData=
[
  ['max_5finterleaving',['max_interleaving',['../group__tcp.html#ace7d6ad760cdb393909b6b4496929d4d',1,'tcp_server_config_t']]],
  ['msg',['msg',['../group__effect__exn.html#a400106a97a1ab0617f39aa7a4e8618b0',1,'lh_exception']]],
  ['memory_20allocation_2e',['Memory Allocation.',['../group__nodec__alloc.html',1,'']]],
  ['mime_20types_2e',['MIME Types.',['../group__nodec__mime.html',1,'']]]
];


<|start_filename|>examples/main.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include <nodec.h>
#include "examples.h"


// Main entry point: choose your example here
static void entry() {
  ex_http_connect();
  //ex_http_server_static();
  //ex_https_connect();
  //ex_https_server_static();
  //ex_fs_search();
}

int main() {
  async_main(entry);
  return 0;
}


<|start_filename|>docs/api/group__nodec__tty.js<|end_filename|>
var group__nodec__tty =
[
    [ "async_stop_on_enter", "group__nodec__tty.html#ga281ad555b956e2811c1b7e328d5455ad", null ],
    [ "async_tty_printf", "group__nodec__tty.html#ga309e4777a21bb0f6070bcc902d532081", null ],
    [ "async_tty_printf_err", "group__nodec__tty.html#gaa9ea83fc379a6e3cafbe62cc49281d48", null ],
    [ "async_tty_readline", "group__nodec__tty.html#ga81a44fa3f9c9379a67f8e6a66f226beb", null ],
    [ "async_tty_vprintf", "group__nodec__tty.html#ga09a924d56ad28e210260a0b5a1e1851f", null ],
    [ "async_tty_vprintf_err", "group__nodec__tty.html#gabeddbbfc2657a4c39ee218c69d63e664", null ],
    [ "async_tty_write", "group__nodec__tty.html#gab5cd068957e59c93f9c6651c1c5740d4", null ],
    [ "async_tty_write_err", "group__nodec__tty.html#gacd7e806a18f986c9310fa889565647e3", null ]
];

<|start_filename|>src/http_request.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>
#include <string.h>

#define HTTP_MAX_HEADERS (8*1024)


#ifdef _MSC_VER
# include <malloc.h>
# define alloca _alloca
#else
# include <alloca.h>
#endif


/*-----------------------------------------------------------------
HTTP Headers
-----------------------------------------------------------------*/


typedef struct _http_header_t {
  const char* name;
  const char* value;
  bool _do_free;
} http_header_t;


typedef struct _http_headers_t {
  size_t  count;         // how many are there
  size_t  size;          // how big is our array
  http_header_t* elems;  // realloc on demand, start with size 16 and double as more comes  
} http_headers_t;

static void http_headers_add(http_headers_t* headers, const char* name, const char* value, bool strdup) {
  if (name == NULL) return;
  if (headers->count >= headers->size) {
    size_t newsize = (headers->size == 0 ? 16 : 2 * headers->size);
    headers->elems = nodec_realloc_n(headers->elems, newsize, http_header_t);
    headers->size = newsize;
  }
  http_header_t* h = &headers->elems[headers->count];
  headers->count++;
  h->name = strdup ? nodec_strdup(name) : name;
  h->value = strdup ? nodec_strdup(value) : value;
  h->_do_free = strdup;
}

static void http_header_clear(http_header_t* header) {
  if (header->_do_free) {
    if (header->name != NULL) nodec_free(header->name);
    if (header->value != NULL) nodec_free(header->value);
  }
  memset(header, 0, sizeof(http_header_t));
}

static void http_headers_clear(http_headers_t* headers) {
  for (size_t i = 0; i < headers->count; i++) {
    http_header_clear(&headers->elems[i]);
  }
  if (headers->elems != NULL) nodec_free(headers->elems);
  memset(headers, 0, sizeof(http_headers_t));
}


// Find a header value and normalize if necessary by appending with commas
static const char* http_headers_lookup_from(http_headers_t* headers, const char* name, size_t from) {
  http_header_t* found = NULL;
  uv_buf_t newvalue = nodec_buf_null();
  for (size_t i = from; i < headers->count; i++) {
    http_header_t* h = &headers->elems[i];
    if (h->name == NULL || h->value == NULL) continue;
    if (nodec_stricmp(name, h->name) == 0) {
      if (found == NULL) {
        // found first entry
        found = h;
      }
      else {
        // found another entry.. we string append into the first entry and NULL this one out
        if (h->value != NULL && h->value[0] != 0) {
          if (nodec_buf_is_null(newvalue)) newvalue = nodec_buf_append_into(newvalue, nodec_buf_str(found->value));
          newvalue = nodec_buf_append_into(newvalue, nodec_buf_str(","));
          newvalue = nodec_buf_append_into(newvalue, nodec_buf_str(h->value));
        }
        http_header_clear(h);  // clear the current entry
      }
    }
  }
  if (found == NULL) return NULL;
  if (!nodec_buf_is_null(newvalue)) {
    // update our found entry with the new appended value
    if (found->_do_free) {
      nodec_free(found->value);
    }
    else {
      found->_do_free = true;
      found->name = nodec_strdup(found->name);
    }
    found->value = newvalue.base;
  }
  return found->value;
}

// Lookup a specific header entry (case insensitive), returning its value or NULL if not found.
static const char* http_headers_lookup(http_headers_t* headers, const char* name) {
  return http_headers_lookup_from(headers, name, 0);
}

// Iterate through all entries. `*iter` should start at 0. Returns NULL if done iterating.
static const char* http_headers_next(http_headers_t* headers, const char** value, size_t* iter) {
  if (value != NULL) *value = NULL;
  if (iter == NULL) return NULL;
  while (*iter < headers->count && headers->elems[*iter].name == NULL) {
    (*iter)++;
  }
  if (*iter >= headers->count) return NULL;
  // normalize the entry by lookup itself directly
  const char* name = headers->elems[*iter].name;
  const char* _value = http_headers_lookup_from(headers, name, *iter);
  if (value != NULL) *value = _value;
  (*iter)++;
  return name;
}

/*-----------------------------------------------------------------
Requests
-----------------------------------------------------------------*/

struct _http_in_t
{
  nodec_bstream_t* stream; // the input stream
  http_parser      parser; // the request parser on the stream
  http_parser_settings parser_settings;

  bool            is_request;
  const char*     url;            // parsed url (for client request)
  http_status_t   status;         // parsed status (for server response)
  const char*     status_info;    // status message
  uint64_t        content_length; // real content length from headers
  http_headers_t  headers; // parsed headers; usually pointing into `prefix`
  uv_buf_t        prefix;  // the initially read buffer that holds all initial headers
  size_t          body_start;     // offset of the body start in the prefix buffer

  uv_buf_t        current_body;   // the last parsed body piece; each on_body pauses the parser so only one is needed
  const char*     current_field;  // during header parsing, holds the last seen header field

  bool            headers_complete;  // true if all initial headers have been parsed
  bool            complete;          // true if the whole message has been parsed
  size_t          body_len;

  nodec_bstream_t* body_stream;
};

// Terminate header fields and values by modifying the read buffer in place. (in `prefix`)
static void terminate(http_in_t* req, const char* at, size_t len) {
  ((char*)at)[len] = 0;
}


static int on_header_field(http_parser* parser, const char* at, size_t len) {
  http_in_t* req = (http_in_t*)parser->data;
  terminate(req, at, len);
  req->current_field = at;
  return 0;
}

static int on_header_value(http_parser* parser, const char* at, size_t len) {
  http_in_t* req = (http_in_t*)parser->data;
  // remove trailing comma and spaces
  while (len > 0 && (at[len - 1] == ',' || at[len - 1] == ' ')) len--;
  terminate(req, at, len);
  // add to the headers
  http_headers_add(&req->headers, req->current_field, at, req->headers_complete); // allocate if the headers are complete as the buffer might have changed
  // treat special headers
  /*
  if (_stricmp(req->current_field, "content-length")==0) {
    long long len = atoll(at);
    // printf("read content-length: %lli\n", len);
    if (len > 0 && len <= SIZE_MAX) req->content_length = (uint64_t)len;
  }
  */
  req->current_field = NULL;
  return 0;
}

static int on_url(http_parser* parser, const char* at, size_t len) {
  http_in_t* req = (http_in_t*)parser->data;
  terminate(req, at, len);
  req->url = at;
  return 0;
}

static int on_status(http_parser* parser, const char* at, size_t len) {
  http_in_t* req = (http_in_t*)parser->data;
  terminate(req, at, len);
  req->status = parser->status_code;
  req->status_info = at;
  return 0;
}


static int on_body(http_parser* parser, const char* at, size_t len) {
  http_in_t* req = (http_in_t*)parser->data;
  terminate(req, at, len); // TODO: I think this is always safe since stream reads always over allocate by 1...
  req->current_body = nodec_buf((char*)at, len);  // remember this body piece
  http_parser_pause(parser, 1);             // and pause the parser, returning from execute! (with parser->errno set to HPE_PAUSED)
  return 0;
}

static int on_headers_complete(http_parser* parser) {
  http_in_t* req = (http_in_t*)parser->data;
  req->headers_complete = true;
  if ((parser->flags & F_CONTENTLENGTH) == F_CONTENTLENGTH) {
    req->content_length = parser->content_length;
  }
  //http_parser_pause(parser, 1);             // and pause the parser, returning from execute! (with parser->errno set to HPE_PAUSED)
  return 0;
}

static int on_message_complete(http_parser* parser) {
  http_in_t* req = (http_in_t*)parser->data;
  req->complete = true;
  return 0;
}

// Clear and free all members of a request
void http_in_clear(http_in_t* req) {
  http_headers_clear(&req->headers);
  if (req->body_stream != NULL && req->body_stream != req->stream) {
    nodec_stream_free(as_stream(req->body_stream));
  }
  if (req->prefix.base != NULL) {
    nodec_buf_free(req->prefix);
  }
  memset(req, 0, sizeof(http_in_t));
  // don't free the stream, it is not owned by us
}

void http_in_clearv(lh_value reqv) {
  http_in_clear((http_in_t*)lh_ptr_value(reqv));
}

static enum http_errno check_http_errno(http_parser* parser) {
  enum http_errno err = HTTP_PARSER_ERRNO(parser);
  if (err != HPE_OK && err != HPE_PAUSED) {  // pausing is ok!
    throw_http_err_str(HTTP_STATUS_BAD_REQUEST, http_errno_description(err));
  }
  return err;
}

void http_in_init(http_in_t* in, nodec_bstream_t* stream, bool is_request)
{
  memset(in, 0, sizeof(http_in_t));
  in->stream = stream;
  in->is_request = is_request;
}

static nodec_bstream_t* http_in_stream_alloc(http_in_t* req);
static nodec_stream_t* nodec_cstream_alloc(uint64_t* content_len, nodec_stream_t* s);

size_t async_http_in_read_headers(http_in_t* in ) 
{
  // and read until the double empty line is seen  (\r\n\r\n); the end of the headers 
  size_t headers_len = 0;
  uv_buf_t buf = async_read_buf_upto(in->stream, "\r\n\r\n", 4, HTTP_MAX_HEADERS);
  headers_len = (size_t)buf.len;
  if (nodec_buf_is_null(buf) || headers_len > HTTP_MAX_HEADERS) {
    if (!nodec_buf_is_null(buf)) nodec_buf_free(buf);
    if (headers_len == 0) {
      // eof; means client closed the stream; no problem
      fprintf(stderr, "stream closed while reading headers\n");
      return 0;
    }
    throw_http_err((headers_len > HTTP_MAX_HEADERS ? HTTP_STATUS_PAYLOAD_TOO_LARGE : HTTP_STATUS_BAD_REQUEST));
  }
  //printf("\n\nraw prefix read: %s\n\n\n", buf.base);  // only print idx length here

  // only if successful initialize a request object
  in->prefix = buf;
  http_parser_init(&in->parser, (in->is_request ? HTTP_REQUEST : HTTP_RESPONSE));
  in->parser.data = in;
  http_parser_settings_init(&in->parser_settings);
  in->parser_settings.on_header_field = &on_header_field;
  in->parser_settings.on_header_value = &on_header_value;
  in->parser_settings.on_headers_complete = &on_headers_complete;
  in->parser_settings.on_message_complete = &on_message_complete;
  in->parser_settings.on_body = &on_body;
  in->parser_settings.on_url = &on_url;
  in->parser_settings.on_status = &on_status;

  // parse the headers
  size_t nread = http_parser_execute(&in->parser, &in->parser_settings, in->prefix.base, 
                    headers_len // do not use in->prefix.len because requests can be pipe-lined.
                  );
  check_http_errno(&in->parser);
  assert(nread == headers_len);
  assert(nodec_buf_is_null(in->current_body));
  assert(in->headers_complete);

  // remember where we are at in the current buffer for further body reads 
  in->body_start = nread;
  in->body_stream = in->stream;
  bool chunked = false;
  if (http_in_header_contains(in, "Transfer-Encoding", "chunked")) {
#ifndef NDEBUG
    fprintf(stderr, "use chunked!\n");
#endif
    chunked = true;
    in->body_stream = nodec_bstream_alloc_on( nodec_cstream_alloc( &in->content_length, as_stream(in->body_stream)) );
  }
  if (http_in_header_contains(in, "Content-Encoding", "gzip")) {
#ifndef NDEBUG
    fprintf(stderr, "use gzip!\n");
#endif
    in->body_stream = nodec_zstream_alloc( as_stream(in->body_stream), chunked);
  }  
  return headers_len;
}

/*-----------------------------------------------------------------
 Chunked streams
-----------------------------------------------------------------*/
typedef enum _chunk_parse_state_t{
  CH_HEADER,
  CH_HEADER_LEN,
  CH_HEADER_SKIP_EXT,
  CH_HEADER_LF,
  CH_DATA,
  CH_DATA_END,
  CH_DATA_ENDLF,
  CH_TRAILER,
  CH_TRAILER_SKIP,
  CH_TRAILER_SKIPLF,
  CH_TRAILER_LF,
  CH_END
} chunk_parse_state_t;

typedef struct _chunk_state_t {
  chunk_parse_state_t parse;
  size_t      body_len;
  uv_buf_t    part;
  const char* next;
  const char* end;
  const char* errormsg;
} chunk_state_t;

typedef enum _chunk_parse_result_t {
  CP_EOF,
  CP_NEEDMORE,
  CP_BODYPART,
  CP_ERR
} chunk_parse_result_t;

void chunk_parse_add(chunk_state_t* s, uv_buf_t buf) {
  assert(s->next >= s->end);
  s->next = buf.base;
  s->end = s->next + buf.len;
}

uv_buf_t chunk_parse_part(chunk_state_t* s) {
  assert(!nodec_buf_is_null(s->part));
  return s->part;
}

chunk_parse_result_t chunk_parse(chunk_state_t* s) {
  if (s->errormsg != NULL) return CP_ERR;
  while (true) {
    char c = 0;
    switch (s->parse) {
    case CH_HEADER: {
      s->body_len = 0;
      s->part = nodec_buf_null();
      // fall through
    }
    case CH_HEADER_LEN: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      size_t d = 16;
      if (c >= '0' && c <= '9') d = (c - '0');
      else if (c >= 'a' && c <= 'f') d = 10 + (c - 'a');
      else if (c >= 'A' && c <= 'F') d = 10 + (c - 'A');
      else if (s->parse == CH_HEADER) return CP_ERR;
      else if (c == '\r') s->parse = CH_HEADER_LF;
      else s->parse = CH_HEADER_SKIP_EXT;
      if (d <= 15) {
        size_t newlen = s->body_len * 16 + d;
        if (newlen < s->body_len) {
          s->errormsg = "chunked part too large";
          return CP_ERR;
        }
        s->body_len = newlen;
        s->parse = CH_HEADER_LEN;
      }
      break;
    }
    case CH_HEADER_SKIP_EXT: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      // TODO: should ignore \r inside quoted extension values
      if (c == '\r') s->parse = CH_HEADER_LF;
      break;
    }
    case CH_HEADER_LF: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      if (c != '\n') {
        s->errormsg = "invalid chunked header (expecting LF)";
        return CP_ERR;
      }
      s->parse = (s->body_len == 0 ? CH_TRAILER : CH_DATA);
      break;
    }
    case CH_DATA: {
      if (s->body_len == 0) {
        s->parse = CH_DATA_END;
        break;
      }
      else {
        if (s->next >= s->end) return CP_NEEDMORE;
        const char* part = s->next;
        size_t available = (s->end - s->next);
        size_t partlen = (available >= s->body_len ? s->body_len : available);
        s->body_len -= partlen;
        s->next += partlen;
        s->part = nodec_buf(part, partlen);
        return CP_BODYPART;
      }
    }
    case CH_DATA_END: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      if (c != '\r') {
        s->errormsg = "invalid chunked data end (expecting CR)";
        return CP_ERR;
      }
      s->parse = CH_DATA_ENDLF;
      break;
    }
    case CH_DATA_ENDLF: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      if (c != '\n') {
        s->errormsg = "invalid chunked data end (expecting LF)";
        return CP_ERR;
      }
      s->parse = CH_HEADER;
      break;
    }
    case CH_TRAILER: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      s->parse = (c == '\r' ? CH_TRAILER_LF : CH_TRAILER_SKIP);
      break;
    }
    case CH_TRAILER_SKIP: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      // TODO: ignore \r inside quoted trailer values
      if (c == '\r') s->parse = CH_TRAILER_SKIPLF;
      break;
    }
    case CH_TRAILER_SKIPLF: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      if (c != '\n') {
        s->errormsg = "invalid chunked trailer (expecting LF)";
        return CP_ERR;
      }
      s->parse = CH_TRAILER;
      break;
    }
    case CH_TRAILER_LF: {
      if (s->next >= s->end) return CP_NEEDMORE;
      c = *(s->next); s->next++;
      if (c != '\n') {
        s->errormsg = "invalid chunked ending (expecting LF)";
        return CP_ERR;
      }
      s->parse = CH_END;
      //s->next = NULL;
      //s->end = NULL;
      return CP_EOF;
    }
    case CH_END: {
      return CP_EOF;
    }
    default: {
      return CP_ERR;
    }
    } // switch
  } // while
}

#define MAX_CHUNK_HEADER (100)

typedef struct _nodec_cstream_t {
  nodec_stream_t  stream;
  nodec_stream_t* source;
  uv_buf_t        current;        // the last read buffer
  bool            owned;
  chunk_state_t   parse;
  uint64_t*       content_len;
} nodec_cstream_t;

static uv_buf_t async_cstream_read_bufx(nodec_stream_t* stream, bool* owned) {
  nodec_cstream_t* cs = (nodec_cstream_t*)stream;
  if (owned != NULL) *owned = false;
  chunk_parse_result_t res;
  while ((res = chunk_parse(&cs->parse)) == CP_NEEDMORE) {
    if (cs->owned) nodec_buf_free(cs->current);
    cs->current = async_read_bufx(cs->source, &cs->owned);
    if (nodec_buf_is_null(cs->current)) {
      return cs->current;
    }
    chunk_parse_add(&cs->parse, cs->current);
  }
  assert(res != CP_NEEDMORE);

  if (res == CP_EOF) {
    return nodec_buf_null();
  }
  else if (res == CP_BODYPART) {
    uv_buf_t part = chunk_parse_part(&cs->parse);
    if (cs->content_len != NULL) *cs->content_len += part.len;
    if (part.base == cs->current.base && part.len == cs->current.len) {
      // hand over our current buffer with ownership
      cs->current = nodec_buf_null();
      if (owned) *owned = cs->owned;
      return part;
    }
    else {
      // owned is false
      return part;
    }
  }
  else {
    nodec_throw_msg(UV_EINVAL, (cs->parse.errormsg != NULL ? cs->parse.errormsg : "chunked read error"));
    return nodec_buf_null();
  }
}

static void async_cstream_write_bufs(nodec_stream_t* stream, uv_buf_t bufs[], size_t count) {
  nodec_cstream_t* cs = (nodec_cstream_t*)stream;
  async_write_bufs(cs->source, bufs, count);
}

static void async_cstream_shutdown(nodec_stream_t* stream) {
  // nothing to do
}

static void async_cstream_free(nodec_stream_t* stream) {
  nodec_cstream_t* cs = (nodec_cstream_t*)stream;
  if (cs->owned) nodec_buf_free(cs->current);
  nodec_free(cs);
}

nodec_stream_t* nodec_cstream_alloc(uint64_t* content_len, nodec_stream_t* source) {
  nodec_cstream_t* cs = nodec_zero_alloc(nodec_cstream_t);
  nodec_stream_init(&cs->stream,
    &async_cstream_read_bufx, &async_cstream_write_bufs,
    &async_cstream_shutdown, &async_cstream_free);
  cs->source = source;
  cs->content_len = content_len;
  if (cs->content_len != NULL) *cs->content_len = 0;
  return &cs->stream;
}



/*-----------------------------------------------------------------
Reading the body of a request
-----------------------------------------------------------------*/


nodec_bstream_t* http_in_body(http_in_t* in) {
  return in->body_stream;
}

// Read asynchronously the entire body of the request. 
// The caller is responsible for buffer deallocation.
// Uses Content-Length is possible to read directly into a continuous buffer without reallocation.
uv_buf_t async_http_in_read_body(http_in_t* req, size_t read_max) {
  uv_buf_t  buf = nodec_buf_null();
  nodec_bstream_t* stream = http_in_body(req);
  if (req->complete || req->body_stream == NULL) return buf;
  //{using_bstream(stream) {
     size_t clen = http_in_content_length(req);
     if (clen > read_max) clen = read_max;
     if (clen > 0 && http_in_header(req, "Content-Encoding") == NULL) {
       buf = nodec_buf_alloc(clen);
       {using_buf_on_abort_free(&buf){
          size_t nread = async_read_into(stream, buf);
          nodec_buf_fit(buf, nread);
       }}       
     }
     else {
       buf = async_read_buf_all(stream, read_max);
     }
  //}}
  nodec_stream_free(as_stream(req->body_stream));
  req->body_stream = NULL;
  return buf;
}


/*-----------------------------------------------------------------
Helpers
-----------------------------------------------------------------*/

// Return the read only request URL (only valid on server requests)
const char* http_in_url(http_in_t* req) {
  return req->url;
}

// Return the read only HTTP Status (only valid on server responses)
http_status_t http_in_status(http_in_t* req) {
  return req->status;
}

const char* http_in_status_info(http_in_t* req) {
  return req->status_info;
}

uint16_t http_in_version(http_in_t* req) {
  return (req->parser.http_major << 8 | req->parser.http_minor);
}

// Return the read only request HTTP method (only valid on server requests)
http_method_t http_in_method(http_in_t* req) {
  return (http_method_t)(req->parser.method);
}

uint64_t http_in_content_length(http_in_t* req) {
  return req->content_length;
}

const char* http_in_header(http_in_t* req, const char* name) {
  return http_headers_lookup(&req->headers, name);
}

const char* http_in_header_next(http_in_t* req, const char** value, size_t* iter) {
  return http_headers_next(&req->headers, value, iter);
}

bool http_in_header_contains(http_in_t* req, const char* name, const char* pattern ) {
  const char* s = http_in_header(req,name);
  return (s != NULL && pattern != NULL && strstr(s, pattern) != NULL);
}

const char* http_header_next_field(const char* header, size_t* len, const char** iter) {
  *len = 0;
  if (*iter == NULL) *iter = header;

  // find start
  const char* p = *iter;
  while (*p == ' ' || *p == ',') p++;
  if (*p == '"') p++;
  if (*p == 0) return NULL;

  // find end
  const char* q = p + 1;
  while (*q != 0 && *q != '"' && *q != ' ' && *q != ',') q++;
  *len = (q - p);
  if (*q != 0) q++;
  *iter = q;
  return p;
}



/*-----------------------------------------------------------------
   HTTP response
-----------------------------------------------------------------*/
struct _http_out_t {
  nodec_stream_t* stream;
  uv_buf_t         head;
  size_t           head_offset;
  bool             status_sent;
};

void http_out_init(http_out_t* out, nodec_stream_t* stream) {
  memset(out, 0, sizeof(http_out_t));
  out->stream = stream;
}

void http_out_init_server(http_out_t* out, nodec_stream_t* stream, const char* server_name) {
  http_out_init(out, stream);
  http_out_add_header(out, "Server", server_name);
}

void http_out_init_client(http_out_t* out, nodec_stream_t* stream, const char* host_name) {
  http_out_init(out, stream);
  http_out_add_header(out, "Host", host_name);
}


void http_out_clear(http_out_t* out) {
  nodec_bufref_free(&out->head);
  out->head_offset = 0;
}

void http_out_clearv(lh_value respv) {
  http_out_clear((http_out_t*)lh_ptr_value(respv));
}

void http_out_add_header(http_out_t* out, const char* field, const char* value) {
  size_t n = strlen(field);
  size_t m = strlen(value);
  size_t extra = n + m + 4; // : \r\n
  out->head = nodec_buf_ensure(out->head, out->head_offset + extra);
  char* p = out->head.base + out->head_offset;
  size_t available = out->head.len - out->head_offset;
  nodec_strncpy(p, available, field, n);
  p[n] = ':';
  p[n + 1] = ' ';
  nodec_strncpy(p + n + 2, available - n - 2, value, m);
  nodec_strncpy(p + n + m + 2, available - n - m - 2, "\r\n", 2);
  out->head_offset += extra;
}

/*
static void http_out_add_header_buf(http_out_t* out, uv_buf_t buf) {
  if (nodec_buf_is_null(buf)) return;
  out->head = nodec_buf_ensure(out->head, out->head_offset + buf.len);
  size_t available = out->head.len - out->head_offset;
  memcpy(out->head.base + out->head_offset, buf.base, buf.len);
  out->head_offset += buf.len;
}

static void http_out_send_raw_str(http_out_t* out, const char* s) {
  async_write(out->stream, s);
}
*/

static void http_out_send_raw_headers(http_out_t* out, uv_buf_t prefix, uv_buf_t postfix) {
  uv_buf_t buf = nodec_buf(out->head.base, out->head_offset);
  uv_buf_t bufs[3] = { prefix, buf, postfix };
#ifndef NDEBUG
  fprintf(stderr, "%s%s%s\n", prefix.base, buf.base, postfix.base);
#endif
  async_write_bufs(out->stream, bufs, 3);
  nodec_bufref_free(&out->head);
  out->head_offset = 0;
  out->status_sent = true;
}

static void http_out_send_headers(http_out_t* out, const char* prefix, const char* postfix) {
  http_out_send_raw_headers(out, nodec_buf_str(prefix), nodec_buf_str(postfix));
}

static void http_out_send_status_headers(http_out_t* out, http_status_t status) {
  // send status to a client
  if (status == 0) status = HTTP_STATUS_OK;
  char line[256];
  snprintf(line, 256, "HTTP/1.1 %i %s\r\nDate: %s\r\n", status, nodec_http_status_str(status), nodec_inet_date_now());
  line[255] = 0;
  http_out_send_headers(out, line, "\r\n");
}

static void http_out_send_request_headers(http_out_t* out, http_method_t method, const char* url) {
  // send request to a server
  char prefix[512];
  snprintf(prefix, 512, "%s %s HTTP/1.1\r\nDate: %s\r\n", nodec_http_method_str(method), url, nodec_inet_date_now());
  prefix[511] = 0;
  http_out_send_headers(out, prefix, "\r\n");
}

static void http_out_add_header_content_type(http_out_t* out, const char* content_type) {
  if (content_type == NULL) return;
  char hvalue[256];
  // lookup default character set
  const char* charset = NULL;
  if (content_type != NULL && strstr(content_type, "charset") == NULL) {
    nodec_info_from_mime(content_type, NULL, NULL, &charset);
  }
  const char* charset_sep = (charset == NULL ? "" : ";");
  if (charset == NULL) charset = "";
  snprintf(hvalue, 256, "%s%s%s", content_type, charset_sep, charset);
  http_out_add_header(out, "Content-Type", hvalue);
}


static void http_out_add_headers_body(http_out_t* out, size_t content_length, const char* content_type) {
  http_out_add_header_content_type(out,content_type);
  if (content_length != NODEC_CHUNKED) {
    char lvalue[64];
    snprintf(lvalue, 64, "%zu", content_length);
    http_out_add_header(out, "Content-Length", lvalue);
  }
  else {
    http_out_add_header(out, "Transfer-Encoding", "chunked");
  }
}

bool http_out_status_sent(http_out_t* out) {
  return out->status_sent;
}

/*-----------------------------------------------------------------
  HTTP out body stream
-----------------------------------------------------------------*/

typedef struct _http_out_stream_t {
  nodec_stream_t    stream;
  nodec_stream_t*   source;
  bool              chunked;
} http_out_stream_t;

static void _http_out_write_bufs(nodec_stream_t* stream, uv_buf_t bufs[], size_t count) {
  if (bufs == NULL || count == 0) return;
  http_out_stream_t* hs = (http_out_stream_t*)stream;
  if (!hs->chunked) {
    async_write_bufs(hs->source, bufs, count);
  }
  else {
    // pre and post fix the buffers, and calculate the total
    uv_buf_t* xbufs = alloca((count + 2) * sizeof(uv_buf_t));
    size_t total = 0;
    for (size_t i = 0; i < count; i++) {
      total += bufs[i].len;
      if (total < bufs[i].len) nodec_check(EOVERFLOW);
      xbufs[i + 1] = bufs[i];
    }
    // prevent terminating the stream pre-maturely and don't write 0-length chunks
    if (total == 0) return;
    // create pre- and postfix
    char prefix[256];
    snprintf(prefix, 256, "%zX\r\n", total );  // hexadecimal chunk length
    xbufs[0] = nodec_buf(prefix, strlen(prefix));
    xbufs[count + 1] = nodec_buf_str("\r\n");
    // and write it out as a chunk
    async_write_bufs(hs->source, xbufs, count + 2);
  }
}

static void _http_out_shutdown(nodec_stream_t* stream) {
  http_out_stream_t* hs = (http_out_stream_t*)stream;
  if (hs->chunked) {
    // write out final 0 chunk
    async_write_buf(hs->source, nodec_buf_str("0\r\n\r\n"));
  }
}

static void _http_out_free(nodec_stream_t* stream) {
  http_out_stream_t* hs = (http_out_stream_t*)stream;
  hs->source = NULL;  // we don't own the underlying TCP stream, don't free it
  nodec_stream_release(&hs->stream);
  nodec_free(hs);
}

static nodec_stream_t* http_out_stream_alloc(nodec_stream_t* source, bool chunked) {
  http_out_stream_t* hs = nodec_alloc(http_out_stream_t);
  hs->source = source;
  hs->chunked = chunked;
  nodec_stream_init(&hs->stream, NULL,
                       &_http_out_write_bufs, &_http_out_shutdown, &_http_out_free);
  return &hs->stream;
}



void http_out_send_status(http_out_t* out, http_status_t status) {
  http_out_add_header(out, "Content-Length", "0");
  http_out_send_status_headers(out, status);
}

nodec_stream_t* http_out_send_status_body(http_out_t* out, http_status_t status, size_t content_length, const char* content_type) {
  http_out_add_headers_body(out, content_length, content_type);
  http_out_send_status_headers(out, status);
  return http_out_stream_alloc(out->stream, (content_length == NODEC_CHUNKED));
}

void http_out_send_request(http_out_t* out, http_method_t method, const char* url) {
  http_out_add_header(out,"Content-Length", "0");
  http_out_send_request_headers(out, method, url);
}

nodec_stream_t* http_out_send_request_body(http_out_t* out, http_method_t method, const char* url, size_t content_length, const char* content_type) {
  http_out_add_headers_body(out, content_length, content_type);
  http_out_send_request_headers(out, method, url);
  return http_out_stream_alloc(out->stream, (content_length == NODEC_CHUNKED));
}




/*-----------------------------------------------------------------
  HTTP server function
-----------------------------------------------------------------*/

implicit_define(http_current_req)
implicit_define(http_current_resp)
implicit_define(http_current_strand_id)
implicit_define(http_current_url)

void nodec_http_serve(int id, nodec_bstream_t* client, lh_value servefunv) {
  nodec_http_servefun* servefun = ((nodec_http_servefun*)lh_fun_ptr_value(servefunv));
  {using_implicit(lh_value_int(id), http_current_strand_id) {
    http_in_t http_in;
    http_in_init(&http_in, client, true);
    {using_implicit_defer(http_in_clearv, lh_value_any_ptr(&http_in), http_current_req) {
      http_out_t http_out;
      http_out_init_server(&http_out, as_stream(client), "NodeC/0.1");
      {using_implicit_defer(http_out_clearv, lh_value_any_ptr(&http_out), http_current_resp) {
        fprintf(stderr,"\n------------------------------\nstrand %i, read headers\n", id);
        if (async_http_in_read_headers(&http_in) > 0) { // if not closed by client
          char urlpath[1024];
          snprintf(urlpath, 1024, "http://%s%s", http_in_header(&http_in, "Host"), http_in_url(&http_in));
          const nodec_url_t* url = nodec_parse_url(urlpath);
          {using_implicit_defer(nodec_url_freev, lh_value_ptr(url), http_current_url) {
            servefun();
          }}
        }
      }}
    }}
  }}
}


void async_http_server_at(const char* host, tcp_server_config_t* config, nodec_http_servefun* servefun)
{
  tcp_server_config_t default_config = tcp_server_config();
  if (config == NULL) config = &default_config;
  struct sockaddr* addr = nodec_parse_sockaddr(host);
  {using_sockaddr(addr) {
    async_tcp_server_at(addr, config, &nodec_http_serve,
      &async_write_http_exnv, lh_value_fun_ptr(servefun));   // by address to prevent conversion between object and function pointer
  }}
}

lh_value async_http_connect_on(const char* host, nodec_bstream_t* connection, http_connect_fun* connectfun, lh_value arg) {
  lh_value result;
  http_in_t in;
  http_in_init(&in, connection, false);
  {defer(http_in_clearv, lh_value_any_ptr(&in)) {
    http_out_t out;
    http_out_init_client(&out, as_stream(connection), host);
    {defer(http_out_clearv, lh_value_any_ptr(&out)) {
      result = connectfun(&in, &out, arg);
    }}
  }}
  return result;
}

lh_value async_http_connect(const char* url, http_connect_fun* connectfun, lh_value arg) {
  lh_value result = lh_value_null;
  nodec_bstream_t* conn = async_tcp_connect(url);
  {using_bstream(conn) {
    const nodec_url_t* u = nodec_parse_url(url);
    {using_url(u) {
      const char* host = nodec_url_host(u);
      async_http_connect_on(host, conn, connectfun, arg);
    }}
  }}
  return result;
}


/*-----------------------------------------------------------------
HTTP server implicitly bound request and response
-----------------------------------------------------------------*/


int http_strand_id() {
  return lh_int_value(implicit_get(http_current_strand_id));
}

http_in_t*  http_req() {
  return (http_in_t*)lh_ptr_value(implicit_get(http_current_req));
}

http_out_t* http_resp() {
  return (http_out_t*)lh_ptr_value(implicit_get(http_current_resp));
}

const nodec_url_t* http_req_parsed_url() {
  return (const nodec_url_t*)lh_ptr_value(implicit_get(http_current_url));
}

// Responses

void http_resp_add_header(const char* field, const char* value) {
  http_out_add_header(http_resp(), field, value);
}

void http_resp_send_status(http_status_t status) {
  http_out_t* resp = http_resp();
  http_out_send_status(resp, status);
}

bool http_resp_status_sent() {
  return http_out_status_sent(http_resp());
}

nodec_stream_t* http_resp_send_status_body(http_status_t status, size_t content_length, const char* content_type) {
  http_out_t* resp = http_resp();
  return http_out_send_status_body(resp, status, content_length, content_type);
}


void http_resp_send_ok() {
  http_resp_send_status(HTTP_STATUS_OK);
}

void http_resp_send_body_buf(http_status_t status, uv_buf_t buf, const char* content_type) {
  http_out_t* resp = http_resp();
  if (nodec_buf_is_null(buf)) {
    http_out_send_status(resp, status);
  }
  else {
    nodec_stream_t* stream = http_out_send_status_body(resp, status, buf.len, content_type);
    {using_stream(stream) {
      async_write_buf(stream, buf);
    }}
  }
}

void http_resp_send_body_str(http_status_t status, const char* body /* can be NULL */, const char* content_type) {
  http_resp_send_body_buf(status, nodec_buf_str(body), content_type);
}




// Requests

const char* http_req_url() {
  return http_in_url(http_req());
}

http_method_t http_req_method() {
  return http_in_method(http_req());
}

const char* http_req_path() {
  return nodec_url_path(http_req_parsed_url());
}

uint64_t http_req_content_length() {
  return http_in_content_length(http_req());
}

const char* http_req_header(const char* name) {
  return http_in_header(http_req(), name);
}

const char* http_req_header_next(const char** value, size_t* iter) {
  return http_in_header_next(http_req(), value, iter);
}

nodec_bstream_t* async_req_body() {
  return http_in_body(http_req());
}

// Read the full body; the returned buf should be deallocated by the caller.
uv_buf_t async_req_read_body(size_t read_max) {
  return async_http_in_read_body(http_req(), read_max);
}

// Read the full body as a string. Only works if the body cannot contain 0 characters.
const char* async_req_read_body_str(size_t read_max) {
  return async_req_read_body(read_max).base;
}



static void http_in_headers_print(http_in_t* in) {
  size_t iter = 0;
  const char* value;
  const char* name;
  while ((name = http_in_header_next(in, &value, &iter)) != NULL) {
    printf(" %s: %s\n", name, value);
  }
  uv_buf_t buf = async_http_in_read_body(in, 4 * NODEC_MB);
  printf(" computed content-length: %zu\n", (size_t)http_in_content_length(in));
  {using_buf(&buf) {
    if (buf.base != NULL) {
      buf.base[buf.len] = 0;
      if (buf.len <= 80) {
        printf("body: %zu bytes: %s\n", (size_t)buf.len, buf.base);
      }
      else {
        buf.base[40] = 0;
        printf("body: %zu bytes: %s\n ...\n %s\n", (size_t)buf.len, buf.base, buf.base + buf.len - 40);
      }
    }
  }}
}


void http_req_print() {
  http_in_t* in = http_req();
  printf("%s %s\n headers: \n", http_method_str(http_in_method(in)), http_in_url(in));
  http_in_headers_print(in);
}


void http_in_status_print(http_in_t* in) {
  const char* info = http_in_status_info(in);
  if (info == NULL) info = "";
  printf("status: %i, %s\n", http_in_status(in), info);
  http_in_headers_print(in);
}


<|start_filename|>docs/api/search/groups_b.js<|end_filename|>
var searchData=
[
  ['tty',['TTY',['../group__nodec__tty.html',1,'']]],
  ['tcp_20connections',['TCP Connections',['../group__tcp.html',1,'']]]
];


<|start_filename|>docs/api/search/classes_0.js<|end_filename|>
var searchData=
[
  ['http_5fstatic_5fconfig_5ft',['http_static_config_t',['../group__nodec__static.html#structhttp__static__config__t',1,'']]]
];


<|start_filename|>docs/api/group__effect_structlh__optag__.js<|end_filename|>
var group__effect_structlh__optag__ =
[
    [ "effect", "group__effect.html#aaba7e2907daa4422cd4f47e91e1b171d", null ],
    [ "opidx", "group__effect.html#ad756a8e13a174bcb47041076d8611593", null ]
];

<|start_filename|>docs/api/search/all_12.js<|end_filename|>
var searchData=
[
  ['various',['Various',['../group__nodec__various.html',1,'']]]
];


<|start_filename|>docs/api/group__nodec__url.js<|end_filename|>
var group__nodec__url =
[
    [ "nodec_url_t", "group__nodec__url.html#gaa2a37a3b5cc32bd3cb0c7eebe1eb397f", null ]
];

<|start_filename|>docs/api/group__effect__exn.js<|end_filename|>
var group__effect__exn =
[
    [ "lh_exception", "group__effect__exn.html#structlh__exception", [
      [ "_is_alloced", "group__effect__exn.html#a42489ecd1468adadd6bd9c8d6fc7850e", null ],
      [ "code", "group__effect__exn.html#a2f415f8397a6e3e497814e50fbb3eb35", null ],
      [ "data", "group__effect__exn.html#a9a780c96e58c551826cb9d7055e0e939", null ],
      [ "msg", "group__effect__exn.html#a400106a97a1ab0617f39aa7a4e8618b0", null ]
    ] ],
    [ "defer", "group__effect__exn.html#ga2df9acd0863570798216432d189093c6", null ],
    [ "defer_exit", "group__effect__exn.html#gabec7bf8798d2386b437e9d069334dcc5", null ],
    [ "lh_exception_alloc", "group__effect__exn.html#gade0dd921fe6097b4c848a2e9d60efd9f", null ],
    [ "lh_exception_alloc_ex", "group__effect__exn.html#gab7825b03a6de262e4477626f8a4eba9b", null ],
    [ "lh_exception_alloc_strdup", "group__effect__exn.html#gad10ff668cc67eba82069135e11fd0a22", null ],
    [ "lh_exception_free", "group__effect__exn.html#ga0e7009f79fe092da3a57d5fc75fcf162", null ],
    [ "lh_strerror", "group__effect__exn.html#gafe25913497ef882eb7a2a5b4c501ce32", null ],
    [ "lh_throw", "group__effect__exn.html#ga1383c391fc3f472b7e80c5ab48036cd3", null ],
    [ "lh_try", "group__effect__exn.html#ga777f696b7d834b82d13aada6f35f4187", null ]
];

<|start_filename|>docs/api/search/variables_5.js<|end_filename|>
var searchData=
[
  ['effect',['effect',['../group__effect__handlers.html#a08413ea484f015bb79d395c349eb1a29',1,'lh_handlerdef']]]
];


<|start_filename|>docs/api/search/variables_4.js<|end_filename|>
var searchData=
[
  ['data',['data',['../group__effect__exn.html#a9a780c96e58c551826cb9d7055e0e939',1,'lh_exception']]]
];


<|start_filename|>docs/api/search/variables_b.js<|end_filename|>
var searchData=
[
  ['name',['name',['../group__nodec__fs.html#ac7ce6fb281bd218f7821f6c52a63a737',1,'uv_dirent_t']]]
];


<|start_filename|>docs/api/search/variables_e.js<|end_filename|>
var searchData=
[
  ['st_5fatim',['st_atim',['../group__nodec__fs.html#a90374cf48291ecde29394d1982b9eec8',1,'uv_stat_t']]],
  ['st_5fbirthtim',['st_birthtim',['../group__nodec__fs.html#a324b6bb9a3dcb70259e0195379c5f9ef',1,'uv_stat_t']]],
  ['st_5fblksize',['st_blksize',['../group__nodec__fs.html#af549bc8ae4793b83fa25b97fc5518e67',1,'uv_stat_t']]],
  ['st_5fblocks',['st_blocks',['../group__nodec__fs.html#a7cb4ce091e3b7418f368451dd60e535e',1,'uv_stat_t']]],
  ['st_5fctim',['st_ctim',['../group__nodec__fs.html#a7785fcfd6909d22b07f8461219ca2133',1,'uv_stat_t']]],
  ['st_5fdev',['st_dev',['../group__nodec__fs.html#ac1f0764990fe34d0b21f7e3af9fef946',1,'uv_stat_t']]],
  ['st_5fgid',['st_gid',['../group__nodec__fs.html#aa6d1907b84accf5662140cb0834e479d',1,'uv_stat_t']]],
  ['st_5fino',['st_ino',['../group__nodec__fs.html#a1442738c45923c0a23f7ee0c2837f584',1,'uv_stat_t']]],
  ['st_5fmode',['st_mode',['../group__nodec__fs.html#a198c4352c514e9eff06e7363c3562a8f',1,'uv_stat_t']]],
  ['st_5fmtim',['st_mtim',['../group__nodec__fs.html#a89f339b980cacb16da47d7a7c04f7e8a',1,'uv_stat_t']]],
  ['st_5fnlink',['st_nlink',['../group__nodec__fs.html#a05b9207a1a204eae78b2c7dc0d3812ab',1,'uv_stat_t']]],
  ['st_5fsize',['st_size',['../group__nodec__fs.html#a454ac6f7cef9a00d73baf2028fe3a7cb',1,'uv_stat_t']]],
  ['st_5fuid',['st_uid',['../group__nodec__fs.html#ac56d6239041749e7bc4ccc472aa800b1',1,'uv_stat_t']]]
];


<|start_filename|>docs/api/structhttp__static__config__t.js<|end_filename|>
var structhttp__static__config__t =
[
    [ "cache_control", "structhttp__static__config__t.html#afd394de5b190c3770c6d3f915316c759", null ],
    [ "content_max_size", "structhttp__static__config__t.html#a51e0cb72c8eb93fef2ca61fb599b9a06", null ],
    [ "gzip_min_size", "structhttp__static__config__t.html#ae9dca6966cebb3b3dae85e79b561ece8", null ],
    [ "gzip_vary", "structhttp__static__config__t.html#a80faaf3f2b7b7d3d9a7271af9f7e667c", null ],
    [ "implicit_exts", "structhttp__static__config__t.html#a35e38162a485696d0259c3789b690f1d", null ],
    [ "implicit_index", "structhttp__static__config__t.html#a6b2390b3780a8912fb6d571aa7a08ba6", null ],
    [ "read_buf_size", "structhttp__static__config__t.html#a31d97bb6440fc5992cb56fc84d0725fa", null ],
    [ "use_etag", "structhttp__static__config__t.html#ab77a0887c2beea113ae3a647bd5051ed", null ]
];

<|start_filename|>docs/api/search/all_7.js<|end_filename|>
var searchData=
[
  ['generic_20values',['Generic Values',['../group__effect__value.html',1,'']]],
  ['gzip_5fmin_5fsize',['gzip_min_size',['../group__nodec__static.html#ae9dca6966cebb3b3dae85e79b561ece8',1,'http_static_config_t']]],
  ['gzip_5fvary',['gzip_vary',['../group__nodec__static.html#a80faaf3f2b7b7d3d9a7271af9f7e667c',1,'http_static_config_t']]]
];


<|start_filename|>docs/api/search/all_4.js<|end_filename|>
var searchData=
[
  ['data',['data',['../group__effect__exn.html#a9a780c96e58c551826cb9d7055e0e939',1,'lh_exception']]],
  ['defer',['defer',['../group__effect__exn.html#ga2df9acd0863570798216432d189093c6',1,'libhandler.h']]],
  ['defer_5fexit',['defer_exit',['../group__effect__exn.html#gabec7bf8798d2386b437e9d069334dcc5',1,'libhandler.h']]],
  ['define_5fip4_5faddr',['define_ip4_addr',['../group__nodec__ip.html#ga66bb31de758bc23d65745d0dd68a7858',1,'nodec.h']]],
  ['define_5fip6_5faddr',['define_ip6_addr',['../group__nodec__ip.html#ga6c2bfc89980866ce4b3abc76128ee31a',1,'nodec.h']]],
  ['debugging',['Debugging',['../group__effect__debug.html',1,'']]]
];


<|start_filename|>docs/api/group__nodec__async.js<|end_filename|>
var group__nodec__async =
[
    [ "using_cancel_scope", "group__nodec__async.html#ga8c7468880da0b583535f0a2c37b2d94b", null ],
    [ "nodec_actionfun_t", "group__nodec__async.html#ga29149a2bf87759864793e9cfe65f1354", null ],
    [ "async_firstof", "group__nodec__async.html#ga973b4300618f50aff84777252c3c92bb", null ],
    [ "async_firstof_ex", "group__nodec__async.html#ga09a47279d849d24f2c113dc725e33791", null ],
    [ "async_interleave", "group__nodec__async.html#gab8d5d41174e20252ff88e131520aa887", null ],
    [ "async_interleave_dynamic", "group__nodec__async.html#ga9c9746a92cec7b3e7d03589a77b81e90", null ],
    [ "async_scoped_cancel", "group__nodec__async.html#gaa9a1df94b296d11d2e32a36727c54dca", null ],
    [ "async_scoped_is_canceled", "group__nodec__async.html#gaed5490358b60ef9e9a0acbe1587e176d", null ],
    [ "async_strand_create", "group__nodec__async.html#ga38aa41fe213fe1f727c553e2c15ae0f2", null ],
    [ "async_timeout", "group__nodec__async.html#gafc630d8de8ba70b0988f45c2f1b6132a", null ],
    [ "async_wait", "group__nodec__async.html#gab3b0f512ea7973c4c49b2f84991f8b71", null ],
    [ "async_yield", "group__nodec__async.html#gacd5465dcbbc0318d617e622c09fdc4a4", null ],
    [ "nodec_current_strand_count", "group__nodec__async.html#gaf6699450ad5b09e545266361c2ea353f", null ]
];

<|start_filename|>src/http_static.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>

#ifndef MAX_PATH
#define MAX_PATH 260
#endif

/*-----------------------------------------------------------------
Static web server
-----------------------------------------------------------------*/
static implicit_define(http_static_config)

static const http_static_config_t* http_static_config() {
  return (const http_static_config_t*)lh_ptr_value(implicit_get(http_static_config));
}


#define ETAG_LEN  30

static lh_value http_try_send_file(uv_file file, const char* path, lh_value statv) {
  // check errors
  const uv_stat_t* stat = (const uv_stat_t*)lh_ptr_value(statv);
  const http_static_config_t* config = http_static_config();
  if (stat->st_size >= SIZE_MAX) {
    nodec_check(UV_E2BIG);
  }
  size_t size = (size_t)stat->st_size;
  if (config->content_max_size > 0 && size > config->content_max_size) {
    nodec_check(UV_E2BIG);
  }

  // Add Cache-Control
  if (config->cache_control != NULL) {
    http_resp_add_header("Cache-Control", config->cache_control);
  }

  // Add Last-Modified and ETag header
  time_t mtime = (time_t)stat->st_mtim.tv_sec;
  if (config->use_last_modified) {
    http_resp_add_header("Last-Modified", nodec_inet_date(mtime));
  }
  if (config->use_etag) {
    char etag[ETAG_LEN + 1];
    // just modified time and size but no inode to cache properly on clusters
    snprintf(etag, ETAG_LEN + 1, "W/%08lx.%08lxT-%08llxS", stat->st_mtim.tv_sec, stat->st_mtim.tv_nsec, (unsigned long long)stat->st_size);
    http_resp_add_header("ETag", etag);

    // Check If-None-Match headers
    const char* if_none_match = http_req_header("If-None-Match");
    if (if_none_match != NULL) {
      const char* iter = NULL;
      size_t      field_len = 0;
      const char* field;
      while ((field = http_header_next_field(if_none_match, &field_len, &iter)) != NULL) {
        if (field_len == ETAG_LEN && strncmp(field, etag, ETAG_LEN) == 0) {
          // matching etag, return a 304 Not-Modified
          printf("match etag! return 304\n");
          http_resp_send_status(HTTP_STATUS_NOT_MODIFIED);
          return lh_value_int(size);
        }
      }
    }
  }

  // Check If-Modified-Since header
  time_t if_modified_since = 0;
  if (nodec_parse_inet_date(http_req_header("If-Modified-Since"), &if_modified_since)) {
    if (if_modified_since >= mtime) {
      // it has not been modified since, return a 304
      printf("match if-modified-since! return 304\n");
      http_resp_send_status(HTTP_STATUS_NOT_MODIFIED);
      return lh_value_int(size);
    }
  }

  // Send content
  bool compressible = false;
  const char* content_type = nodec_mime_info_from_fname(path,&compressible,NULL);
  nodec_stream_t* stream;

  // If gzip is enabled, and this mime type is compressible, send a VARY header for better caching
  if (config->gzip_min_size < SIZE_MAX && compressible && config->gzip_vary) {
    http_resp_add_header("Vary", "Accept-Encoding");
  }
  
  // Check if we should compress this content
  bool gzip = false;
  if (compressible && size > config->gzip_min_size) { 
    const char* accept_enc = http_req_header("Accept-Encoding");
    gzip = (accept_enc != NULL && strstr(accept_enc, "gzip") != NULL);
  }
  if (gzip) {
    printf("use gzip response\n");
    http_resp_add_header("Content-Encoding", "gzip");
  }

  if (http_req_method() == HTTP_HEAD) {
    // don't send a body on HEAD requests
    http_resp_send_ok();
  }
  else {
    // Otherwise send the body 
    if (gzip) {
      // TODO: check if <filename>.gz exists and use that if possible instead of zipping at runtime
      // send zipped content in chunks (as we don't know the final length)
      stream = http_resp_send_status_body(HTTP_STATUS_OK, NODEC_CHUNKED, content_type);
      stream = as_stream(nodec_zstream_alloc(stream,true)); // gzip'd stream
    }
    else {
      // send as one body
      stream = http_resp_send_status_body(HTTP_STATUS_OK, size, content_type);
    }
    {using_stream(stream) {
      // read the file in parts of `write_buf_size` length and send as we read
      // TODO: overlap the reading of the next part with the sending of the current part?
      uv_buf_t buf = nodec_buf_alloc(config->read_buf_size);
      size_t nread = 0;
      {using_buf(&buf) {
        while ((nread = async_fs_read_into(file, buf, -1)) > 0) {
          async_write_buf(stream, nodec_buf(buf.base, nread));
        };
      }}
    }}
  }
  return lh_value_int(size);
}

const char* http_static_implicit_exts[] = { "html", "htm", NULL };

static bool http_try_send(const http_static_config_t* config, const char* root, const char* path, const char* ext) {
  // check if it exists
  char fname[MAX_PATH];
  const char* pathsep = (root == NULL ? "" : "/");
  const char* extsep = (ext == NULL ? "" : ".");
  const char* base = (config->implicit_index != NULL && (nodec_ends_with(path, "/") || (path[0] == 0)) ? config->implicit_index : "");
  snprintf(fname, MAX_PATH, "%s%s%s%s%s%s", (root == NULL ? "" : root), pathsep, path, base, extsep, (ext == NULL ? "" : ext));
  if (strlen(fname) == 0) return false;

  uv_stat_t stat;
  uv_errno_t err = asyncx_fs_stat(fname, &stat);
  if (err == UV_ENOENT && ext == NULL && config->implicit_exts != NULL) {
    // not found, try implicit extensions
    bool res = false;
    for (size_t i = 0; !res && config->implicit_exts[i] != NULL; i++) {
      res = http_try_send(config, root, path, config->implicit_exts[i]);
    }
    return res;
  }
  else if (err != 0) {
    // error
    return false;
  }
  else if ((stat.st_mode & S_IFREG) == S_IFREG) {
    // a regular file, send it
    using_async_fs_open(fname, O_RDONLY, 0, &http_try_send_file, lh_value_any_ptr(&stat));
    return true;
  }
  else if ((stat.st_mode & S_IFDIR) == S_IFDIR) {
    // a directory, redirect to the directory by appending a final /
    char dirpath[MAX_PATH];
    snprintf(dirpath, MAX_PATH, (path[0] == 0 ? "/" : "/%s/"), path);
    http_resp_add_header("Location", dirpath);
    http_resp_send_status(HTTP_STATUS_MOVED_PERMANENTLY);
    return true;
  }
  else {
    // some other device or symbolic link
    // TODO: follow symbolic links and redirect?
    return false;
  }
}

void http_serve_static(const char* root, const http_static_config_t* config) {
  http_method_t method = http_req_method();
  if (method == HTTP_GET || method == HTTP_HEAD) {
    static http_static_config_t _default_config = http_static_default_config();
    if (config == NULL) config = &_default_config;
    bool ok = false;
    {using_implicit(lh_value_any_ptr(config), http_static_config) {
      // get request path
      const char* path = http_req_path();
      ok = http_try_send(config, root, path, NULL);
    }}
    if (!ok) http_resp_send_status(HTTP_STATUS_NOT_FOUND);
  }
}

<|start_filename|>examples/ex-https-connect.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include <nodec.h>
#include "examples.h"


/*-----------------------------------------------------------------
  Example of an HTTPS connection
-----------------------------------------------------------------*/

static lh_value with_connection(http_in_t* in, http_out_t* out, lh_value arg) {
  http_out_add_header(out, "Connection", "close");
  http_out_add_header(out, "Accept-Encoding", "gzip");
  http_out_send_request(out, HTTP_GET, "/");
  async_http_in_read_headers(in); // wait for response
  printf("secure received, status: %i, content length: %llu\n", http_in_status(in), (unsigned long long)http_in_content_length(in));
  http_in_status_print(in);
  return lh_value_null;
}

void ex_https_connect() {
  nodec_ssl_config_t* ssl_config = nodec_ssl_config_client();
  {using_ssl_config(ssl_config) {
    async_ssl_config_add_system_certs(ssl_config);
    async_https_connect(ssl_config, "https://www.google.com", with_connection, lh_value_null);
  }}
}


<|start_filename|>docs/api/search/classes_4.js<|end_filename|>
var searchData=
[
  ['yieldargs',['yieldargs',['../group__effect__handlers.html#structyieldargs',1,'']]]
];


<|start_filename|>docs/api/search/enums_2.js<|end_filename|>
var searchData=
[
  ['nodec_5flog_5flevel_5ft',['nodec_log_level_t',['../group__log.html#ga397b9a049d0edb35a8c82b5e31908b2e',1,'nodec.h']]],
  ['nodec_5fopen_5fflags_5ft',['nodec_open_flags_t',['../group__nodec__fs.html#ga6106956895e925e621b1ddfb478d3091',1,'nodec-extra-doc.h']]]
];


<|start_filename|>src/tty.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-internal.h"
#include "nodec-primitive.h"
#include <assert.h>

static uv_stream_t* stream_of_tty(uv_tty_t* tty) {
  return (uv_stream_t*)tty;  // super class
}

typedef struct _tty_t {
  nodec_bstream_t* _stdin;
  uv_tty_t* _stdout;
  uv_tty_t* _stderr;
  int       mode;
} tty_t;

static tty_t* nodec_tty_alloc() {
  return nodec_zero_alloc(tty_t);
}

lh_value _nodec_tty_allocv() {
  return lh_value_ptr(nodec_tty_alloc());
}


static void nodec_tty_free(tty_t* tty) {
  if (tty->_stdin != NULL) nodec_stream_free(as_stream(tty->_stdin));
  if (tty->_stdout != NULL) nodec_uv_stream_free(stream_of_tty(tty->_stdout));
  if (tty->_stderr != NULL) nodec_uv_stream_free(stream_of_tty(tty->_stderr));
  nodec_free(tty);
}


void _nodec_tty_freev(lh_value ttyv) {
  async_tty_shutdown();
  uv_tty_reset_mode();
  nodec_tty_free((tty_t*)lh_ptr_value(ttyv));
}

implicit_define(tty)

static tty_t* tty_get() {
  return (tty_t*)lh_ptr_value(implicit_get(tty));
}

char* async_tty_readline() {
  tty_t* tty = tty_get();
  if (tty->_stdin == NULL) {
    uv_tty_t* hstdin = nodec_zero_alloc(uv_tty_t);
    nodec_check(uv_tty_init(async_loop(), hstdin, 0, 1));
    tty->_stdin = nodec_bstream_alloc_read_ex(stream_of_tty(hstdin), 64, 64);
  }
  return async_read_line(tty->_stdin);
}

void async_tty_write(const char* s) {
  tty_t* tty = tty_get();
  if (tty->_stdout == NULL) {
    tty->_stdout = nodec_zero_alloc(uv_tty_t);
    nodec_check(uv_tty_init(async_loop(), tty->_stdout, 1, 0));
  }
  async_uv_write_buf(stream_of_tty(tty->_stdout), nodec_buf_str(s));
}

void async_tty_vprintf(const char* fmt, va_list args) {
  char buf[512];
  vsnprintf(buf, 512, fmt, args); buf[511] = 0;
  async_tty_write(buf);
}

void async_tty_printf(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  async_tty_vprintf(fmt, args);
  va_end(args);
}

void async_tty_write_err(const char* s) {
  tty_t* tty = tty_get();
  if (tty->_stderr == NULL) {
    tty->_stderr = nodec_zero_alloc(uv_tty_t);
    nodec_check(uv_tty_init(async_loop(), tty->_stderr, 2, 0));
  }
  async_uv_write_buf(stream_of_tty(tty->_stderr), nodec_buf_str(s));
}

void async_tty_vprintf_err(const char* fmt, va_list args) {
  char buf[512];
  vsnprintf(buf, 512, fmt, args); buf[511] = 0;
  async_tty_write_err(buf);
}

void async_tty_printf_err(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  async_tty_vprintf_err(fmt, args);
  va_end(args);
}

// Flush any outstanding writes
void async_tty_shutdown() {
  tty_t* tty = tty_get();
  if (tty->_stdout == NULL) async_uv_stream_shutdown(stream_of_tty(tty->_stdout));
  if (tty->_stderr == NULL) async_uv_stream_shutdown(stream_of_tty(tty->_stderr));
}

static void wait_for_enter() {
  async_tty_write("press enter to quit...");
  const char* s = async_tty_readline();
  nodec_free(s);
  async_tty_write("canceling...");
}

void async_stop_on_enter(nodec_actionfun_t* action) {
  async_firstof(action, &wait_for_enter);
}

<|start_filename|>ide/msvc/make-combined-lib.cmd<|end_filename|>
SETLOCAL ENABLEEXTENSIONS
@ECHO OFF
SET PLATFORM=%1
SET CONFIG=%2

ECHO Platform: %PLATFORM%
ECHO Configuration: %CONFIG%

SET XPLATFORM=""
SET XCONFIG=""

IF "%PLATFORM%"=="x64"   SET ZPLATFORM=x64
IF "%PLATFORM%"=="Win32" SET ZPLATFORM=x86
IF "%CONFIG%"=="Debug"   SET ZCONFIG=Debug
IF "%CONFIG%"=="Release" SET ZCONFIG=ReleaseWithoutAsm
IF "XCONFIG"=="" GOTO ERROR
IF "XPLATFORM"=="" GOTO ERROR

SET ROOT=.\..\..\deps

SET LIBUV=%ROOT%\libuv\%CONFIG%\lib\libuv.lib
IF NOT EXIST %LIBUV% (
  ECHO %LIBUV% does not exist
  GOTO ERROR
)

SET MBEDTLS=%ROOT%\mbedtls\visualc\VS2010\%PLATFORM%\%CONFIG%\mbedtls-vs2017.lib
IF NOT EXIST %MBEDTLS% (
   ECHO %MBEDTLS% does not exist
   GOTO ERROR
)

SET ZLIB=%ROOT%\zlib\contrib\vstudio\vc14\%ZPLATFORM%\ZlibStat%ZCONFIG%\zlibstat.lib
IF NOT EXIST %ZLIB% (
  ECHO %ZLIB% does not exist
  GOTO ERROR
)

SET LIBHANDLER=%ROOT%\libhandler\out\msvc-%PLATFORM%\libhandler\%CONFIG%\libhandler.lib
IF NOT EXIST %LIBHANDLER% (
  ECHO %LIBHANDLER% does not exist
  GOTO ERROR
)

SET NODEC=.\..\..\out\msvc-%PLATFORM%\nodec\%CONFIG%\nodec.lib
IF NOT EXIST %NODEC% (
  ECHO %NODEC% does not exist
  GOTO ERROR
)

SET NODECX_LIBDIR=.\..\..\out\msvc-%PLATFORM%\nodecx\%CONFIG%\lib
IF NOT EXIST %NODECX_LIBDIR% (
  ECHO Creating %NODECX_LIBDIR%
  MKDIR %NODECX_LIBDIR%
)

REM Create combined library as nodecx
SET LIBS=%LIBUV% %ZLIB% %LIBHANDLER% %MBEDTLS% %NODEC%
SET NODECX_LIB=%NODECX_LIBDIR%\nodecx.lib
@ECHO Creating %NODECX_LIB% from %LIBS%
lib /OUT:%NODECX_LIB% %LIBS%
IF NOT ERRORLEVEL 0 GOTO ERROR

REM Copy pdb files for debugging
IF "%CONFIG%"=="Debug" (  
  FOR %%A in (%LIBS%) DO (
    IF "%%~nA"=="libuv" (
      @ECHO copy /Y %%~dpA..\libuv.pdb %%~dpAlibuv.pdb
      COPY /Y %%~dpA..\libuv.pdb %%~dpAlibuv.pdb
    )
    @ECHO copy /Y %%~dpnA.pdb %NODECX_LIBDIR%
    @COPY /Y %%~dpnA.pdb %NODECX_LIBDIR%
  )
)


SET NODECX_INCDIR=.\..\..\out\msvc-%PLATFORM%\nodecx\%CONFIG%\include
IF NOT EXIST %NODECX_INCDIR% (
  ECHO Creating %NODECX_INCDIR%
  MKDIR %NODECX_INCDIR%
  ECHO Creating %NODECX_INCDIR%\libuv\include\uv
  MKDIR %NODECX_INCDIR%\libuv\include\uv
  ECHO Creating %NODECX_INCDIR%\http-parser
  MKDIR %NODECX_INCDIR%\http-parser
  ECHO Creating %NODECX_INCDIR%\libhandler\inc
  MKDIR %NODECX_INCDIR%\libhandler\inc
  ECHO Creating %NODECX_INCDIR%\mbedtls
  MKDIR %NODECX_INCDIR%\mbedtls
)

@ECHO Collecting include files to %NODECX_INCDIR%
COPY /Y %ROOT%\libuv\include\*.h  %NODECX_INCDIR%\libuv\include
COPY /Y %ROOT%\libuv\include\uv\*.h  %NODECX_INCDIR%\libuv\include\uv
REM COPY %ROOT%\mbedtls\include\mbedtls\*.h %INCDIR%\mbedtls
COPY /Y %ROOT%\http-parser\http_parser.h %NODECX_INCDIR%\http-parser
COPY /Y %ROOT%\libhandler\inc\libhandler.h %NODECX_INCDIR%\libhandler\inc
COPY /Y .\..\..\inc\nodec.h %NODECX_INCDIR%
COPY /Y .\..\..\inc\nodec-primitive.h %NODECX_INCDIR%


ENDLOCAL
GOTO END
:ERROR
ENDLOCAL
SET ERRORLEVEL=1
:END


<|start_filename|>docs/api/search/all_5.js<|end_filename|>
var searchData=
[
  ['effect',['effect',['../group__effect__handlers.html#a08413ea484f015bb79d395c349eb1a29',1,'lh_handlerdef::effect()'],['../group__effect.html',1,'(Global Namespace)']]],
  ['effect_20definition_20helpers',['Effect Definition helpers',['../group__effect__def.html',1,'']]],
  ['exceptions_20and_20finally_20clauses',['Exceptions and Finally clauses',['../group__effect__exn.html',1,'']]]
];


<|start_filename|>src/fs.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>
#include <fcntl.h>



/*-----------------------------------------------------------------
  handling file system requests
-----------------------------------------------------------------*/

// Await a file system request
uv_errno_t asyncx_await_fs(uv_fs_t* req) {
  return asyncx_await((uv_req_t*)req,0,NULL);
}

static void async_await_fs(uv_fs_t* req) {
  async_await_once((uv_req_t*)req);
}

// The entry point for filesystem callbacks
void async_fs_resume(uv_fs_t* uvreq) {
  if (uvreq == NULL) return;
  uv_errno_t err = (uvreq->result >= 0 ? 0 : (uv_errno_t)uvreq->result);
  async_req_resume((uv_req_t*)uvreq, err);
}


#define using_fs_req(req,loop)  uv_loop_t* loop = async_loop(); \
                               using_req(uv_fs_t,req)



static void async_await_file(uv_fs_t* req, uv_file owner) {
  async_await_owned((uv_req_t*)req, (void*)((intptr_t)owner));
}




/*-----------------------------------------------------------------
  Stat
-----------------------------------------------------------------*/


uv_errno_t asyncx_fs_stat(const char* path, uv_stat_t* stat) {
  nodec_zero(uv_stat_t, stat);
  uv_errno_t err = 0;
  {using_fs_req(req, loop) {
    nodec_check(uv_fs_stat(loop, req, path, &async_fs_resume));
    err = asyncx_await_fs(req);
    if (err == 0) *stat = req->statbuf;
  }}
  return err;
}

uv_stat_t async_fs_stat(const char* path) {
  uv_stat_t stat;
  nodec_check_msg(asyncx_fs_stat(path, &stat), path);
  return stat;
}


uv_stat_t async_fs_fstat(uv_file file) {
  uv_stat_t stat; 
  nodec_zero(uv_stat_t, &stat);
  {using_fs_req(req, loop) {
    nodec_check(uv_fs_fstat(loop, req, file, &async_fs_resume));
    async_await_fs(req);
    stat = req->statbuf;
  }}  
  return stat;
}


/*-----------------------------------------------------------------
  Open, close, reading
-----------------------------------------------------------------*/


uv_errno_t asyncx_fs_open(const char* path, nodec_open_flags_t flags, int mode, uv_file* file) {
  *file = -1;
  uv_errno_t err = 0;
  {using_fs_req(req, loop) {
    nodec_check_msg(uv_fs_open(loop, req, path, flags, mode, &async_fs_resume), path);
    err = asyncx_await_fs(req);
    if (err == 0) *file = (uv_file)(req->result);
  }}
  return err;
}

uv_file async_fs_open(const char* path, nodec_open_flags_t flags, int mode) {
  uv_file file = -1;
  nodec_check_msg(asyncx_fs_open(path, flags, mode, &file), path);
  return file;
}

static void nodec_fs_close(uv_file file) {
  nodec_owner_release((void*)((intptr_t)file));
}

static void nodec_fs_closev(lh_value filev) {
  nodec_fs_close((uv_file)lh_int_value(filev));
}

void async_fs_close(uv_file file) {
  if (file < 0) return;
  {defer(nodec_fs_closev, lh_value_int(file)) {
    {using_fs_req(req, loop) {
      nodec_check(uv_fs_close(loop, req, file, &async_fs_resume));
      async_await_file(req, file);
    }}
  }}
}

static void async_fs_closev(lh_value vfile) {
  uv_file file = lh_int_value(vfile);
  if (file >= 0) async_fs_close(file);
}

static size_t _async_fs_read_into(uv_file file, uv_buf_t buf, int64_t file_offset) {
  size_t read = 0;
  {using_fs_req(req, loop) {
    nodec_check(uv_fs_read(loop, req, file, &buf, 1, file_offset, &async_fs_resume));
    async_await_file(req, file);
    read = (size_t)req->result;
  }}
  return read;
}


typedef struct __fopen_args {
  lh_value arg;
  uv_file  file;
  const char* path;
  nodec_file_fun* action;
} _fopen_args;

static lh_value _fopen_action(lh_value argsv) {
  _fopen_args* args = (_fopen_args*)lh_ptr_value(argsv);
  return args->action(args->file, args->path, args->arg);
}

uv_errno_t using_asyncx_fs_open(const char* path, nodec_open_flags_t flags, int mode, nodec_file_fun* action, lh_value arg, lh_value* result) {
  if (result != NULL) *result = lh_value_null;
  _fopen_args args;
  args.arg = arg;
  args.path = path;
  args.action = action;
  uv_errno_t err = asyncx_fs_open(path, flags, mode, &args.file);
  if (err == 0) {
    lh_value res = lh_finally(
      &_fopen_action, lh_value_any_ptr(&args),
      &async_fs_closev, lh_value_int(args.file)
    );
    if (result != NULL) *result = res;
  }
  return err;
}

lh_value using_async_fs_open(const char* path, nodec_open_flags_t flags, int mode, nodec_file_fun* action, lh_value arg ) {
  _fopen_args args;
  args.arg = arg;
  args.path = path;
  args.action = action;
  args.file = async_fs_open(path, flags, 0);
  return lh_finally(
    &_fopen_action, lh_value_any_ptr(&args),
    &async_fs_closev, lh_value_int(args.file)
  );
}


size_t async_fs_read_into(uv_file file, uv_buf_t buf , int64_t file_offset) {
  size_t total = 0;
  while (total < buf.len) {
    uv_buf_t view = nodec_buf(buf.base + total, buf.len - total);
    size_t nread = _async_fs_read_into(file, view, -1);
    if (nread == 0) break;
    total += nread;
    if (total > buf.len) total = buf.len; // paranoia
  }
  buf.base[total] = 0;
  return total;
}

uv_buf_t async_fs_read_buf(uv_file file, size_t max, int64_t file_offset) {
  uv_buf_t buf = nodec_buf_alloc(max);
  size_t nread = 0;
  {using_buf_on_abort_free(&buf) {
    nread = async_fs_read_into(file, buf, file_offset);
  }}  
  if (nread == 0) {
    nodec_buf_free(buf);
    return nodec_buf_null();
  }
  else {
    return nodec_buf_fit(buf, nread);
  }
}

uv_buf_t async_fs_read_buf_all(uv_file file, size_t max ) {
  uv_stat_t stat = async_fs_fstat(file);
  if (stat.st_size >= SIZE_MAX) nodec_check(UV_E2BIG);
  size_t size = (size_t)stat.st_size;
  return async_fs_read_buf(file, (max > 0 && size > max ? max : size), -1);
}

static lh_value async_fs_read_allv(uv_file file, const char* path, lh_value bufv) {
  uv_buf_t* buf = (uv_buf_t*)(lh_ptr_value(bufv));
  *buf = async_fs_read_buf_all(file, 0);
  return lh_value_null;
}

uv_buf_t async_fs_read_buf_from(const char* path) {
  uv_buf_t buf = nodec_buf_null();
  using_async_fs_open(path, O_RDONLY, 0, &async_fs_read_allv, lh_value_any_ptr(&buf));
  return buf;
}


char* async_fs_read_from(const char* path) {
  uv_buf_t buf = async_fs_read_buf_from(path);
  return buf.base;
}

/*-----------------------------------------------------------------
  Scan dir
-----------------------------------------------------------------*/

void nodec_scandir_free(nodec_scandir_t* scanreq) {
  nodec_req_free((uv_req_t*)scanreq);
}
void nodec_scandir_freev(lh_value scanreqv) {
  nodec_scandir_free((nodec_scandir_t*)lh_ptr_value(scanreqv));
}

nodec_scandir_t* async_fs_scandir(const char* path ) {
  uv_fs_t* fsreq = nodec_zero_alloc(uv_fs_t);
  {on_abort(nodec_freev, lh_value_ptr(fsreq)) {
    nodec_check(uv_fs_scandir(async_loop(), fsreq, path, 0, &async_fs_resume));
    async_await_fs(fsreq);
  }}
  return fsreq;
}

// Returns false if there are no more directories to scan;
bool async_fs_scandir_next(nodec_scandir_t* scanreq, uv_dirent_t* dirent) {
  uverr_t err = uv_fs_scandir_next(scanreq, dirent);
  if (err == UV_EOF) return false;
  nodec_check(err);
  return true;
}

/*-----------------------------------------------------------------
  Efficient stack data structure that allocates in blocks.
  Can be pushed to, popped from, and iterated over the entire 
  contents.

  Declare "nodec_stack_dirent" type for `uv_diren_t` elements:
  ```
  NODEC_STACK_DECLARE(dirent,uv_dirent_t,64)
  ```
  and define its implemenation as:
  ```
  NODEC_STACK_DEFINE(dirent,uv_dirent_t,64)
  ```
  Use as:
  ```
  NODEC_STACK(dirent) stack;
  NODEC_STACK_MEMBER(dirent,init)(&stack,NULL);
  
  uv_dirent_t dirent;
  NODEC_STACK_MEMBER(dirent,push)(&stack, dirent);

  size_t index;
  NODEC_STACK_ITER(dirent) iter;
  NODEC_STACK_MEMBER(dirent,iterate)(&stack, &iter);
  while( NODEC_STACK_MEMBER(dirent,next)(&iter, &dirent, &index ) ) {
    ...
  }
  ```
-----------------------------------------------------------------*/

#define NODEC_STACK_BLOCK(T,sz)    nodec_stack_block_##T##_##sz
#define NODEC_STACK_ITER(T)        nodec_stack_iter_##T
#define NODEC_STACK(T)             nodec_stack_##T
#define NODEC_STACK_MEMBER(T,name) nodec_stack_##T##_##name

#define NODEC_STACK_DECLARE(T,TP,sz) \
  typedef struct _nodec_stack_block_##T { \
    struct _nodec_stack_block_##T* next; \
    struct _nodec_stack_block_##T* prev; \
    TP elems[sz]; \
  } NODEC_STACK_BLOCK(T,sz); \
  typedef struct _nodec_stack_##T { \
    NODEC_STACK_BLOCK(T,sz) first; \
    NODEC_STACK_BLOCK(T,sz)* last; \
    size_t count; \
    size_t last_count; \
    void   (*free_fun)(TP elem); \
  } NODEC_STACK(T); \
  typedef struct _nodec_stack_iter_##T { \
    NODEC_STACK_BLOCK(T,sz)* current; \
    size_t block_index; \
    size_t todo; \
    size_t index; \
  } NODEC_STACK_ITER(T); \
  size_t NODEC_STACK_MEMBER(T,count)(NODEC_STACK(T)* stack); \
  bool   NODEC_STACK_MEMBER(T,empty)(NODEC_STACK(T)* stack); \
  void   NODEC_STACK_MEMBER(T,push)(NODEC_STACK(T)* stack, TP elem); \
  bool   NODEC_STACK_MEMBER(T,pop)(NODEC_STACK(T)* stack, TP* elem); \
  bool   NODEC_STACK_MEMBER(T,at)(NODEC_STACK(T)* stack, size_t index, TP* elem); \
  void   NODEC_STACK_MEMBER(T,free)(NODEC_STACK(T)* stack); \
  void   NODEC_STACK_MEMBER(T,init)(NODEC_STACK(T)* stack, void (*free_fun)(TP elem) ); \
  void   NODEC_STACK_MEMBER(T,iterate)(NODEC_STACK(T)* stack, NODEC_STACK_ITER(T)* iter); \
  bool   NODEC_STACK_MEMBER(T,next)(NODEC_STACK_ITER(T)* iter, TP* elem, size_t* index ); \

#define NODEC_STACK_DEFINE(T,TP,sz) \
  size_t NODEC_STACK_MEMBER(T,count)(NODEC_STACK(T)* stack) { \
    return stack->count; \
  } \
  bool NODEC_STACK_MEMBER(T,empty)(NODEC_STACK(T)* stack) { \
    return (stack->count == 0); \
  } \
  void NODEC_STACK_MEMBER(T,push)(NODEC_STACK(T)* stack, TP elem) { \
    if (stack->last==NULL) stack->last = &stack->first; \
    if (stack->last_count >= sz) { \
      /* expand */ \
      NODEC_STACK_BLOCK(T,sz)* block = nodec_zero_alloc(NODEC_STACK_BLOCK(T,sz)); \
      stack->last->next = block; \
      block->prev = stack->last; \
      stack->last = block; \
      stack->last_count = 0; \
      \
    } \
    stack->last->elems[stack->last_count] = elem; \
    stack->count++; \
    stack->last_count++; \
  } \
  bool NODEC_STACK_MEMBER(T,pop)(NODEC_STACK(T)* stack, TP* elem) { \
    if (stack->last==NULL) stack->last = &stack->first; \
    if (stack->count == 0) return false; \
    assert(stack->last_count > 0 && stack->count >= stack->last_count); \
    if (stack->last_count == 0) return false; \
    stack->count--; \
    stack->last_count--; \
    if (elem != NULL) *elem = stack->last->elems[stack->last_count]; \
    if (stack->last_count == 0 && stack->last != &stack->first) { \
      /* contract */ \
      NODEC_STACK_BLOCK(T, sz)* block = stack->last; \
      stack->last = block->prev; \
      stack->last->next = NULL; \
      assert(stack->count >= sz); \
      stack->last_count = sz; \
      nodec_free(block); \
    } \
    return true; \
  } \
  bool NODEC_STACK_MEMBER(T,at)(NODEC_STACK(T)* stack, size_t index, TP* elem) { \
    if (stack->last==NULL) stack->last = &stack->first; \
    if (index >= stack->count) return false; \
    if (elem==NULL) return true; \
    NODEC_STACK_BLOCK(T,sz)* current = &stack->first; \
    while(index > sz && current != NULL) { \
      current = current->next; \
    } \
    assert(current != NULL); \
    if (current == NULL) return false; \
    assert((current != stack->last && index < sz) || (current==stack->last && index < stack->last_count)); \
    *elem = current->elems[index]; \
    return true; \
  } \
  void NODEC_STACK_MEMBER(T,free)(NODEC_STACK(T)* stack) { \
    for( NODEC_STACK_BLOCK(T,sz)* current = &stack->first; current != NULL;  ) {\
      if (stack->free_fun != NULL) { \
        size_t max = (current==stack->last ? stack->last_count : sz); \
        for(size_t i = 0; i < max; i++) { stack->free_fun(current->elems[i]); } \
      } \
      NODEC_STACK_BLOCK(T,sz)* next = current->next; \
      if (current != &stack->first) nodec_free(current); \
      current = next; \
    } \
    NODEC_STACK_MEMBER(T,init)(stack, stack->free_fun); \
  } \
  void NODEC_STACK_MEMBER(T,init)(NODEC_STACK(T)* stack, void (*free_fun)(TP elem)) { \
    stack->last = &stack->first; \
    stack->count = 0; \
    stack->last_count = 0; \
    stack->free_fun = free_fun; \
  } \
  void NODEC_STACK_MEMBER(T,iterate)(NODEC_STACK(T)* stack, NODEC_STACK_ITER(T)* iter) { \
    iter->current = &stack->first; \
    iter->todo = stack->count; \
    iter->index = 0; \
    iter->block_index = 0; \
  } \
  bool NODEC_STACK_MEMBER(T,next)(NODEC_STACK_ITER(T)* iter, TP* elem, size_t* index ) { \
    if (index != NULL) *index = iter->index; \
    if (iter->todo == 0) return false; \
    if (iter->block_index >= sz) { \
      iter->current = iter->current->next; \
      assert(iter->current != NULL); \
      if (iter->current == NULL) { iter->todo = 0; return false; }; \
    } \
    if (elem!=NULL) *elem = iter->current->elems[iter->block_index]; \
    iter->block_index++; \
    iter->index++; \
    iter->todo--; \
    return true; \
  } \

NODEC_STACK_DECLARE(dirent, uv_dirent_t, 64)
NODEC_STACK_DEFINE(dirent, uv_dirent_t, 64)



<|start_filename|>docs/api/group__log.js<|end_filename|>
var group__log =
[
    [ "nodec_log_level_t", "group__log.html#ga397b9a049d0edb35a8c82b5e31908b2e", null ]
];

<|start_filename|>docs/api/search/all_14.js<|end_filename|>
var searchData=
[
  ['x_5fok',['X_OK',['../group__nodec__fs.html#ga5e995ef7690698b62bf016b14fbc45ca',1,'nodec-extra-doc.h']]]
];


<|start_filename|>examples/ex-http-server-static.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include <nodec.h>
#include "examples.h"

/*-----------------------------------------------------------------
  Example of a static http server
-----------------------------------------------------------------*/
static void http_serve() {
#ifndef NDEBUG
  fprintf(stderr,"\nrequest url: %s, content length: %llu\n", http_req_url(), (unsigned long long)http_req_content_length());
  http_req_print();
#endif
  http_serve_static( "./examples/web", NULL );
}

static void http_server() {
  const char* host = "127.0.0.1:8080";
  fprintf(stderr,"serving at: %s\n", host);
  async_http_server_at( host, NULL, &http_serve);
}

void ex_http_server_static() {
  async_stop_on_enter(&http_server);
}


<|start_filename|>src/timer.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "cenv.h"
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h>
#include <time.h>

static uv_handle_t* handle_of_timer(uv_timer_t* timer) {
  return (uv_handle_t*)timer;
}

uv_timer_t* nodec_timer_alloc() {
  uv_timer_t* timer = nodec_zero_alloc(uv_timer_t);
  nodec_check(uv_timer_init(async_loop(), timer));
  return timer;
}

static void _timer_close_cb(uv_handle_t* timer) {
  nodec_free(timer);
}

void nodec_timer_close(uv_timer_t* timer) {
  if (timer!=NULL) {
    uv_close(handle_of_timer(timer), &_timer_close_cb);
  }
}

void nodec_timer_free(uv_timer_t* timer, bool owner_release) {
  nodec_timer_close(timer);
  if (owner_release) nodec_owner_release(timer);  
}

void nodec_timer_freev(lh_value timerv) {
  nodec_timer_free((uv_timer_t*)lh_ptr_value(timerv),true);
}

static void _async_timer_resume(uv_timer_t* timer) {
  uv_req_t* req = (uv_req_t*)timer->data;
  async_req_resume(req, 0);
}

void async_wait(uint64_t timeout) {
  uv_timer_t* timer = nodec_timer_alloc();
  {defer(nodec_timer_freev, lh_value_ptr(timer)) {
    {using_req(uv_req_t, req) {  // use a dummy request so we can await the timer handle
      timer->data = req;
      nodec_check(uv_timer_start(timer, &_async_timer_resume, timeout, 0));
      async_await_owned(req, timer);
    }}
  }}
}

void async_yield() {
  async_wait(0);
}


/* ----------------------------------------------------------------------------
  Internal timeout routine to delay certain function calls.
  Used for cancelation resumptions
-----------------------------------------------------------------------------*/

typedef struct _timeout_args {
  uv_timeoutfun* cb;
  void*          arg;
} timeout_args;

static void _timeout_cb(uv_timer_t* timer) {
  if (timer==NULL) return;
  timeout_args args = *((timeout_args*)timer->data);
  nodec_free(timer->data);
  nodec_timer_free(timer, false);
  args.cb(args.arg);
}

uv_errno_t _uv_set_timeout(uv_loop_t* loop, uv_timeoutfun* cb, void* arg, uint64_t timeout) {
  uv_timer_t* timer = nodecx_alloc(uv_timer_t);
  if (timer == NULL) return UV_ENOMEM;
  timeout_args* args = nodecx_alloc(timeout_args);
  if (args==NULL) { nodec_free(timer); return UV_ENOMEM; }
  nodec_zero(uv_timer_t, timer);
  uv_timer_init(loop, timer);
  args->cb = cb;
  args->arg = arg;
  timer->data = args;
  return uv_timer_start(timer, &_timeout_cb, timeout, 0);
}



/* ----------------------------------------------------------------------------
  Get current Date in fixed size RFC 1123 format; 
  updated at most once a second for efficiency
  Sun, 06 Nov 1994 08:49:37 GMT
-----------------------------------------------------------------------------*/

#ifdef HAS_GMTIME_S
int nodec_gmtime(struct tm* _tm, const time_t* const t) {
  return gmtime_s(_tm, t);
}
#else
int nodec_gmtime(struct tm* _tm, const time_t* const t) {
  *_tm = *(gmtime(t));
  return 0;
}
#endif

#ifdef HAS_LOCALTIME_S
int nodec_localtime(struct tm* _tm, const time_t* const t) {
  return localtime_s(_tm, t);
}
#else
int nodec_localtime(struct tm* _tm, const time_t* const t) {
  *_tm = *(localtime(t));
  return 0;
}
#endif


#ifdef HAS__MKGMTIME
static time_t nodec_mkgmtime(struct tm* _tm) {
  return _mkgmtime(_tm);
}
#elif defined(HAS_TIMEGM) 
static time_t nodec_mkgmtime(struct tm* _tm) {
  return timegm(_tm);
}
#else
static time_t nodec_mkgmtime(struct tm * _tm) {
  // TODO: needs testing
  time_t t = mktime(_tm);

  struct tm gt;
  struct tm lt;
  nodec_gmtime(&gt,&t);
  nodec_localtime(&lt,&t); // normalize
  
  // adjust with the difference
  lt.tm_year -= gt.tm_year - lt.tm_year;
  lt.tm_mon  -= gt.tm_mon - lt.tm_mon;
  lt.tm_mday -= gt.tm_mday - lt.tm_mday;
  lt.tm_hour -= gt.tm_hour - lt.tm_hour;
  lt.tm_min  -= gt.tm_min - lt.tm_min;
  lt.tm_sec  -= gt.tm_sec - lt.tm_sec;

  // and convert again
  return mktime(&lt);
}
#endif

#define INET_DATE_LEN 29

static const char* days[7] =
  { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char* months[12] =
  { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

const char* nodec_inet_date( time_t now )
{
  static char inet_date[INET_DATE_LEN + 1] = "Thu, 01 Jan 1972 00:00:00 GMT";
  static time_t inet_time = 0;

  if (now == inet_time) return inet_date;
  struct tm tm;
  nodec_gmtime(&tm, &now);
  strftime(inet_date, INET_DATE_LEN + 1, "---, %d --- %Y %H:%M:%S GMT", &tm);
  if (tm.tm_wday >= 0 && tm.tm_wday < 7) memcpy(inet_date, days[tm.tm_wday], 3);
  if (tm.tm_mon >= 0 && tm.tm_mon < 12) memcpy(inet_date + 8, months[tm.tm_mon], 3);
  return inet_date;
}

const char* nodec_inet_date_now() {
  time_t now;
  time(&now);
  return nodec_inet_date(now);
}

bool nodec_parse_inet_date(const char* date, time_t* t) {
  struct tm g;
  memset(&g, 0, sizeof(g));  
  char month[4];
  *t = 0;
  if (date == NULL || strlen(date) != INET_DATE_LEN) return false;
#ifdef HAS_SSCANF_S
  int res = sscanf_s(date, "%*3s, %2d %3s %4d %2d:%2d:%2d GMT",
                      &g.tm_mday, month, 4, &g.tm_year,
                      &g.tm_hour, &g.tm_min, &g.tm_sec);
#else
  int res = sscanf(date, "%*3s, %2d %3s %4d %2d:%2d:%2d GMT",
    &g.tm_mday, month, &g.tm_year,
    &g.tm_hour, &g.tm_min, &g.tm_sec);
#endif
  if (res == EOF) return false;
  
  month[3] = 0; // paranoia
  for (int i = 0; i < 12; i++) {
    if (strncmp(month, months[i], 3) == 0) {
      g.tm_mon = i;
      break;
    }
  }
  g.tm_year -= 1900;
  *t = nodec_mkgmtime(&g);  // or 'timegm' ? 
  return true;
}


<|start_filename|>src/interleave.c<|end_filename|>
/* ----------------------------------------------------------------------------
  Copyright (c) 2018, Microsoft Research, <NAME>
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h> 

/*-----------------------------------------------------------------
  Interleave
-----------------------------------------------------------------*/


static void isolate(lh_actionfun* action, lh_value arg, lh_value* xres, lh_exception** xexn, volatile size_t* todo) {
  lh_exception* exn = NULL;
  lh_value      res = lh_value_null;
  if (async_scoped_is_canceled()) {
    // an earlier strand called `cancel` already, don't start this strand
    if (xexn != NULL) exn = lh_exception_alloc_cancel();
  }
  else {
    res = lh_try_all(&exn, action, arg);
  }
  if (todo != NULL) *todo = *todo - 1;
  if (xres != NULL) *xres = res;
  if (xexn != NULL) {
    *xexn = exn;
  }
  else {
    if (exn != NULL) lh_exception_free(exn);
  }
}


typedef struct _isolate_args_t {
  lh_actionfun*  action;
  lh_value       arg;
  lh_value*      res;
  lh_exception** exception;
  volatile size_t* todo;
} isolate_args_t;

static lh_value isolate_action(lh_value vargs) {
  // copy args by value
  isolate_args_t* args = ((isolate_args_t*)lh_ptr_value(vargs));
  isolate(args->action, args->arg, args->res, args->exception, args->todo);
  return lh_value_null;
}

static void _handle_interleave_strand(channel_t* channel, isolate_args_t* args) {
  _channel_async_handler(channel, &isolate_action, lh_value_any_ptr(args));
}


LH_DEFINE_EFFECT2(strand,create,count)

lh_value async_strand_create(lh_actionfun* action, lh_value arg, lh_exception** exn) {
  return lh_yieldN(LH_OPTAG(strand, create), 3, lh_value_fun_ptr(action), arg, lh_value_any_ptr(exn));
}

size_t nodec_current_strand_count() {
  return (size_t)(lh_long_value(lh_yield(LH_OPTAG(strand, count), lh_value_null)));
}

typedef struct _strand_local_t {
  channel_t*       channel;
  volatile size_t* todo;
} strand_local_t;


static lh_value _strand_create(lh_resume resume, lh_value localv, lh_value argsv) {
  yieldargs* yargs = lh_yieldargs_value(resume, argsv);
  assert(yargs->argcount == 3);
  // execute action in the context just outside of the strand handler (and under the channel handler)
  strand_local_t* local = (strand_local_t*)lh_ptr_value(localv);
  lh_actionfun* action = (lh_actionfun*)lh_fun_ptr_value(yargs->args[0]);
  lh_exception** exn = (lh_exception**)lh_ptr_value(yargs->args[2]);
  *local->todo = *local->todo + 1;
  lh_value res = lh_value_null;
  isolate_args_t iargs = { action, yargs->args[1], &res, exn, local->todo };
  _handle_interleave_strand(local->channel, &iargs);
  // and return
  return lh_tail_resume(resume, localv, res);
}

static lh_value _strand_count(lh_resume resume, lh_value localv, lh_value arg) {
  strand_local_t* local = (strand_local_t*)lh_ptr_value(localv);
  return lh_tail_resume(resume, localv, lh_value_long(*local->todo));
}

static const lh_operation strand_ops[] = {
  { LH_OP_TAIL, LH_OPTAG(strand,create), &_strand_create},
  { LH_OP_TAIL_NOOP, LH_OPTAG(strand,count), &_strand_count },
  { LH_OP_NULL, lh_op_null, NULL }
};
static const lh_handlerdef strand_def = { LH_EFFECT(strand), NULL, NULL, NULL, strand_ops };

static lh_value handle_strands(channel_t* channel, volatile size_t* todo, lh_actionfun action, lh_value arg) {
  lh_value res;
  {using_zero_alloc(strand_local_t, local) {
    local->channel = channel;
    local->todo = todo;
    res = lh_handle(&strand_def, lh_value_ptr(local), action, arg);
  }}
  return res;
}

typedef struct _strands_action_args_t {
  isolate_args_t iargs;
  channel_t*     channel;  
} strands_action_args_t;

static lh_value strands_action(lh_value argsv) {
  // run the action inside interleave strand itself
  strands_action_args_t* args = (strands_action_args_t*)lh_ptr_value(argsv);
  volatile size_t* todo = args->iargs.todo;
  channel_t* channel = args->channel;
  _handle_interleave_strand(channel, &args->iargs);
  while (*todo > 0) {
    // a receive should never be canceled since it should wait until
    // it children are canceled (and then continue). 
    lh_value resumev;
    lh_value arg;
    int err = channel_receive_nocancel(channel, &resumev, &arg);
    if (resumev != lh_value_null) { // can happen on cancel
      lh_release_resume((lh_resume)lh_ptr_value(resumev), arg, lh_value_int(err));
    }
  }
  return lh_value_null;
}

lh_value async_interleave_dynamic(lh_actionfun action, lh_value arg) {
  lh_value res = lh_value_null;
  lh_exception* exn = NULL;
  volatile size_t* todo = nodec_alloc(size_t);
  {using_free((void*)(todo)){
    *todo = 1;
    {using_channel(channel) {
      strands_action_args_t sargs = { { action, arg, &res, &exn, todo }, channel };
      handle_strands(channel,todo,&strands_action, lh_value_any_ptr(&sargs));            
    }}
  }}
  if (exn != NULL) lh_throw(exn);
  return res;
}


typedef struct _interleave_n_args_t{
  size_t n;
  lh_actionfun** actions;
  lh_value* arg_results;
  lh_exception** exceptions;
} interleave_n_args_t;

static lh_value interleave_n_action(lh_value argsv) {
  interleave_n_args_t* args = (interleave_n_args_t*)lh_ptr_value(argsv);
  for (size_t i = 0; i < args->n; i++) {
    args->arg_results[i] = async_strand_create(args->actions[i], args->arg_results[i], &args->exceptions[i]);
  }
  return lh_value_null;
}

static void  _async_interleave_n(size_t n, lh_actionfun** actions, lh_value* arg_results, lh_exception** exceptions) {
  interleave_n_args_t args = { n, actions, arg_results, exceptions };
  async_interleave_dynamic(&interleave_n_action, lh_value_any_ptr(&args));
}

static void nodec_free_if_notnull(lh_value pv) {
  if (pv!=lh_value_null) nodec_freev(pv);
}

void asyncx_interleave(size_t n, lh_actionfun* actions[], lh_value arg_results[], lh_exception* exceptions[]) {
  if (n == 0 || actions == NULL) return;

  //lh_exception* exn = NULL;
  lh_value* local_args = NULL;
  lh_exception** local_exns = NULL;
  if (arg_results==NULL) {
    local_args = nodec_calloc(n, sizeof(lh_value));
    arg_results = local_args;
  }
  {defer(&nodec_free_if_notnull, lh_value_ptr(local_args)) {
    if (exceptions==NULL) {
      local_exns = nodec_calloc(n, sizeof(lh_exception*));
      exceptions = local_exns;
    }
    {defer(&nodec_free_if_notnull, lh_value_ptr(local_exns)) {
      _async_interleave_n(n, actions, arg_results, exceptions);
    }}
  }}
}

void async_interleave(size_t n, lh_actionfun* actions[], lh_value arg_results[]) {
  if (n == 0 || actions == NULL) return;
  if (n == 1) {
    lh_value res = (actions[0])(arg_results==NULL ? lh_value_null : arg_results[0]);
    if (arg_results!=NULL) arg_results[0] = res;
  }
  else {
    lh_exception* exn = NULL;
    {using_zero_alloc_n(n, lh_exception*, exceptions) {
      asyncx_interleave(n, actions, arg_results, exceptions);
      // rethrow the first exception and release the others
      for (size_t i = 0; i < n; i++) {
        if (exceptions[i] != NULL) {
          if (exn == NULL) {
            exn = exceptions[i];
          }
          else {
            lh_exception_free(exceptions[i]);
          }
        }
      }
    }}
    if (exn != NULL) lh_throw(exn);
  }
}

typedef struct _firstof_args_t {
  lh_actionfun* action;
  lh_value      arg;
} firstof_args_t;

static void firstof_cancel(lh_value _arg) {
  async_scoped_cancel();
}

static lh_value firstof_action(lh_value argsv) {
  firstof_args_t* args = (firstof_args_t*)lh_ptr_value(argsv);
  return lh_finally( args->action, args->arg, &firstof_cancel, lh_value_null);
}

lh_value async_firstof_ex(lh_actionfun* action1, lh_value arg1, lh_actionfun* action2, lh_value arg2, bool* first, bool ignore_exn ) {
  firstof_args_t args[2]       = { {action1, arg1}, {action2, arg2} };
  lh_actionfun* actions[2]     = { &firstof_action, &firstof_action };
  lh_value      arg_results[2] = { lh_value_any_ptr(&args[0]), lh_value_any_ptr(&args[1]) };
  lh_exception* exceptions[2]  = { NULL, NULL };
  {using_cancel_scope() {
    asyncx_interleave(2, actions, arg_results, exceptions);
  }}

  // pick the winner
  int pick = 0;
  // ignore cancel exceptions
  if (lh_exception_is_cancel(exceptions[0])) pick = 1;
  else if (lh_exception_is_cancel(exceptions[1])) pick = 0;
  // if not ignoring exceptions, pick the first exception raising one
  else if (!ignore_exn) {
    if (exceptions[0] != NULL) pick = 0;
    else if (exceptions[1] != NULL) pick = 1;
  }
  // or pick the first one with a result
  else if (exceptions[0] != NULL) pick = 0;
  else pick = 1;

  // Return the result
  if (first) *first = (pick == 0);
  if (pick==1) {
    if (exceptions[0]!=NULL) lh_exception_free(exceptions[0]);
    if (exceptions[1]!=NULL) lh_throw(exceptions[1]);
    return arg_results[1];
  }
  else {
    if (exceptions[1]!=NULL) lh_exception_free(exceptions[1]);
    if (exceptions[0]!=NULL) lh_throw(exceptions[0]);
    return arg_results[0];
  }
}

lh_value _firstof_action(lh_value actionv) {
  nodec_actionfun_t* action = (nodec_actionfun_t*)lh_fun_ptr_value(actionv);
  action();
  return lh_value_null;
}

bool async_firstof(nodec_actionfun_t* action1, nodec_actionfun_t* action2) {
  bool first = false;
  async_firstof_ex(&_firstof_action, lh_value_fun_ptr(action1), &_firstof_action, lh_value_fun_ptr(action2), &first, false);
  return first;
}


static lh_value _timeout_wait(lh_value timeoutv) {
  uint64_t timeout = lh_uint64_t_value(timeoutv);
  async_wait(timeout);
  return lh_value_null;
}

lh_value async_timeout(lh_actionfun* action, lh_value arg, uint64_t timeout, bool* timedout) {
  return async_firstof_ex(_timeout_wait, lh_value_uint64_t(timeout), action, arg, timedout, false);
}

<|start_filename|>docs/api/search/enums_0.js<|end_filename|>
var searchData=
[
  ['http_5fmethod_5ft',['http_method_t',['../group__http__setup.html#ga2fd930fba66817097e73ba9b75800eb2',1,'nodec-extra-doc.h']]],
  ['http_5fstatus_5ft',['http_status_t',['../group__http__setup.html#gaf5ffe69ea1b5908893c7eddf74c8151b',1,'nodec-extra-doc.h']]]
];


<|start_filename|>docs/api/search/variables_6.js<|end_filename|>
var searchData=
[
  ['gzip_5fmin_5fsize',['gzip_min_size',['../group__nodec__static.html#ae9dca6966cebb3b3dae85e79b561ece8',1,'http_static_config_t']]],
  ['gzip_5fvary',['gzip_vary',['../group__nodec__static.html#a80faaf3f2b7b7d3d9a7271af9f7e667c',1,'http_static_config_t']]]
];


<|start_filename|>docs/api/search/variables_1.js<|end_filename|>
var searchData=
[
  ['argcount',['argcount',['../group__effect__handlers.html#ab797acdcd17ed8d49fd79980409be48b',1,'yieldargs']]],
  ['args',['args',['../group__effect__handlers.html#a2dd60935d436953098e73cbdd77fdcd2',1,'yieldargs']]]
];


<|start_filename|>docs/api/search/classes_2.js<|end_filename|>
var searchData=
[
  ['tcp_5fserver_5fconfig_5ft',['tcp_server_config_t',['../group__tcp.html#structtcp__server__config__t',1,'']]]
];


<|start_filename|>src/tcp.c<|end_filename|>
/* ----------------------------------------------------------------------------
Copyright (c) 2018, Microsoft Research, <NAME>
This is free software; you can redistribute it and/or modify it under the
terms of the Apache License, Version 2.0. A copy of the License can be
found in the file "license.txt" at the root of this distribution.
-----------------------------------------------------------------------------*/
#include "nodec.h"
#include "nodec-internal.h"
#include "nodec-primitive.h"
#include <assert.h>

void nodec_sockname(const struct sockaddr* addr, char* buf, size_t bufsize) {
  buf[0] = 0;
  if (addr != NULL) {
    if (addr->sa_family == AF_INET6) {
      uv_ip6_name((const struct sockaddr_in6*)addr, buf, bufsize);
    }
    else {
      uv_ip4_name((const struct sockaddr_in*)addr, buf, bufsize);
    }
  }
  buf[bufsize - 1] = 0;
}

void check_uv_err_addr(int err, const struct sockaddr* addr) {
  // todo: switch to ipv6 address if needed
  if (err != 0) {
    char buf[256];
    nodec_sockname(addr, buf, sizeof(buf));
    nodec_check_msg(err, buf);
  }
}

/*-----------------------------------------------------------------
  handling tcp
-----------------------------------------------------------------*/


void nodec_tcp_free(uv_tcp_t* tcp) {
  nodec_uv_stream_free((uv_stream_t*)tcp);
}

void nodec_tcp_freev(lh_value tcp) {
  nodec_tcp_free(lh_ptr_value(tcp));
}

uv_tcp_t* nodec_tcp_alloc() {
  uv_tcp_t* tcp = nodec_zero_alloc(uv_tcp_t);
  nodec_check( uv_tcp_init(async_loop(), tcp) );  
  return tcp;
}


void nodec_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags) {
  check_uv_err_addr(uv_tcp_bind(handle, addr, flags), addr);
} 

static void _listen_cb(uv_stream_t* server, int status) {
#ifndef NDEBUG
  fprintf(stderr, "connection request\n");
#endif
  uv_tcp_t* client = NULL;
  channel_t* ch = NULL;
  int err = 0;
  if (status != 0) {
    err = status;
  }
  else if (server==NULL) {
    err = UV_EINVAL;
  }
  else if ((ch = (channel_t*)server->data) == NULL) {
    err = UV_EINVAL;
  }
  else if (channel_is_full(ch)) {
    err = UV_ENOSPC; // stop accepting connections if the queue is full
  }
  else {
    client = (uv_tcp_t*)nodecx_calloc(1,sizeof(uv_tcp_t));
    if (client == NULL) {
      err = UV_ENOMEM;
    }
    else {
      err = uv_tcp_init(server->loop, client);
      if (err == 0) {
        err = uv_accept(server, (uv_stream_t*)client);
        if (err == 0) {
          // here we emit into the channel
          // this will either queue the element, or call a listener
          // entering a listener is ok since that will be a resume 
          // under an async/try handler again.
          // TODO: we should have a size limited queue and check the emit return value
      #ifndef NDEBUG
          fprintf(stderr, "connection accepted, emitted in channel\n");
      #endif
          err = channel_emit(ch, lh_value_ptr(client), lh_value_null, 0);  // if err==UV_NOSPC the channel was full
        }
      }
    }
  }
  if (err!=0) {
    // deallocate client on error
    if (client!=NULL) {
      nodec_uv_stream_free((uv_stream_t*)client);
      client = NULL;
    }
    fprintf(stderr, "connection error: %i: %s\n", err, uv_strerror(err));
  }
}

// Free TCP stream associated with a tcp channel
static void _channel_release_tcp(lh_value tcpv) {
  uv_tcp_t* tcp = (uv_tcp_t*)lh_ptr_value(tcpv);
  tcp->data = NULL; // is the channel itself; don't free it in the stream_free
  nodec_tcp_free(tcp);
}

static void _channel_release_client(lh_value data, lh_value arg, int err) {
  uv_stream_t* client = (uv_stream_t*)lh_ptr_value(data);
  if (client != NULL) {
    nodec_uv_stream_free(client);    
  }
}

tcp_channel_t* nodec_tcp_listen(uv_tcp_t* tcp, int backlog, bool channel_owns_tcp) {
  if (backlog <= 0) backlog = 128;
  nodec_check(uv_listen((uv_stream_t*)tcp, backlog, &_listen_cb));
  tcp_channel_t* ch = (tcp_channel_t*)channel_alloc_ex(8, // TODO: should be small?
                          (channel_owns_tcp ? &_channel_release_tcp : NULL), 
                              lh_value_ptr(tcp), &_channel_release_client );  
  tcp->data = ch;
  return ch;
}

void nodec_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) {
  nodec_check(uv_ip4_addr(ip, port, addr));
}

void nodec_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) {
  nodec_check(uv_ip6_addr(ip, port, addr));
}

tcp_channel_t* nodec_tcp_listen_at(const struct sockaddr* addr, int backlog) {
  uv_tcp_t* tcp = nodec_tcp_alloc();
  tcp_channel_t* ch = NULL;
  {on_abort(nodec_tcp_freev, lh_value_ptr(tcp)) {
    nodec_tcp_bind(tcp, addr, 0);
    ch = nodec_tcp_listen(tcp, backlog, true);
  }}
  return ch;
}

struct sockaddr* nodec_parse_sockaddr(const char* host) {
  struct sockaddr*    addr = NULL;
  nodec_url_t* url = nodec_parse_host(host);
  {using_url(url) {
    if (nodec_url_is_ip6(url)) {
      struct sockaddr_in6* addr6 = nodec_zero_alloc(struct sockaddr_in6);
      nodec_ip6_addr(nodec_url_host(url), nodec_url_port(url), addr6);
      addr = (struct sockaddr*)addr6;
    }
    else {
      struct sockaddr_in* addr4 = nodec_zero_alloc(struct sockaddr_in);
      nodec_ip4_addr(nodec_url_host(url), nodec_url_port(url), addr4);
      addr = (struct sockaddr*)addr4;
    }
  }}
  return addr;
}


uv_stream_t* async_tcp_channel_receive(tcp_channel_t* ch) {
  lh_value data = lh_value_null;
  channel_receive(ch, &data, NULL);
  //printf("got a connection!\n");
  return (uv_stream_t*)lh_ptr_value(data);
}


static void connect_cb(uv_connect_t* req, int status) {
  async_req_resume((uv_req_t*)req, status >= 0 ? 0 : status);
}

nodec_bstream_t* async_tcp_connect_at(const struct sockaddr* addr, const char* host) {
  uv_tcp_t* tcp = nodec_tcp_alloc();
  {on_abort(nodec_tcp_freev, lh_value_ptr(tcp)) {
    {using_req(uv_connect_t, req) {
      nodec_check_msg(uv_tcp_connect(req, tcp, addr, &connect_cb), host);
      nodec_check_msg(asyncx_await_once((uv_req_t*)req),host);
    }}
  }}
  return nodec_bstream_alloc_read( (uv_stream_t*)tcp );
}

nodec_bstream_t* async_tcp_connect_at_host(const char* host, const char* service) {
  struct addrinfo* info = async_getaddrinfo(host, (service==NULL ? "http" : service), NULL);
  if (info==NULL) nodec_check_msg(UV_EAI_NONAME,host);
  nodec_bstream_t* tcp = NULL;
  {using_addrinfo(info) {
    tcp = async_tcp_connect_at(info->ai_addr, host);
  }}
  return tcp;
}

nodec_bstream_t* async_tcp_connect(const char* host) {
  nodec_url_t* url = nodec_parse_url(host);
  nodec_bstream_t* stream = NULL;
  {using_url(url) {
    const char* port = nodec_url_port_str(url);
    if (port == NULL) port = nodec_url_schema(url);
    stream = async_tcp_connect_at_host(nodec_url_host(url), port);
  }}
  return stream;
}

/*-----------------------------------------------------------------
    A TCP server
-----------------------------------------------------------------*/

static lh_value async_log_tcp_exn(lh_value exnv) {
  lh_exception* exn = (lh_exception*)lh_ptr_value(exnv);
  if (exn == NULL || exn->data == NULL) return lh_value_null;
  fprintf(stderr, "tcp server error: %i: %s\n", exn->code, (exn->msg == NULL ? "unknown" : exn->msg));
  return lh_value_null;
}




typedef struct _tcp_serve_args {
  nodec_tcp_connection_wrap_t* connection_wrap;
  nodec_tcp_connection_fun_t* serve;
  lh_value            serve_arg;
  lh_value            wrap_arg;
  tcp_channel_t*      ch;
  size_t              max_interleaving;
  uint64_t            timeout_total;
  uint64_t            timeout_keepalive;
  lh_actionfun*       on_exn;
  uv_stream_t*        uvclient;
} tcp_serve_args;


static lh_value tcp_connection(lh_value argsv) {
  tcp_connection_args* args = (tcp_connection_args*)lh_ptr_value(argsv);
  args->connection_fun(args->id, args->client, args->arg);
  return lh_value_null;
}

static lh_value tcp_connection_timeout(lh_value argsv) {
  tcp_connection_args* args = (tcp_connection_args*)lh_ptr_value(argsv);
  if (args->timeout_total == 0) {
    return tcp_connection(argsv);
  }
  else {
    bool timedout = false;
    lh_value result = async_timeout(&tcp_connection, argsv, args->timeout_total, &timedout);
    if (timedout) throw_http_err(408);
    return result;
  }
}

static lh_value tcp_connection_keepalive(lh_value argsv) {
  tcp_connection_args* args = (tcp_connection_args*)lh_ptr_value(argsv);
  if (args->timeout_keepalive == 0) {
    return tcp_connection_timeout(argsv);
  }
  else {
    lh_value result = lh_value_null;
    uverr_t err = 0;
    //nodec_check(uv_tcp_keepalive((uv_tcp_t*)args->client, 1, (unsigned)args->keepalive));
    fprintf(stderr, "use keep alive connection %i.\n", args->id);
    do {
      result = tcp_connection_timeout(argsv);
      err = asyncx_uv_stream_await_available(args->uvclient, args->timeout_keepalive);
    } while (err == 0);
    fprintf(stderr, "closed keep alive connection %i.\n", args->id);
    return result;
  }
}

void nodec_tcp_connection_wrap(const tcp_connection_args* args, lh_value ignored_arg) {
  lh_exception* exn;
  lh_try(&exn, &tcp_connection_keepalive, lh_value_any_ptr(args));
  if (exn != NULL) {
    // ignore closed client connections..
    if (!(exn->data == args->uvclient && exn->code == UV_ECANCELED)) {
      // send an exception response
      // wrap in try itself in case writing gives an error too!
      lh_exception* wrap = lh_exception_alloc(exn->code, exn->msg);
      wrap->data = args->client;
      lh_exception* ignore_exn = NULL;
      lh_try(&ignore_exn, args->on_exn, lh_value_any_ptr(wrap));
      lh_exception_free(wrap);
      lh_exception_free(ignore_exn);
    }
    lh_exception_free(exn);
  }
}


static lh_value tcp_serve_connection(lh_value argsv) {
  static int id = 0;
  tcp_serve_args args = *((tcp_serve_args*)lh_ptr_value(argsv)); // copy by value
  nodec_uv_stream_t* client = nodec_uv_stream_alloc(args.uvclient);    
  {using_uv_stream(client) {
    // TODO: what if an exception happens here?
    // TODO: make initial read allocation a parameter? Maybe needs to be enlarged for https?
    nodec_uv_stream_read_start(client, 8 * 1024, 0); // initial allocation at 8kb (for the header)
    tcp_connection_args cargs = { args.serve, id++, as_bstream(client), args.serve_arg, args.timeout_total, args.timeout_keepalive, args.on_exn, client };
    (*args.connection_wrap)(&cargs, args.wrap_arg);
  }}
  return lh_value_null;
}

static lh_value tcp_servev(lh_value argsv) {
  tcp_serve_args args = *((tcp_serve_args*)lh_ptr_value(argsv));
  do {
    uv_stream_t* uvclient = async_tcp_channel_receive(args.ch);
    if (nodec_current_strand_count() > args.max_interleaving) {
      // if too much concurrency, close the connection immediately
      nodec_uv_stream_free(uvclient);
    }
    else {
      tcp_serve_args sargs = args; // copy
      sargs.uvclient = uvclient;
      async_strand_create(&tcp_serve_connection, lh_value_any_ptr(&sargs), NULL);
    }
  } while (true);  // should be until termination
  return lh_value_null;
}

void async_tcp_server_at_ex(const struct sockaddr* addr, 
  tcp_server_config_t* config,
  nodec_tcp_connection_fun_t* servefun, 
  nodec_tcp_connection_wrap_t* wrapfun,
  lh_actionfun* on_exn,
  lh_value serve_arg,
  lh_value wrap_arg)
{
  tcp_server_config_t default_config = tcp_server_config();
  if (config == NULL) config = &default_config;
  tcp_channel_t* ch = nodec_tcp_listen_at(addr, config->backlog);
  {using_tcp_channel(ch) {
    {using_zero_alloc(tcp_serve_args, sargs) {
      sargs->connection_wrap = wrapfun;
      sargs->wrap_arg = wrap_arg;
      sargs->serve = servefun;
      sargs->serve_arg = serve_arg;
      sargs->ch = ch;
      sargs->max_interleaving = config->max_interleaving;
      sargs->timeout_total = config->timeout_total;
      sargs->timeout_keepalive = config->timeout;
      sargs->on_exn = (on_exn == NULL ? &async_log_tcp_exn : on_exn);
      async_interleave_dynamic(&tcp_servev, lh_value_ptr(sargs));
    }}
  }}
}

void async_tcp_server_at(const struct sockaddr* addr,
  tcp_server_config_t* config,
  nodec_tcp_connection_fun_t* servefun,
  lh_actionfun* on_exn,
  lh_value arg)
{
  async_tcp_server_at_ex(addr, config, servefun, 
    &nodec_tcp_connection_wrap, on_exn, arg, lh_value_null);
}

<|start_filename|>docs/api/search/groups_c.js<|end_filename|>
var searchData=
[
  ['url',['URL',['../group__nodec__url.html',1,'']]]
];


<|start_filename|>docs/api/search/functions_4.js<|end_filename|>
var searchData=
[
  ['using_5fasync_5ffs_5fopen',['using_async_fs_open',['../group__nodec__fs.html#gac2c5469313c7fc23b4578d546ff5dde2',1,'nodec.h']]],
  ['using_5fasyncx_5ffs_5fopen',['using_asyncx_fs_open',['../group__nodec__fs.html#ga2f19335f15eaf41304f16e195ae8eba2',1,'nodec.h']]]
];


<|start_filename|>docs/api/search/typedefs_0.js<|end_filename|>
var searchData=
[
  ['http_5fin_5ft',['http_in_t',['../group__http__in__out.html#ga9c23d2a40e3792bf162f3a3e7bcb40df',1,'nodec.h']]],
  ['http_5fout_5ft',['http_out_t',['../group__http__in__out.html#ga71b82996867f08b9f7aaed6d10f507bf',1,'nodec.h']]]
];


<|start_filename|>docs/api/nodec-doxygen.css<|end_filename|>
body {
	margin: 1ex 1ex 0ex 1ex;
	border: 1px solid black;
}
.contents table, .contents div, .contents p, .contents dl {
	font-size: 16px; 
	line-height: 1.44;
}
body #nav-tree .label {
	font-size: 14px;
}
a.external {
	text-decoration: underline;
}
#side-nav {
	margin-left: 1ex;
	border-left: 1px solid black;
}
#nav-tree {
	padding-left: 1ex;
}
#nav-path {
	display: none;
}
#titlearea {
	padding: 1ex;
	width: auto !important;
}

<|start_filename|>docs/api/search/classes_1.js<|end_filename|>
var searchData=
[
  ['lh_5fexception',['lh_exception',['../group__effect__exn.html#structlh__exception',1,'']]],
  ['lh_5fhandlerdef',['lh_handlerdef',['../group__effect__handlers.html#structlh__handlerdef',1,'']]],
  ['lh_5foperation',['lh_operation',['../group__effect__handlers.html#structlh__operation',1,'']]],
  ['lh_5foptag_5f',['lh_optag_',['../group__effect__handlers.html#structlh__optag__',1,'']]]
];


<|start_filename|>docs/api/group__effect__handlers_structyieldargs.js<|end_filename|>
var group__effect__handlers_structyieldargs =
[
    [ "argcount", "group__effect__handlers.html#ab797acdcd17ed8d49fd79980409be48b", null ],
    [ "args", "group__effect__handlers.html#a2dd60935d436953098e73cbdd77fdcd2", null ]
];

<|start_filename|>docs/api/group__http.js<|end_filename|>
var group__http =
[
    [ "HTTP Setup", "group__http__setup.html", "group__http__setup" ],
    [ "HTTP Input and Output.", "group__http__in__out.html", "group__http__in__out" ],
    [ "HTTP Server Requests and Responses.", "group__http__req__resp.html", "group__http__req__resp" ],
    [ "HTTP Static Content Server.", "group__nodec__static.html", "group__nodec__static" ],
    [ "HTTPS secure connections", "group__nodec__https.html", null ]
];

<|start_filename|>docs/api/group__nodec__fs_structuv__stat__t.js<|end_filename|>
var group__nodec__fs_structuv__stat__t =
[
    [ "st_atim", "group__nodec__fs.html#a90374cf48291ecde29394d1982b9eec8", null ],
    [ "st_birthtim", "group__nodec__fs.html#a324b6bb9a3dcb70259e0195379c5f9ef", null ],
    [ "st_blksize", "group__nodec__fs.html#af549bc8ae4793b83fa25b97fc5518e67", null ],
    [ "st_blocks", "group__nodec__fs.html#a7cb4ce091e3b7418f368451dd60e535e", null ],
    [ "st_ctim", "group__nodec__fs.html#a7785fcfd6909d22b07f8461219ca2133", null ],
    [ "st_dev", "group__nodec__fs.html#ac1f0764990fe34d0b21f7e3af9fef946", null ],
    [ "st_flags", "group__nodec__fs.html#a14bc9565a316b65e422dde506d4c6428", null ],
    [ "st_gen", "group__nodec__fs.html#ab5036343ef8adaabecc2362f5905b9cb", null ],
    [ "st_gid", "group__nodec__fs.html#aa6d1907b84accf5662140cb0834e479d", null ],
    [ "st_ino", "group__nodec__fs.html#a1442738c45923c0a23f7ee0c2837f584", null ],
    [ "st_mode", "group__nodec__fs.html#a198c4352c514e9eff06e7363c3562a8f", null ],
    [ "st_mtim", "group__nodec__fs.html#a89f339b980cacb16da47d7a7c04f7e8a", null ],
    [ "st_nlink", "group__nodec__fs.html#a05b9207a1a204eae78b2c7dc0d3812ab", null ],
    [ "st_rdev", "group__nodec__fs.html#af0cc8e07491b42cd8b82d797ec5af0b4", null ],
    [ "st_size", "group__nodec__fs.html#a454ac6f7cef9a00d73baf2028fe3a7cb", null ],
    [ "st_uid", "group__nodec__fs.html#ac56d6239041749e7bc4ccc472aa800b1", null ]
];

<|start_filename|>docs/api/group__buffers_structuv__buf__t.js<|end_filename|>
var group__buffers_structuv__buf__t =
[
    [ "base", "group__buffers.html#a728c18d77668f4691201c4937133d7f0", null ],
    [ "len", "group__buffers.html#a1f258533d380bfb77f226e03f590388d", null ]
];

<|start_filename|>docs/api/search/classes_3.js<|end_filename|>
var searchData=
[
  ['uv_5fbuf_5ft',['uv_buf_t',['../group__buffers.html#structuv__buf__t',1,'']]],
  ['uv_5fdirent_5ft',['uv_dirent_t',['../group__nodec__fs.html#structuv__dirent__t',1,'']]],
  ['uv_5fstat_5ft',['uv_stat_t',['../group__nodec__fs.html#structuv__stat__t',1,'']]],
  ['uv_5ftimespec_5ft',['uv_timespec_t',['../group__nodec__fs.html#structuv__timespec__t',1,'']]]
];


<|start_filename|>docs/api/group__nodec__static.js<|end_filename|>
var group__nodec__static =
[
    [ "http_static_config_t", "group__nodec__static.html#structhttp__static__config__t", [
      [ "cache_control", "group__nodec__static.html#afd394de5b190c3770c6d3f915316c759", null ],
      [ "content_max_size", "group__nodec__static.html#a51e0cb72c8eb93fef2ca61fb599b9a06", null ],
      [ "gzip_min_size", "group__nodec__static.html#ae9dca6966cebb3b3dae85e79b561ece8", null ],
      [ "gzip_vary", "group__nodec__static.html#a80faaf3f2b7b7d3d9a7271af9f7e667c", null ],
      [ "implicit_exts", "group__nodec__static.html#a35e38162a485696d0259c3789b690f1d", null ],
      [ "implicit_index", "group__nodec__static.html#a6b2390b3780a8912fb6d571aa7a08ba6", null ],
      [ "read_buf_size", "group__nodec__static.html#a31d97bb6440fc5992cb56fc84d0725fa", null ],
      [ "use_etag", "group__nodec__static.html#ab77a0887c2beea113ae3a647bd5051ed", null ],
      [ "use_last_modified", "group__nodec__static.html#a28d756b30046e7e7ee6b88d4dfe3dcce", null ]
    ] ],
    [ "http_static_default_config", "group__nodec__static.html#gafee4501f26dcb4de2253ee690fcc9cfa", null ],
    [ "http_serve_static", "group__nodec__static.html#gaaa8f361e185522a1dea817d83c8d784b", null ],
    [ "http_static_implicit_exts", "group__nodec__static.html#gae8ac7620c8122033e3239beee552339f", null ]
];

<|start_filename|>src/async.c<|end_filename|>
#include "nodec.h"
#include "nodec-primitive.h"
#include "nodec-internal.h"
#include <assert.h> 
#include <string.h>

/* todos:
- default handling operations (so cancel_async becomes more efficient)
- single timer for setImmediate and yielding (to make them more lightweight)
*/

// forwards

typedef struct _cancel_scope_t cancel_scope_t;
typedef struct _async_request_t async_request_t;
typedef struct _async_local_t async_local_t;

async_request_t* async_request_alloc(uv_req_t* uvreq, bool nocancel, uint64_t timeout, void* owner);

/*-----------------------------------------------------------------
Async effect operations
-----------------------------------------------------------------*/
typedef uv_loop_t*            uv_loop_ptr;
typedef uv_req_t*             uv_req_ptr;
typedef uv_handle_t*          uv_handle_ptr;
typedef async_request_t*      async_request_ptr;
typedef const cancel_scope_t* cancel_scope_ptr;

#define lh_uv_loop_ptr_value(v)         ((uv_loop_t*)lh_ptr_value(v))
#define lh_value_uv_loop_ptr(h)         lh_value_ptr(h)
#define lh_async_request_ptr_value(v)   ((async_request_t*)lh_ptr_value(v))
#define lh_value_async_request_ptr(r)   lh_value_ptr(r)
#define lh_cancel_scope_ptr_value(v)    ((const cancel_scope_t*)lh_ptr_value(v))
#define lh_value_cancel_scope_ptr(r)    lh_value_ptr(r)

LH_DEFINE_EFFECT5(async, req_await, uv_loop, req_register, uv_cancel, owner_release)
LH_DEFINE_OP0(async, uv_loop, uv_loop_ptr)
LH_DEFINE_OP1(async, req_await, int, async_request_ptr)
LH_DEFINE_VOIDOP1(async, req_register, async_request_ptr)
LH_DEFINE_VOIDOP1(async, uv_cancel, cancel_scope_ptr)
LH_DEFINE_VOIDOP1(async, owner_release, lh_voidptr)


// Wrappers around the primitive operations for async.
uv_loop_t* async_loop() {
  return async_uv_loop();
}

void nodec_owner_release(void* owner) {
  async_owner_release(owner);
}


uv_errno_t asyncx_nocancel_await(uv_req_t* uvreq) {
  async_request_t* req = async_request_alloc(uvreq,true,0,NULL);
  uv_errno_t err = async_req_await(req);
  assert(err != UV_ETHROWCANCEL);
  return err;
}

uv_errno_t asyncxx_await(uv_req_t* uvreq, uint64_t timeout, void* owner) {
  async_request_t* req = async_request_alloc(uvreq, false, timeout, owner);
  return async_req_await(req);
}


uv_errno_t asyncx_await(uv_req_t* uvreq, uint64_t timeout, void* owner) {
  uv_errno_t err = asyncxx_await(uvreq, timeout, owner); 
  if (err == UV_ETHROWCANCEL) {
    lh_throw_cancel();
  }
  return err;
}

uv_errno_t asyncx_await_once(uv_req_t* uvreq) {
  return asyncx_await(uvreq, 0, NULL);
}

void async_await_once(uv_req_t* uvreq) {
  nodec_check(asyncx_await_once(uvreq));
}

uv_errno_t asyncx_await_owned(uv_req_t* uvreq, void* owner) {
  return asyncx_await(uvreq, 0, owner);
}

void async_await_owned(uv_req_t* uvreq, void* owner) {
  nodec_check(asyncx_await_owned(uvreq,owner));
}




/*-----------------------------------------------------------------
Throw on errors
-----------------------------------------------------------------*/
nodec_errno_t nodec_error_from(long err, error_kind_t err_kind) {
  return ((nodec_errno_t)err_kind + err);
}

long nodec_error_to(nodec_errno_t err, error_kind_t err_kind) {
  return (err - (nodec_errno_t)err_kind);
}

bool nodec_is_error_of(nodec_errno_t err, error_kind_t err_kind) {
  return (err >= (err_kind - 0xFFFF) && err <= (err_kind + 0xFFFF));
}

#ifdef USE_MBEDTLS
#include <mbedtls/include/mbedtls/error.h>
#endif

void nodec_strerror(nodec_errno_t err, const char* msg, uv_buf_t buf) {
  const char* msgsep = (msg == NULL ? "" : ": ");
  if (msg == NULL) msg = "";
  const char* errmsg = "";
  char errmsg_buf[256];
  if (err >= 0) {
    errmsg = "unknown error";
  }
#ifdef USE_MBEDTLS
  else if (nodec_is_error_of(err, ERRKIND_MBEDTLS)) {
    int tls_err = nodec_error_to(err, ERRKIND_MBEDTLS);
    mbedtls_strerror(tls_err, errmsg_buf, 256); errmsg_buf[255] = 0;
    errmsg = errmsg_buf;
  }
#endif
  else if (nodec_is_error_of(err, ERRKIND_HTTP)) {
    http_status_t status = nodec_error_to(err, ERRKIND_HTTP);
    errmsg = nodec_http_status_str(status);
  }
  else {
    errmsg = uv_strerror(err);
  }
  snprintf(buf.base, buf.len, "%s%s%s", msg, msgsep, errmsg);
}

void nodec_throw_msg_data(nodec_errno_t err, const char* msg, void* data) {
  lh_exception* exn;
  char errmsg[256];
  if (err == UV_ETHROWCANCEL) {
    exn = lh_exception_alloc_cancel();
  }
  else {
    nodec_strerror(err, msg, nodec_buf(errmsg, 255));
    exn = lh_exception_alloc_strdup(err, errmsg);
  }
  exn->data = data;
  lh_throw(exn);
}

void nodec_throw(nodec_errno_t err) {
  nodec_throw_msg_data(err, NULL, NULL);
}

void nodec_throw_msg(nodec_errno_t err, const char* msg) {
  nodec_throw_msg_data(err, msg, NULL);
}


// Check an error result, throwing on error
void nodec_check(nodec_errno_t err) {
  if (err != 0) {
    nodec_throw(err);
  }
}

// Check an error result, throwing on error
void nodec_check_msg(nodec_errno_t err, const char* msg) {
  if (err != 0) {
    nodec_throw_msg(err, msg);
  }
}

void nodec_check_data(nodec_errno_t err, void* data) {
  if (err != 0) {
    nodec_throw_msg_data(err, NULL, data);
  }
}
void nodec_check_msg_data(nodec_errno_t err, const char* msg, void* data) {
  if (err != 0) {
    nodec_throw_msg_data(err, msg, data);
  }
}



/*-----------------------------------------------------------------
  Scopes
-----------------------------------------------------------------*/

struct _cancel_scope_t {
  const struct _cancel_scope_t* parent;
  bool canceled;
};

implicit_define(_cancel_scope)

static const cancel_scope_t* cancel_scope() {
  return (const cancel_scope_t*)lh_ptr_value(implicit_get(_cancel_scope));
}

lh_value _cancel_scope_alloc() {
  cancel_scope_t* scope = nodec_alloc(cancel_scope_t);
  scope->parent = cancel_scope();
  scope->canceled = false;
  return lh_value_ptr(scope);
}


// #define using_cancel_scope()        using_implicit_defer(nodec_freev,_cancel_scope_alloc(),_cancel_scope)
#define using_outer_cancel_scope()  using_implicit(lh_value_null,_cancel_scope)

static bool in_scope_of(const cancel_scope_t* scope, const cancel_scope_t* top ) {
  while (scope != NULL && scope != top) {
    scope = scope->parent;
  }
  return (scope == top);
}

void async_scoped_cancel_under(cancel_scope_t* scope) {
  scope->canceled = true;
  async_uv_cancel(scope);
}

void async_scoped_cancel() {
  async_scoped_cancel_under((cancel_scope_t*)cancel_scope());
}

bool async_scoped_is_canceled() {
  for (const cancel_scope_t* scope = cancel_scope(); scope != NULL; scope = scope->parent) {
    if (scope->canceled) return true;
  }
  return false;
}


/*-----------------------------------------------------------------
   Free requests
-----------------------------------------------------------------*/
void nodec_req_force_free(uv_req_t* uvreq) {
  if (uvreq != NULL) {
    if (uvreq->type == UV_FS) {
      uv_fs_req_cleanup((uv_fs_t*)uvreq);
    }
    nodec_free(uvreq);
  }
}

void nodec_req_force_freev(lh_value uvreq) {
  nodec_req_force_free(lh_ptr_value(uvreq));
}


// When deallocating a request, some special values in the data field will not deallocate 
// quite yet.

#define UVREQ_FREE_ON_RESUME          ((void*)(-3))
#define UVREQ_FREE_ON_OWNER_RELEASE   ((void*)(-1))

void nodec_req_free(uv_req_t* uvreq) {
  if ((uvreq != NULL && 
       uvreq->data != UVREQ_FREE_ON_OWNER_RELEASE) && 
       uvreq->data != UVREQ_FREE_ON_RESUME) 
  {
    nodec_req_force_free(uvreq);
  }
}

void nodec_req_freev(lh_value uvreq) {
  nodec_req_free(lh_ptr_value(uvreq));
}


/*-----------------------------------------------------------------
  Asynchronous requests
-----------------------------------------------------------------*/

typedef void(async_resume_fun)(lh_resume r, lh_value local, uv_req_t* req, int err);


// Every libuv request will have a pointer to our request structure in its
// `data` field. This allows us to resume to the `async_await` point.
// The actual resuming is done in the `resumefun` such that we can sometimes change
// the resume behaviour: normally it resumes but we can also emit results into 
// a channel for example.
struct _async_request_t {
  async_request_t*      next;
  async_request_t*      prev;
  lh_resume             resume;
  lh_value              local;
  const cancel_scope_t* scope;
  uv_req_t*             uvreq;
  uverr_t               canceled_err;
  void*                 owner;
  uint64_t              due;
  async_resume_fun*     resumefun;
};


async_request_t* async_request_alloc(uv_req_t* uvreq, bool nocancel, uint64_t timeout, void* owner) {
  async_request_t* req = nodec_zero_alloc(async_request_t);
  uvreq->data = req;
  req->uvreq = uvreq;
  req->owner = owner;
  req->scope = cancel_scope();
  if (timeout>0) {
    uint64_t now = async_loop()->time;
    req->due = (UINT64_MAX - now < timeout ? UINT64_MAX : now + timeout);
  }
  if (!nocancel || timeout!=0) async_req_register(req);
  return req;
}

static void async_request_free(async_request_t* req) {
  // unregister ourselves from outstanding requests
  async_request_t* prev = req->prev;
  if (req->prev != NULL) {    // only NULL for `nocancel` requests; otherwise always valid since we use a dummy head element
    prev->next = req->next;
    if (req->next != NULL) req->next->prev = prev; // link back
  }
  // and free
  nodec_free(req);
}

static void async_resume_default(lh_resume resume, lh_value local, uv_req_t* req, int err) {
  //lh_assert(r != NULL);
  if (resume != NULL) {
    lh_release_resume(resume, local, lh_value_int(err));
  }
}


static void async_request_resume(async_request_t* req, uv_req_t* uvreq, int err) {
  assert(uvreq!=NULL);
  assert(uvreq->data == req);
  assert(req->uvreq == NULL || req->uvreq == uvreq);
  if (req->uvreq != NULL && req->uvreq->data == req) { // invoke only once!
    // put resume arguments in locals
    async_resume_fun* resumefun = req->resumefun;
    lh_resume resume = req->resume;
    lh_value local = req->local;
    
    // if we were canceled explicitly, we cannot deallocate the orginal
    // request right away because it might still modified, or its callback
    // might be called once the request is satisfied. Therefore, if there
    // is an owner set, we put `UVREQ_FREE_ON_OWNER_RELEASE` in the 
    // uvrequest `data` field to signify
    // it should not yet be deallocated -- but only once the owner is
    // deallocated (usually a `uv_handle_t*`)._
    // We use `UVREQ_FREE_ON_RESUME` to signify to deallocate once the 
    // original callback is called.
    // this is for request that have no particular owner and where the callback
    // will be called! like most file system requests
    if (req->canceled_err!=0) {
      err = req->canceled_err;
      if (req->owner != NULL) {
        uvreq->data = UVREQ_FREE_ON_OWNER_RELEASE; // signify to not deallocate in `nodec_req_free`
        // we leave it in the outstanding request where it will be freed when
        // the `owner` (usually a `uv_handle_t`) gets released.
      }
      else {
        uvreq->data = UVREQ_FREE_ON_RESUME; // signify to not deallocate in `nodec_req_free`
        // we free the request object, and free the uv request once its callback
        // is called
        async_request_free(req);
      }
    }
    else {
      // unlink the request
      uvreq->data = NULL;
      async_request_free(req);
    }
    (*resumefun)(resume, local, uvreq, err);
  }
}



// The main entry point for regular request callbacks which will resume
// to the point where `async_await` was called on that request.
void async_req_resume(uv_req_t* uvreq, int err) {
  assert(uvreq != NULL);
  async_request_t* req = (async_request_t*)uvreq->data;
  if (req != NULL) {
    if (req == UVREQ_FREE_ON_RESUME) {
      // was explicitly canceled, deallocate now
      nodec_req_force_free(uvreq);
    }
    else if (req == UVREQ_FREE_ON_OWNER_RELEASE) {
      // was expliclitly canceled, but has an owner (usually a uv_handle_t) and
      // will be deallocated later when the owner is released
    }
    else {
      // regular resumption
      async_request_resume(req, uvreq, err);
    }
  }
}



/*-----------------------------------------------------------------
  Main Async Handler
-----------------------------------------------------------------*/

struct _async_local_t {
  uv_loop_t*      loop;      // current event loop
  async_request_t requests;  // empty request to be the head of the queue of outstanding requests
                             // these can include canceled request that are deallocated when
                             // their owner is released (`uvreq.data` == UVREQ_FREE_ON_OWNER_RELEASE)                              
  uv_timer_t*     periodic;  // interval timer. currently only used for timing out requests
                             // on a slow interval but in the future could be used for light
                             // weight timers sharing a single handle.
};

/*-----------------------------------------------------------------
  Timeouts and cancelation
-----------------------------------------------------------------*/

static void _periodic_force_timeout_cb(void* data) {
  uv_req_t* uvreq = (uv_req_t*)data;
  async_req_resume(uvreq, UV_ETIMEDOUT);
}

static void _periodic_cb(uv_timer_t* timer) {
  async_local_t* local = (async_local_t*)(timer->data);
  uint64_t now = timer->loop->time;
  uv_timer_again(timer);
  // check if any outstanding requests timed out.
  for (async_request_t* req = local->requests.next; req != NULL; req = req->next) {
    if (req->uvreq != NULL && req->canceled_err==0 && req->due != 0 && req->due < now) {
      // try primitive cancelation first; guarantees the callback is called with UV_ECANCELED
      req->canceled_err = UV_ETIMEDOUT;
      uv_errno_t err = uv_cancel(req->uvreq);
      if (err != 0) {
        // cancel failed; cancel it explicitly (through the eventloop instead using a 0 timeout)
        // this is risky as async_req_resume can be invoked twice and the first return might 
        // trigger deallocation of the uv_req_t structure which would be too early. Therefore,
        // we set its `data` field to `UVREQ_FREE_ON_XXX` and check for that before deallocating a request.
        _uv_set_timeout(local->loop, &_periodic_force_timeout_cb, req->uvreq, 0);
        // todo: dont ignore errors here?
      }
    }
  }
}

static void _explicit_cancel_cb(void* data) {
  uv_req_t* uvreq = (uv_req_t*)data;
  async_req_resume(uvreq, UV_ETHROWCANCEL);
}

static lh_value _async_uv_cancel(lh_resume resume, lh_value localv, lh_value scopev) {
  async_local_t* local = (async_local_t*)lh_ptr_value(localv);
  const cancel_scope_t* scope = lh_cancel_scope_ptr_value(scopev);
  for (async_request_t* req = local->requests.next; req != NULL; req = req->next) {
    if (req->uvreq != NULL && req->canceled_err==0 && in_scope_of(req->scope, scope)) {
      // try primitive cancelation first; guarantees the callback is called with UV_ECANCELED
      req->canceled_err = UV_ETHROWCANCEL;
      uv_errno_t err = uv_cancel(req->uvreq);
      if (err != 0) {
        // cancel failed; cancel it explicitly (through the eventloop instead using a 0 timeout)
        // this is risky as async_req_resume can be invoked twice and the first return might 
        // trigger deallocation of the uv_req_t structure which would be too early. Therefore,
        // we set its `data` field to `UVREQ_FREE_ON_XXX` and check for that before deallocating a request.
        _uv_set_timeout(local->loop, &_explicit_cancel_cb, req->uvreq, 0);
        // todo: dont ignore errors here?
      }
    }
  }
  return lh_tail_resume(resume, localv, lh_value_null);
}

/*-----------------------------------------------------------------
  Main Async Handler primitives
-----------------------------------------------------------------*/

// Await an asynchronous request
static lh_value _async_req_await(lh_resume resume, lh_value local, lh_value arg) {
  async_request_t* req = lh_async_request_ptr_value(arg);
  assert(req != NULL);
  assert(req->uvreq != NULL);
  assert(req->uvreq->data == req);
  req->local = local;
  req->resume = resume;
  if (req->resumefun==NULL) req->resumefun = &async_resume_default;
  return lh_value_null;  // this exits our async handler back to the main event loop
}

// Return the current libUV event loop
static lh_value _async_uv_loop(lh_resume r, lh_value localv, lh_value arg) {
  async_local_t* local = (async_local_t*)lh_ptr_value(localv);
  return lh_tail_resume(r, localv, lh_value_ptr(local->loop));
}

// Register an outstanding request
static lh_value _async_req_register(lh_resume r, lh_value localv, lh_value arg) {
  async_local_t* local = (async_local_t*)lh_ptr_value(localv);
  async_request_t* req = lh_async_request_ptr_value(arg);
  assert(req != NULL);
  // initialize periodic timer if necessary
  if (local->periodic==NULL && req->due != 0) {
    local->periodic = nodecx_zero_alloc(uv_timer_t);
    if (local->periodic != NULL) {
      uv_timer_init(local->loop, local->periodic);
      local->periodic->data = local;
      uv_timer_start(local->periodic, &_periodic_cb, 500, 500);
    }
  }
  // insert in front
  req->next = local->requests.next;
  if (req->next != NULL) req->next->prev = req;             // link back
  req->prev = &local->requests;   
  local->requests.next = req;
  return lh_tail_resume(r, localv, lh_value_null);
}

static bool uvreq_is_pending(uv_req_t* uvreq) {
  if (uvreq == NULL) return false;
  switch (uvreq->type) {
  case UV_WRITE: {
#ifdef _WIN32
    uv_stream_t* s = ((uv_write_t*)uvreq)->handle;
    bool pending = s->reqs_pending > 0;
#else
    // TODO: do the right check for unix
    bool pending = true; // conservative for now
#endif
    return pending;
  }
  case UV_UNKNOWN_REQ: // Our internal requests for timers for example
    return false;  
  default: 
    return true; // conservate for now; this might delay deallocation of requests but is safe
  }
}

// Deallocate outstanding requests when their owner is released
// This usualy only happens for explicitly canceled requests
static lh_value _async_owner_release(lh_resume r, lh_value localv, lh_value arg) {
  async_local_t* local = (async_local_t*)lh_ptr_value(localv);
  void*          owner = lh_ptr_value(arg);
  if (owner != NULL) {
    for (async_request_t* req = local->requests.next; req != NULL; ) {  // TODO: linear lookup -- but probably ok.
      async_request_t* next = req->next;
      if (req->canceled_err != 0 && req->owner == owner) {
        assert(req->uvreq != NULL && req->uvreq->data == UVREQ_FREE_ON_OWNER_RELEASE);
        // We cannot always free a request at this point either.. it might
        // still be in the pending request queue of the uv event loop
        // we check for that first:
        if (uvreq_is_pending(req->uvreq)) {
          // assume it will be called later on, and set the data to `UVREQ_FREE_ON_RESUME` to be deallocated 
          // at that time
          req->uvreq->data = UVREQ_FREE_ON_RESUME;
        }
        else {
          // free it now
          nodec_req_force_free(req->uvreq);
        }
        async_request_free(req);  // will unlink properly
      }
      req = next;
    }
  }
  return lh_tail_resume(r, localv, lh_value_null);
}

static void _async_release(lh_value localv) {
  async_local_t* local = (async_local_t*)lh_ptr_value(localv);
  assert(local != NULL);
  assert(local->requests.next == NULL);
  // stop reaper interval
  if (local->periodic!=NULL) {
    uv_timer_stop(local->periodic);
    nodec_free(local->periodic);
    local->periodic = NULL;
  }
  // paranoia: clean up any left over outstanding requests
  for (async_request_t* req = local->requests.next; req != NULL; ) { 
    async_request_t* next = req->next;
    nodec_req_force_free(req->uvreq);
    async_request_free(req);  // will unlink properly
    req = next;
  }
  nodec_free(local);
}



// The main async handler
static const lh_operation _async_ops[] = {
  { LH_OP_GENERAL, LH_OPTAG(async,req_await), &_async_req_await },
  { LH_OP_TAIL_NOOP, LH_OPTAG(async,uv_loop), &_async_uv_loop },
  { LH_OP_TAIL_NOOP, LH_OPTAG(async,req_register), &_async_req_register },
  { LH_OP_TAIL_NOOP, LH_OPTAG(async,uv_cancel), &_async_uv_cancel },
  { LH_OP_TAIL_NOOP, LH_OPTAG(async,owner_release), &_async_owner_release },
  { LH_OP_NULL, lh_op_null, NULL }
};
static const lh_handlerdef _async_def = { LH_EFFECT(async), NULL, _async_release, NULL, _async_ops };


lh_value async_handler(uv_loop_t* loop, lh_value(*action)(lh_value), lh_value arg) {
  async_local_t* local = nodecx_zero_alloc(async_local_t);
  if (local == NULL) return lh_value_null;
  local->loop = loop;
  local->requests.next = NULL;
  local->requests.prev = NULL;
  return lh_handle(&_async_def, lh_value_ptr(local), action, arg);  
}


/*-----------------------------------------------------------------
  Channel async handler for interleave
-----------------------------------------------------------------*/

static lh_value _channel_async_req_await(lh_resume r, lh_value local, lh_value arg) {
  async_request_t* req = lh_async_request_ptr_value(arg);
  assert(req != NULL);
  assert(req->uvreq != NULL);
  assert(req->uvreq->data == req);
  req->resume = r;
  req->local = local;
  if (req->resumefun==NULL) req->resumefun = &_channel_async_req_resume;
  return lh_value_null;  // exit to our local async handler back to interleaved
}

static const lh_operation _channel_async_ops[] = {
  { LH_OP_GENERAL, LH_OPTAG(async,req_await), &_channel_async_req_await },
  { LH_OP_FORWARD, LH_OPTAG(async,uv_loop), NULL },
  { LH_OP_FORWARD, LH_OPTAG(async,req_register), NULL },
  { LH_OP_FORWARD, LH_OPTAG(async,uv_cancel), NULL },
  { LH_OP_FORWARD, LH_OPTAG(async,owner_release), NULL },
  { LH_OP_NULL, lh_op_null, NULL }
};
static const lh_handlerdef _channel_async_hdef = { LH_EFFECT(async), NULL, NULL, NULL, _channel_async_ops };

// The channel async handler
// Resume by emmitting a local resume into a channel
void _channel_async_req_resume(lh_resume r, lh_value local, uv_req_t* req, int err) {
  assert(r != NULL);
  assert(local != lh_value_null);
  if (r != NULL) {
    channel_emit((channel_t*)lh_ptr_value(local), lh_value_ptr(r), local, err);
  }
}

lh_value _channel_async_handler(channel_t* channel, lh_actionfun* action, lh_value arg) {
  return lh_handle(&_channel_async_hdef, lh_value_ptr(channel), action, arg);
}

/*-----------------------------------------------------------------
Main wrapper
-----------------------------------------------------------------*/
static lh_value uv_main_action(lh_value ventry) {
  nodec_main_fun_t* entry = (nodec_main_fun_t*)lh_fun_ptr_value(ventry);
  {using_tty() {
    {using_log(LOG_DEFAULT) {
      entry();
    }}
  }}
  return lh_value_null;
}

static lh_value uv_main_try_action(lh_value entry) {
  lh_exception* exn;
  {using_outer_cancel_scope() {
    lh_try(&exn, uv_main_action, entry);
    if (exn != NULL) {
      fprintf(stderr,"\nNodeC: unhandled exception: %s\n", exn->msg);
      lh_exception_free(exn);
    }
  }}
  return lh_value_null;
}

static void uv_main_cb(uv_timer_t* t_start) {
  async_handler(t_start->loop, &uv_main_try_action, lh_value_fun_ptr(*((nodec_main_fun_t**)t_start->data)));
  nodec_timer_free(t_start,false);
}

uv_errno_t async_main( nodec_main_fun_t* entry  ) {
  uv_replace_allocator(&_nodecx_malloc, &_nodecx_realloc, &_nodecx_calloc, (void(*)(void*))(&_nodec_free));
  uv_loop_t* loop = nodecx_zero_alloc(uv_loop_t);
  if (loop == NULL) return UV_ENOMEM;
  uv_errno_t err = uv_loop_init(loop);
  if (err == 0) {
    uv_timer_t* t_start = nodecx_zero_alloc(uv_timer_t);
    if (t_start == NULL) err = UV_ENOMEM;
    if (err == 0) {
      err = uv_timer_init(loop, t_start);
      if (err == 0) {
        t_start->data = &entry;  
        err = uv_timer_start(t_start, &uv_main_cb, 0, 0);
        if (err == 0) {
          // printf("starting event loop\n");
          err = uv_run(loop, UV_RUN_DEFAULT);
          t_start = NULL;
        }
      }
    }
    if (t_start != NULL) nodec_free(t_start);
  }
  uv_loop_close(loop);
  nodec_free(loop);
  nodec_check_memory();
  lh_debug_wait_for_enter();
  return err;
}


<|start_filename|>docs/api/search/groups_a.js<|end_filename|>
var searchData=
[
  ['streams',['Streams',['../group__streams.html',1,'']]]
];


<|start_filename|>docs/api/search/groups_1.js<|end_filename|>
var searchData=
[
  ['buffer_27s',['Buffer&apos;s',['../group__buffers.html',1,'']]]
];


<|start_filename|>docs/api/search/variables_d.js<|end_filename|>
var searchData=
[
  ['read_5fbuf_5fsize',['read_buf_size',['../group__nodec__static.html#a31d97bb6440fc5992cb56fc84d0725fa',1,'http_static_config_t']]],
  ['resultfun',['resultfun',['../group__effect__handlers.html#afe5be74288279eb0ec913ca483942ddd',1,'lh_handlerdef']]]
];


<|start_filename|>docs/api/search/typedefs_2.js<|end_filename|>
var searchData=
[
  ['nodec_5factionfun_5ft',['nodec_actionfun_t',['../group__nodec__async.html#ga29149a2bf87759864793e9cfe65f1354',1,'nodec.h']]],
  ['nodec_5fbstream_5ft',['nodec_bstream_t',['../group__streams.html#gadaf4d5e2ac9a12cc14da8fa006d85916',1,'nodec.h']]],
  ['nodec_5ffile_5ffun',['nodec_file_fun',['../group__nodec__fs.html#ga340bba31d68b120ee5416378d133d4e3',1,'nodec.h']]],
  ['nodec_5fmain_5ffun_5ft',['nodec_main_fun_t',['../group__nodec__various.html#ga50770568a93c37eaa128d15aefb9ff49',1,'nodec.h']]],
  ['nodec_5fscandir_5ft',['nodec_scandir_t',['../group__nodec__fs.html#ga61fb79bbfca6d44d44f575ace13a53e1',1,'nodec.h']]],
  ['nodec_5fstream_5ft',['nodec_stream_t',['../group__streams.html#gaee920c8bd173ce7bf3b57f42cbcf3f5f',1,'nodec.h']]],
  ['nodec_5ftcp_5fconnection_5ffun_5ft',['nodec_tcp_connection_fun_t',['../group__tcp.html#ga6e7766bd8a316202634ad28c1c2a7d6d',1,'nodec.h']]],
  ['nodec_5furl_5ft',['nodec_url_t',['../group__nodec__url.html#gaa2a37a3b5cc32bd3cb0c7eebe1eb397f',1,'nodec.h']]]
];


<|start_filename|>docs/api/structlh__optag__.js<|end_filename|>
var structlh__optag__ =
[
    [ "effect", "structlh__optag__.html#aaba7e2907daa4422cd4f47e91e1b171d", null ],
    [ "opidx", "structlh__optag__.html#ad756a8e13a174bcb47041076d8611593", null ]
];

<|start_filename|>docs/api/group__nodec__mime.js<|end_filename|>
var group__nodec__mime =
[
    [ "nodec_ext_from_mime", "group__nodec__mime.html#gaa4dc59d468820d27e2001dfc66b018fa", null ],
    [ "nodec_info_from_mime", "group__nodec__mime.html#gaccb1fd0c6fa867bb593a6b58cf8756a4", null ],
    [ "nodec_mime_from_fname", "group__nodec__mime.html#ga7b0602fccd1407557b341e4d4a5c808b", null ],
    [ "nodec_mime_info_from_fname", "group__nodec__mime.html#ga3561c055d8e934a49e64105f78dfd4d6", null ]
];

<|start_filename|>docs/api/search/all_6.js<|end_filename|>
var searchData=
[
  ['file_20system',['File System',['../group__nodec__fs.html',1,'']]]
];


<|start_filename|>msvc-build.bat<|end_filename|>
@echo off
cd %~dp0
set curdir=%CD%

@rem The visual studio solutions now target a specific windows SDK :-(
set winsdk=10.0.17134.0

@rem Defaults
set config=Debug
set platform=x64
set vsplatform=x64
set target=Build

@rem Help?
if /i "%1"=="help" goto help
if /i "%1"=="--help" goto help
if /i "%1"=="-help" goto help
if /i "%1"=="/help" goto help
if /i "%1"=="?" goto help
if /i "%1"=="-?" goto help
if /i "%1"=="--?" goto help
if /i "%1"=="/?" goto help

@rem Process arguments
:next-arg
if "%1"=="" goto args-done
if /i "%1"=="debug"        set config=Debug&goto arg-ok
if /i "%1"=="release"      set config=Release&goto arg-ok
if /i "%1"=="x86"          set platform=x86&set vsplatform=Win32&goto arg-ok
if /i "%1"=="win32"        set platform=x86&set vsplatform=Win32&goto arg-ok
if /i "%1"=="x64"          set platform=x64&set vsplatform=x64&goto arg-ok
:arg-ok
shift
goto next-arg
:args-done

echo Looking for Visual Studio 2017
@rem Check if VS2017 is already setup, and for the requested arch.
if "_%VisualStudioVersion%_" == "_15.0_" if "_%VSCMD_ARG_TGT_ARCH%_"=="_%platform%_" goto found_vs2017
set "VSINSTALLDIR="
call deps\libuv\tools\vswhere_usability_wrapper.cmd
if "_%VCINSTALLDIR%_" == "__" goto error-need-vs2017
@rem Need to clear VSINSTALLDIR for vcvarsall to work as expected.
@rem Keep current working directory after call to vcvarsall
set "VSCMD_START_DIR=%CD%"
set vcvars_call="%VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat" %platform%
echo calling: %vcvars_call%
call %vcvars_call%
if "_%VisualStudioVersion%_" == "_15.0_" if "_%VSCMD_ARG_TGT_ARCH%_"=="_%platform%_" goto found_vs2017
goto error-need-vs2017

:found_vs2017
echo Found MSVC version %VisualStudioVersion%

@rem Check if VS build env is available
if not defined VCINSTALLDIR goto error-need-msbuild
if not defined WindowsSDKDir goto error-need-msbuild

@rem Check Windows SDK version
if "_%WINDOWSSDKLIBVERSION%_" NEQ "_%winsdk%\_" goto error-wrong-winsdk

@rem Check for Python
if not defined PYTHON set PYTHON=python
@"%PYTHON%" --version
if errorlevel 1 goto error-need-python /b 1

echo.
echo Start build...


@rem Build libhandler
echo Build Libhandler... for %platform%, %vsplatform%
echo msbuild deps/libhandler/ide/msvc/libhandler.vcxproj /t:%target% /p:Configuration=%config% /p:Platform="%vsplatform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
msbuild deps/libhandler/ide/msvc/libhandler.vcxproj /t:%target% /p:Configuration=%config% /p:Platform="%vsplatform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
if errorlevel 1 goto error-build /b 1

@rem Build libuv
echo.
echo Build LibUV...
call deps/libuv/vcbuild.bat %platform% %config% vs2017 static
cd %curdir%
if errorlevel 1 goto error-build /b 1

@rem Build zlib
echo.
echo Build ZLib...
set zlib-config=%config%
if "_%zlib-config%_"=="_Release_" set zlib-config=ReleaseWithoutAsm
echo msbuild deps/zlib/contrib/vstudio/vc14/zlibstat.vcxproj /t:%target% /p:Configuration=%zlib-config% /p:Platform="%vsplatform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
msbuild deps/zlib/contrib/vstudio/vc14/zlibstat.vcxproj /t:%target% /p:Configuration=%zlib-config% /p:Platform="%vsplatform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
if errorlevel 1 goto error-build /b 1

@rem Build mbedTLS
echo.
echo Build mbedTLS...
copy /Y deps\mbedTLS-vs2017.vcxproj deps\mbedtls\visualc\VS2010
echo msbuild deps/mbedtls/visualc/VS2010/mbedTLS-vs2017.vcxproj /t:%target% /p:Configuration=%config% /p:Platform="%vsplatform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
msbuild deps/mbedtls/visualc/VS2010/mbedTLS-vs2017.vcxproj /t:%target% /p:Configuration=%config% /p:Platform="%vsplatform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
if errorlevel 1 goto error-build /b 1


@rem Build NodeC
echo Build NodeC...
msbuild ide/msvc/nodec.vcxproj /t:%target% /p:Configuration=%config% /p:Platform="%vsplatform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
if errorlevel 1 goto error-build /b 1


:done
echo.
echo Success!
echo NodeC library generated as : "out/msvc-%vsplatform%/nodecx/%config%/nodecx.lib"
echo NodeC includes are found in: "out/msvc-%vsplatform%/nodecx/inc"
echo done.
goto end

:error-need-vs2017
echo ERROR: cannot find Visual Studio 2017. 
echo You can install the free community edition from: https://visualstudio.microsoft.com/downloads
goto end

:error-need-msbuild
echo ERROR: cannot find msbuild, this file needs to run from the Visual Studio Developer command prompt.
goto end

:error-need-python
echo ERROR: cannot find Python -- it is needed to build libuv.
echo Either set the PYTHON environment variable, or
echo Install Python 2.7 from: https://www.python.org/downloads/release/python-2715
goto end

:error-wrong-winsdk
echo ERROR: Wrong Windows SDK version! Found "%WINDOWSSDKLIBVERSION%", but need "%winsdk%\".
echo Try "https://blogs.msdn.microsoft.com/chuckw/2018/05/02/windows-10-april-2018-update-sdk/" to upgrade the Windows SDK?
goto end

:error-build
echo ERROR: Build error.
goto end

:help
echo msvc-build.bat [debug/release] [x86/x64]
echo Examples:
echo   msvc-build.bat            : builds debug x64 
echo   msvc-build.bat release x86: builds 32bit release build 
goto end


:end


<|start_filename|>docs/api/group__nodec__fs.js<|end_filename|>
var group__nodec__fs =
[
    [ "uv_timespec_t", "group__nodec__fs.html#structuv__timespec__t", [
      [ "tv_nsec", "group__nodec__fs.html#af1552c618f27286c9734be801d0ecf3f", null ],
      [ "tv_sec", "group__nodec__fs.html#a6c1e6a2cd2d9f10dc4df27ab3a79bc9d", null ]
    ] ],
    [ "uv_stat_t", "group__nodec__fs.html#structuv__stat__t", [
      [ "st_atim", "group__nodec__fs.html#a90374cf48291ecde29394d1982b9eec8", null ],
      [ "st_birthtim", "group__nodec__fs.html#a324b6bb9a3dcb70259e0195379c5f9ef", null ],
      [ "st_blksize", "group__nodec__fs.html#af549bc8ae4793b83fa25b97fc5518e67", null ],
      [ "st_blocks", "group__nodec__fs.html#a7cb4ce091e3b7418f368451dd60e535e", null ],
      [ "st_ctim", "group__nodec__fs.html#a7785fcfd6909d22b07f8461219ca2133", null ],
      [ "st_dev", "group__nodec__fs.html#ac1f0764990fe34d0b21f7e3af9fef946", null ],
      [ "st_flags", "group__nodec__fs.html#a14bc9565a316b65e422dde506d4c6428", null ],
      [ "st_gen", "group__nodec__fs.html#ab5036343ef8adaabecc2362f5905b9cb", null ],
      [ "st_gid", "group__nodec__fs.html#aa6d1907b84accf5662140cb0834e479d", null ],
      [ "st_ino", "group__nodec__fs.html#a1442738c45923c0a23f7ee0c2837f584", null ],
      [ "st_mode", "group__nodec__fs.html#a198c4352c514e9eff06e7363c3562a8f", null ],
      [ "st_mtim", "group__nodec__fs.html#a89f339b980cacb16da47d7a7c04f7e8a", null ],
      [ "st_nlink", "group__nodec__fs.html#a05b9207a1a204eae78b2c7dc0d3812ab", null ],
      [ "st_rdev", "group__nodec__fs.html#af0cc8e07491b42cd8b82d797ec5af0b4", null ],
      [ "st_size", "group__nodec__fs.html#a454ac6f7cef9a00d73baf2028fe3a7cb", null ],
      [ "st_uid", "group__nodec__fs.html#ac56d6239041749e7bc4ccc472aa800b1", null ]
    ] ],
    [ "uv_dirent_t", "group__nodec__fs.html#structuv__dirent__t", [
      [ "name", "group__nodec__fs.html#ac7ce6fb281bd218f7821f6c52a63a737", null ],
      [ "type", "group__nodec__fs.html#a7343234f6f445689c599b147eeac9658", null ]
    ] ],
    [ "R_OK", "group__nodec__fs.html#ga24a7a0ceb1e8f5f44cb1038bb829030d", null ],
    [ "using_fs_scandir", "group__nodec__fs.html#ga6dd6912567933b893bc5d548f7c93b6e", null ],
    [ "W_OK", "group__nodec__fs.html#gae5acf4043c0903cda7436b108e29e8e6", null ],
    [ "X_OK", "group__nodec__fs.html#ga5e995ef7690698b62bf016b14fbc45ca", null ],
    [ "nodec_file_fun", "group__nodec__fs.html#ga340bba31d68b120ee5416378d133d4e3", null ],
    [ "nodec_scandir_t", "group__nodec__fs.html#ga61fb79bbfca6d44d44f575ace13a53e1", null ],
    [ "uv_file", "group__nodec__fs.html#ga0c35c148bf40627f47bc6c2eeb19ea77", null ],
    [ "nodec_open_flags_t", "group__nodec__fs.html#ga6106956895e925e621b1ddfb478d3091", [
      [ "UV_FS_O_APPEND", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a5e55c321069fbde51d1c73c7b6380db3", null ],
      [ "UV_FS_O_CREAT", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a01151e2942507154f8e694a950183720", null ],
      [ "UV_FS_O_EXCL", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a7b4d0987e0ad7d469157390da427b744", null ],
      [ "UV_FS_O_RANDOM", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a341aae3e5d903898b8a3155c4d4456de", null ],
      [ "UV_FS_O_RDONLY", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091aea8b2813a8dbc3239b75518fac33a782", null ],
      [ "UV_FS_O_RDWR", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091ab7bd494e74106113bccb7159ab5269cc", null ],
      [ "UV_FS_O_SEQUENTIAL", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a0f3915adbc9debeee85b7aea1c0de2fa", null ],
      [ "UV_FS_O_SHORT_LIVED", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a0b92d55ae479bd0f30ea3320470955c7", null ],
      [ "UV_FS_O_TEMPORARY", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a8180afffb5f59a3f056015cfcc425730", null ],
      [ "UV_FS_O_TRUNC", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a9d4d3b852ae0eeffd11d3bb2cc05f928", null ],
      [ "UV_FS_O_WRONLY", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091aa6ab9adaedf4911b17a998aaf84e2618", null ],
      [ "UV_FS_O_DIRECT", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091ab04df9a4b5eab498be39ccbc9ee33793", null ],
      [ "UV_FS_O_DIRECTORY", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a55de3a2fc80e58c471437064df836fcd", null ],
      [ "UV_FS_O_DSYNC", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091aedd8398c1ed440fa728bf1e9401e70c4", null ],
      [ "UV_FS_O_EXLOCK", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a396c5ed401245f1d9a07571852db822a", null ],
      [ "UV_FS_O_NOATIME", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a56ce6efd72a88357032a119468975998", null ],
      [ "UV_FS_O_NOCTTY", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a10938f94907d49f2ef35b72c12b91f78", null ],
      [ "UV_FS_O_NOFOLLOW", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a92e37f26361e14c4b682957ed3953d44", null ],
      [ "UV_FS_O_NONBLOCK", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a0d3b834b425d85f5cda06e0a502c7e03", null ],
      [ "UV_FS_O_SYMLINK", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091af98cfcc223663708b6dbf9c725fd2b72", null ],
      [ "UV_FS_O_SYNC", "group__nodec__fs.html#gga6106956895e925e621b1ddfb478d3091a0c5d789e712be5a9a5ead4be23dbb689", null ]
    ] ],
    [ "uv_dirent_type_t", "group__nodec__fs.html#gab17cb1a692145e20d98e980c343a1335", [
      [ "UV_DIRENT_UNKNOWN", "group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335a947786154c585ee4d79c61f260565b9b", null ],
      [ "UV_DIRENT_FILE", "group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335ad63276ed636f67c2fb72c1876e500d8c", null ],
      [ "UV_DIRENT_DIR", "group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335a8328c19ef64ea79b0526b3dae9f3a555", null ],
      [ "UV_DIRENT_LINK", "group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335ab72e22fb2ed44083615274b541a16360", null ],
      [ "UV_DIRENT_FIFO", "group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335a0fea07d0ee17d3cd9394682eb937be8b", null ],
      [ "UV_DIRENT_SOCKET", "group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335ad6adf83f62c4773850963ef9a8fa26de", null ],
      [ "UV_DIRENT_CHAR", "group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335a3ab846891d83853555d15168a01d37c1", null ],
      [ "UV_DIRENT_BLOCK", "group__nodec__fs.html#ggab17cb1a692145e20d98e980c343a1335aa070fc602168c3bf34807eba51cd48a2", null ]
    ] ],
    [ "async_fs_close", "group__nodec__fs.html#ga8d7a8b33060538966cd96341b3f50f7f", null ],
    [ "async_fs_fstat", "group__nodec__fs.html#ga8c16a77ce261f6b8e012fe83cb15c58d", null ],
    [ "async_fs_open", "group__nodec__fs.html#gac2bf74e83604a17f5cef879ec99cb4f6", null ],
    [ "async_fs_read_buf", "group__nodec__fs.html#ga97387ae4fd2fddfcaf964ab3941bd184", null ],
    [ "async_fs_read_buf_all", "group__nodec__fs.html#ga02e4f8c156fb631d167cd0488ae7d5cf", null ],
    [ "async_fs_read_buf_from", "group__nodec__fs.html#gaa6fc903d284054a673cc931e276a848b", null ],
    [ "async_fs_read_from", "group__nodec__fs.html#gae3b908d2c12c6bb8d2ed8493d69a0e6a", null ],
    [ "async_fs_read_into", "group__nodec__fs.html#ga1e1887c16a425c58545788efb5b7cd9b", null ],
    [ "async_fs_scandir", "group__nodec__fs.html#ga3cc87b8f36d127ef984319f00e76a40c", null ],
    [ "async_fs_scandir_next", "group__nodec__fs.html#gafa78969b259943998622377b987cff5c", null ],
    [ "async_fs_stat", "group__nodec__fs.html#ga3ebae44242383c0a5a16b42641e2230a", null ],
    [ "asyncx_fs_open", "group__nodec__fs.html#ga3937ef3449947843429fdc056ed7031c", null ],
    [ "asyncx_fs_stat", "group__nodec__fs.html#ga87628a7a27ae3ad2f0605e9cace40f9c", null ],
    [ "using_async_fs_open", "group__nodec__fs.html#gac2c5469313c7fc23b4578d546ff5dde2", null ],
    [ "using_asyncx_fs_open", "group__nodec__fs.html#ga2f19335f15eaf41304f16e195ae8eba2", null ]
];

<|start_filename|>docs/api/group__effect__handlers_structlh__optag__.js<|end_filename|>
var group__effect__handlers_structlh__optag__ =
[
    [ "effect", "group__effect__handlers.html#aaba7e2907daa4422cd4f47e91e1b171d", null ],
    [ "opidx", "group__effect__handlers.html#ad756a8e13a174bcb47041076d8611593", null ]
];

<|start_filename|>docs/api/search/all_e.js<|end_filename|>
var searchData=
[
  ['r_5fok',['R_OK',['../group__nodec__fs.html#ga24a7a0ceb1e8f5f44cb1038bb829030d',1,'nodec-extra-doc.h']]],
  ['read_5fbuf_5fsize',['read_buf_size',['../group__nodec__static.html#a31d97bb6440fc5992cb56fc84d0725fa',1,'http_static_config_t']]],
  ['resultfun',['resultfun',['../group__effect__handlers.html#afe5be74288279eb0ec913ca483942ddd',1,'lh_handlerdef']]]
];


<|start_filename|>docs/api/group__nodec__various.js<|end_filename|>
var group__nodec__various =
[
    [ "NODEC_GB", "group__nodec__various.html#ga12d650b02a11f47f9d14b2267438bde7", null ],
    [ "NODEC_KB", "group__nodec__various.html#ga73adc7fccbee908a44707c93620b35b6", null ],
    [ "NODEC_MB", "group__nodec__various.html#gad8c50b6c480fd1c09e40a5eaa36f87bb", null ],
    [ "nodec_main_fun_t", "group__nodec__various.html#ga50770568a93c37eaa128d15aefb9ff49", null ],
    [ "async_main", "group__nodec__various.html#ga8ca7a8eead029327f56e7181199eea4f", null ],
    [ "nodec_ends_with", "group__nodec__various.html#gab48824ff3f0fab32ddef8ac00155ab10", null ],
    [ "nodec_ends_withi", "group__nodec__various.html#ga5fd5694004902170bbff8bff8b92ad88", null ],
    [ "nodec_inet_date", "group__nodec__various.html#gaf84590dca27af53d91189e599f524b54", null ],
    [ "nodec_inet_date_now", "group__nodec__various.html#ga52af212bb34613e5079d1ddf3eb870c0", null ],
    [ "nodec_parse_inet_date", "group__nodec__various.html#gaa91b31eb8f2fe9557323440b9e1240b7", null ],
    [ "nodec_starts_with", "group__nodec__various.html#ga1f6e5d595c708809db28efd4c4506241", null ],
    [ "nodec_starts_withi", "group__nodec__various.html#ga1e6a44db408192675d8923ac4ea03ebc", null ],
    [ "nodec_throw", "group__nodec__various.html#ga21d7c9d7a03822b0263fb3431e7a3e3b", null ],
    [ "nodec_throw_msg", "group__nodec__various.html#ga43af10da1f49d2ef6fb7af2118ad5960", null ]
];