diff --git a/bos/drivers/b_drv_esp12f.c b/bos/drivers/b_drv_esp12f.c
index 2f04ec1be9732c9396412b4baaee14500fb3dbee..50d032401c0b07189feb1dccecefdb9539e68981 100644
--- a/bos/drivers/b_drv_esp12f.c
+++ b/bos/drivers/b_drv_esp12f.c
@@ -221,7 +221,11 @@ static void _bAtCmdCb(uint8_t isok, void *user_data)
     b_log_w("at result:%d\r\n", isok);
 }
 
-static void *bAtNewDataMalloc(uint8_t *pbuf, uint16_t len)
+static void _bEsp12fFree(void *addr)
+{
+    bFree(addr);
+}
+static void *_bAtNewDataMalloc(uint8_t *pbuf, uint16_t len)
 {
     if (len == 0 || pbuf == NULL)
     {
@@ -242,11 +246,11 @@ static void *bAtNewDataMalloc(uint8_t *pbuf, uint16_t len)
     memcpy(ptmp, pbuf, len);
     pdat->len     = len;
     pdat->pbuf    = ptmp;
-    pdat->release = bFree;
+    pdat->release = _bEsp12fFree;
     return pdat;
 }
 
-static void bAtNewDataFree(void *p)
+static void _bAtNewDataFree(void *p)
 {
     bTcpUdpData_t *dat = (bTcpUdpData_t *)p;
     if (dat == NULL)
@@ -286,11 +290,11 @@ static void _bAtNewDataCb(uint8_t *pbuf, uint16_t len, void (*pfree)(void *), vo
                 p = strstr(p, ":");
                 p = p + 1;
                 b_log("read:%p, %d\r\n", p, rlen);
-                pdat = (bTcpUdpData_t *)bAtNewDataMalloc((uint8_t *)p, rlen);
+                pdat = (bTcpUdpData_t *)_bAtNewDataMalloc((uint8_t *)p, rlen);
                 if (pdat != NULL)
                 {
                     memcpy(&pdat->conn, &_priv->conn[conn_id].info, sizeof(bTcpUdpInfo_t));
-                    _priv->cb.cb(B_EVT_CONN_NEW_DATA, pdat, bAtNewDataFree, _priv->cb.user_data);
+                    _priv->cb.cb(B_EVT_CONN_NEW_DATA, pdat, _bAtNewDataFree, _priv->cb.user_data);
                 }
             }
         }
@@ -747,7 +751,7 @@ int bESP12F_Init(bDriverInterface_t *pdrv)
 #endif
 bDRIVER_REG_INIT(B_DRIVER_ESP12F, bESP12F_Init);
 #ifdef BSECTION_NEED_PRAGMA
-#pragma section 
+#pragma section
 #endif
 
 /**
diff --git a/bos/modules/b_mod_wifi.c b/bos/modules/b_mod_wifi.c
index 809c0233fd836cd59305754162e47be9bf9b08d1..2e3d1dfc6967cd7c491cce3a79e7a4c1e112f7d1 100644
--- a/bos/modules/b_mod_wifi.c
+++ b/bos/modules/b_mod_wifi.c
@@ -155,6 +155,11 @@ static bWifiModule_t bWifiModule = {
  * \{
  */
 
+static void _bWifiFree(void *addr)
+{
+    bFree(addr);
+}
+
 static void _bWifiResult(uint8_t cmd, uint8_t isok, void *arg, void (*release)(void *))
 {
     bWifiModule.busy = 0;
@@ -607,7 +612,7 @@ int bWifiSend(const char *remote, uint16_t port, uint8_t *pbuf, uint16_t len)
     }
     memcpy(dat.pbuf, pbuf, len);
     dat.len     = len;
-    dat.release = bFree;
+    dat.release = _bWifiFree;
     retval      = bCtl(bWifiModule.fd, bCMD_WIFI_TCPUDP_SEND, &dat);
     if (retval >= 0)
     {
diff --git a/bos/services/Kconfig b/bos/services/Kconfig
index 472f96e17a32e60c5d13c7c6cfcb337c94a06594..e21fdfb3a5394a02d03be1bb9e62258503b8b330 100644
--- a/bos/services/Kconfig
+++ b/bos/services/Kconfig
@@ -14,6 +14,8 @@ config _TCPIP_SERVICE_ENABLE
     bool "TCPIP Service Enable/Disable"
     default n
     depends on _BOS_SERVICES_ENABLE 
+    select _HTTP_PARSER_ENABLE
+    select _MEMP_ENABLE
 
 config _NTP_SERVER_1
     string "ntp server 1"
@@ -30,4 +32,15 @@ config _NTP_SERVER_3
     default "ntp3.aliyun.com"
     depends on _TCPIP_SERVICE_ENABLE    
 
+config _HTTP_HOST_LEN_MAX
+    int "The maximum length of a URL host"
+    default 64
+    depends on _TCPIP_SERVICE_ENABLE 
+
+config _HTTP_PATH_LEN_MAX
+    int "The maximum length of a URL path"
+    default 128
+    depends on _TCPIP_SERVICE_ENABLE
+
+
 endmenu
diff --git a/bos/services/b_srv_tcpip.c b/bos/services/b_srv_tcpip.c
index ad7e5c4cfa86b4b339df7cb05d2dd0b0eb02b19c..bc3987c9237931fbe9921ea96bc48abf8e030c44 100644
--- a/bos/services/b_srv_tcpip.c
+++ b/bos/services/b_srv_tcpip.c
@@ -34,11 +34,14 @@
 
 #if (defined(_TCPIP_SERVICE_ENABLE) && (_TCPIP_SERVICE_ENABLE == 1))
 
+#include <stdio.h>
 #include <string.h>
 
 #include "core/inc/b_sem.h"
 #include "core/inc/b_task.h"
+#include "thirdparty/http-parser/http_parser.h"
 #include "utils/inc/b_util_log.h"
+#include "utils/inc/b_util_memp.h"
 #include "utils/inc/b_util_utc.h"
 
 /**
@@ -89,6 +92,37 @@ typedef struct
     uint32_t  interval_s;
 } bNtpPcb_t;
 
+//----------------------------------------------------------------------------
+typedef enum
+{
+    B_HTTP_STA_INIT,
+    B_HTTP_STA_CONNECTING,
+    B_HTTP_STA_CONNECTED,
+    B_HTTP_STA_RECV_DATA,
+    B_HTTP_STA_DISCONNECT,
+    B_HTTP_STA_DEINIT,
+    B_HTTP_STA_DESTROY,
+} bHttpState_t;
+typedef struct
+{
+    uint8_t              is_https;
+    bHttpState_t         state;
+    char                 host[_HTTP_HOST_LEN_MAX + 1];
+    char                 path[_HTTP_PATH_LEN_MAX + 1];
+    uint16_t             port;
+    pHttpCb_t            callback;
+    void                *user_data;
+    char                *request;
+    int                  sockfd;
+    bTaskAttr_t          attr;
+    bTaskId_t            task_id;
+    http_parser          parse;
+    http_parser_settings parse_cb;
+    uint8_t             *precv;
+    uint16_t             recvbuf_len;
+    uint16_t             recvbuf_index;
+} bHttpStruct_t;
+
 /**
  * \}
  */
@@ -123,6 +157,9 @@ static bNtpPcb_t bNtpPcb = {
 };
 B_TASK_CREATE_ATTR(bNtpTask);
 static const char *bNtpServer[B_NTP_SERVER_NUM] = {_NTP_SERVER_1, _NTP_SERVER_2, _NTP_SERVER_3};
+
+//------------------------------------------------------------------------------------------------
+
 /**
  * \}
  */
@@ -141,6 +178,11 @@ static const char *bNtpServer[B_NTP_SERVER_NUM] = {_NTP_SERVER_1, _NTP_SERVER_2,
  * \{
  */
 
+static void _bTcpipSrvFree(void *addr)
+{
+    bFree(addr);
+}
+
 static void _bNtpConnCallback(bTransEvent_t event, void *param, void *arg)
 {
     ;
@@ -202,6 +244,266 @@ PT_THREAD(_bNtpTaskFunc)(struct pt *pt, void *arg)
     PT_END(pt);
 }
 
+//--------------------------------------------------------http--
+//--------------------------------------------------------http--
+static int _bHttpParseUrl(const char *url, char *host, char *path, uint16_t *port, uint8_t *ishttps)
+{
+    // 检查是否是HTTPS
+    if (strncmp(url, "https://", 8) == 0)
+    {
+        *ishttps = 1;
+    }
+    else if (strncmp(url, "http://", 7) == 0)
+    {
+        *ishttps = 0;
+    }
+    else
+    {
+        return -1;
+    }
+
+    // 跳过"http://"或"https://"
+    const char *start = url + (*ishttps ? 8 : 7);
+
+    // 查找host的结束位置
+    const char *end = strchr(start, '/');
+    if (end == NULL)
+    {
+        end = url + strlen(url);
+    }
+    if ((end - start) > _HTTP_HOST_LEN_MAX)
+    {
+        return -1;
+    }
+    // 复制host到目标字符串
+    strncpy(host, start, end - start);
+    host[end - start] = '\0';
+
+    // 解析port
+    const char *portStart = strchr(host, ':');
+    if (portStart != NULL)
+    {
+        *port = atoi(portStart + 1);
+    }
+    else
+    {
+        *port = (*ishttps ? 443 : 80);
+    }
+
+    // 解析path
+    if (*end != '\0')
+    {
+        if (strlen(end) > _HTTP_PATH_LEN_MAX)
+        {
+            return -1;
+        }
+        strcpy(path, end);
+    }
+    else
+    {
+        strcpy(path, "/");
+    }
+    return 0;
+}
+
+static char *_bHttpGetRequest(bHttpStruct_t *http)
+{
+    char *request      = NULL;
+    int   request_size = 0;
+    request_size       = strlen("GET /") + strlen(http->path) + strlen(" HTTP/1.1\r\n") +
+                   strlen("Host: ") + strlen(http->host) + strlen("\r\n") +
+                   strlen("Connection: close\r\n") + strlen("\r\n") + 1;
+
+    request = (char *)bMalloc(request_size);
+    if (request != NULL)
+    {
+        memset(request, 0, request_size);
+        snprintf(request, request_size, "GET /%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
+                 http->path, http->host);
+    }
+    return request;
+}
+
+static char *_bHttpPostRequest(bHttpStruct_t *http, const char *head, const char *body)
+{
+    char *request      = NULL;
+    int   request_size = 0;
+
+    request_size = strlen("POST /") + strlen(http->path) + strlen(" HTTP/1.1\r\n") +
+                   strlen("Host: ") + strlen(http->host) + strlen("\r\n") +
+                   strlen("Content-Length: xxxxxx\r\n") + strlen("\r\n") + strlen(body) +
+                   strlen("\r\n") + 1;
+    if (head != NULL)
+    {
+        request_size += strlen(head);
+    }
+    request = (char *)bMalloc(request_size);
+    if (request == NULL)
+    {
+        return NULL;
+    }
+    memset(request, 0, request_size);
+    // 组装请求字符串
+    if (head != NULL)
+    {
+        snprintf(request, request_size,
+                 "POST /%s HTTP/1.1\r\nHost: %s\r\n%sContent-Length: %d\r\n\r\n"
+                 "%s\r\n",
+                 http->path, http->host, head, strlen(body), body);
+    }
+    else
+    {
+        snprintf(request, request_size,
+                 "POST /%s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n"
+                 "%s\r\n",
+                 http->path, http->host, strlen(body), body);
+    }
+    return request;
+}
+
+static void _bHttpResult(bHttpStruct_t *http, bHttpEvent_t evt, void *param)
+{
+    if (evt < 0 || evt == B_HTTP_EVENT_RECV_DATA || evt == B_HTTP_STA_DESTROY)
+    {
+        if (http->request)
+        {
+            bFree(http->request);
+            http->request = NULL;
+        }
+        http->sockfd = -1;
+        http->state  = B_HTTP_STA_DEINIT;
+    }
+    http->callback(evt, param, http->user_data);
+    if (http->precv)
+    {
+        bFree(http->precv);
+        http->precv = NULL;
+    }
+    http->recvbuf_index = 0;
+    http->recvbuf_len   = 0;
+}
+
+static void _bHttpTransCb(bTransEvent_t event, void *param, void *arg)
+{
+}
+
+int on_message_complete(http_parser *parser)
+{
+    bHttpStruct_t *http = (bHttpStruct_t *)parser->data;
+    http->state         = B_HTTP_STA_RECV_DATA;
+    return 0;
+}
+
+PT_THREAD(_bHttpTaskFunc)(struct pt *pt, void *arg)
+{
+    bHttpStruct_t *http = (bHttpStruct_t *)arg;
+    PT_BEGIN(pt);
+    while (1)
+    {
+        if (http->state == B_HTTP_STA_DEINIT)
+        {
+            break;
+        }
+        if (http->state == B_HTTP_STA_DESTROY)
+        {
+            if (http->sockfd > 0)
+            {
+                b_log_e("http destroy...\r\n");
+                PT_WAIT_UNTIL_FOREVER(pt, bShutdown(http->sockfd) >= 0);
+                b_log_e("shutdown..\r\n");
+                _bHttpResult(http, B_HTTP_EVENT_DESTROY, NULL);
+            }
+            bTaskRemove(http->task_id);
+            bFree(http);
+            return 0;
+        }
+        http->sockfd = bSocket(B_TRANS_CONN_TCP, _bHttpTransCb, http);
+        if (http->sockfd < 0)
+        {
+            b_log_e("socket fail...\r\n");
+            _bHttpResult(http, B_HTTP_EVENT_ERROR, NULL);
+            break;
+        }
+        b_log("sockfd: %x %s %d %s\r\n", http->sockfd, http->host, http->port, http->path);
+        bConnect(http->sockfd, http->host, http->port);
+        PT_WAIT_UNTIL(pt, bSockIsWriteable(http->sockfd) == 1, 5000);
+        if (pt->retval == PT_RETVAL_TIMEOUT)
+        {
+            b_log_e("http conn fail...\r\n");
+            PT_WAIT_UNTIL_FOREVER(pt, bShutdown(http->sockfd) >= 0);
+            b_log_e("shutdown..\r\n");
+            _bHttpResult(http, B_HTTP_EVENT_ERROR, NULL);
+            break;
+        }
+        _bHttpResult(http, B_HTTP_EVENT_CONNECTED, NULL);
+        b_log("send:%s\r\n", http->request);
+        bSend(http->sockfd, (uint8_t *)http->request, strlen(http->request), NULL);
+        http->parse.data = http;
+        http_parser_init(&http->parse, HTTP_RESPONSE);
+        http_parser_settings_init(&http->parse_cb);
+        http->parse_cb.on_message_complete = on_message_complete;
+        int      parse_len                 = 0;
+        uint16_t readlen                   = 0;
+        for (;;)
+        {
+            PT_WAIT_UNTIL(pt, bSockIsReadable(http->sockfd) == 1, 5000);
+            if (pt->retval == PT_RETVAL_TIMEOUT)
+            {
+                b_log_e("http recv timeout.. \r\n");
+                PT_WAIT_UNTIL_FOREVER(pt, bShutdown(http->sockfd) >= 0);
+                b_log_e("shutdown..\r\n");
+                _bHttpResult(http, B_HTTP_EVENT_ERROR, NULL);
+                break;
+            }
+            if ((http->recvbuf_len - http->recvbuf_index) <= 128)
+            {
+                b_log("realloc-----\r\n");
+                http->recvbuf_len += 1024;
+                http->precv = bRealloc(http->precv, http->recvbuf_len);
+                if (http->precv == NULL)
+                {
+                    b_log_e("malloc error.. \r\n");
+                    PT_WAIT_UNTIL_FOREVER(pt, bShutdown(http->sockfd) >= 0);
+                    b_log_e("shutdown..\r\n");
+                    _bHttpResult(http, B_HTTP_EVENT_ERROR, NULL);
+                    break;
+                }
+            }
+            bRecv(http->sockfd, http->precv + http->recvbuf_index,
+                  http->recvbuf_len - http->recvbuf_index, &readlen);
+            if (readlen > 0)
+            {
+                parse_len =
+                    http_parser_execute(&http->parse, &http->parse_cb,
+                                        (const char *)(http->precv + http->recvbuf_index), readlen);
+                b_log("parse_len %d readlen %d index:%d\r\n", parse_len, readlen,
+                      http->recvbuf_index);
+                http->recvbuf_index += readlen;
+                if (http->state == B_HTTP_STA_RECV_DATA)
+                {
+                    bHttpRecvData_t dat;
+                    dat.pdat    = http->precv;
+                    dat.len     = http->recvbuf_index;
+                    dat.release = _bTcpipSrvFree;
+                    PT_WAIT_UNTIL_FOREVER(pt, bShutdown(http->sockfd) >= 0);
+                    _bHttpResult(http, B_HTTP_EVENT_RECV_DATA, &dat);
+                    http->precv = NULL;
+                    break;
+                }
+                else if (parse_len < 0)
+                {
+                    b_log_e("parse error.. \r\n");
+                    PT_WAIT_UNTIL_FOREVER(pt, bShutdown(http->sockfd) >= 0);
+                    b_log_e("shutdown..\r\n");
+                    _bHttpResult(http, B_HTTP_EVENT_ERROR, NULL);
+                    break;
+                }
+            }
+        }
+    }
+    PT_END(pt);
+}
+
 /**
  * \}
  */
@@ -233,6 +535,95 @@ int bSntpStart(uint32_t interval_s)
     return 0;
 }
 
+//----------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------
+
+int bHttpInit(pHttpCb_t cb, void *user_data)
+{
+    bHttpStruct_t *http = NULL;
+    if (cb == NULL)
+    {
+        return -1;
+    }
+    http = (bHttpStruct_t *)bMalloc(sizeof(bHttpStruct_t));
+    if (http == NULL)
+    {
+        return -2;
+    }
+    memset(http, 0, sizeof(bHttpStruct_t));
+    http->callback      = cb;
+    http->request       = NULL;
+    http->sockfd        = -1;
+    http->state         = B_HTTP_STA_DEINIT;
+    http->user_data     = user_data;
+    http->precv         = NULL;
+    http->recvbuf_index = 0;
+    http->recvbuf_len   = 0;
+    if ((http->task_id = bTaskCreate(NULL, _bHttpTaskFunc, http, &http->attr)) == NULL)
+    {
+        b_log_e("task create fail..\r\n");
+        bFree(http);
+        return -3;
+    }
+    return ((int)http);
+}
+
+int bHttpRequest(int httpfd, bHttpReqType_t type, const char *url, const char *head,
+                 const char *body)
+{
+    int            ret     = -1;
+    char          *request = NULL;
+    bHttpStruct_t *http    = (bHttpStruct_t *)httpfd;
+    if (httpfd <= 0 || !HTTPREQ_TYPE_IS_VALID(type) || url == NULL || http->callback == NULL)
+    {
+        b_log_e("http param errror..%p %d %p\r\n", http, type, url);
+        return -1;
+    }
+
+    if (http->state != B_HTTP_STA_DEINIT)
+    {
+        b_log_e("http busy...\r\n");
+        return -2;
+    }
+    memset(http->host, 0, sizeof(http->host));
+    memset(http->path, 0, sizeof(http->path));
+    ret = _bHttpParseUrl(url, http->host, http->path, &http->port, &http->is_https);
+    if (ret < 0)
+    {
+        b_log_e("parse url fail..%d \r\n", ret);
+        return -3;
+    }
+    b_log("host:%s port %d\n", http->host, http->port);
+    b_log("path:%s \n", http->path);
+    if (type == B_HTTP_GET)
+    {
+        request = _bHttpGetRequest(http);
+    }
+    else if (type == B_HTTP_POST)
+    {
+        request = _bHttpPostRequest(http, head, body);
+    }
+    if (request == NULL)
+    {
+        b_log_e("http create request fail...\r\n");
+        return -4;
+    }
+    http->request = request;
+    http->state   = B_HTTP_STA_INIT;
+    return 0;
+}
+
+int bHttpDeInit(int httpfd)
+{
+    if (httpfd <= 0)
+    {
+        return -1;
+    }
+    bHttpStruct_t *http = (bHttpStruct_t *)httpfd;
+    http->state         = B_HTTP_STA_DESTROY;
+    return 0;
+}
+
 /**
  * \}
  */
diff --git a/bos/services/inc/b_srv_tcpip.h b/bos/services/inc/b_srv_tcpip.h
index 2453793edd10eb58306095b8cde8569c048b5da7..de4098412d7034f4d0ed6629caaf155ee24196cc 100644
--- a/bos/services/inc/b_srv_tcpip.h
+++ b/bos/services/inc/b_srv_tcpip.h
@@ -65,6 +65,31 @@ extern "C" {
  * \{
  */
 
+typedef enum
+{
+    B_HTTP_GET,
+    B_HTTP_POST
+} bHttpReqType_t;
+#define HTTPREQ_TYPE_IS_VALID(t) ((t) == B_HTTP_GET || (t) == B_HTTP_POST)
+
+typedef struct
+{
+    uint8_t *pdat;
+    uint16_t len;
+    void (*release)(void *);
+} bHttpRecvData_t;
+
+typedef enum
+{
+    B_HTTP_EVENT_CONNECTED = 0,
+    B_HTTP_EVENT_RECV_DATA,  // callback param : bHttpRecvData_t
+    B_HTTP_EVENT_DESTROY,
+    B_HTTP_EVENT_ERR_BASE = -100,
+    B_HTTP_EVENT_ERROR,
+} bHttpEvent_t;
+
+typedef void (*pHttpCb_t)(bHttpEvent_t event, void *param, void *arg);
+
 /**
  * \}
  */
@@ -87,6 +112,11 @@ int bTcpipSrvInit(void);
 
 int bSntpStart(uint32_t interval_s);
 
+int bHttpInit(pHttpCb_t cb, void *user_data);
+int bHttpDeInit(int httpfd);
+// 默认头部有Content-Length; head为自定义头部,以\r\n结尾
+int bHttpRequest(int httpfd, bHttpReqType_t type, const char *url, const char *head,
+                 const char *body);
 /**
  * \}
  */
diff --git a/bos/thirdparty/Kconfig b/bos/thirdparty/Kconfig
index 12bb6256b1048541e4c75a4b78ccec717cbd708c..beccb1d3218c15d8836c0a03542a68a4e28035ba 100644
--- a/bos/thirdparty/Kconfig
+++ b/bos/thirdparty/Kconfig
@@ -214,4 +214,8 @@ config _QRCODE_ENABLE
     bool "QRcode Enable/Disable"
     default n
 
+config _HTTP_PARSER_ENABLE
+    bool "Http-Parser Enable/Disable"
+    default n
+
 endmenu
diff --git a/bos/thirdparty/cjson/cjson.c b/bos/thirdparty/cjson/cjson.c
index 999f137ea517c03bb65ad97ea4f627e15e9e965d..eb34b8bdde59324414c2bc49fa2aab90f8ca525b 100644
--- a/bos/thirdparty/cjson/cjson.c
+++ b/bos/thirdparty/cjson/cjson.c
@@ -168,9 +168,19 @@ typedef struct internal_hooks
 #if (defined(CJSON_MEM_USE_BMALLOC) && (CJSON_MEM_USE_BMALLOC == 1))
 
 #include "utils/inc/b_util_memp.h"
-#define internal_malloc bMalloc
-#define internal_free bFree
-#define internal_realloc NULL
+
+static void *CJSON_CDECL internal_malloc(size_t size)
+{
+    return bMalloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+    bFree(pointer);
+}
+static void *CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+    return bRealloc(pointer, size);
+}
 
 #else
 
diff --git a/bos/thirdparty/http-parser/AUTHORS b/bos/thirdparty/http-parser/AUTHORS
new file mode 100644
index 0000000000000000000000000000000000000000..5323b685caefb91ef74b5fcf5f630fd09c97b33f
--- /dev/null
+++ b/bos/thirdparty/http-parser/AUTHORS
@@ -0,0 +1,68 @@
+# Authors ordered by first contribution.
+Ryan Dahl <ry@tinyclouds.org>
+Jeremy Hinegardner <jeremy@hinegardner.org>
+Sergey Shepelev <temotor@gmail.com>
+Joe Damato <ice799@gmail.com>
+tomika <tomika_nospam@freemail.hu>
+Phoenix Sol <phoenix@burninglabs.com>
+Cliff Frey <cliff@meraki.com>
+Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
+Santiago Gala <sgala@apache.org>
+Tim Becker <tim.becker@syngenio.de>
+Jeff Terrace <jterrace@gmail.com>
+Ben Noordhuis <info@bnoordhuis.nl>
+Nathan Rajlich <nathan@tootallnate.net>
+Mark Nottingham <mnot@mnot.net>
+Aman Gupta <aman@tmm1.net>
+Tim Becker <tim.becker@kuriositaet.de>
+Sean Cunningham <sean.cunningham@mandiant.com>
+Peter Griess <pg@std.in>
+Salman Haq <salman.haq@asti-usa.com>
+Cliff Frey <clifffrey@gmail.com>
+Jon Kolb <jon@b0g.us>
+Fouad Mardini <f.mardini@gmail.com>
+Paul Querna <pquerna@apache.org>
+Felix Geisendörfer <felix@debuggable.com>
+koichik <koichik@improvement.jp>
+Andre Caron <andre.l.caron@gmail.com>
+Ivo Raisr <ivosh@ivosh.net>
+James McLaughlin <jamie@lacewing-project.org>
+David Gwynne <loki@animata.net>
+Thomas LE ROUX <thomas@november-eleven.fr>
+Randy Rizun <rrizun@ortivawireless.com>
+Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
+Simon Zimmermann <simonz05@gmail.com>
+Erik Dubbelboer <erik@dubbelboer.com>
+Martell Malone <martellmalone@gmail.com>
+Bertrand Paquet <bpaquet@octo.com>
+BogDan Vatra <bogdan@kde.org>
+Peter Faiman <peter@thepicard.org>
+Corey Richardson <corey@octayn.net>
+Tóth Tamás <tomika_nospam@freemail.hu>
+Cam Swords <cam.swords@gmail.com>
+Chris Dickinson <christopher.s.dickinson@gmail.com>
+Uli Köhler <ukoehler@btronik.de>
+Charlie Somerville <charlie@charliesomerville.com>
+Patrik Stutz <patrik.stutz@gmail.com>
+Fedor Indutny <fedor.indutny@gmail.com>
+runner <runner.mei@gmail.com>
+Alexis Campailla <alexis@janeasystems.com>
+David Wragg <david@wragg.org>
+Vinnie Falco <vinnie.falco@gmail.com>
+Alex Butum <alexbutum@linux.com>
+Rex Feng <rexfeng@gmail.com>
+Alex Kocharin <alex@kocharin.ru>
+Mark Koopman <markmontymark@yahoo.com>
+Helge Heß <me@helgehess.eu>
+Alexis La Goutte <alexis.lagoutte@gmail.com>
+George Miroshnykov <george.miroshnykov@gmail.com>
+Maciej Małecki <me@mmalecki.com>
+Marc O'Morain <github.com@marcomorain.com>
+Jeff Pinner <jpinner@twitter.com>
+Timothy J Fontaine <tjfontaine@gmail.com>
+Akagi201 <akagi201@gmail.com>
+Romain Giraud <giraud.romain@gmail.com>
+Jay Satiro <raysatiro@yahoo.com>
+Arne Steen <Arne.Steen@gmx.de>
+Kjell Schubert <kjell.schubert@gmail.com>
+Olivier Mengué <dolmen@cpan.org>
diff --git a/bos/thirdparty/http-parser/LICENSE-MIT b/bos/thirdparty/http-parser/LICENSE-MIT
new file mode 100644
index 0000000000000000000000000000000000000000..1ec0ab4e1746be2b5a7a817eb9c92cf8e3ff7e6d
--- /dev/null
+++ b/bos/thirdparty/http-parser/LICENSE-MIT
@@ -0,0 +1,19 @@
+Copyright Joyent, Inc. and other Node contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE. 
diff --git a/bos/thirdparty/http-parser/http_parser.c b/bos/thirdparty/http-parser/http_parser.c
new file mode 100644
index 0000000000000000000000000000000000000000..8d7f41f53045960adc5859333e414ef32e5432dd
--- /dev/null
+++ b/bos/thirdparty/http-parser/http_parser.c
@@ -0,0 +1,2857 @@
+/* Copyright Joyent, Inc. and other Node contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "http_parser.h"
+#if (defined(_HTTP_PARSER_ENABLE) && (_HTTP_PARSER_ENABLE == 1))
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE;
+
+#ifndef ULLONG_MAX
+#define ULLONG_MAX ((uint64_t)-1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+#define BIT_AT(a, i) \
+    (!!((unsigned int)(a)[(unsigned int)(i) >> 3] & (1 << ((unsigned int)(i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+#define ELEM_AT(a, i, v) ((unsigned int)(i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
+
+#define SET_ERRNO(e)                \
+    do                              \
+    {                               \
+        parser->nread      = nread; \
+        parser->http_errno = (e);   \
+    } while (0)
+
+#define CURRENT_STATE() p_state
+#define UPDATE_STATE(V) p_state = (enum state)(V);
+#define RETURN(V)                        \
+    do                                   \
+    {                                    \
+        parser->nread = nread;           \
+        parser->state = CURRENT_STATE(); \
+        return (V);                      \
+    } while (0);
+#define REEXECUTE() goto reexecute;
+
+#ifdef __GNUC__
+#define LIKELY(X) __builtin_expect(!!(X), 1)
+#define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+#define LIKELY(X) (X)
+#define UNLIKELY(X) (X)
+#endif
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER)                                \
+    do                                                           \
+    {                                                            \
+        B_ASSERT(HTTP_PARSER_ERRNO(parser) == HPE_OK);           \
+                                                                 \
+        if (LIKELY(settings->on_##FOR))                          \
+        {                                                        \
+            parser->state = CURRENT_STATE();                     \
+            if (UNLIKELY(0 != settings->on_##FOR(parser)))       \
+            {                                                    \
+                SET_ERRNO(HPE_CB_##FOR);                         \
+            }                                                    \
+            UPDATE_STATE(parser->state);                         \
+                                                                 \
+            /* We either errored above or got paused; get out */ \
+            if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK))   \
+            {                                                    \
+                return (ER);                                     \
+            }                                                    \
+        }                                                        \
+    } while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER)                                              \
+    do                                                                            \
+    {                                                                             \
+        B_ASSERT(HTTP_PARSER_ERRNO(parser) == HPE_OK);                            \
+                                                                                  \
+        if (FOR##_mark)                                                           \
+        {                                                                         \
+            if (LIKELY(settings->on_##FOR))                                       \
+            {                                                                     \
+                parser->state = CURRENT_STATE();                                  \
+                if (UNLIKELY(0 != settings->on_##FOR(parser, FOR##_mark, (LEN)))) \
+                {                                                                 \
+                    SET_ERRNO(HPE_CB_##FOR);                                      \
+                }                                                                 \
+                UPDATE_STATE(parser->state);                                      \
+                                                                                  \
+                /* We either errored above or got paused; get out */              \
+                if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK))                \
+                {                                                                 \
+                    return (ER);                                                  \
+                }                                                                 \
+            }                                                                     \
+            FOR##_mark = NULL;                                                    \
+        }                                                                         \
+    } while (0)
+
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR) CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR) CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR)           \
+    do                      \
+    {                       \
+        if (!FOR##_mark)    \
+        {                   \
+            FOR##_mark = p; \
+        }                   \
+    } while (0)
+
+/* Don't allow the total size of the HTTP headers (including the status
+ * line) to exceed max_header_size.  This check is here to protect
+ * embedders against denial-of-service attacks where the attacker feeds
+ * us a never-ending header that the embedder keeps buffering.
+ *
+ * This check is arguably the responsibility of embedders but we're doing
+ * it on the embedder's behalf because most won't bother and this way we
+ * make the web a little safer.  max_header_size is still far bigger
+ * than any reasonable request or response so this should never affect
+ * day-to-day operation.
+ */
+#define COUNT_HEADER_SIZE(V)                   \
+    do                                         \
+    {                                          \
+        nread += (uint32_t)(V);                \
+        if (UNLIKELY(nread > max_header_size)) \
+        {                                      \
+            SET_ERRNO(HPE_HEADER_OVERFLOW);    \
+            goto error;                        \
+        }                                      \
+    } while (0)
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+static const char *method_strings[] = {
+#define XX(num, name, string) #string,
+    HTTP_METHOD_MAP(XX)
+#undef XX
+};
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ *        token       = 1*<any CHAR except CTLs or separators>
+ *     separators     = "(" | ")" | "<" | ">" | "@"
+ *                    | "," | ";" | ":" | "\" | <">
+ *                    | "/" | "[" | "]" | "?" | "="
+ *                    | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+    /*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+    0, 0, 0, 0, 0, 0, 0, 0,
+    /*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+    0, 0, 0, 0, 0, 0, 0, 0,
+    /*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+    0, 0, 0, 0, 0, 0, 0, 0,
+    /*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+    0, 0, 0, 0, 0, 0, 0, 0,
+    /*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+    ' ', '!', 0, '#', '$', '%', '&', '\'',
+    /*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+    0, 0, '*', '+', 0, '-', '.', 0,
+    /*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+    '0', '1', '2', '3', '4', '5', '6', '7',
+    /*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+    '8', '9', 0, 0, 0, 0, 0, 0,
+    /*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+    0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+    /*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+    /*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+    'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+    /*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+    'x', 'y', 'z', 0, 0, 0, '^', '_',
+    /*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+    '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+    /* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+    /* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+    'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+    /* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+    'x', 'y', 'z', 0, '|', 0, '~', 0};
+
+static const int8_t unhex[256] = {
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  -1, -1, -1, -1, -1, -1, -1, 10,
+    11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
+
+#if HTTP_PARSER_STRICT
+#define T(v) 0
+#else
+#define T(v) v
+#endif
+
+static const uint8_t normal_url_char[32] = {
+    /*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+    0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+    /*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+    0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
+    /*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+    0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+    /*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+    0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+    /*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+    0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
+    /*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
+    /*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+    /* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+    1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
+};
+
+#undef T
+
+enum state
+{
+    s_dead = 1 /* important that this is > 0 */
+
+    ,
+    s_start_req_or_res,
+    s_res_or_resp_H,
+    s_start_res,
+    s_res_H,
+    s_res_HT,
+    s_res_HTT,
+    s_res_HTTP,
+    s_res_http_major,
+    s_res_http_dot,
+    s_res_http_minor,
+    s_res_http_end,
+    s_res_first_status_code,
+    s_res_status_code,
+    s_res_status_start,
+    s_res_status,
+    s_res_line_almost_done
+
+    ,
+    s_start_req
+
+    ,
+    s_req_method,
+    s_req_spaces_before_url,
+    s_req_schema,
+    s_req_schema_slash,
+    s_req_schema_slash_slash,
+    s_req_server_start,
+    s_req_server,
+    s_req_server_with_at,
+    s_req_path,
+    s_req_query_string_start,
+    s_req_query_string,
+    s_req_fragment_start,
+    s_req_fragment,
+    s_req_http_start,
+    s_req_http_H,
+    s_req_http_HT,
+    s_req_http_HTT,
+    s_req_http_HTTP,
+    s_req_http_I,
+    s_req_http_IC,
+    s_req_http_major,
+    s_req_http_dot,
+    s_req_http_minor,
+    s_req_http_end,
+    s_req_line_almost_done
+
+    ,
+    s_header_field_start,
+    s_header_field,
+    s_header_value_discard_ws,
+    s_header_value_discard_ws_almost_done,
+    s_header_value_discard_lws,
+    s_header_value_start,
+    s_header_value,
+    s_header_value_lws
+
+    ,
+    s_header_almost_done
+
+    ,
+    s_chunk_size_start,
+    s_chunk_size,
+    s_chunk_parameters,
+    s_chunk_size_almost_done
+
+    ,
+    s_headers_almost_done,
+    s_headers_done
+
+    /* Important: 's_headers_done' must be the last 'header' state. All
+     * states beyond this must be 'body' states. It is used for overflow
+     * checking. See the PARSING_HEADER() macro.
+     */
+
+    ,
+    s_chunk_data,
+    s_chunk_data_almost_done,
+    s_chunk_data_done
+
+    ,
+    s_body_identity,
+    s_body_identity_eof
+
+    ,
+    s_message_done
+};
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+enum header_states
+{
+    h_general = 0,
+    h_C,
+    h_CO,
+    h_CON
+
+    ,
+    h_matching_connection,
+    h_matching_proxy_connection,
+    h_matching_content_length,
+    h_matching_transfer_encoding,
+    h_matching_upgrade
+
+    ,
+    h_connection,
+    h_content_length,
+    h_content_length_num,
+    h_content_length_ws,
+    h_transfer_encoding,
+    h_upgrade
+
+    ,
+    h_matching_transfer_encoding_token_start,
+    h_matching_transfer_encoding_chunked,
+    h_matching_transfer_encoding_token
+
+    ,
+    h_matching_connection_token_start,
+    h_matching_connection_keep_alive,
+    h_matching_connection_close,
+    h_matching_connection_upgrade,
+    h_matching_connection_token
+
+    ,
+    h_transfer_encoding_chunked,
+    h_connection_keep_alive,
+    h_connection_close,
+    h_connection_upgrade
+};
+
+enum http_host_state
+{
+    s_http_host_dead = 1,
+    s_http_userinfo_start,
+    s_http_userinfo,
+    s_http_host_start,
+    s_http_host_v6_start,
+    s_http_host,
+    s_http_host_v6,
+    s_http_host_v6_end,
+    s_http_host_v6_zone_start,
+    s_http_host_v6_zone,
+    s_http_host_port_start,
+    s_http_host_port
+};
+
+/* Macros for character classes; depends on strict-mode  */
+#define CR '\r'
+#define LF '\n'
+#define LOWER(c) (unsigned char)(c | 0x20)
+#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c)                                                                       \
+    ((c) == '-' || (c) == '_' || (c) == '.' || (c) == '!' || (c) == '~' || (c) == '*' || \
+     (c) == '\'' || (c) == '(' || (c) == ')')
+#define IS_USERINFO_CHAR(c)                                                                  \
+    (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || (c) == ';' || (c) == ':' || (c) == '&' || \
+     (c) == '=' || (c) == '+' || (c) == '$' || (c) == ',')
+
+#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c) STRICT_TOKEN(c)
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c) tokens[(unsigned char)c]
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+/**
+ * Verify that a char is a valid visible (printable) US-ASCII
+ * character or %x80-FF
+ **/
+#define IS_HEADER_CHAR(ch) \
+    (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+#if HTTP_PARSER_STRICT
+#define STRICT_CHECK(cond)         \
+    do                             \
+    {                              \
+        if (cond)                  \
+        {                          \
+            SET_ERRNO(HPE_STRICT); \
+            goto error;            \
+        }                          \
+    } while (0)
+#define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+#define STRICT_CHECK(cond)
+#define NEW_MESSAGE() start_state
+#endif
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) {"HPE_" #n, s},
+static struct
+{
+    const char *name;
+    const char *description;
+} http_strerror_tab[] = {HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(const http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state parse_url_char(enum state s, const char ch)
+{
+    if (ch == ' ' || ch == '\r' || ch == '\n')
+    {
+        return s_dead;
+    }
+
+#if HTTP_PARSER_STRICT
+    if (ch == '\t' || ch == '\f')
+    {
+        return s_dead;
+    }
+#endif
+
+    switch (s)
+    {
+        case s_req_spaces_before_url:
+            /* Proxied requests are followed by scheme of an absolute URI (alpha).
+             * All methods except CONNECT are followed by '/' or '*'.
+             */
+
+            if (ch == '/' || ch == '*')
+            {
+                return s_req_path;
+            }
+
+            if (IS_ALPHA(ch))
+            {
+                return s_req_schema;
+            }
+
+            break;
+
+        case s_req_schema:
+            if (IS_ALPHA(ch))
+            {
+                return s;
+            }
+
+            if (ch == ':')
+            {
+                return s_req_schema_slash;
+            }
+
+            break;
+
+        case s_req_schema_slash:
+            if (ch == '/')
+            {
+                return s_req_schema_slash_slash;
+            }
+
+            break;
+
+        case s_req_schema_slash_slash:
+            if (ch == '/')
+            {
+                return s_req_server_start;
+            }
+
+            break;
+
+        case s_req_server_with_at:
+            if (ch == '@')
+            {
+                return s_dead;
+            }
+
+        /* fall through */
+        case s_req_server_start:
+        case s_req_server:
+            if (ch == '/')
+            {
+                return s_req_path;
+            }
+
+            if (ch == '?')
+            {
+                return s_req_query_string_start;
+            }
+
+            if (ch == '@')
+            {
+                return s_req_server_with_at;
+            }
+
+            if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']')
+            {
+                return s_req_server;
+            }
+
+            break;
+
+        case s_req_path:
+            if (IS_URL_CHAR(ch))
+            {
+                return s;
+            }
+
+            switch (ch)
+            {
+                case '?':
+                    return s_req_query_string_start;
+
+                case '#':
+                    return s_req_fragment_start;
+            }
+
+            break;
+
+        case s_req_query_string_start:
+        case s_req_query_string:
+            if (IS_URL_CHAR(ch))
+            {
+                return s_req_query_string;
+            }
+
+            switch (ch)
+            {
+                case '?':
+                    /* allow extra '?' in query string */
+                    return s_req_query_string;
+
+                case '#':
+                    return s_req_fragment_start;
+            }
+
+            break;
+
+        case s_req_fragment_start:
+            if (IS_URL_CHAR(ch))
+            {
+                return s_req_fragment;
+            }
+
+            switch (ch)
+            {
+                case '?':
+                    return s_req_fragment;
+
+                case '#':
+                    return s;
+            }
+
+            break;
+
+        case s_req_fragment:
+            if (IS_URL_CHAR(ch))
+            {
+                return s;
+            }
+
+            switch (ch)
+            {
+                case '?':
+                case '#':
+                    return s;
+            }
+
+            break;
+
+        default:
+            break;
+    }
+
+    /* We should never fall out of the switch above unless there's an error */
+    return s_dead;
+}
+
+size_t http_parser_execute(http_parser *parser, const http_parser_settings *settings,
+                           const char *data, size_t len)
+{
+    char               c, ch;
+    int8_t             unhex_val;
+    const char        *p                    = data;
+    const char        *header_field_mark    = 0;
+    const char        *header_value_mark    = 0;
+    const char        *url_mark             = 0;
+    const char        *body_mark            = 0;
+    const char        *status_mark          = 0;
+    enum state         p_state              = (enum state)parser->state;
+    const unsigned int lenient              = parser->lenient_http_headers;
+    const unsigned int allow_chunked_length = parser->allow_chunked_length;
+
+    uint32_t nread = parser->nread;
+
+    /* We're in an error state. Don't bother doing anything. */
+    if (HTTP_PARSER_ERRNO(parser) != HPE_OK)
+    {
+        return 0;
+    }
+
+    if (len == 0)
+    {
+        switch (CURRENT_STATE())
+        {
+            case s_body_identity_eof:
+                /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+                 * we got paused.
+                 */
+                CALLBACK_NOTIFY_NOADVANCE(message_complete);
+                return 0;
+
+            case s_dead:
+            case s_start_req_or_res:
+            case s_start_res:
+            case s_start_req:
+                return 0;
+
+            default:
+                SET_ERRNO(HPE_INVALID_EOF_STATE);
+                return 1;
+        }
+    }
+
+    if (CURRENT_STATE() == s_header_field)
+        header_field_mark = data;
+    if (CURRENT_STATE() == s_header_value)
+        header_value_mark = data;
+    switch (CURRENT_STATE())
+    {
+        case s_req_path:
+        case s_req_schema:
+        case s_req_schema_slash:
+        case s_req_schema_slash_slash:
+        case s_req_server_start:
+        case s_req_server:
+        case s_req_server_with_at:
+        case s_req_query_string_start:
+        case s_req_query_string:
+        case s_req_fragment_start:
+        case s_req_fragment:
+            url_mark = data;
+            break;
+        case s_res_status:
+            status_mark = data;
+            break;
+        default:
+            break;
+    }
+
+    for (p = data; p != data + len; p++)
+    {
+        ch = *p;
+
+        if (PARSING_HEADER(CURRENT_STATE()))
+            COUNT_HEADER_SIZE(1);
+
+    reexecute:
+        switch (CURRENT_STATE())
+        {
+
+            case s_dead:
+                /* this state is used after a 'Connection: close' message
+                 * the parser will error out if it reads another message
+                 */
+                if (LIKELY(ch == CR || ch == LF))
+                    break;
+
+                SET_ERRNO(HPE_CLOSED_CONNECTION);
+                goto error;
+
+            case s_start_req_or_res:
+            {
+                if (ch == CR || ch == LF)
+                    break;
+                parser->flags                  = 0;
+                parser->uses_transfer_encoding = 0;
+                parser->content_length         = ULLONG_MAX;
+
+                if (ch == 'H')
+                {
+                    UPDATE_STATE(s_res_or_resp_H);
+
+                    CALLBACK_NOTIFY(message_begin);
+                }
+                else
+                {
+                    parser->type = HTTP_REQUEST;
+                    UPDATE_STATE(s_start_req);
+                    REEXECUTE();
+                }
+
+                break;
+            }
+
+            case s_res_or_resp_H:
+                if (ch == 'T')
+                {
+                    parser->type = HTTP_RESPONSE;
+                    UPDATE_STATE(s_res_HT);
+                }
+                else
+                {
+                    if (UNLIKELY(ch != 'E'))
+                    {
+                        SET_ERRNO(HPE_INVALID_CONSTANT);
+                        goto error;
+                    }
+
+                    parser->type   = HTTP_REQUEST;
+                    parser->method = HTTP_HEAD;
+                    parser->index  = 2;
+                    UPDATE_STATE(s_req_method);
+                }
+                break;
+
+            case s_start_res:
+            {
+                if (ch == CR || ch == LF)
+                    break;
+                parser->flags                  = 0;
+                parser->uses_transfer_encoding = 0;
+                parser->content_length         = ULLONG_MAX;
+
+                if (ch == 'H')
+                {
+                    UPDATE_STATE(s_res_H);
+                }
+                else
+                {
+                    SET_ERRNO(HPE_INVALID_CONSTANT);
+                    goto error;
+                }
+
+                CALLBACK_NOTIFY(message_begin);
+                break;
+            }
+
+            case s_res_H:
+                STRICT_CHECK(ch != 'T');
+                UPDATE_STATE(s_res_HT);
+                break;
+
+            case s_res_HT:
+                STRICT_CHECK(ch != 'T');
+                UPDATE_STATE(s_res_HTT);
+                break;
+
+            case s_res_HTT:
+                STRICT_CHECK(ch != 'P');
+                UPDATE_STATE(s_res_HTTP);
+                break;
+
+            case s_res_HTTP:
+                STRICT_CHECK(ch != '/');
+                UPDATE_STATE(s_res_http_major);
+                break;
+
+            case s_res_http_major:
+                if (UNLIKELY(!IS_NUM(ch)))
+                {
+                    SET_ERRNO(HPE_INVALID_VERSION);
+                    goto error;
+                }
+
+                parser->http_major = ch - '0';
+                UPDATE_STATE(s_res_http_dot);
+                break;
+
+            case s_res_http_dot:
+            {
+                if (UNLIKELY(ch != '.'))
+                {
+                    SET_ERRNO(HPE_INVALID_VERSION);
+                    goto error;
+                }
+
+                UPDATE_STATE(s_res_http_minor);
+                break;
+            }
+
+            case s_res_http_minor:
+                if (UNLIKELY(!IS_NUM(ch)))
+                {
+                    SET_ERRNO(HPE_INVALID_VERSION);
+                    goto error;
+                }
+
+                parser->http_minor = ch - '0';
+                UPDATE_STATE(s_res_http_end);
+                break;
+
+            case s_res_http_end:
+            {
+                if (UNLIKELY(ch != ' '))
+                {
+                    SET_ERRNO(HPE_INVALID_VERSION);
+                    goto error;
+                }
+
+                UPDATE_STATE(s_res_first_status_code);
+                break;
+            }
+
+            case s_res_first_status_code:
+            {
+                if (!IS_NUM(ch))
+                {
+                    if (ch == ' ')
+                    {
+                        break;
+                    }
+
+                    SET_ERRNO(HPE_INVALID_STATUS);
+                    goto error;
+                }
+                parser->status_code = ch - '0';
+                UPDATE_STATE(s_res_status_code);
+                break;
+            }
+
+            case s_res_status_code:
+            {
+                if (!IS_NUM(ch))
+                {
+                    switch (ch)
+                    {
+                        case ' ':
+                            UPDATE_STATE(s_res_status_start);
+                            break;
+                        case CR:
+                        case LF:
+                            UPDATE_STATE(s_res_status_start);
+                            REEXECUTE();
+                            break;
+                        default:
+                            SET_ERRNO(HPE_INVALID_STATUS);
+                            goto error;
+                    }
+                    break;
+                }
+
+                parser->status_code *= 10;
+                parser->status_code += ch - '0';
+
+                if (UNLIKELY(parser->status_code > 999))
+                {
+                    SET_ERRNO(HPE_INVALID_STATUS);
+                    goto error;
+                }
+
+                break;
+            }
+
+            case s_res_status_start:
+            {
+                MARK(status);
+                UPDATE_STATE(s_res_status);
+                parser->index = 0;
+
+                if (ch == CR || ch == LF)
+                    REEXECUTE();
+
+                break;
+            }
+
+            case s_res_status:
+                if (ch == CR)
+                {
+                    UPDATE_STATE(s_res_line_almost_done);
+                    CALLBACK_DATA(status);
+                    break;
+                }
+
+                if (ch == LF)
+                {
+                    UPDATE_STATE(s_header_field_start);
+                    CALLBACK_DATA(status);
+                    break;
+                }
+
+                break;
+
+            case s_res_line_almost_done:
+                STRICT_CHECK(ch != LF);
+                UPDATE_STATE(s_header_field_start);
+                break;
+
+            case s_start_req:
+            {
+                if (ch == CR || ch == LF)
+                    break;
+                parser->flags                  = 0;
+                parser->uses_transfer_encoding = 0;
+                parser->content_length         = ULLONG_MAX;
+
+                if (UNLIKELY(!IS_ALPHA(ch)))
+                {
+                    SET_ERRNO(HPE_INVALID_METHOD);
+                    goto error;
+                }
+
+                parser->method = (enum http_method)0;
+                parser->index  = 1;
+                switch (ch)
+                {
+                    case 'A':
+                        parser->method = HTTP_ACL;
+                        break;
+                    case 'B':
+                        parser->method = HTTP_BIND;
+                        break;
+                    case 'C':
+                        parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */
+                        break;
+                    case 'D':
+                        parser->method = HTTP_DELETE;
+                        break;
+                    case 'G':
+                        parser->method = HTTP_GET;
+                        break;
+                    case 'H':
+                        parser->method = HTTP_HEAD;
+                        break;
+                    case 'L':
+                        parser->method = HTTP_LOCK; /* or LINK */
+                        break;
+                    case 'M':
+                        parser->method =
+                            HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */
+                        break;
+                    case 'N':
+                        parser->method = HTTP_NOTIFY;
+                        break;
+                    case 'O':
+                        parser->method = HTTP_OPTIONS;
+                        break;
+                    case 'P':
+                        parser->method = HTTP_POST;
+                        /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+                        break;
+                    case 'R':
+                        parser->method = HTTP_REPORT; /* or REBIND */
+                        break;
+                    case 'S':
+                        parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */
+                        break;
+                    case 'T':
+                        parser->method = HTTP_TRACE;
+                        break;
+                    case 'U':
+                        parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */
+                        break;
+                    default:
+                        SET_ERRNO(HPE_INVALID_METHOD);
+                        goto error;
+                }
+                UPDATE_STATE(s_req_method);
+
+                CALLBACK_NOTIFY(message_begin);
+
+                break;
+            }
+
+            case s_req_method:
+            {
+                const char *matcher;
+                if (UNLIKELY(ch == '\0'))
+                {
+                    SET_ERRNO(HPE_INVALID_METHOD);
+                    goto error;
+                }
+
+                matcher = method_strings[parser->method];
+                if (ch == ' ' && matcher[parser->index] == '\0')
+                {
+                    UPDATE_STATE(s_req_spaces_before_url);
+                }
+                else if (ch == matcher[parser->index])
+                {
+                    ; /* nada */
+                }
+                else if ((ch >= 'A' && ch <= 'Z') || ch == '-')
+                {
+
+                    switch (parser->method << 16 | parser->index << 8 | ch)
+                    {
+#define XX(meth, pos, ch, new_meth)           \
+    case (HTTP_##meth << 16 | pos << 8 | ch): \
+        parser->method = HTTP_##new_meth;     \
+        break;
+
+                        XX(POST, 1, 'U', PUT)
+                        XX(POST, 1, 'A', PATCH)
+                        XX(POST, 1, 'R', PROPFIND)
+                        XX(PUT, 2, 'R', PURGE)
+                        XX(CONNECT, 1, 'H', CHECKOUT)
+                        XX(CONNECT, 2, 'P', COPY)
+                        XX(MKCOL, 1, 'O', MOVE)
+                        XX(MKCOL, 1, 'E', MERGE)
+                        XX(MKCOL, 1, '-', MSEARCH)
+                        XX(MKCOL, 2, 'A', MKACTIVITY)
+                        XX(MKCOL, 3, 'A', MKCALENDAR)
+                        XX(SUBSCRIBE, 1, 'E', SEARCH)
+                        XX(SUBSCRIBE, 1, 'O', SOURCE)
+                        XX(REPORT, 2, 'B', REBIND)
+                        XX(PROPFIND, 4, 'P', PROPPATCH)
+                        XX(LOCK, 1, 'I', LINK)
+                        XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
+                        XX(UNLOCK, 2, 'B', UNBIND)
+                        XX(UNLOCK, 3, 'I', UNLINK)
+#undef XX
+                        default:
+                            SET_ERRNO(HPE_INVALID_METHOD);
+                            goto error;
+                    }
+                }
+                else
+                {
+                    SET_ERRNO(HPE_INVALID_METHOD);
+                    goto error;
+                }
+
+                ++parser->index;
+                break;
+            }
+
+            case s_req_spaces_before_url:
+            {
+                if (ch == ' ')
+                    break;
+
+                MARK(url);
+                if (parser->method == HTTP_CONNECT)
+                {
+                    UPDATE_STATE(s_req_server_start);
+                }
+
+                UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+                if (UNLIKELY(CURRENT_STATE() == s_dead))
+                {
+                    SET_ERRNO(HPE_INVALID_URL);
+                    goto error;
+                }
+
+                break;
+            }
+
+            case s_req_schema:
+            case s_req_schema_slash:
+            case s_req_schema_slash_slash:
+            case s_req_server_start:
+            {
+                switch (ch)
+                {
+                    /* No whitespace allowed here */
+                    case ' ':
+                    case CR:
+                    case LF:
+                        SET_ERRNO(HPE_INVALID_URL);
+                        goto error;
+                    default:
+                        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+                        if (UNLIKELY(CURRENT_STATE() == s_dead))
+                        {
+                            SET_ERRNO(HPE_INVALID_URL);
+                            goto error;
+                        }
+                }
+
+                break;
+            }
+
+            case s_req_server:
+            case s_req_server_with_at:
+            case s_req_path:
+            case s_req_query_string_start:
+            case s_req_query_string:
+            case s_req_fragment_start:
+            case s_req_fragment:
+            {
+                switch (ch)
+                {
+                    case ' ':
+                        UPDATE_STATE(s_req_http_start);
+                        CALLBACK_DATA(url);
+                        break;
+                    case CR:
+                    case LF:
+                        parser->http_major = 0;
+                        parser->http_minor = 9;
+                        UPDATE_STATE((ch == CR) ? s_req_line_almost_done : s_header_field_start);
+                        CALLBACK_DATA(url);
+                        break;
+                    default:
+                        UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+                        if (UNLIKELY(CURRENT_STATE() == s_dead))
+                        {
+                            SET_ERRNO(HPE_INVALID_URL);
+                            goto error;
+                        }
+                }
+                break;
+            }
+
+            case s_req_http_start:
+                switch (ch)
+                {
+                    case ' ':
+                        break;
+                    case 'H':
+                        UPDATE_STATE(s_req_http_H);
+                        break;
+                    case 'I':
+                        if (parser->method == HTTP_SOURCE)
+                        {
+                            UPDATE_STATE(s_req_http_I);
+                            break;
+                        }
+                        /* fall through */
+                    default:
+                        SET_ERRNO(HPE_INVALID_CONSTANT);
+                        goto error;
+                }
+                break;
+
+            case s_req_http_H:
+                STRICT_CHECK(ch != 'T');
+                UPDATE_STATE(s_req_http_HT);
+                break;
+
+            case s_req_http_HT:
+                STRICT_CHECK(ch != 'T');
+                UPDATE_STATE(s_req_http_HTT);
+                break;
+
+            case s_req_http_HTT:
+                STRICT_CHECK(ch != 'P');
+                UPDATE_STATE(s_req_http_HTTP);
+                break;
+
+            case s_req_http_I:
+                STRICT_CHECK(ch != 'C');
+                UPDATE_STATE(s_req_http_IC);
+                break;
+
+            case s_req_http_IC:
+                STRICT_CHECK(ch != 'E');
+                UPDATE_STATE(s_req_http_HTTP); /* Treat "ICE" as "HTTP". */
+                break;
+
+            case s_req_http_HTTP:
+                STRICT_CHECK(ch != '/');
+                UPDATE_STATE(s_req_http_major);
+                break;
+
+            case s_req_http_major:
+                if (UNLIKELY(!IS_NUM(ch)))
+                {
+                    SET_ERRNO(HPE_INVALID_VERSION);
+                    goto error;
+                }
+
+                parser->http_major = ch - '0';
+                UPDATE_STATE(s_req_http_dot);
+                break;
+
+            case s_req_http_dot:
+            {
+                if (UNLIKELY(ch != '.'))
+                {
+                    SET_ERRNO(HPE_INVALID_VERSION);
+                    goto error;
+                }
+
+                UPDATE_STATE(s_req_http_minor);
+                break;
+            }
+
+            case s_req_http_minor:
+                if (UNLIKELY(!IS_NUM(ch)))
+                {
+                    SET_ERRNO(HPE_INVALID_VERSION);
+                    goto error;
+                }
+
+                parser->http_minor = ch - '0';
+                UPDATE_STATE(s_req_http_end);
+                break;
+
+            case s_req_http_end:
+            {
+                if (ch == CR)
+                {
+                    UPDATE_STATE(s_req_line_almost_done);
+                    break;
+                }
+
+                if (ch == LF)
+                {
+                    UPDATE_STATE(s_header_field_start);
+                    break;
+                }
+
+                SET_ERRNO(HPE_INVALID_VERSION);
+                goto error;
+                break;
+            }
+
+            /* end of request line */
+            case s_req_line_almost_done:
+            {
+                if (UNLIKELY(ch != LF))
+                {
+                    SET_ERRNO(HPE_LF_EXPECTED);
+                    goto error;
+                }
+
+                UPDATE_STATE(s_header_field_start);
+                break;
+            }
+
+            case s_header_field_start:
+            {
+                if (ch == CR)
+                {
+                    UPDATE_STATE(s_headers_almost_done);
+                    break;
+                }
+
+                if (ch == LF)
+                {
+                    /* they might be just sending \n instead of \r\n so this would be
+                     * the second \n to denote the end of headers*/
+                    UPDATE_STATE(s_headers_almost_done);
+                    REEXECUTE();
+                }
+
+                c = TOKEN(ch);
+
+                if (UNLIKELY(!c))
+                {
+                    SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+                    goto error;
+                }
+
+                MARK(header_field);
+
+                parser->index = 0;
+                UPDATE_STATE(s_header_field);
+
+                switch (c)
+                {
+                    case 'c':
+                        parser->header_state = h_C;
+                        break;
+
+                    case 'p':
+                        parser->header_state = h_matching_proxy_connection;
+                        break;
+
+                    case 't':
+                        parser->header_state = h_matching_transfer_encoding;
+                        break;
+
+                    case 'u':
+                        parser->header_state = h_matching_upgrade;
+                        break;
+
+                    default:
+                        parser->header_state = h_general;
+                        break;
+                }
+                break;
+            }
+
+            case s_header_field:
+            {
+                const char *start = p;
+                for (; p != data + len; p++)
+                {
+                    ch = *p;
+                    c  = TOKEN(ch);
+
+                    if (!c)
+                        break;
+
+                    switch (parser->header_state)
+                    {
+                        case h_general:
+                        {
+                            size_t      left = data + len - p;
+                            const char *pe   = p + MIN(left, max_header_size);
+                            while (p + 1 < pe && TOKEN(p[1]))
+                            {
+                                p++;
+                            }
+                            break;
+                        }
+
+                        case h_C:
+                            parser->index++;
+                            parser->header_state = (c == 'o' ? h_CO : h_general);
+                            break;
+
+                        case h_CO:
+                            parser->index++;
+                            parser->header_state = (c == 'n' ? h_CON : h_general);
+                            break;
+
+                        case h_CON:
+                            parser->index++;
+                            switch (c)
+                            {
+                                case 'n':
+                                    parser->header_state = h_matching_connection;
+                                    break;
+                                case 't':
+                                    parser->header_state = h_matching_content_length;
+                                    break;
+                                default:
+                                    parser->header_state = h_general;
+                                    break;
+                            }
+                            break;
+
+                            /* connection */
+
+                        case h_matching_connection:
+                            parser->index++;
+                            if (parser->index > sizeof(CONNECTION) - 1 ||
+                                c != CONNECTION[parser->index])
+                            {
+                                parser->header_state = h_general;
+                            }
+                            else if (parser->index == sizeof(CONNECTION) - 2)
+                            {
+                                parser->header_state = h_connection;
+                            }
+                            break;
+
+                            /* proxy-connection */
+
+                        case h_matching_proxy_connection:
+                            parser->index++;
+                            if (parser->index > sizeof(PROXY_CONNECTION) - 1 ||
+                                c != PROXY_CONNECTION[parser->index])
+                            {
+                                parser->header_state = h_general;
+                            }
+                            else if (parser->index == sizeof(PROXY_CONNECTION) - 2)
+                            {
+                                parser->header_state = h_connection;
+                            }
+                            break;
+
+                            /* content-length */
+
+                        case h_matching_content_length:
+                            parser->index++;
+                            if (parser->index > sizeof(CONTENT_LENGTH) - 1 ||
+                                c != CONTENT_LENGTH[parser->index])
+                            {
+                                parser->header_state = h_general;
+                            }
+                            else if (parser->index == sizeof(CONTENT_LENGTH) - 2)
+                            {
+                                parser->header_state = h_content_length;
+                            }
+                            break;
+
+                            /* transfer-encoding */
+
+                        case h_matching_transfer_encoding:
+                            parser->index++;
+                            if (parser->index > sizeof(TRANSFER_ENCODING) - 1 ||
+                                c != TRANSFER_ENCODING[parser->index])
+                            {
+                                parser->header_state = h_general;
+                            }
+                            else if (parser->index == sizeof(TRANSFER_ENCODING) - 2)
+                            {
+                                parser->header_state           = h_transfer_encoding;
+                                parser->uses_transfer_encoding = 1;
+                            }
+                            break;
+
+                            /* upgrade */
+
+                        case h_matching_upgrade:
+                            parser->index++;
+                            if (parser->index > sizeof(UPGRADE) - 1 || c != UPGRADE[parser->index])
+                            {
+                                parser->header_state = h_general;
+                            }
+                            else if (parser->index == sizeof(UPGRADE) - 2)
+                            {
+                                parser->header_state = h_upgrade;
+                            }
+                            break;
+
+                        case h_connection:
+                        case h_content_length:
+                        case h_transfer_encoding:
+                        case h_upgrade:
+                            if (ch != ' ')
+                                parser->header_state = h_general;
+                            break;
+
+                        default:
+                            B_ASSERT(0 && "Unknown header_state");
+                            break;
+                    }
+                }
+
+                if (p == data + len)
+                {
+                    --p;
+                    COUNT_HEADER_SIZE(p - start);
+                    break;
+                }
+
+                COUNT_HEADER_SIZE(p - start);
+
+                if (ch == ':')
+                {
+                    UPDATE_STATE(s_header_value_discard_ws);
+                    CALLBACK_DATA(header_field);
+                    break;
+                }
+
+                SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+                goto error;
+            }
+
+            case s_header_value_discard_ws:
+                if (ch == ' ' || ch == '\t')
+                    break;
+
+                if (ch == CR)
+                {
+                    UPDATE_STATE(s_header_value_discard_ws_almost_done);
+                    break;
+                }
+
+                if (ch == LF)
+                {
+                    UPDATE_STATE(s_header_value_discard_lws);
+                    break;
+                }
+
+                /* fall through */
+
+            case s_header_value_start:
+            {
+                MARK(header_value);
+
+                UPDATE_STATE(s_header_value);
+                parser->index = 0;
+
+                c = LOWER(ch);
+
+                switch (parser->header_state)
+                {
+                    case h_upgrade:
+                        parser->flags |= F_UPGRADE;
+                        parser->header_state = h_general;
+                        break;
+
+                    case h_transfer_encoding:
+                        /* looking for 'Transfer-Encoding: chunked' */
+                        if ('c' == c)
+                        {
+                            parser->header_state = h_matching_transfer_encoding_chunked;
+                        }
+                        else
+                        {
+                            parser->header_state = h_matching_transfer_encoding_token;
+                        }
+                        break;
+
+                    /* Multi-value `Transfer-Encoding` header */
+                    case h_matching_transfer_encoding_token_start:
+                        break;
+
+                    case h_content_length:
+                        if (UNLIKELY(!IS_NUM(ch)))
+                        {
+                            SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                            goto error;
+                        }
+
+                        if (parser->flags & F_CONTENTLENGTH)
+                        {
+                            SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+                            goto error;
+                        }
+
+                        parser->flags |= F_CONTENTLENGTH;
+                        parser->content_length = ch - '0';
+                        parser->header_state   = h_content_length_num;
+                        break;
+
+                    /* when obsolete line folding is encountered for content length
+                     * continue to the s_header_value state */
+                    case h_content_length_ws:
+                        break;
+
+                    case h_connection:
+                        /* looking for 'Connection: keep-alive' */
+                        if (c == 'k')
+                        {
+                            parser->header_state = h_matching_connection_keep_alive;
+                            /* looking for 'Connection: close' */
+                        }
+                        else if (c == 'c')
+                        {
+                            parser->header_state = h_matching_connection_close;
+                        }
+                        else if (c == 'u')
+                        {
+                            parser->header_state = h_matching_connection_upgrade;
+                        }
+                        else
+                        {
+                            parser->header_state = h_matching_connection_token;
+                        }
+                        break;
+
+                    /* Multi-value `Connection` header */
+                    case h_matching_connection_token_start:
+                        break;
+
+                    default:
+                        parser->header_state = h_general;
+                        break;
+                }
+                break;
+            }
+
+            case s_header_value:
+            {
+                const char        *start   = p;
+                enum header_states h_state = (enum header_states)parser->header_state;
+                for (; p != data + len; p++)
+                {
+                    ch = *p;
+                    if (ch == CR)
+                    {
+                        UPDATE_STATE(s_header_almost_done);
+                        parser->header_state = h_state;
+                        CALLBACK_DATA(header_value);
+                        break;
+                    }
+
+                    if (ch == LF)
+                    {
+                        UPDATE_STATE(s_header_almost_done);
+                        COUNT_HEADER_SIZE(p - start);
+                        parser->header_state = h_state;
+                        CALLBACK_DATA_NOADVANCE(header_value);
+                        REEXECUTE();
+                    }
+
+                    if (!lenient && !IS_HEADER_CHAR(ch))
+                    {
+                        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+                        goto error;
+                    }
+
+                    c = LOWER(ch);
+
+                    switch (h_state)
+                    {
+                        case h_general:
+                        {
+                            size_t      left = data + len - p;
+                            const char *pe   = p + MIN(left, max_header_size);
+
+                            for (; p != pe; p++)
+                            {
+                                ch = *p;
+                                if (ch == CR || ch == LF)
+                                {
+                                    --p;
+                                    break;
+                                }
+                                if (!lenient && !IS_HEADER_CHAR(ch))
+                                {
+                                    SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+                                    goto error;
+                                }
+                            }
+                            if (p == data + len)
+                                --p;
+                            break;
+                        }
+
+                        case h_connection:
+                        case h_transfer_encoding:
+                            B_ASSERT(0 && "Shouldn't get here.");
+                            break;
+
+                        case h_content_length:
+                            if (ch == ' ')
+                                break;
+                            h_state = h_content_length_num;
+                            /* fall through */
+
+                        case h_content_length_num:
+                        {
+                            uint64_t t;
+
+                            if (ch == ' ')
+                            {
+                                h_state = h_content_length_ws;
+                                break;
+                            }
+
+                            if (UNLIKELY(!IS_NUM(ch)))
+                            {
+                                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                                parser->header_state = h_state;
+                                goto error;
+                            }
+
+                            t = parser->content_length;
+                            t *= 10;
+                            t += ch - '0';
+
+                            /* Overflow? Test against a conservative limit for simplicity. */
+                            if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length))
+                            {
+                                SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                                parser->header_state = h_state;
+                                goto error;
+                            }
+
+                            parser->content_length = t;
+                            break;
+                        }
+
+                        case h_content_length_ws:
+                            if (ch == ' ')
+                                break;
+                            SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                            parser->header_state = h_state;
+                            goto error;
+
+                        /* Transfer-Encoding: chunked */
+                        case h_matching_transfer_encoding_token_start:
+                            /* looking for 'Transfer-Encoding: chunked' */
+                            if ('c' == c)
+                            {
+                                h_state = h_matching_transfer_encoding_chunked;
+                            }
+                            else if (STRICT_TOKEN(c))
+                            {
+                                /* TODO(indutny): similar code below does this, but why?
+                                 * At the very least it seems to be inconsistent given that
+                                 * h_matching_transfer_encoding_token does not check for
+                                 * `STRICT_TOKEN`
+                                 */
+                                h_state = h_matching_transfer_encoding_token;
+                            }
+                            else if (c == ' ' || c == '\t')
+                            {
+                                /* Skip lws */
+                            }
+                            else
+                            {
+                                h_state = h_general;
+                            }
+                            break;
+
+                        case h_matching_transfer_encoding_chunked:
+                            parser->index++;
+                            if (parser->index > sizeof(CHUNKED) - 1 || c != CHUNKED[parser->index])
+                            {
+                                h_state = h_matching_transfer_encoding_token;
+                            }
+                            else if (parser->index == sizeof(CHUNKED) - 2)
+                            {
+                                h_state = h_transfer_encoding_chunked;
+                            }
+                            break;
+
+                        case h_matching_transfer_encoding_token:
+                            if (ch == ',')
+                            {
+                                h_state       = h_matching_transfer_encoding_token_start;
+                                parser->index = 0;
+                            }
+                            break;
+
+                        case h_matching_connection_token_start:
+                            /* looking for 'Connection: keep-alive' */
+                            if (c == 'k')
+                            {
+                                h_state = h_matching_connection_keep_alive;
+                                /* looking for 'Connection: close' */
+                            }
+                            else if (c == 'c')
+                            {
+                                h_state = h_matching_connection_close;
+                            }
+                            else if (c == 'u')
+                            {
+                                h_state = h_matching_connection_upgrade;
+                            }
+                            else if (STRICT_TOKEN(c))
+                            {
+                                h_state = h_matching_connection_token;
+                            }
+                            else if (c == ' ' || c == '\t')
+                            {
+                                /* Skip lws */
+                            }
+                            else
+                            {
+                                h_state = h_general;
+                            }
+                            break;
+
+                        /* looking for 'Connection: keep-alive' */
+                        case h_matching_connection_keep_alive:
+                            parser->index++;
+                            if (parser->index > sizeof(KEEP_ALIVE) - 1 ||
+                                c != KEEP_ALIVE[parser->index])
+                            {
+                                h_state = h_matching_connection_token;
+                            }
+                            else if (parser->index == sizeof(KEEP_ALIVE) - 2)
+                            {
+                                h_state = h_connection_keep_alive;
+                            }
+                            break;
+
+                        /* looking for 'Connection: close' */
+                        case h_matching_connection_close:
+                            parser->index++;
+                            if (parser->index > sizeof(CLOSE) - 1 || c != CLOSE[parser->index])
+                            {
+                                h_state = h_matching_connection_token;
+                            }
+                            else if (parser->index == sizeof(CLOSE) - 2)
+                            {
+                                h_state = h_connection_close;
+                            }
+                            break;
+
+                        /* looking for 'Connection: upgrade' */
+                        case h_matching_connection_upgrade:
+                            parser->index++;
+                            if (parser->index > sizeof(UPGRADE) - 1 || c != UPGRADE[parser->index])
+                            {
+                                h_state = h_matching_connection_token;
+                            }
+                            else if (parser->index == sizeof(UPGRADE) - 2)
+                            {
+                                h_state = h_connection_upgrade;
+                            }
+                            break;
+
+                        case h_matching_connection_token:
+                            if (ch == ',')
+                            {
+                                h_state       = h_matching_connection_token_start;
+                                parser->index = 0;
+                            }
+                            break;
+
+                        case h_transfer_encoding_chunked:
+                            if (ch != ' ')
+                                h_state = h_matching_transfer_encoding_token;
+                            break;
+
+                        case h_connection_keep_alive:
+                        case h_connection_close:
+                        case h_connection_upgrade:
+                            if (ch == ',')
+                            {
+                                if (h_state == h_connection_keep_alive)
+                                {
+                                    parser->flags |= F_CONNECTION_KEEP_ALIVE;
+                                }
+                                else if (h_state == h_connection_close)
+                                {
+                                    parser->flags |= F_CONNECTION_CLOSE;
+                                }
+                                else if (h_state == h_connection_upgrade)
+                                {
+                                    parser->flags |= F_CONNECTION_UPGRADE;
+                                }
+                                h_state       = h_matching_connection_token_start;
+                                parser->index = 0;
+                            }
+                            else if (ch != ' ')
+                            {
+                                h_state = h_matching_connection_token;
+                            }
+                            break;
+
+                        default:
+                            UPDATE_STATE(s_header_value);
+                            h_state = h_general;
+                            break;
+                    }
+                }
+                parser->header_state = h_state;
+
+                if (p == data + len)
+                    --p;
+
+                COUNT_HEADER_SIZE(p - start);
+                break;
+            }
+
+            case s_header_almost_done:
+            {
+                if (UNLIKELY(ch != LF))
+                {
+                    SET_ERRNO(HPE_LF_EXPECTED);
+                    goto error;
+                }
+
+                UPDATE_STATE(s_header_value_lws);
+                break;
+            }
+
+            case s_header_value_lws:
+            {
+                if (ch == ' ' || ch == '\t')
+                {
+                    if (parser->header_state == h_content_length_num)
+                    {
+                        /* treat obsolete line folding as space */
+                        parser->header_state = h_content_length_ws;
+                    }
+                    UPDATE_STATE(s_header_value_start);
+                    REEXECUTE();
+                }
+
+                /* finished the header */
+                switch (parser->header_state)
+                {
+                    case h_connection_keep_alive:
+                        parser->flags |= F_CONNECTION_KEEP_ALIVE;
+                        break;
+                    case h_connection_close:
+                        parser->flags |= F_CONNECTION_CLOSE;
+                        break;
+                    case h_transfer_encoding_chunked:
+                        parser->flags |= F_CHUNKED;
+                        break;
+                    case h_connection_upgrade:
+                        parser->flags |= F_CONNECTION_UPGRADE;
+                        break;
+                    default:
+                        break;
+                }
+
+                UPDATE_STATE(s_header_field_start);
+                REEXECUTE();
+            }
+
+            case s_header_value_discard_ws_almost_done:
+            {
+                STRICT_CHECK(ch != LF);
+                UPDATE_STATE(s_header_value_discard_lws);
+                break;
+            }
+
+            case s_header_value_discard_lws:
+            {
+                if (ch == ' ' || ch == '\t')
+                {
+                    UPDATE_STATE(s_header_value_discard_ws);
+                    break;
+                }
+                else
+                {
+                    switch (parser->header_state)
+                    {
+                        case h_connection_keep_alive:
+                            parser->flags |= F_CONNECTION_KEEP_ALIVE;
+                            break;
+                        case h_connection_close:
+                            parser->flags |= F_CONNECTION_CLOSE;
+                            break;
+                        case h_connection_upgrade:
+                            parser->flags |= F_CONNECTION_UPGRADE;
+                            break;
+                        case h_transfer_encoding_chunked:
+                            parser->flags |= F_CHUNKED;
+                            break;
+                        case h_content_length:
+                            /* do not allow empty content length */
+                            SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                            goto error;
+                            break;
+                        default:
+                            break;
+                    }
+
+                    /* header value was empty */
+                    MARK(header_value);
+                    UPDATE_STATE(s_header_field_start);
+                    CALLBACK_DATA_NOADVANCE(header_value);
+                    REEXECUTE();
+                }
+            }
+
+            case s_headers_almost_done:
+            {
+                STRICT_CHECK(ch != LF);
+
+                if (parser->flags & F_TRAILING)
+                {
+                    /* End of a chunked request */
+                    UPDATE_STATE(s_message_done);
+                    CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
+                    REEXECUTE();
+                }
+
+                /* Cannot use transfer-encoding and a content-length header together
+                   per the HTTP specification. (RFC 7230 Section 3.3.3) */
+                if ((parser->uses_transfer_encoding == 1) && (parser->flags & F_CONTENTLENGTH))
+                {
+                    /* Allow it for lenient parsing as long as `Transfer-Encoding` is
+                     * not `chunked` or allow_length_with_encoding is set
+                     */
+                    if (parser->flags & F_CHUNKED)
+                    {
+                        if (!allow_chunked_length)
+                        {
+                            SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+                            goto error;
+                        }
+                    }
+                    else if (!lenient)
+                    {
+                        SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+                        goto error;
+                    }
+                }
+
+                UPDATE_STATE(s_headers_done);
+
+                /* Set this here so that on_headers_complete() callbacks can see it */
+                if ((parser->flags & F_UPGRADE) && (parser->flags & F_CONNECTION_UPGRADE))
+                {
+                    /* For responses, "Upgrade: foo" and "Connection: upgrade" are
+                     * mandatory only when it is a 101 Switching Protocols response,
+                     * otherwise it is purely informational, to announce support.
+                     */
+                    parser->upgrade = (parser->type == HTTP_REQUEST || parser->status_code == 101);
+                }
+                else
+                {
+                    parser->upgrade = (parser->method == HTTP_CONNECT);
+                }
+
+                /* Here we call the headers_complete callback. This is somewhat
+                 * different than other callbacks because if the user returns 1, we
+                 * will interpret that as saying that this message has no body. This
+                 * is needed for the annoying case of recieving a response to a HEAD
+                 * request.
+                 *
+                 * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+                 * we have to simulate it by handling a change in errno below.
+                 */
+                if (settings->on_headers_complete)
+                {
+                    switch (settings->on_headers_complete(parser))
+                    {
+                        case 0:
+                            break;
+
+                        case 2:
+                            parser->upgrade = 1;
+
+                            /* fall through */
+                        case 1:
+                            parser->flags |= F_SKIPBODY;
+                            break;
+
+                        default:
+                            SET_ERRNO(HPE_CB_headers_complete);
+                            RETURN(p - data); /* Error */
+                    }
+                }
+
+                if (HTTP_PARSER_ERRNO(parser) != HPE_OK)
+                {
+                    RETURN(p - data);
+                }
+
+                REEXECUTE();
+            }
+
+            case s_headers_done:
+            {
+                int hasBody;
+                STRICT_CHECK(ch != LF);
+
+                parser->nread = 0;
+                nread         = 0;
+
+                hasBody = parser->flags & F_CHUNKED ||
+                          (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
+                if (parser->upgrade &&
+                    (parser->method == HTTP_CONNECT || (parser->flags & F_SKIPBODY) || !hasBody))
+                {
+                    /* Exit, the rest of the message is in a different protocol. */
+                    UPDATE_STATE(NEW_MESSAGE());
+                    CALLBACK_NOTIFY(message_complete);
+                    RETURN((p - data) + 1);
+                }
+
+                if (parser->flags & F_SKIPBODY)
+                {
+                    UPDATE_STATE(NEW_MESSAGE());
+                    CALLBACK_NOTIFY(message_complete);
+                }
+                else if (parser->flags & F_CHUNKED)
+                {
+                    /* chunked encoding - ignore Content-Length header,
+                     * prepare for a chunk */
+                    UPDATE_STATE(s_chunk_size_start);
+                }
+                else if (parser->uses_transfer_encoding == 1)
+                {
+                    if (parser->type == HTTP_REQUEST && !lenient)
+                    {
+                        /* RFC 7230 3.3.3 */
+
+                        /* If a Transfer-Encoding header field
+                         * is present in a request and the chunked transfer coding is not
+                         * the final encoding, the message body length cannot be determined
+                         * reliably; the server MUST respond with the 400 (Bad Request)
+                         * status code and then close the connection.
+                         */
+                        SET_ERRNO(HPE_INVALID_TRANSFER_ENCODING);
+                        RETURN(p - data); /* Error */
+                    }
+                    else
+                    {
+                        /* RFC 7230 3.3.3 */
+
+                        /* If a Transfer-Encoding header field is present in a response and
+                         * the chunked transfer coding is not the final encoding, the
+                         * message body length is determined by reading the connection until
+                         * it is closed by the server.
+                         */
+                        UPDATE_STATE(s_body_identity_eof);
+                    }
+                }
+                else
+                {
+                    if (parser->content_length == 0)
+                    {
+                        /* Content-Length header given but zero: Content-Length: 0\r\n */
+                        UPDATE_STATE(NEW_MESSAGE());
+                        CALLBACK_NOTIFY(message_complete);
+                    }
+                    else if (parser->content_length != ULLONG_MAX)
+                    {
+                        /* Content-Length header given and non-zero */
+                        UPDATE_STATE(s_body_identity);
+                    }
+                    else
+                    {
+                        if (!http_message_needs_eof(parser))
+                        {
+                            /* Assume content-length 0 - read the next */
+                            UPDATE_STATE(NEW_MESSAGE());
+                            CALLBACK_NOTIFY(message_complete);
+                        }
+                        else
+                        {
+                            /* Read body until EOF */
+                            UPDATE_STATE(s_body_identity_eof);
+                        }
+                    }
+                }
+
+                break;
+            }
+
+            case s_body_identity:
+            {
+                uint64_t to_read = MIN(parser->content_length, (uint64_t)((data + len) - p));
+
+                B_ASSERT(parser->content_length != 0 && parser->content_length != ULLONG_MAX);
+
+                /* The difference between advancing content_length and p is because
+                 * the latter will automaticaly advance on the next loop iteration.
+                 * Further, if content_length ends up at 0, we want to see the last
+                 * byte again for our message complete callback.
+                 */
+                MARK(body);
+                parser->content_length -= to_read;
+                p += to_read - 1;
+
+                if (parser->content_length == 0)
+                {
+                    UPDATE_STATE(s_message_done);
+
+                    /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+                     *
+                     * The alternative to doing this is to wait for the next byte to
+                     * trigger the data callback, just as in every other case. The
+                     * problem with this is that this makes it difficult for the test
+                     * harness to distinguish between complete-on-EOF and
+                     * complete-on-length. It's not clear that this distinction is
+                     * important for applications, but let's keep it for now.
+                     */
+                    CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+                    REEXECUTE();
+                }
+
+                break;
+            }
+
+            /* read until EOF */
+            case s_body_identity_eof:
+                MARK(body);
+                p = data + len - 1;
+
+                break;
+
+            case s_message_done:
+                UPDATE_STATE(NEW_MESSAGE());
+                CALLBACK_NOTIFY(message_complete);
+                if (parser->upgrade)
+                {
+                    /* Exit, the rest of the message is in a different protocol. */
+                    RETURN((p - data) + 1);
+                }
+                break;
+
+            case s_chunk_size_start:
+            {
+                B_ASSERT(nread == 1);
+                B_ASSERT(parser->flags & F_CHUNKED);
+
+                unhex_val = unhex[(unsigned char)ch];
+                if (UNLIKELY(unhex_val == -1))
+                {
+                    SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+                    goto error;
+                }
+
+                parser->content_length = unhex_val;
+                UPDATE_STATE(s_chunk_size);
+                break;
+            }
+
+            case s_chunk_size:
+            {
+                uint64_t t;
+
+                B_ASSERT(parser->flags & F_CHUNKED);
+
+                if (ch == CR)
+                {
+                    UPDATE_STATE(s_chunk_size_almost_done);
+                    break;
+                }
+
+                unhex_val = unhex[(unsigned char)ch];
+
+                if (unhex_val == -1)
+                {
+                    if (ch == ';' || ch == ' ')
+                    {
+                        UPDATE_STATE(s_chunk_parameters);
+                        break;
+                    }
+
+                    SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+                    goto error;
+                }
+
+                t = parser->content_length;
+                t *= 16;
+                t += unhex_val;
+
+                /* Overflow? Test against a conservative limit for simplicity. */
+                if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length))
+                {
+                    SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+                    goto error;
+                }
+
+                parser->content_length = t;
+                break;
+            }
+
+            case s_chunk_parameters:
+            {
+                B_ASSERT(parser->flags & F_CHUNKED);
+                /* just ignore this shit. TODO check for overflow */
+                if (ch == CR)
+                {
+                    UPDATE_STATE(s_chunk_size_almost_done);
+                    break;
+                }
+                break;
+            }
+
+            case s_chunk_size_almost_done:
+            {
+                B_ASSERT(parser->flags & F_CHUNKED);
+                STRICT_CHECK(ch != LF);
+
+                parser->nread = 0;
+                nread         = 0;
+
+                if (parser->content_length == 0)
+                {
+                    parser->flags |= F_TRAILING;
+                    UPDATE_STATE(s_header_field_start);
+                }
+                else
+                {
+                    UPDATE_STATE(s_chunk_data);
+                }
+                CALLBACK_NOTIFY(chunk_header);
+                break;
+            }
+
+            case s_chunk_data:
+            {
+                uint64_t to_read = MIN(parser->content_length, (uint64_t)((data + len) - p));
+
+                B_ASSERT(parser->flags & F_CHUNKED);
+                B_ASSERT(parser->content_length != 0 && parser->content_length != ULLONG_MAX);
+
+                /* See the explanation in s_body_identity for why the content
+                 * length and data pointers are managed this way.
+                 */
+                MARK(body);
+                parser->content_length -= to_read;
+                p += to_read - 1;
+
+                if (parser->content_length == 0)
+                {
+                    UPDATE_STATE(s_chunk_data_almost_done);
+                }
+
+                break;
+            }
+
+            case s_chunk_data_almost_done:
+                B_ASSERT(parser->flags & F_CHUNKED);
+                B_ASSERT(parser->content_length == 0);
+                STRICT_CHECK(ch != CR);
+                UPDATE_STATE(s_chunk_data_done);
+                CALLBACK_DATA(body);
+                break;
+
+            case s_chunk_data_done:
+                B_ASSERT(parser->flags & F_CHUNKED);
+                STRICT_CHECK(ch != LF);
+                parser->nread = 0;
+                nread         = 0;
+                UPDATE_STATE(s_chunk_size_start);
+                CALLBACK_NOTIFY(chunk_complete);
+                break;
+
+            default:
+                B_ASSERT(0 && "unhandled state");
+                SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+                goto error;
+        }
+    }
+
+    /* Run callbacks for any marks that we have leftover after we ran out of
+     * bytes. There should be at most one of these set, so it's OK to invoke
+     * them in series (unset marks will not result in callbacks).
+     *
+     * We use the NOADVANCE() variety of callbacks here because 'p' has already
+     * overflowed 'data' and this allows us to correct for the off-by-one that
+     * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+     * value that's in-bounds).
+     */
+
+    B_ASSERT(((header_field_mark ? 1 : 0) + (header_value_mark ? 1 : 0) + (url_mark ? 1 : 0) +
+              (body_mark ? 1 : 0) + (status_mark ? 1 : 0)) <= 1);
+
+    CALLBACK_DATA_NOADVANCE(header_field);
+    CALLBACK_DATA_NOADVANCE(header_value);
+    CALLBACK_DATA_NOADVANCE(url);
+    CALLBACK_DATA_NOADVANCE(body);
+    CALLBACK_DATA_NOADVANCE(status);
+
+    RETURN(len);
+
+error:
+    if (HTTP_PARSER_ERRNO(parser) == HPE_OK)
+    {
+        SET_ERRNO(HPE_UNKNOWN);
+    }
+
+    RETURN(p - data);
+}
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int http_message_needs_eof(const http_parser *parser)
+{
+    if (parser->type == HTTP_REQUEST)
+    {
+        return 0;
+    }
+
+    /* See RFC 2616 section 4.4 */
+    if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+        parser->status_code == 204 ||     /* No Content */
+        parser->status_code == 304 ||     /* Not Modified */
+        parser->flags & F_SKIPBODY)
+    { /* response to a HEAD request */
+        return 0;
+    }
+
+    /* RFC 7230 3.3.3, see `s_headers_almost_done` */
+    if ((parser->uses_transfer_encoding == 1) && (parser->flags & F_CHUNKED) == 0)
+    {
+        return 1;
+    }
+
+    if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX)
+    {
+        return 0;
+    }
+
+    return 1;
+}
+
+int http_should_keep_alive(const http_parser *parser)
+{
+    if (parser->http_major > 0 && parser->http_minor > 0)
+    {
+        /* HTTP/1.1 */
+        if (parser->flags & F_CONNECTION_CLOSE)
+        {
+            return 0;
+        }
+    }
+    else
+    {
+        /* HTTP/1.0 or earlier */
+        if (!(parser->flags & F_CONNECTION_KEEP_ALIVE))
+        {
+            return 0;
+        }
+    }
+
+    return !http_message_needs_eof(parser);
+}
+
+const char *http_method_str(enum http_method m)
+{
+    return ELEM_AT(method_strings, m, "<unknown>");
+}
+
+const char *http_status_str(enum http_status s)
+{
+    switch (s)
+    {
+#define XX(num, name, string) \
+    case HTTP_STATUS_##name:  \
+        return #string;
+        HTTP_STATUS_MAP(XX)
+#undef XX
+        default:
+            return "<unknown>";
+    }
+}
+
+void http_parser_init(http_parser *parser, enum http_parser_type t)
+{
+    void *data = parser->data; /* preserve application data */
+    memset(parser, 0, sizeof(*parser));
+    parser->data = data;
+    parser->type = t;
+    parser->state =
+        (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+    parser->http_errno = HPE_OK;
+}
+
+void http_parser_settings_init(http_parser_settings *settings)
+{
+    memset(settings, 0, sizeof(*settings));
+}
+
+const char *http_errno_name(enum http_errno err)
+{
+    B_ASSERT(((size_t)err) < ARRAY_SIZE(http_strerror_tab));
+    return http_strerror_tab[err].name;
+}
+
+const char *http_errno_description(enum http_errno err)
+{
+    B_ASSERT(((size_t)err) < ARRAY_SIZE(http_strerror_tab));
+    return http_strerror_tab[err].description;
+}
+
+static enum http_host_state http_parse_host_char(enum http_host_state s, const char ch)
+{
+    switch (s)
+    {
+        case s_http_userinfo:
+        case s_http_userinfo_start:
+            if (ch == '@')
+            {
+                return s_http_host_start;
+            }
+
+            if (IS_USERINFO_CHAR(ch))
+            {
+                return s_http_userinfo;
+            }
+            break;
+
+        case s_http_host_start:
+            if (ch == '[')
+            {
+                return s_http_host_v6_start;
+            }
+
+            if (IS_HOST_CHAR(ch))
+            {
+                return s_http_host;
+            }
+
+            break;
+
+        case s_http_host:
+            if (IS_HOST_CHAR(ch))
+            {
+                return s_http_host;
+            }
+
+        /* fall through */
+        case s_http_host_v6_end:
+            if (ch == ':')
+            {
+                return s_http_host_port_start;
+            }
+
+            break;
+
+        case s_http_host_v6:
+            if (ch == ']')
+            {
+                return s_http_host_v6_end;
+            }
+
+        /* fall through */
+        case s_http_host_v6_start:
+            if (IS_HEX(ch) || ch == ':' || ch == '.')
+            {
+                return s_http_host_v6;
+            }
+
+            if (s == s_http_host_v6 && ch == '%')
+            {
+                return s_http_host_v6_zone_start;
+            }
+            break;
+
+        case s_http_host_v6_zone:
+            if (ch == ']')
+            {
+                return s_http_host_v6_end;
+            }
+
+        /* fall through */
+        case s_http_host_v6_zone_start:
+            /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
+            if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || ch == '~')
+            {
+                return s_http_host_v6_zone;
+            }
+            break;
+
+        case s_http_host_port:
+        case s_http_host_port_start:
+            if (IS_NUM(ch))
+            {
+                return s_http_host_port;
+            }
+
+            break;
+
+        default:
+            break;
+    }
+    return s_http_host_dead;
+}
+
+static int http_parse_host(const char *buf, struct http_parser_url *u, int found_at)
+{
+    enum http_host_state s;
+
+    const char *p;
+    size_t      buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+    B_ASSERT(u->field_set & (1 << UF_HOST));
+
+    u->field_data[UF_HOST].len = 0;
+
+    s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+    for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++)
+    {
+        enum http_host_state new_s = http_parse_host_char(s, *p);
+
+        if (new_s == s_http_host_dead)
+        {
+            return 1;
+        }
+
+        switch (new_s)
+        {
+            case s_http_host:
+                if (s != s_http_host)
+                {
+                    u->field_data[UF_HOST].off = (uint16_t)(p - buf);
+                }
+                u->field_data[UF_HOST].len++;
+                break;
+
+            case s_http_host_v6:
+                if (s != s_http_host_v6)
+                {
+                    u->field_data[UF_HOST].off = (uint16_t)(p - buf);
+                }
+                u->field_data[UF_HOST].len++;
+                break;
+
+            case s_http_host_v6_zone_start:
+            case s_http_host_v6_zone:
+                u->field_data[UF_HOST].len++;
+                break;
+
+            case s_http_host_port:
+                if (s != s_http_host_port)
+                {
+                    u->field_data[UF_PORT].off = (uint16_t)(p - buf);
+                    u->field_data[UF_PORT].len = 0;
+                    u->field_set |= (1 << UF_PORT);
+                }
+                u->field_data[UF_PORT].len++;
+                break;
+
+            case s_http_userinfo:
+                if (s != s_http_userinfo)
+                {
+                    u->field_data[UF_USERINFO].off = (uint16_t)(p - buf);
+                    u->field_data[UF_USERINFO].len = 0;
+                    u->field_set |= (1 << UF_USERINFO);
+                }
+                u->field_data[UF_USERINFO].len++;
+                break;
+
+            default:
+                break;
+        }
+        s = new_s;
+    }
+
+    /* Make sure we don't end somewhere unexpected */
+    switch (s)
+    {
+        case s_http_host_start:
+        case s_http_host_v6_start:
+        case s_http_host_v6:
+        case s_http_host_v6_zone_start:
+        case s_http_host_v6_zone:
+        case s_http_host_port_start:
+        case s_http_userinfo:
+        case s_http_userinfo_start:
+            return 1;
+        default:
+            break;
+    }
+
+    return 0;
+}
+
+void http_parser_url_init(struct http_parser_url *u)
+{
+    memset(u, 0, sizeof(*u));
+}
+
+int http_parser_parse_url(const char *buf, size_t buflen, int is_connect, struct http_parser_url *u)
+{
+    enum state                  s;
+    const char                 *p;
+    enum http_parser_url_fields uf, old_uf;
+    int                         found_at = 0;
+
+    if (buflen == 0)
+    {
+        return 1;
+    }
+
+    u->port = u->field_set = 0;
+    s                      = is_connect ? s_req_server_start : s_req_spaces_before_url;
+    old_uf                 = UF_MAX;
+
+    for (p = buf; p < buf + buflen; p++)
+    {
+        s = parse_url_char(s, *p);
+
+        /* Figure out the next field that we're operating on */
+        switch (s)
+        {
+            case s_dead:
+                return 1;
+
+            /* Skip delimeters */
+            case s_req_schema_slash:
+            case s_req_schema_slash_slash:
+            case s_req_server_start:
+            case s_req_query_string_start:
+            case s_req_fragment_start:
+                continue;
+
+            case s_req_schema:
+                uf = UF_SCHEMA;
+                break;
+
+            case s_req_server_with_at:
+                found_at = 1;
+
+            /* fall through */
+            case s_req_server:
+                uf = UF_HOST;
+                break;
+
+            case s_req_path:
+                uf = UF_PATH;
+                break;
+
+            case s_req_query_string:
+                uf = UF_QUERY;
+                break;
+
+            case s_req_fragment:
+                uf = UF_FRAGMENT;
+                break;
+
+            default:
+                B_ASSERT(!"Unexpected state");
+                return 1;
+        }
+
+        /* Nothing's changed; soldier on */
+        if (uf == old_uf)
+        {
+            u->field_data[uf].len++;
+            continue;
+        }
+
+        u->field_data[uf].off = (uint16_t)(p - buf);
+        u->field_data[uf].len = 1;
+
+        u->field_set |= (1 << uf);
+        old_uf = uf;
+    }
+
+    /* host must be present if there is a schema */
+    /* parsing http:///toto will fail */
+    if ((u->field_set & (1 << UF_SCHEMA)) && (u->field_set & (1 << UF_HOST)) == 0)
+    {
+        return 1;
+    }
+
+    if (u->field_set & (1 << UF_HOST))
+    {
+        if (http_parse_host(buf, u, found_at) != 0)
+        {
+            return 1;
+        }
+    }
+
+    /* CONNECT requests can only contain "hostname:port" */
+    if (is_connect && u->field_set != ((1 << UF_HOST) | (1 << UF_PORT)))
+    {
+        return 1;
+    }
+
+    if (u->field_set & (1 << UF_PORT))
+    {
+        uint16_t      off;
+        uint16_t      len;
+        const char   *p;
+        const char   *end;
+        unsigned long v;
+
+        off = u->field_data[UF_PORT].off;
+        len = u->field_data[UF_PORT].len;
+        end = buf + off + len;
+
+        /* NOTE: The characters are already validated and are in the [0-9] range */
+        B_ASSERT((size_t)(off + len) <= buflen && "Port number overflow");
+        v = 0;
+        for (p = buf + off; p < end; p++)
+        {
+            v *= 10;
+            v += *p - '0';
+
+            /* Ports have a max value of 2^16 */
+            if (v > 0xffff)
+            {
+                return 1;
+            }
+        }
+
+        u->port = (uint16_t)v;
+    }
+
+    return 0;
+}
+
+void http_parser_pause(http_parser *parser, int paused)
+{
+    /* Users should only be pausing/unpausing a parser that is not in an error
+     * state. In non-debug builds, there's not much that we can do about this
+     * other than ignore it.
+     */
+    if (HTTP_PARSER_ERRNO(parser) == HPE_OK || HTTP_PARSER_ERRNO(parser) == HPE_PAUSED)
+    {
+        uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
+        SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+    }
+    else
+    {
+        B_ASSERT(0 && "Attempting to pause parser in error state");
+    }
+}
+
+int http_body_is_final(const struct http_parser *parser)
+{
+    return parser->state == s_message_done;
+}
+
+unsigned long http_parser_version(void)
+{
+    return HTTP_PARSER_VERSION_MAJOR * 0x10000 | HTTP_PARSER_VERSION_MINOR * 0x00100 |
+           HTTP_PARSER_VERSION_PATCH * 0x00001;
+}
+
+void http_parser_set_max_header_size(uint32_t size)
+{
+    max_header_size = size;
+}
+
+#endif
diff --git a/bos/thirdparty/http-parser/http_parser.h b/bos/thirdparty/http-parser/http_parser.h
new file mode 100644
index 0000000000000000000000000000000000000000..a98ef082f46811e2333e781239892ec8b80c9cbb
--- /dev/null
+++ b/bos/thirdparty/http-parser/http_parser.h
@@ -0,0 +1,442 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef http_parser_h
+#define http_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "b_config.h"
+
+#if (defined(_HTTP_PARSER_ENABLE) && (_HTTP_PARSER_ENABLE == 1))
+
+/* Also update SONAME in the Makefile whenever you change these. */
+#define HTTP_PARSER_VERSION_MAJOR 2
+#define HTTP_PARSER_VERSION_MINOR 9
+#define HTTP_PARSER_VERSION_PATCH 4
+
+#include <stddef.h>
+#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER < 1600) && \
+    !defined(__WINE__)
+#include <BaseTsd.h>
+typedef __int8           int8_t;
+typedef unsigned __int8  uint8_t;
+typedef __int16          int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32          int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64          int64_t;
+typedef unsigned __int64 uint64_t;
+#elif (defined(__sun) || defined(__sun__)) && defined(__SunOS_5_9)
+#include <sys/inttypes.h>
+#else
+#include <stdint.h>
+#endif
+
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+#define HTTP_PARSER_STRICT 1
+#endif
+
+/* Maximium header size allowed. If the macro is not defined
+ * before including this header then the default is used. To
+ * change the maximum header size, define the macro in the build
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
+ * the effective limit on the size of the header, define the macro
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
+ */
+#ifndef HTTP_MAX_HEADER_SIZE
+#define HTTP_MAX_HEADER_SIZE (80 * 1024)
+#endif
+
+typedef struct http_parser          http_parser;
+typedef struct http_parser_settings http_parser_settings;
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * chunked' headers that indicate the presence of a body.
+ *
+ * Returning `2` from on_headers_complete will tell parser that it should not
+ * expect neither a body nor any futher responses on this connection. This is
+ * useful for handling responses to a CONNECT request which may not contain
+ * `Upgrade` or `Connection: upgrade` headers.
+ *
+ * http_data_cb does not return data chunks. It will be called arbitrarily
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
+ * each providing just a few characters more data.
+ */
+typedef int (*http_data_cb)(http_parser *, const char *at, size_t length);
+typedef int (*http_cb)(http_parser *);
+
+/* Status Codes */
+#define HTTP_STATUS_MAP(XX)                                                   \
+    XX(100, CONTINUE, Continue)                                               \
+    XX(101, SWITCHING_PROTOCOLS, Switching Protocols)                         \
+    XX(102, PROCESSING, Processing)                                           \
+    XX(200, OK, OK)                                                           \
+    XX(201, CREATED, Created)                                                 \
+    XX(202, ACCEPTED, Accepted)                                               \
+    XX(203, NON_AUTHORITATIVE_INFORMATION, Non - Authoritative Information)   \
+    XX(204, NO_CONTENT, No Content)                                           \
+    XX(205, RESET_CONTENT, Reset Content)                                     \
+    XX(206, PARTIAL_CONTENT, Partial Content)                                 \
+    XX(207, MULTI_STATUS, Multi - Status)                                     \
+    XX(208, ALREADY_REPORTED, Already Reported)                               \
+    XX(226, IM_USED, IM Used)                                                 \
+    XX(300, MULTIPLE_CHOICES, Multiple Choices)                               \
+    XX(301, MOVED_PERMANENTLY, Moved Permanently)                             \
+    XX(302, FOUND, Found)                                                     \
+    XX(303, SEE_OTHER, See Other)                                             \
+    XX(304, NOT_MODIFIED, Not Modified)                                       \
+    XX(305, USE_PROXY, Use Proxy)                                             \
+    XX(307, TEMPORARY_REDIRECT, Temporary Redirect)                           \
+    XX(308, PERMANENT_REDIRECT, Permanent Redirect)                           \
+    XX(400, BAD_REQUEST, Bad Request)                                         \
+    XX(401, UNAUTHORIZED, Unauthorized)                                       \
+    XX(402, PAYMENT_REQUIRED, Payment Required)                               \
+    XX(403, FORBIDDEN, Forbidden)                                             \
+    XX(404, NOT_FOUND, Not Found)                                             \
+    XX(405, METHOD_NOT_ALLOWED, Method Not Allowed)                           \
+    XX(406, NOT_ACCEPTABLE, Not Acceptable)                                   \
+    XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required)     \
+    XX(408, REQUEST_TIMEOUT, Request Timeout)                                 \
+    XX(409, CONFLICT, Conflict)                                               \
+    XX(410, GONE, Gone)                                                       \
+    XX(411, LENGTH_REQUIRED, Length Required)                                 \
+    XX(412, PRECONDITION_FAILED, Precondition Failed)                         \
+    XX(413, PAYLOAD_TOO_LARGE, Payload Too Large)                             \
+    XX(414, URI_TOO_LONG, URI Too Long)                                       \
+    XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type)                   \
+    XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable)                     \
+    XX(417, EXPECTATION_FAILED, Expectation Failed)                           \
+    XX(421, MISDIRECTED_REQUEST, Misdirected Request)                         \
+    XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity)                       \
+    XX(423, LOCKED, Locked)                                                   \
+    XX(424, FAILED_DEPENDENCY, Failed Dependency)                             \
+    XX(426, UPGRADE_REQUIRED, Upgrade Required)                               \
+    XX(428, PRECONDITION_REQUIRED, Precondition Required)                     \
+    XX(429, TOO_MANY_REQUESTS, Too Many Requests)                             \
+    XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
+    XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons)     \
+    XX(500, INTERNAL_SERVER_ERROR, Internal Server Error)                     \
+    XX(501, NOT_IMPLEMENTED, Not Implemented)                                 \
+    XX(502, BAD_GATEWAY, Bad Gateway)                                         \
+    XX(503, SERVICE_UNAVAILABLE, Service Unavailable)                         \
+    XX(504, GATEWAY_TIMEOUT, Gateway Timeout)                                 \
+    XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported)           \
+    XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates)                 \
+    XX(507, INSUFFICIENT_STORAGE, Insufficient Storage)                       \
+    XX(508, LOOP_DETECTED, Loop Detected)                                     \
+    XX(510, NOT_EXTENDED, Not Extended)                                       \
+    XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required)
+
+enum http_status
+{
+#define XX(num, name, string) HTTP_STATUS_##name = num,
+    HTTP_STATUS_MAP(XX)
+#undef XX
+};
+
+/* Request Methods */
+#define HTTP_METHOD_MAP(XX)          \
+    XX(0, DELETE, DELETE)            \
+    XX(1, GET, GET)                  \
+    XX(2, HEAD, HEAD)                \
+    XX(3, POST, POST)                \
+    XX(4, PUT, PUT)                  \
+    /* pathological */               \
+    XX(5, CONNECT, CONNECT)          \
+    XX(6, OPTIONS, OPTIONS)          \
+    XX(7, TRACE, TRACE)              \
+    /* WebDAV */                     \
+    XX(8, COPY, COPY)                \
+    XX(9, LOCK, LOCK)                \
+    XX(10, MKCOL, MKCOL)             \
+    XX(11, MOVE, MOVE)               \
+    XX(12, PROPFIND, PROPFIND)       \
+    XX(13, PROPPATCH, PROPPATCH)     \
+    XX(14, SEARCH, SEARCH)           \
+    XX(15, UNLOCK, UNLOCK)           \
+    XX(16, BIND, BIND)               \
+    XX(17, REBIND, REBIND)           \
+    XX(18, UNBIND, UNBIND)           \
+    XX(19, ACL, ACL)                 \
+    /* subversion */                 \
+    XX(20, REPORT, REPORT)           \
+    XX(21, MKACTIVITY, MKACTIVITY)   \
+    XX(22, CHECKOUT, CHECKOUT)       \
+    XX(23, MERGE, MERGE)             \
+    /* upnp */                       \
+    XX(24, MSEARCH, M - SEARCH)      \
+    XX(25, NOTIFY, NOTIFY)           \
+    XX(26, SUBSCRIBE, SUBSCRIBE)     \
+    XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+    /* RFC-5789 */                   \
+    XX(28, PATCH, PATCH)             \
+    XX(29, PURGE, PURGE)             \
+    /* CalDAV */                     \
+    XX(30, MKCALENDAR, MKCALENDAR)   \
+    /* RFC-2068, section 19.6.1.2 */ \
+    XX(31, LINK, LINK)               \
+    XX(32, UNLINK, UNLINK)           \
+    /* icecast */                    \
+    XX(33, SOURCE, SOURCE)
+
+enum http_method
+{
+#define XX(num, name, string) HTTP_##name = num,
+    HTTP_METHOD_MAP(XX)
+#undef XX
+};
+
+enum http_parser_type
+{
+    HTTP_REQUEST,
+    HTTP_RESPONSE,
+    HTTP_BOTH
+};
+
+/* Flag values for http_parser.flags field */
+enum flags
+{
+    F_CHUNKED               = 1 << 0,
+    F_CONNECTION_KEEP_ALIVE = 1 << 1,
+    F_CONNECTION_CLOSE      = 1 << 2,
+    F_CONNECTION_UPGRADE    = 1 << 3,
+    F_TRAILING              = 1 << 4,
+    F_UPGRADE               = 1 << 5,
+    F_SKIPBODY              = 1 << 6,
+    F_CONTENTLENGTH         = 1 << 7
+};
+
+/* Map for errno-related constants
+ *
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX)                                                           \
+    /* No error */                                                                   \
+    XX(OK, "success")                                                                \
+                                                                                     \
+    /* Callback-related errors */                                                    \
+    XX(CB_message_begin, "the on_message_begin callback failed")                     \
+    XX(CB_url, "the on_url callback failed")                                         \
+    XX(CB_header_field, "the on_header_field callback failed")                       \
+    XX(CB_header_value, "the on_header_value callback failed")                       \
+    XX(CB_headers_complete, "the on_headers_complete callback failed")               \
+    XX(CB_body, "the on_body callback failed")                                       \
+    XX(CB_message_complete, "the on_message_complete callback failed")               \
+    XX(CB_status, "the on_status callback failed")                                   \
+    XX(CB_chunk_header, "the on_chunk_header callback failed")                       \
+    XX(CB_chunk_complete, "the on_chunk_complete callback failed")                   \
+                                                                                     \
+    /* Parsing-related errors */                                                     \
+    XX(INVALID_EOF_STATE, "stream ended at an unexpected time")                      \
+    XX(HEADER_OVERFLOW, "too many header bytes seen; overflow detected")             \
+    XX(CLOSED_CONNECTION, "data received after completed connection: close message") \
+    XX(INVALID_VERSION, "invalid HTTP version")                                      \
+    XX(INVALID_STATUS, "invalid HTTP status code")                                   \
+    XX(INVALID_METHOD, "invalid HTTP method")                                        \
+    XX(INVALID_URL, "invalid URL")                                                   \
+    XX(INVALID_HOST, "invalid host")                                                 \
+    XX(INVALID_PORT, "invalid port")                                                 \
+    XX(INVALID_PATH, "invalid path")                                                 \
+    XX(INVALID_QUERY_STRING, "invalid query string")                                 \
+    XX(INVALID_FRAGMENT, "invalid fragment")                                         \
+    XX(LF_EXPECTED, "LF character expected")                                         \
+    XX(INVALID_HEADER_TOKEN, "invalid character in header")                          \
+    XX(INVALID_CONTENT_LENGTH, "invalid character in content-length header")         \
+    XX(UNEXPECTED_CONTENT_LENGTH, "unexpected content-length header")                \
+    XX(INVALID_CHUNK_SIZE, "invalid character in chunk size header")                 \
+    XX(INVALID_CONSTANT, "invalid constant string")                                  \
+    XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")              \
+    XX(STRICT, "strict mode assertion failed")                                       \
+    XX(PAUSED, "parser is paused")                                                   \
+    XX(UNKNOWN, "an unknown error occurred")                                         \
+    XX(INVALID_TRANSFER_ENCODING, "request has invalid transfer-encoding")
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno
+{
+    HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+/* Get an http_errno value from an http_parser */
+#define HTTP_PARSER_ERRNO(p) ((enum http_errno)(p)->http_errno)
+
+struct http_parser
+{
+    /** PRIVATE **/
+    unsigned int type : 2;                   /* enum http_parser_type */
+    unsigned int flags : 8;                  /* F_* values from 'flags' enum; semi-public */
+    unsigned int state : 7;                  /* enum state from http_parser.c */
+    unsigned int header_state : 7;           /* enum header_state from http_parser.c */
+    unsigned int index : 5;                  /* index into current matcher */
+    unsigned int uses_transfer_encoding : 1; /* Transfer-Encoding header is present */
+    unsigned int allow_chunked_length : 1;   /* Allow headers with both
+                                              * `Content-Length` and
+                                              * `Transfer-Encoding: chunked` set */
+    unsigned int lenient_http_headers : 1;
+
+    uint32_t nread;          /* # bytes read in various scenarios */
+    uint64_t content_length; /* # bytes in body. `(uint64_t) -1` (all bits one)
+                              * if no Content-Length header.
+                              */
+
+    /** READ-ONLY **/
+    unsigned short http_major;
+    unsigned short http_minor;
+    unsigned int   status_code : 16; /* responses only */
+    unsigned int   method : 8;       /* requests only */
+    unsigned int   http_errno : 7;
+
+    /* 1 = Upgrade header was present and the parser has exited because of that.
+     * 0 = No upgrade header present.
+     * Should be checked when http_parser_execute() returns in addition to
+     * error checking.
+     */
+    unsigned int upgrade : 1;
+
+    /** PUBLIC **/
+    void *data; /* A pointer to get hook to the "connection" or "socket" object */
+};
+
+struct http_parser_settings
+{
+    http_cb      on_message_begin;
+    http_data_cb on_url;
+    http_data_cb on_status;
+    http_data_cb on_header_field;
+    http_data_cb on_header_value;
+    http_cb      on_headers_complete;
+    http_data_cb on_body;
+    http_cb      on_message_complete;
+    /* When on_chunk_header is called, the current chunk length is stored
+     * in parser->content_length.
+     */
+    http_cb on_chunk_header;
+    http_cb on_chunk_complete;
+};
+
+enum http_parser_url_fields
+{
+    UF_SCHEMA   = 0,
+    UF_HOST     = 1,
+    UF_PORT     = 2,
+    UF_PATH     = 3,
+    UF_QUERY    = 4,
+    UF_FRAGMENT = 5,
+    UF_USERINFO = 6,
+    UF_MAX      = 7
+};
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url
+{
+    uint16_t field_set; /* Bitmask of (1 << UF_*) values */
+    uint16_t port;      /* Converted UF_PORT string */
+
+    struct
+    {
+        uint16_t off; /* Offset into buffer in which field starts */
+        uint16_t len; /* Length of run in buffer */
+    } field_data[UF_MAX];
+};
+
+/* Returns the library version. Bits 16-23 contain the major version number,
+ * bits 8-15 the minor version number and bits 0-7 the patch level.
+ * Usage example:
+ *
+ *   unsigned long version = http_parser_version();
+ *   unsigned major = (version >> 16) & 255;
+ *   unsigned minor = (version >> 8) & 255;
+ *   unsigned patch = version & 255;
+ *   printf("http_parser v%u.%u.%u\n", major, minor, patch);
+ */
+unsigned long http_parser_version(void);
+
+void http_parser_init(http_parser *parser, enum http_parser_type type);
+
+/* Initialize http_parser_settings members to 0
+ */
+void http_parser_settings_init(http_parser_settings *settings);
+
+/* Executes the parser. Returns number of parsed bytes. Sets
+ * `parser->http_errno` on error. */
+size_t http_parser_execute(http_parser *parser, const http_parser_settings *settings,
+                           const char *data, size_t len);
+
+/* If http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns 0, then this should be
+ * the last message on the connection.
+ * If you are the server, respond with the "Connection: close" header.
+ * If you are the client, close the connection.
+ */
+int http_should_keep_alive(const http_parser *parser);
+
+/* Returns a string version of the HTTP method. */
+const char *http_method_str(enum http_method m);
+
+/* Returns a string version of the HTTP status code. */
+const char *http_status_str(enum http_status s);
+
+/* Return a string name of the given error */
+const char *http_errno_name(enum http_errno err);
+
+/* Return a string description of the given error */
+const char *http_errno_description(enum http_errno err);
+
+/* Initialize all http_parser_url members to 0 */
+void http_parser_url_init(struct http_parser_url *u);
+
+/* Parse a URL; return nonzero on failure */
+int http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+                          struct http_parser_url *u);
+
+/* Pause or un-pause the parser; a nonzero value pauses */
+void http_parser_pause(http_parser *parser, int paused);
+
+/* Checks if this is the final chunk of the body. */
+int http_body_is_final(const http_parser *parser);
+
+/* Change the maximum header size provided at compile time. */
+void http_parser_set_max_header_size(uint32_t size);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/bos/utils/b_util_at.c b/bos/utils/b_util_at.c
index c98845518ff4dce7b92cd609691e24e4a352253e..9f5deb6adc385f131c34bc9645a97e6ba3a84db2 100644
--- a/bos/utils/b_util_at.c
+++ b/bos/utils/b_util_at.c
@@ -131,6 +131,11 @@ PT_THREAD(bAtTask)(struct pt *pt, void *arg)
     PT_END(pt);
 }
 
+static void _bAtFree(void *arg)
+{
+    bFree(arg);
+}
+
 /**
  * \}
  */
@@ -207,7 +212,7 @@ int bAtFeedData(bAtStruct_t *pat, uint8_t *pbuf, uint16_t len)
             return -1;
         }
         memcpy(p, pbuf, len);
-        pat->data_cb(p, len, bFree, pat->user_data);
+        pat->data_cb(p, len, _bAtFree, pat->user_data);
     }
     return 0;
 }
diff --git a/bos/utils/b_util_memp.c b/bos/utils/b_util_memp.c
index 226fd974f4ad6e3ea88228bb7285ec6d43a551be..570224367d4f73fe11a5d4c24f6aaca1c0751b7c 100644
--- a/bos/utils/b_util_memp.c
+++ b/bos/utils/b_util_memp.c
@@ -222,6 +222,18 @@ static uint32_t _bGetFreeSize()
     return ret;
 }
 
+static uint32_t _bGetUsableSize(void *addr)
+{
+    bMempUnitHead_t *phead = (bMempUnitHead_t *)(addr - sizeof(bMempUnitHead_t));
+    if (phead->status != MEMP_UNIT_USED ||
+        (uint32_t)addr < (((uint32_t)bMempBuf) + sizeof(bMempUnitHead_t)) ||
+        (uint32_t)addr > (((uint32_t)bMempBuf) + MEMP_SIZE - sizeof(bMempUnitHead_t)))
+    {
+        return 0;
+    }
+    return phead->size;
+}
+
 /**
  * \}
  */
@@ -250,6 +262,30 @@ static void _bFree(void *paddr)
     }
     _bMempFree((uint32_t)paddr);
 }
+
+static void *_bRealloc(void *paddr, uint32_t size)
+{
+    if (paddr == NULL)
+    {
+        return bMalloc(size);
+    }
+    if (size == 0)
+    {
+        bFree(paddr);
+        return NULL;
+    }
+    void *new_ptr = bMalloc(size);
+    if (new_ptr == NULL)
+    {
+        return NULL;
+    }
+    uint32_t old_size  = _bGetUsableSize(paddr);
+    uint32_t copy_size = (size < old_size) ? size : old_size;
+    memcpy(new_ptr, paddr, copy_size);
+    bFree(paddr);
+    return new_ptr;
+}
+
 #else
 void *bMalloc(uint32_t size)
 {
@@ -269,6 +305,30 @@ void bFree(void *paddr)
     }
     _bMempFree((uint32_t)paddr);
 }
+
+void *bRealloc(void *paddr, uint32_t size)
+{
+    if (paddr == NULL)
+    {
+        return bMalloc(size);
+    }
+    if (size == 0)
+    {
+        bFree(paddr);
+        return NULL;
+    }
+    void *new_ptr = bMalloc(size);
+    if (new_ptr == NULL)
+    {
+        return NULL;
+    }
+    uint32_t old_size  = _bGetUsableSize(paddr);
+    uint32_t copy_size = (size < old_size) ? size : old_size;
+    memcpy(new_ptr, paddr, copy_size);
+    bFree(paddr);
+    return new_ptr;
+}
+
 #endif
 
 uint32_t bGetFreeSize()
@@ -311,6 +371,13 @@ void bFreePlus(void *ptr, const char *func, int line)
     b_log("free in %s, %d, address %p\r\n", func, line, ptr);
 }
 
+void *bReallocPlus(void *ptr, uint32_t size, const char *func, int line)
+{
+    void *new_ptr = _bRealloc(ptr, size);
+    b_log("realloc in %s, %d, address %p size %d\r\n", func, line, ptr, size);
+    return new_ptr;
+}
+
 #endif
 
 /**
diff --git a/bos/utils/inc/b_util_memp.h b/bos/utils/inc/b_util_memp.h
index bf9c4962c1373af32d370fe3816f771e619aa4ea..0eed4adc4db843828de160fb112c681001844fe5 100644
--- a/bos/utils/inc/b_util_memp.h
+++ b/bos/utils/inc/b_util_memp.h
@@ -60,15 +60,16 @@ extern "C" {
 
 void* bMallocPlus(uint32_t size, const char* func, int line);
 void  bFreePlus(void* ptr, const char* func, int line);
+void* bReallocPlus(void* ptr, uint32_t size, const char* func, int line);
 
 #define bMalloc(size) bMallocPlus((size), __func__, __LINE__)
 #define bFree(addr) bFreePlus((addr), __func__, __LINE__)
-
+#define bRealloc(addr, size) bReallocPlus((addr), (size), __func__, __LINE__)
 #else
 
 void *bMalloc(uint32_t size);
 void  bFree(void *paddr);
-
+void *bRealloc(void *paddr, uint32_t size);
 #endif
 
 uint32_t bGetFreeSize(void);
diff --git a/test/malloc/_config/.bconfig b/test/malloc/_config/.bconfig
index 1db3631b9156b7a8afa37dee8101cfa49a352983..089647f3510cc5292927efc8b534d638e3d8e4ea 100644
--- a/test/malloc/_config/.bconfig
+++ b/test/malloc/_config/.bconfig
@@ -16,6 +16,7 @@ VENDOR_UBUNTU=y
 # VENDOR_HC is not set
 # VENDOR_WCH is not set
 # VENDOR_HOLTEK is not set
+# VENDOR_ARTERY is not set
 
 #
 # Algorithm Configuration
@@ -27,6 +28,21 @@ _BOS_ALGO_ENABLE=y
 # _ALGO_UNICODE_ENABLE is not set
 _ALGO_MD5_ENABLE=y
 _ALGO_CRC_ENABLE=y
+# _ALGO_CRC8_ENABLE is not set
+# _ALGO_CRC8_ITU_ENABLE is not set
+# _ALGO_CRC8_ROHC_ENABLE is not set
+# _ALGO_CRC8_MAXIM_ENABLE is not set
+# _ALGO_CRC16_IBM_ENABLE is not set
+# _ALGO_CRC16_MAXIM_ENABLE is not set
+# _ALGO_CRC16_USB_ENABLE is not set
+# _ALGO_CRC16_MODBUS_ENABLE is not set
+# _ALGO_CRC16_CCITT_ENABLE is not set
+# _ALGO_CRC16_CCITT_FALSE_ENABLE is not set
+# _ALGO_CRC16_X25_ENABLE is not set
+# _ALGO_CRC16_XMODEM_ENABLE is not set
+# _ALGO_CRC16_DNP_ENABLE is not set
+# _ALGO_CRC32_ENABLE is not set
+# _ALGO_CRC32_MPEG2_ENABLE is not set
 # end of Algorithm Configuration
 
 #
@@ -36,6 +52,12 @@ PCF8574_DEFAULT_OUTPUT=0
 MATRIX_KEYS_ROWS=4
 MATRIX_KEYS_COLUMNS=4
 ESP12F_UART_RX_BUF_LEN=1024
+ESP12F_CMD_BUF_LEN=128
+ESP12F_CMD_TIMEOUT=5000
+RS485_RX_BUF_LEN=128
+RS485_RX_IDLE_MS=50
+_485_USE_CALLBACK=y
+# _485_USE_SELECT is not set
 # end of Drivers Configuration
 
 #
@@ -50,12 +72,14 @@ _BOS_MODULES_ENABLE=y
 # _XMODEM128_ENABLE is not set
 # _YMODEM_ENABLE is not set
 # _MENU_ENABLE is not set
-# _TIMER_ENABLE is not set
 # _PWM_ENABLE is not set
 # _PARAM_ENABLE is not set
 # _STATE_ENABLE is not set
 # _IAP_ENABLE is not set
 # _WIFI_ENABLE is not set
+# _SELECT_ENABLE is not set
+# _GUI_ENABLE is not set
+# _NETIF_ENABLE is not set
 # end of Modules Configuration
 
 #
@@ -68,19 +92,34 @@ _BOS_MODULES_ENABLE=y
 # _FS_ENABLE is not set
 # _CJSON_ENABLE is not set
 # _COREMARK_ENABLE is not set
-_UNITY_ENABLE=y
+# _UNITY_ENABLE is not set
+# _ARM_2D_ENABLE is not set
+# _QRCODE_ENABLE is not set
 # end of Thirdparty Configuration
 
 #
 # Utils Configuration
 #
 _DEBUG_ENABLE=y
-LOG_UART=0
-LOG_LEVEL_INFO=y
+LOG_LEVEL_ALL=y
+# LOG_LEVEL_INFO is not set
 # LOG_LEVEL_WARNING is not set
 # LOG_LEVEL_ERROR is not set
+# LOG_LEVEL_CUSTOMIZE is not set
 LOG_BUF_SIZE=256
+_LOG_VIA_UART=y
+# _LOG_VIA_USER_SPECIFIED is not set
+LOG_UART=0
 _MEMP_ENABLE=y
 MEMP_MAX_SIZE=10240
 # _MEMP_MONITOR_ENABLE is not set
+# _AT_ENABLE is not set
 # end of Utils Configuration
+
+#
+# Services Configuration
+#
+_BOS_SERVICES_ENABLE=y
+# _PROTOCOL_SERVICE_ENABLE is not set
+# _TCPIP_SERVICE_ENABLE is not set
+# end of Services Configuration
diff --git a/test/malloc/_config/.bconfig.old b/test/malloc/_config/.bconfig.old
new file mode 100644
index 0000000000000000000000000000000000000000..8d6aff10363da45dafdffb02e1a86de1fca89dea
--- /dev/null
+++ b/test/malloc/_config/.bconfig.old
@@ -0,0 +1,125 @@
+# Generated by BabyOS ConfigurationHW_VERSION=211212
+FW_VERSION=80106
+FW_NAME="BabyOS"
+
+#
+# Hal Configuration
+#
+TICK_FRQ_HZ=1000
+# _HALIF_VARIABLE_ENABLE is not set
+# end of Hal Configuration
+
+VENDOR_UBUNTU=y
+# VENDOR_ST is not set
+# VENDOR_NATION is not set
+# VENDOR_MM is not set
+# VENDOR_HC is not set
+# VENDOR_WCH is not set
+# VENDOR_HOLTEK is not set
+# VENDOR_ARTERY is not set
+
+#
+# Algorithm Configuration
+#
+_BOS_ALGO_ENABLE=y
+# _ALGO_BASE64_ENABLE is not set
+# _ALGO_SHA1_ENABLE is not set
+# _ALGO_SORT_ENABLE is not set
+# _ALGO_UNICODE_ENABLE is not set
+_ALGO_MD5_ENABLE=y
+_ALGO_CRC_ENABLE=y
+# _ALGO_CRC8_ENABLE is not set
+# _ALGO_CRC8_ITU_ENABLE is not set
+# _ALGO_CRC8_ROHC_ENABLE is not set
+# _ALGO_CRC8_MAXIM_ENABLE is not set
+# _ALGO_CRC16_IBM_ENABLE is not set
+# _ALGO_CRC16_MAXIM_ENABLE is not set
+# _ALGO_CRC16_USB_ENABLE is not set
+# _ALGO_CRC16_MODBUS_ENABLE is not set
+# _ALGO_CRC16_CCITT_ENABLE is not set
+# _ALGO_CRC16_CCITT_FALSE_ENABLE is not set
+# _ALGO_CRC16_X25_ENABLE is not set
+# _ALGO_CRC16_XMODEM_ENABLE is not set
+# _ALGO_CRC16_DNP_ENABLE is not set
+# _ALGO_CRC32_ENABLE is not set
+# _ALGO_CRC32_MPEG2_ENABLE is not set
+# end of Algorithm Configuration
+
+#
+# Drivers Configuration
+#
+PCF8574_DEFAULT_OUTPUT=0
+MATRIX_KEYS_ROWS=4
+MATRIX_KEYS_COLUMNS=4
+ESP12F_UART_RX_BUF_LEN=1024
+ESP12F_CMD_BUF_LEN=128
+ESP12F_CMD_TIMEOUT=5000
+RS485_RX_BUF_LEN=128
+RS485_RX_IDLE_MS=50
+_485_USE_CALLBACK=y
+# _485_USE_SELECT is not set
+# end of Drivers Configuration
+
+#
+# Modules Configuration
+#
+_BOS_MODULES_ENABLE=y
+# _ADCHUB_ENABLE is not set
+# _ERROR_MANAGE_ENABLE is not set
+# _MODBUS_ENABLE is not set
+# _PROTO_ENABLE is not set
+# _KV_ENABLE is not set
+# _XMODEM128_ENABLE is not set
+# _YMODEM_ENABLE is not set
+# _MENU_ENABLE is not set
+# _PWM_ENABLE is not set
+# _PARAM_ENABLE is not set
+# _STATE_ENABLE is not set
+# _IAP_ENABLE is not set
+# _WIFI_ENABLE is not set
+# _SELECT_ENABLE is not set
+# _GUI_ENABLE is not set
+# _NETIF_ENABLE is not set
+# end of Modules Configuration
+
+#
+# Thirdparty Configuration
+#
+# _CMBACKTRACE_ENABLE is not set
+# _NR_MICRO_SHELL_ENABLE is not set
+# _FLEXIBLEBUTTON_ENABLE is not set
+# _UGUI_ENABLE is not set
+# _FS_ENABLE is not set
+# _CJSON_ENABLE is not set
+# _COREMARK_ENABLE is not set
+# _UNITY_ENABLE is not set
+# _ARM_2D_ENABLE is not set
+# _QRCODE_ENABLE is not set
+# end of Thirdparty Configuration
+
+#
+# Utils Configuration
+#
+_DEBUG_ENABLE=y
+# LOG_LEVEL_ALL is not set
+LOG_LEVEL_INFO=y
+# LOG_LEVEL_WARNING is not set
+# LOG_LEVEL_ERROR is not set
+# LOG_LEVEL_CUSTOMIZE is not set
+LOG_BUF_SIZE=256
+_LOG_VIA_UART=y
+# _LOG_VIA_USER_SPECIFIED is not set
+LOG_UART=0
+_MEMP_ENABLE=y
+MEMP_MAX_SIZE=10240
+# _MEMP_MONITOR_ENABLE is not set
+# _AT_ENABLE is not set
+# end of Utils Configuration
+
+#
+# Services Configuration
+#
+_BOS_SERVICES_ENABLE=y
+# _PROTOCOL_SERVICE_ENABLE is not set
+# _TCPIP_SERVICE_ENABLE is not set
+# end of Services Configuration
diff --git a/test/malloc/_config/b_config.h b/test/malloc/_config/b_config.h
index 588409831a62f46d19258c43878e0fbdaab9781d..9a5d999fc7f07d0f08bb265893718fafb78ec5dc 100644
--- a/test/malloc/_config/b_config.h
+++ b/test/malloc/_config/b_config.h
@@ -14,14 +14,20 @@
 #define MATRIX_KEYS_ROWS 4
 #define MATRIX_KEYS_COLUMNS 4
 #define ESP12F_UART_RX_BUF_LEN 1024
+#define ESP12F_CMD_BUF_LEN 128
+#define ESP12F_CMD_TIMEOUT 5000
+#define RS485_RX_BUF_LEN 128
+#define RS485_RX_IDLE_MS 50
+#define _485_USE_CALLBACK 1
 #define _BOS_MODULES_ENABLE 1
-#define _UNITY_ENABLE 1
 #define _DEBUG_ENABLE 1
-#define LOG_UART 0
-#define LOG_LEVEL_INFO 1
+#define LOG_LEVEL_ALL 1
 #define LOG_BUF_SIZE 256
+#define _LOG_VIA_UART 1
+#define LOG_UART 0
 #define _MEMP_ENABLE 1
 #define MEMP_MAX_SIZE 10240
+#define _BOS_SERVICES_ENABLE 1
 
 
 #include "b_type.h" 
diff --git a/test/malloc/malloc_main.c b/test/malloc/malloc_main.c
index a84cfb2d988f05b5f4adcbe4ed169c944391934f..5dddadf21fb9c223affee7d4e955493ff542e71d 100644
--- a/test/malloc/malloc_main.c
+++ b/test/malloc/malloc_main.c
@@ -10,7 +10,7 @@
 #include "../port.h"
 #include "b_os.h"
 
-#if (defined(_UNITY_ENABLE) && (_UNITY_ENABLE == 0))
+#if (!(defined(_UNITY_ENABLE) && (_UNITY_ENABLE == 1)))
 void test()
 {
     uint32_t m_size = 0;
@@ -20,6 +20,16 @@ void test()
     pbuf   = bMalloc(m_size);
     b_log("malloc %d %p\r\n", m_size, pbuf);
     bFree(pbuf);
+
+    b_log("free size:::%d\r\n", bGetFreeSize());
+
+    pbuf = bMalloc(1024);
+    memcpy(pbuf, "BABYOS", strlen("BABYOS"));
+    b_log("malloc 1024 %p %s\r\n", pbuf, pbuf);
+    pbuf = bRealloc(pbuf, 2048);
+    b_log("realloc 2048 %p %s\r\n", pbuf, pbuf);
+    pbuf = bRealloc(pbuf, 0);
+    b_log("free size:::%d\r\n", bGetFreeSize());
 }
 
 int main()
diff --git a/test/netif/Makefile b/test/netif/Makefile
index 916dd3842f3fc745f3f0e94229502f102395b53b..e8baa30e214652a0feca0d18f820114e132363eb 100644
--- a/test/netif/Makefile
+++ b/test/netif/Makefile
@@ -41,7 +41,8 @@ $(SOURCE_ROOT)/bos/thirdparty/nr_micro_shell/src/*.c \
 $(SOURCE_ROOT)/bos/drivers/sfud/*.c \
 $(SOURCE_ROOT)/bos/thirdparty/ugui/*.c \
 $(SOURCE_ROOT)/bos/thirdparty/coremark/*.c \
-$(SOURCE_ROOT)/bos/thirdparty/cjson/*.c  
+$(SOURCE_ROOT)/bos/thirdparty/cjson/*.c  \
+$(SOURCE_ROOT)/bos/thirdparty/http-parser/*.c
 
 BOS_DRV_SOURCE_DIR = \
 $(SOURCE_ROOT)/bos/drivers/b_drv_testflash.c
diff --git a/test/netif/_config/.bconfig b/test/netif/_config/.bconfig
index b0bd20c84b3264084e94dbfdba27ca435d7958ab..e3e633c6b909fe30ecc0cba8655b46921867bbaf 100644
--- a/test/netif/_config/.bconfig
+++ b/test/netif/_config/.bconfig
@@ -31,6 +31,8 @@ PCF8574_DEFAULT_OUTPUT=0
 MATRIX_KEYS_ROWS=4
 MATRIX_KEYS_COLUMNS=4
 ESP12F_UART_RX_BUF_LEN=1024
+ESP12F_CMD_BUF_LEN=128
+ESP12F_CMD_TIMEOUT=5000
 RS485_RX_BUF_LEN=128
 RS485_RX_IDLE_MS=50
 _485_USE_CALLBACK=y
@@ -59,7 +61,7 @@ _BOS_MODULES_ENABLE=y
 _NETIF_ENABLE=y
 # _NETIF_USE_LWIP is not set
 REMOTE_ADDR_LEN_MAX=128
-CONNECT_RECVBUF_MAX=4096
+CONNECT_RECVBUF_MAX=10240
 SERVER_MAX_CONNECTIONS=2
 # end of Modules Configuration
 
@@ -71,11 +73,13 @@ SERVER_MAX_CONNECTIONS=2
 # _FLEXIBLEBUTTON_ENABLE is not set
 # _UGUI_ENABLE is not set
 # _FS_ENABLE is not set
-# _CJSON_ENABLE is not set
+_CJSON_ENABLE=y
+CJSON_MEM_USE_BMALLOC=y
 # _COREMARK_ENABLE is not set
 # _UNITY_ENABLE is not set
 # _ARM_2D_ENABLE is not set
 # _QRCODE_ENABLE is not set
+_HTTP_PARSER_ENABLE=y
 # end of Thirdparty Configuration
 
 #
@@ -87,11 +91,14 @@ LOG_LEVEL_ALL=y
 # LOG_LEVEL_WARNING is not set
 # LOG_LEVEL_ERROR is not set
 # LOG_LEVEL_CUSTOMIZE is not set
-LOG_BUF_SIZE=256
+LOG_BUF_SIZE=100000
 _LOG_VIA_UART=y
 # _LOG_VIA_USER_SPECIFIED is not set
 LOG_UART=0
-# _MEMP_ENABLE is not set
+_MEMP_ENABLE=y
+MEMP_MAX_SIZE=102400
+# _MEMP_MONITOR_ENABLE is not set
+# _AT_ENABLE is not set
 # end of Utils Configuration
 
 #
@@ -103,4 +110,6 @@ _TCPIP_SERVICE_ENABLE=y
 _NTP_SERVER_1="ntp1.aliyun.com"
 _NTP_SERVER_2="ntp2.aliyun.com"
 _NTP_SERVER_3="ntp3.aliyun.com"
+_HTTP_HOST_LEN_MAX=64
+_HTTP_PATH_LEN_MAX=128
 # end of Services Configuration
diff --git a/test/netif/_config/.bconfig.old b/test/netif/_config/.bconfig.old
index 85798cf6c0ebd90f1bd056b7fe01c2ae9036581e..affe0aa81dd4457d6413145118f3399e28d97dc8 100644
--- a/test/netif/_config/.bconfig.old
+++ b/test/netif/_config/.bconfig.old
@@ -31,6 +31,8 @@ PCF8574_DEFAULT_OUTPUT=0
 MATRIX_KEYS_ROWS=4
 MATRIX_KEYS_COLUMNS=4
 ESP12F_UART_RX_BUF_LEN=1024
+ESP12F_CMD_BUF_LEN=128
+ESP12F_CMD_TIMEOUT=5000
 RS485_RX_BUF_LEN=128
 RS485_RX_IDLE_MS=50
 _485_USE_CALLBACK=y
@@ -56,7 +58,11 @@ _BOS_MODULES_ENABLE=y
 # _WIFI_ENABLE is not set
 # _SELECT_ENABLE is not set
 # _GUI_ENABLE is not set
-# _NETIF_ENABLE is not set
+_NETIF_ENABLE=y
+# _NETIF_USE_LWIP is not set
+REMOTE_ADDR_LEN_MAX=128
+CONNECT_RECVBUF_MAX=10240
+SERVER_MAX_CONNECTIONS=2
 # end of Modules Configuration
 
 #
@@ -72,6 +78,7 @@ _BOS_MODULES_ENABLE=y
 # _UNITY_ENABLE is not set
 # _ARM_2D_ENABLE is not set
 # _QRCODE_ENABLE is not set
+_HTTP_PARSER_ENABLE=y
 # end of Thirdparty Configuration
 
 #
@@ -83,11 +90,14 @@ LOG_LEVEL_ALL=y
 # LOG_LEVEL_WARNING is not set
 # LOG_LEVEL_ERROR is not set
 # LOG_LEVEL_CUSTOMIZE is not set
-LOG_BUF_SIZE=256
+LOG_BUF_SIZE=100000
 _LOG_VIA_UART=y
 # _LOG_VIA_USER_SPECIFIED is not set
 LOG_UART=0
-# _MEMP_ENABLE is not set
+_MEMP_ENABLE=y
+MEMP_MAX_SIZE=102400
+# _MEMP_MONITOR_ENABLE is not set
+# _AT_ENABLE is not set
 # end of Utils Configuration
 
 #
@@ -95,5 +105,10 @@ LOG_UART=0
 #
 _BOS_SERVICES_ENABLE=y
 # _PROTOCOL_SERVICE_ENABLE is not set
-# _TCPIP_SERVICE_ENABLE is not set
+_TCPIP_SERVICE_ENABLE=y
+_NTP_SERVER_1="ntp1.aliyun.com"
+_NTP_SERVER_2="ntp2.aliyun.com"
+_NTP_SERVER_3="ntp3.aliyun.com"
+_HTTP_HOST_LEN_MAX=64
+_HTTP_PATH_LEN_MAX=128
 # end of Services Configuration
diff --git a/test/netif/_config/b_config.h b/test/netif/_config/b_config.h
index c1ee5a27c9037685b7e6b6472b6ccc05ccf5dae3..cf5e6f3bd4e5b84be109558e50d63e4767207467 100644
--- a/test/netif/_config/b_config.h
+++ b/test/netif/_config/b_config.h
@@ -11,24 +11,33 @@
 #define MATRIX_KEYS_ROWS 4
 #define MATRIX_KEYS_COLUMNS 4
 #define ESP12F_UART_RX_BUF_LEN 1024
+#define ESP12F_CMD_BUF_LEN 128
+#define ESP12F_CMD_TIMEOUT 5000
 #define RS485_RX_BUF_LEN 128
 #define RS485_RX_IDLE_MS 50
 #define _485_USE_CALLBACK 1
 #define _BOS_MODULES_ENABLE 1
 #define _NETIF_ENABLE 1
 #define REMOTE_ADDR_LEN_MAX 128
-#define CONNECT_RECVBUF_MAX 4096
+#define CONNECT_RECVBUF_MAX 10240
 #define SERVER_MAX_CONNECTIONS 2
+#define _CJSON_ENABLE 1
+#define CJSON_MEM_USE_BMALLOC 1
+#define _HTTP_PARSER_ENABLE 1
 #define _DEBUG_ENABLE 1
 #define LOG_LEVEL_ALL 1
-#define LOG_BUF_SIZE 256
+#define LOG_BUF_SIZE 100000
 #define _LOG_VIA_UART 1
 #define LOG_UART 0
+#define _MEMP_ENABLE 1
+#define MEMP_MAX_SIZE 102400
 #define _BOS_SERVICES_ENABLE 1
 #define _TCPIP_SERVICE_ENABLE 1
 #define _NTP_SERVER_1 "ntp1.aliyun.com"
 #define _NTP_SERVER_2 "ntp2.aliyun.com"
 #define _NTP_SERVER_3 "ntp3.aliyun.com"
+#define _HTTP_HOST_LEN_MAX 64
+#define _HTTP_PATH_LEN_MAX 128
 
 
 #include "b_type.h" 
diff --git a/test/netif/utc_main.c b/test/netif/utc_main.c
index d4f0475914b0614f9d12959c20190c6162d45b4f..3a2d4381b17bab7ad1b6757f98759a7b506ffc57 100644
--- a/test/netif/utc_main.c
+++ b/test/netif/utc_main.c
@@ -12,6 +12,7 @@
 #include "../port.h"
 #include "b_os.h"
 
+int  httpfd = -1;
 void ntp_test()
 {
     bUTC_DateTime_t tm;
@@ -20,6 +21,67 @@ void ntp_test()
     bUTC2Struct(&tm, now_utc, 8);
     b_log("%d-%02d-%02d %02d:%02d:%02d %02d\r\n", tm.year, tm.month, tm.day, tm.hour, tm.minute,
           tm.second, tm.week);
+    b_log_w(":::::%d\r\n", bGetFreeSize());
+    bHttpRequest(httpfd, B_HTTP_GET,
+                 "http://restapi.amap.com/v3/weather/"
+                 "weatherInfo?city=440300&key=",
+                 NULL, NULL);
+}
+
+void HttpCb(bHttpEvent_t event, void *param, void *arg)
+{
+    if (event == B_HTTP_EVENT_RECV_DATA)
+    {
+        bHttpRecvData_t *dat = (bHttpRecvData_t *)param;
+        if (dat->pdat != NULL && dat->len > 0)
+        {
+            char *pstr = strstr(dat->pdat, "\r\n\r\n");
+            if (pstr != NULL)
+            {
+                cJSON *root = cJSON_Parse(pstr);
+                if (root)
+                {
+                    cJSON *lives = cJSON_GetObjectItem(root, "lives");
+                    if (lives != NULL && lives->type == cJSON_Array)
+                    {
+                        lives         = cJSON_GetArrayItem(lives, 0);
+                        cJSON *wether = cJSON_GetObjectItem(lives, "weather");
+                        cJSON *temp   = cJSON_GetObjectItem(lives, "temperature");
+                        cJSON *hum    = cJSON_GetObjectItem(lives, "humidity");
+                        char  *we     = "Cloudy";
+                        if (strstr(wether->valuestring, "晴") != NULL)
+                        {
+                            we = "Sunny";
+                        }
+                        if (strstr(wether->valuestring, "雨") != NULL)
+                        {
+                            we = "Rainy";
+                        }
+                        if (strstr(wether->valuestring, "雪") != NULL)
+                        {
+                            we = "Snowy";
+                        }
+                        b_log("w:%s\r\n", we);
+                        b_log("t:%s\r\n", temp->valuestring);
+                        b_log("h:%s\r\n", hum->valuestring);
+                    }
+                    cJSON_Delete(root);
+                }
+            }
+        }
+        if (dat)
+        {
+            if (dat->release)
+            {
+                dat->release(dat->pdat);
+            }
+        }
+    }
+}
+
+void bMallocFailedHook()
+{
+    b_log_e("=========================\r\n");
 }
 
 int main()
@@ -28,11 +90,12 @@ int main()
     bInit();
 
     bTcpipSrvInit();
-    bSntpStart(600);
+    bSntpStart(60 * 60);
+    httpfd = bHttpInit(HttpCb, NULL);
     while (1)
     {
         bExec();
-        BOS_PERIODIC_TASK(ntp_test, 10000);
+        BOS_PERIODIC_TASK(ntp_test, 1000 * 60);
     }
     return 0;
 }