#include "lip_http_server_cfg_defs.h"

#if LIP_HTTP_SERVER_ENABLE
    #include "lip_http_server.h"
    #include "lcspec.h"
    #include "lip/transport/tcp/lip_tcp.h"

    #include "lip/lip_log.h"
    #include <string.h>

    #define lip_http_print_dbg(...)  lip_printf(LIP_LOG_MODULE_HTTP, LIP_LOG_LVL_DEBUG_MEDIUM, 0, "lip http: " __VA_ARGS__)
    #define lip_http_print_pkterr(...)  lip_printf(LIP_LOG_MODULE_HTTP, LIP_LOG_LVL_ERROR_LOW, 0, "lip http: " __VA_ARGS__)
    #define lip_http_print_apperr(...)  lip_printf(LIP_LOG_MODULE_HTTP, LIP_LOG_LVL_ERROR_MEDIUM, 0, "lip http: " __VA_ARGS__)


    LIP_HTTP_SERVER_RXBUF_MEM(static uint8_t f_rx_bufs[LIP_HTTP_SERVER_CONNECTIONS_CNT][LIP_HTTP_SERVER_RXBUF_SIZE]);
    LIP_HTTP_SERVER_TXBUF_MEM(static uint8_t f_tx_bufs[LIP_HTTP_SERVER_CONNECTIONS_CNT][LIP_HTTP_SERVER_TXBUF_SIZE]);
    LIP_HTTP_SERVER_RXTMPBUF_MEM(static uint8_t f_tmp_bufs[LIP_HTTP_SERVER_CONNECTIONS_CNT][LIP_HTTP_SERVER_MAX_RXLINE_SIZE]);
    LIP_HTTP_SERVER_TXTMPBUF_MEM(static uint8_t f_tx_tmp_buf[LIP_HTTP_SERVER_MAX_TXLINE_SIZE]);

    #define LIP_HTTP_VER_PREFIX         "HTTP/"
    #define LIP_HTTP_VER_PREFIX_SIZE    5

    #define LIP_HTTP_NUM_MAX 0x7FFFFFFF

    typedef enum  {
        LIP_HTTP_RXCONSTATE_REQ_WAIT,
        LIP_HTTP_RXCONSTATE_REQ_READ_HDR,
        LIP_HTTP_RXCONSTATE_REQ_READ_BODY,
        LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_LEN,
        LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_DATA_END,
        LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_TRAILER,
        LIP_HTTP_RXCONSTATE_REQ_FINISH,
    } e_LIP_HTTP_RXCONSTATE;


    typedef enum {
        LIP_HTTP_TXCONSTATE_WAIT,
        LIP_HTTP_TXCONSTATE_SND_CONTINUE,
        LIP_HTTP_TXCONSTATE_SND_STATUS,
        LIP_HTTP_TXCONSTATE_SND_HDR,
        LIP_HTTP_TXCONSTATE_SND_BODY,
        LIP_HTTP_TXCONSTATE_SND_FINISH
    } t_LIP_HTTP_TXCONSTATE;




    typedef enum {
        /* признак, что запрос нужно отбросить и его уже не нужно передовать
         * пользователю (если при обработке возникла ошибка, но не требующая
         * закрытия соединения) */
        LIP_HTTP_CONFLAG_REQ_DROP     = 1 << 0,
        /* признак, что соединение будет закрыта после завершения обработки текущего
         * запроса-ответа */
        LIP_HTTP_CONFLAG_CON_CLOSE    = 1 << 1,
        /* признак, что соединение должно быть разорвано, не дожидаясь приема
         * всего запроса */
        LIP_HTTP_CONFLAG_CON_ABORT    = 1 << 2,
        /* Признак, что клиент ожидает посылки 100 continue после образобтки заголовка */
        LIP_HTTP_CONFLAG_EXP_CONTINUE = 1 << 3,
        /* признак, что подключение идет с клиентом версии HTTP1.0 и часть
         * функций из 1.1 недоступны */
        LIP_HTTP_CONFLAG_V1_0         = 1 << 4,
    } t_LIP_HTTP_CONFLAGS;

    typedef struct {
        void *ctx;
        const char *name;
        const char *value;
        int name_len;
        int value_len;
        int param;
        t_lip_http_server_get_resp_hdrfield_cb value_cb;
    } t_lip_http_resp_field_params;

    struct st_lip_http_resp {
        t_lip_http_status_code status;
        t_lip_http_resp_field_params hdrs[LIP_HTTP_SERVER_MAX_RESP_HDR_FIELDS];
        int hdr_cnt;
        int data_len;
        const uint8_t *data;
        t_lip_http_server_get_resp_data_cb data_cb;
        void *data_ctx;
        t_lip_http_server_resp_finish_cb finish_cb;
        void *finish_ctx;
    };

    typedef struct {
        t_lip_http_resp params;
        t_LIP_HTTP_TXCONSTATE state;
        int tx_pos;
        uint8_t *tx_buf;
    } t_lip_http_tx_ctx;

    typedef struct {
        uint8_t    active;
        uint16_t   port;
        t_lsock_id socket;
        uint32_t   flags;
        e_LIP_HTTP_RXCONSTATE rxstate;
        t_lip_http_method method;

        uint8_t *proc_buf;
        int proc_buf_pos;
        int proc_buf_end;


        int req_data_pos;
        t_lip_http_tx_ctx resp;

        t_lip_http_server_req_cb accept_cb;
        t_lip_http_req_proc_params proc_req_params;
        t_lip_http_req_body_params req_body_params;
        /** @todo для поддержкания нескольких кодировок при необходимости сделать
         * массив с указателем на последнюю и применять декодирование в обратном порядке */
        t_lip_http_transf_encoding req_body_encoding;
    } t_lip_httpsrv_con_ctx;

    #define LIP_HTTPCON_TX_ACTIVE(httpcon) (((httpcon)->resp.state != LIP_HTTP_TXCONSTATE_WAIT) && \
                                           ((httpcon)->resp.state != LIP_HTTP_TXCONSTATE_SND_FINISH))
    #define LIP_HTTPCON_RX_ACTIVE(httpcon) (((httpcon)->rxstate != LIP_HTTP_RXCONSTATE_REQ_WAIT) && \
                                           ((httpcon)->rxstate != LIP_HTTP_RXCONSTATE_REQ_FINISH))


    typedef struct {
        t_lip_http_method method;
        const char *name;
        int name_len;
    } t_lip_http_method_descr;


    static const t_lip_http_method_descr f_methods[] = {
        {LIP_HTTP_METHOD_GET, "GET", 3},
        {LIP_HTTP_METHOD_HEAD, "HEAD", 4},
        {LIP_HTTP_METHOD_PUT, "PUT", 3},
        {LIP_HTTP_METHOD_POST, "POST", 4},
        {LIP_HTTP_METHOD_DELETE, "DELETE", 6},
    };

/* макрос для вызова указанной callback-функции для обработки запроса, если
 * он установлена и если запрос не должен быть отброшен.
 * по вызову проверяется возвращаемый код и устанавливается ошибка обработки запроса
 * при коде возврата, отличном от LIP_HTTP_STATUS_OK */
#define LIP_HTTPCON_REQ_CLIENT_CB(httpcon, _cb, ...) do { \
        if (!((httpcon)->flags & LIP_HTTP_CONFLAG_REQ_DROP) && ((httpcon)->proc_req_params._cb != NULL)) { \
            t_lip_http_status_code cb_err = \
                    (httpcon)->proc_req_params._cb( \
                        (httpcon)->socket, \
                        (httpcon)->proc_req_params.context, \
                        __VA_ARGS__ \
                        ); \
            if (cb_err != LIP_HTTP_STATUS_OK) { \
                f_set_req_error(httpcon, cb_err, 0); \
            } \
        } \
    } while(0)

    t_lip_httpsrv_con_ctx f_con_list[LIP_HTTP_SERVER_CONNECTIONS_CNT];

    static void f_tx_resp_start(t_lip_httpsrv_con_ctx *httpcon);
    static void f_resp_init(t_lip_http_resp *params);

    /* сравнение двух элементов по размеру и содержимому */
    static LINLINE int f_equ_buf_item(const char *buf1, int size1, const char *buf2, int size2) {
        return (size1 == size2) && !memcmp(buf1, buf2, size1);
    }


    static int f_find_eol_pos(const uint8_t *buf, int size) {
        int pos_idx = -1;
        const uint8_t *endpos = buf + size - 1;
        const uint8_t *starpos = buf;
        for (  ; (pos_idx < 0) && (buf  < endpos); buf++) {
            if ((*buf == '\r') || (*(buf+1) == '\n')) {
                pos_idx = buf - starpos;
            }
        }
        return pos_idx;
    }

    static int f_find_sym(const char *buf, int size, char sym) {
        int pos_idx = -1;
        const char *endpos = buf + size;
        const char *starpos = buf;
        for (  ; (pos_idx < 0) && (buf  < endpos); buf++) {
            if (*buf == sym) {
                pos_idx = buf - starpos;
            }
        }
        return pos_idx;
    }

    static LINLINE int f_find_sp(const char *buf, int size) {
        return f_find_sym(buf, size, ' ');
    }

    static LINLINE int f_is_ws(const char buf) {
        return (buf == ' ') || (buf == '\t');
    }

    /* функция подсчитывает кол-во пробельных символов с начала буфера */
    static int f_ws_count(const char *buf, const char *endbuf) {
        int ret = 0;         
        for ( ; (buf < endbuf) && f_is_ws(*buf) ; buf++, ret++) {
        }
        return ret;
    }

    /* функция подсчитывает кол-во пробельных символов с конца буфера
       (с символа на 1 раньше, чем указывает endbuf) */
    static int f_ws_end_count(const char *buf, const char *endbuf) {
        int ret = 0;
        const char *pos;
        for (pos = endbuf - 1; (pos >= buf) && f_is_ws(*pos) ; pos--, ret++) {
        }
        return ret;
    }


    /* Функция преобразует беззнаковое число из строки с явным ограничением размере буфера.
     * При превышении LIP_HTTP_NUM_MAX возвращается -1.
     * Также функция возвращает указатель сразу за последним символом числа */
    static int f_get_num(const char *buf, const char *endbuf, const char **endptr) {
        int ret = 0;
        static const int num_max = (LIP_HTTP_NUM_MAX - 10)/10;        
        for (  ; (buf < endbuf) && (*buf >= '0') && (*buf <= '9'); buf++) {
            if (ret < num_max) {
                ret = 10*ret + (*buf - '0');
            } else {
                ret = -1;
            }
        }

        if (endptr)
            *endptr = buf;

        return ret;
    }

    /* Функция читает число в hex-коде до появления символа, отличного от действительного.
     * При превышении LIP_HTTP_NUM_MAX возвращается -1.
     * Также функция возвращает указатель сразу за последним символом числа */
    static int f_get_hex(const char *buf, const char *endbuf, const char **endptr) {
        int ret = 0;
        int ret_dgt = 0;
        int out = 0;

        for (  ; (buf < endbuf) && !out; buf++) {
            int dgt = 0;
            if ((*buf >= '0') && (*buf <= '9')) {
                dgt = *buf - '0';
            } else if ((*buf >= 'A') && (*buf <= 'F')) {
                dgt = *buf - 'A' + 10;
            } else if ((*buf >= 'a') && (*buf <= 'f')) {
                dgt = *buf - 'a' + 10;
            }

            if ((ret != 0) || (dgt != 0)) {
                ret_dgt++;
                if ((ret_dgt < 8) || ((ret_dgt == 8) && (dgt < 8))) {
                    ret = (ret << 4) | dgt;
                } else {
                    ret = -1;
                }
            }
        }

        if (endptr)
            *endptr = buf;

        return ret;
    }

    /* Функция возвращает указатели на начало и конец строки после отбрасывания
     * пробельных символов вначале и в конце */
    static char *f_get_trimmed_item(char *buf, char *endbuf, char **penditem) {
        char *item;
        item  = buf + f_ws_count(buf, endbuf);
        *penditem = endbuf - f_ws_end_count(item, endbuf);
        return item;
    }

    /* преобразование строки, которая безразлична к регистру, для последующего
     * простого сравнения strcmp или memcmp. Переводит все в нижний регистр и
     * все определения тегов заданы также в нижним регистре */
    static void f_case_insensitive_conv(char *buf, char *endbuf) {
        for ( ; buf < endbuf; buf++) {
            if ((*buf >= 'A') && (*buf <= 'Z')) {
                *buf -= 'A' - 'a';
            }
        }
    }

    /* Функция для выделения элемента из массива значений, разделенных запятыми,
     * с опциональными пробелами, как это реализовано во многих значениях полей
     * заголовка http.
     * Функция выделяет элемент, возвращает указатель на него (без пробелов) в качестве
     * результата, в penditem возвращает указатель на символ за последним символом элемента,
     * а в pnextitem - символ начала следующего элемента (после запятой, но до пробелов).
     * Для разбора списка функция вызывается в цикле, передавая в качестве buf следующего
     * вызова pnextitem предыдущего, пока pnextitem не станет равным endbuf */
    static char *f_parse_commalist_item(char *buf, char *endbuf, char **penditem, char **pnextitem) {
        int pos;
        char *enditem;


        pos = f_find_sym(buf, endbuf - buf, ',');
        if (pos < 0) {
            *pnextitem = enditem = endbuf;
        } else {
            enditem = buf + pos;
            *pnextitem = enditem + 1;
        }
        return f_get_trimmed_item(buf, enditem, penditem);
    }


    /* Функция для формирования значения поля заголовка, равному строковому представлению
     * целого числа. Число передается в указателе вместо контекста */
    static int f_hdrfield_int_cb(t_lsock_id sock, void *ctx, const char *name, uint8_t *buf, int buf_size) {
        int val = (int)ctx;
        int digts=1, tx_pos = 0;
        unsigned div, uval;
        static const unsigned max_div = (1UL << (sizeof(int)*8-1))/10;

        if (val < 0) {
            uval = -val;

            if (buf && (tx_pos < buf_size))
                buf[tx_pos] = '-';
            tx_pos++;

        } else {
            uval = val;
        }


        /* определяем кол-во цифр */
        for (digts=1, div=10; (uval >= div) && (div < max_div); digts++, div*=10)
        {}

        if (uval >= div) {
            digts++;
        } else {
            div/=10;
        }

        while (div!=0) {
            uint8_t c;
            c = uval/div;
            uval %= div;
            if (buf && (tx_pos < buf_size))
                buf[tx_pos] = '0' + c;
            tx_pos++;
            div/=10;
        }

        return tx_pos;
    }


    static t_lip_http_status_code f_check_version(const char *buf, int size, t_lip_http_ver *ver) {
        t_lip_http_status_code err = LIP_HTTP_STATUS_OK;
        if (strncmp(buf, LIP_HTTP_VER_PREFIX, LIP_HTTP_VER_PREFIX_SIZE) != 0) {
            err = LIP_HTTP_STATUS_BAD_REQUEST;
        } else {
            const char *ver_ptr = buf + LIP_HTTP_VER_PREFIX_SIZE;
            const char *ver_end = buf + size;
            const char *cur_buf = ver_ptr;


            ver->major = f_get_num(cur_buf, ver_end, &cur_buf);
            if ((cur_buf == ver_ptr) || (cur_buf >= (ver_end - 1)) || (*cur_buf != '.')) {
                err = LIP_HTTP_STATUS_BAD_REQUEST;
            } else {
                ver->minor = f_get_num(cur_buf + 1, ver_end, &cur_buf);
                if (cur_buf != ver_end) {
                    err = LIP_HTTP_STATUS_BAD_REQUEST;
                } else {
                    if (ver->major > LIP_HTTP_MAX_SUPPORTED_VERSION) {
                        err = LIP_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED;
                    }
                }
            }
        }
        return err;
    }


    static t_lip_http_status_code f_proc_reqline(char *buf, int size, t_lip_http_req_params *req) {
        t_lip_http_status_code err = LIP_HTTP_STATUS_OK;
        int meth_size, url_start_pos, url_size;

        meth_size = f_find_sp(buf, size);
        if (meth_size <= 0) {
            err = LIP_HTTP_STATUS_BAD_REQUEST;
        } else {
            url_start_pos = meth_size+1;
            url_size = f_find_sp(&buf[url_start_pos], size - url_start_pos);
            if (url_size <= 0) {
                err = LIP_HTTP_STATUS_BAD_REQUEST;
            } else {
                int ver_start_pos = url_start_pos + url_size + 1;
                int ver_size = size - ver_start_pos;
                if (f_find_sp(&buf[ver_start_pos], ver_size) >= 0) {
                    err = LIP_HTTP_STATUS_BAD_REQUEST;
                } else {
                    err = f_check_version(&buf[ver_start_pos], ver_size, &req->http_version);
                }
            }
        }

        if (err == LIP_HTTP_STATUS_OK) {
            unsigned meth_idx = 0;
            int fnd_method = 0;

            for (meth_idx = 0; !fnd_method &&
                 (meth_idx < sizeof(f_methods)/sizeof(f_methods[0])); meth_idx++) {
                if (f_equ_buf_item((const char *)buf, meth_size, f_methods[meth_idx].name, f_methods[meth_idx].name_len)) {
                    fnd_method = 1;
                    req->method = f_methods[meth_idx].method;
                }
            }

            if (!fnd_method) {
                err = LIP_HTTP_STATUS_NOT_IMPLEMENTED;
            }
        }

        if (err == LIP_HTTP_STATUS_OK) {
            buf[url_start_pos + url_size] = '\0';
            req->target = (const char*)&buf[url_start_pos];
            req->target_len = url_size;
        }

        return err;
    }

    /* разбор строки заголовка и сохранение его параметров в hdrfield. поля hdrfiedl по прежнему указывают на строку,
     * так что она не должна изменяться после вызова фукнции.
     * Функция добавляет нулевой символ по завершению названия поля и его значения для возможности отладочного
     * вывода (хотя при обработке лучше использовать явно возвращенную длину полей) */
    static t_lip_http_status_code f_prarse_hdrfield(char *buf, int size, t_lip_http_hdr_req_field_params *hdrfield) {
        t_lip_http_status_code err = LIP_HTTP_STATUS_OK;
        int field_name_end_pos = f_find_sym(buf, size, ':');
        char *endbuf = buf + size;

        if (field_name_end_pos <= 0) {
            err = LIP_HTTP_STATUS_BAD_REQUEST;
            lip_http_print_pkterr("no hdr field name delimiter\n");
        } else if (f_is_ws(*buf) || f_is_ws(buf[field_name_end_pos-1])) {
            /* пробел в конце и в начале названия поля не допускается */
            err = LIP_HTTP_STATUS_BAD_REQUEST;
            lip_http_print_pkterr("bad space in field name\n");
        } else {
            int val_size;
            char *field_name = buf;
            char *field_value;

            field_name[field_name_end_pos] = '\0';
            /* т.к. название поля не зависит от регистра, то для простоты стравнения
             * приводим его к нижнему регистру */
            f_case_insensitive_conv(field_name, field_name + field_name_end_pos);

            field_value = f_get_trimmed_item(&buf[field_name_end_pos + 1], endbuf, &endbuf);            
            val_size = endbuf - field_value;            
            /* после значения всегда идут CRLF, поэтому даже без пробела
             * есть символ, который можно безболезненно заменить на нулевой для
             * возможности отладочного вывода строки */
            *endbuf = '\0';

            hdrfield->name = field_name;
            hdrfield->name_len = field_name_end_pos;
            hdrfield->value = field_value;
            hdrfield->value_len = val_size;
            lip_http_print_dbg("rx hdr field - %s: %s\n", hdrfield->name, hdrfield->value);
        }
        return err;
    }


    /* Стандартная обработка полей заголовков стеком на уроене http */
    static t_lip_http_status_code f_proc_std_hdrfields(t_lip_httpsrv_con_ctx *httpcon, const t_lip_http_hdr_req_field_params *hdr) {
        t_lip_http_status_code err = LIP_HTTP_STATUS_OK;
        char *endval = hdr->value + hdr->value_len;
        if (f_equ_buf_item(hdr->name, hdr->name_len, LIP_HTTP_HDRFIELD_CONTENTLEN,
                           LIP_HTTP_HDRFIELD_CONTENTLEN_SIZE)) {
            const char *endptr;

            int len = f_get_num(hdr->value, endval, &endptr);
            /* проверка, что все сиволы поля соответствуют числу, а также
             * что не произошло переполнение числа (функция возвращает в этом случае -1) */
            if ((endptr != endval) || (len < 0)) {
                err = LIP_HTTP_STATUS_BAD_REQUEST;
                lip_http_print_pkterr("bad content length!\n");
            } else if (httpcon->req_body_params.len_type == LIP_HTTP_BODYLEN_EXPLICIT) {
                /* если в пакете более одного поля Content-Lenght, то они должны быть
                 * равны */
                if (httpcon->req_body_params.len != len) {
                    err = LIP_HTTP_STATUS_BAD_REQUEST;
                    lip_http_print_pkterr("multiple nonequ content length!\n");
                }
            } else if (httpcon->req_body_params.len_type == LIP_HTTP_BODYLEN_IMPLICIT) {
                /* Длина берется из Content-Length только если не было Transfer-Encoding
                 * и она соответственно  (rfc7230 p33) еще не определена  */
                httpcon->req_body_params.len_type = LIP_HTTP_BODYLEN_EXPLICIT;
                httpcon->req_body_params.len = len;
            }
        } else if (f_equ_buf_item(hdr->name, hdr->name_len, LIP_HTTP_HDRFIELD_CONNECTION,
                                  LIP_HTTP_HDRFIELD_CONNECTION_SIZE)) {
            /* разбор списка полей в Connection */
            char *item, *enditem;
            char *next = hdr->value;
            do {
                item = f_parse_commalist_item(next, endval, &enditem, &next);
                if (item != enditem) {
                    f_case_insensitive_conv(item, enditem);
                    /* при приеме Connection: close, помечаем, что соединение нужно
                     * закрыть по заверешнию передачи ответа */
                    if (f_equ_buf_item(item, enditem - item, LIP_HTTP_CONNECTION_OPT_CLOSE,
                                       LIP_HTTP_CONNECTION_OPT_CLOSE_SIZE)) {
                        httpcon->flags |= LIP_HTTP_CONFLAG_CON_CLOSE;
                    }
                }
            } while (next < endval);
        } else if (f_equ_buf_item(hdr->name, hdr->name_len, LIP_HTTP_HDRFIELD_EXPECT,
                                  LIP_HTTP_HDRFIELD_EXPECT_SIZE)) {
            char *item, *enditem;
            item = f_get_trimmed_item(hdr->value, endval, &enditem);
            f_case_insensitive_conv(item, enditem);
            if (f_equ_buf_item(item, enditem - item, "100-continue", 12)) {
                if (!(httpcon->flags & LIP_HTTP_CONFLAG_V1_0)) {
                    /* для http/1.0 игнорируем expect (rfc 7231 p35),
                     * иначе помечаем, что нужно будет послать 100-continue
                     * по приему всего заголовка */
                    httpcon->flags |= LIP_HTTP_CONFLAG_EXP_CONTINUE;
                }
            } else {
                err = LIP_HTTP_STATUS_EXPECTATION_FAILED;
            }
        } else if (f_equ_buf_item(hdr->name, hdr->name_len, LIP_HTTP_HDRFIELD_TRANSFER_ENCODING,
                                  LIP_HTTP_HDRFIELD_TRANSFER_ENCODING_SIZE)) {
            char *item, *enditem;
            char *next = hdr->value;
            do {
                item = f_parse_commalist_item(next, endval, &enditem, &next);
                if (item != enditem) {
                    f_case_insensitive_conv(item, enditem);
                    if (f_equ_buf_item(item, enditem - item, LIP_HTTP_TRANSF_ENCODING_NAME_CHUNKED,
                                       LIP_HTTP_TRANSF_ENCODING_NAME_CHUNKED_SIZE)) {
                        httpcon->req_body_params.len = -1;
                        httpcon->req_body_params.len_type = LIP_HTTP_BODYLEN_CHUNKED;
                        httpcon->req_body_encoding = LIP_HTTP_TRANSF_ENCODING_CHUNKED;
                        lip_http_print_dbg("chunked encoding!\n");
                    } else {
                        httpcon->req_body_params.len = -1;
                        /** @note остальные варианты опциональны и в настоящий момент
                         * не поддерживаем. если их реализовывать, то нужно учетсть, что
                         * chunked должен быть последним, т.е. если при нахождении
                         * дургой опции уже len_type == LIP_HTTP_BODYLEN_CHUNKED,
                         * то возвращать LIP_HTTP_STATUS_BAD_REQUEST (rfc7230 p32).
                         * Иначе смена len_type на LIP_HTTP_BODYLEN_ON_CLOSE */
                        err = LIP_HTTP_STATUS_NOT_IMPLEMENTED;
                        lip_http_print_pkterr("bad encoding!\n");
                    }
                }
            } while (next < endval);
        }
        return err;
    }



    /* установка ошибки для посылки в ответе.
     * Если ошибка не фатальная, то после ошибки пользовательские callback-функции
     * не будут вызываться и принимаемый запрос будет отброшен
     * (но принят целиком, чтобы можно было принять следующий без закрытия соединения).
     * Немедленно будет начата передача ответа с ошибокй.
     * Если ошибка фатальная, это значит что нельзя опеределить границы запроса,
     * и соединение будет разорвано немедленно без приема других байт */
    static LINLINE void f_set_req_error(t_lip_httpsrv_con_ctx *httpcon, t_lip_http_status_code err, int fatal) {
        if (httpcon->resp.params.status == LIP_HTTP_STATUS_OK) {
            httpcon->resp.params.status = err;
        }

        if (!(httpcon->flags & LIP_HTTP_CONFLAG_REQ_DROP)) {
            httpcon->flags |= LIP_HTTP_CONFLAG_REQ_DROP;
            f_tx_resp_start(httpcon);
        }
        if (fatal)
            httpcon->flags |= LIP_HTTP_CONFLAG_CON_ABORT | LIP_HTTP_CONFLAG_CON_CLOSE;
    }

    static void f_start_wait_req(t_lip_httpsrv_con_ctx *httpcon) {
        httpcon->proc_buf_pos = httpcon->proc_buf_end = 0;
        httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_WAIT;
        httpcon->flags = 0;

        /* сброс параметров обработки */
        httpcon->proc_req_params.context = NULL;
        httpcon->proc_req_params.hdr_field_cb = NULL;
        httpcon->proc_req_params.data_start_cb = NULL;
        httpcon->proc_req_params.data_block_cb = NULL;
        httpcon->proc_req_params.req_finish_cb = NULL;
        /* по-умолчанию тело запроса имеет нулевую длину */
        httpcon->req_body_params.len = 0;
        httpcon->req_body_params.len_type = LIP_HTTP_BODYLEN_IMPLICIT;

        httpcon->req_body_encoding = LIP_HTTP_TRANSF_ENCODING_NONE;

        httpcon->resp.state = LIP_HTTP_TXCONSTATE_WAIT;
        httpcon->req_data_pos = 0;
        f_resp_init(&httpcon->resp.params);

    }

    static void f_start_listen(t_lip_httpsrv_con_ctx *httpcon) {
        lip_http_print_dbg("start listen socket %d, port %d\n", httpcon->socket, httpcon->port);
        lsock_listen(httpcon->socket, httpcon->port);
        f_start_wait_req(httpcon);
    }

    static void f_close(t_lip_httpsrv_con_ctx *httpcon) {
        lsock_close(httpcon->socket, LIP_TCP_DEFAULT_CLOSE_TOUT);
        httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_WAIT;
        httpcon->resp.state = LIP_HTTP_TXCONSTATE_WAIT;
    }

    static void f_abort(t_lip_httpsrv_con_ctx *httpcon) {
        lsock_abort(httpcon->socket);
        httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_WAIT;
        httpcon->resp.state = LIP_HTTP_TXCONSTATE_WAIT;
    }

    static void f_rx_req_proc(t_lip_httpsrv_con_ctx *httpcon) {
        if (httpcon->rxstate != LIP_HTTP_RXCONSTATE_REQ_FINISH) {
            t_lip_tcp_sock_state sock_state = lsock_state(httpcon->socket);
            /* прием запроса может быть как в рабочем состоянии, так и дочитываение его в случае закрытия
             * соединения с другой стороны */
            if ((sock_state == LIP_TCP_SOCK_STATE_ESTABLISHED) || (sock_state == LIP_TCP_SOCK_STATE_CLOSE_WAIT)) {
                int rdy = lsock_recv_rdy_size(httpcon->socket);
                if ((rdy > 0) || (sock_state == LIP_TCP_SOCK_STATE_CLOSE_WAIT)) {
                    int proc_done = 0;
                    int buf_size = LIP_HTTP_SERVER_MAX_RXLINE_SIZE - httpcon->proc_buf_end;
                    if (rdy > buf_size)
                        rdy = buf_size;
                    if (rdy > 0) {
                        lsock_recv(httpcon->socket, &httpcon->proc_buf[httpcon->proc_buf_end], buf_size);
                        lip_http_print_dbg("received %d bytes\n", rdy);
                        httpcon->proc_buf_end += rdy;
                    }

                    while (!proc_done && !(httpcon->flags & LIP_HTTP_CONFLAG_CON_ABORT)) {
                        /* прием тела запроса обрабатывается отдельно. в нем
                         * осущставляется прием сырого потока байт до заданной длины
                         * или до закрытия соединения */
                        if (httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_READ_BODY) {
                            int data_rdy = httpcon->proc_buf_end;
                            int data_end = 0;



                            /* если длина данных (или конца блока для chunked) известна,
                             * то используем ее для определения признака конца данных */
                            if (httpcon->req_body_params.len >= 0) {
                                int remain_size = httpcon->req_body_params.len - httpcon->req_data_pos;
                                lip_http_print_dbg("rx data rdy = %d, rem_size %d\n", data_rdy, remain_size);
                                if (data_rdy >= remain_size) {
                                    data_rdy = remain_size;
                                    data_end = 1;
                                }
                            } else if ((data_rdy == 0) &&
                                       (httpcon->req_body_params.len_type == LIP_HTTP_BODYLEN_ON_CLOSE) &&
                                       (sock_state == LIP_TCP_SOCK_STATE_CLOSE_WAIT)) {
                                /* обработка конца данных по закрытию соединения */
                                data_end = 1;
                            }

                            /* обработка вычитанного блока данных */
                            if (data_rdy > 0) {
                                LIP_HTTPCON_REQ_CLIENT_CB(httpcon, data_block_cb, httpcon->proc_buf, data_rdy);

                                httpcon->req_data_pos += data_rdy;

                                httpcon->proc_buf_pos = 0;
                                httpcon->proc_buf_end -= data_rdy;
                                if (httpcon->proc_buf_end > 0) {
                                    memmove(httpcon->proc_buf, &httpcon->proc_buf[data_rdy], httpcon->proc_buf_end);
                                }
                            } else {
                                proc_done = 1;
                            }

                            if (data_end) {
                                if (httpcon->req_body_params.len_type == LIP_HTTP_BODYLEN_CHUNKED) {
                                    lip_http_print_dbg("rx chunk done\n");
                                    /* после завершеня блока в chunk должен идти завершающий
                                     * конец строки, который можем обработать в этом цикле */
                                    httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_DATA_END;
                                    proc_done = 0;
                                } else {
                                    lip_http_print_dbg("rx data done\n");
                                    httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_FINISH;
                                    LIP_HTTPCON_REQ_CLIENT_CB(httpcon, req_finish_cb, httpcon->resp.params.status);
                                    proc_done = 1;
                                }
                            }
                        } else if (rdy > 0) {
                            /* в остальных случаях обработка идет по строкам до
                             * символа конца строки */
                            int eol_pos = httpcon->proc_buf_pos;
                            int next_pos;
                            /* проверяем конец строки только в новых данных,
                             * т.к. в предыдущих уже проверено, но
                             * также захватываем один предыдущий символ, чтобы
                             * отследить ситуацию, когда пред. буфер закончился
                             * первым символом из 2-х конца строки.
                             * Кроме того, сдвигаемся еще на один символ, т.к.
                             * при обработка конца строки в конце буфера обрабытывается
                             * не всегда (для обнаружения obs-fold) */
                            if (eol_pos > 0)
                                eol_pos--;
                            if (eol_pos > 0)
                                eol_pos--;

                            eol_pos = f_find_eol_pos(&httpcon->proc_buf[eol_pos], httpcon->proc_buf_end - eol_pos);
                            lip_http_print_dbg("eol pos - %d of %d\n", eol_pos, httpcon->proc_buf_end);

                            next_pos = eol_pos + 2;
                            /* если строка не пустая, то обрабатываем ее только при наличии следующего
                             * символа (всегда должен быть, т.к. запрос всегда оканчивается пустой строкой),
                             * чтобы определить возможный перенос загловка на новую строку (obs-fold) */
                            if ((eol_pos == 0) || ((eol_pos > 0) && (next_pos < httpcon->proc_buf_end))) {

                                /* обработка стартовой строки запроса */
                                if (httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_WAIT) {
                                    t_lip_http_req_params req;
                                    t_lip_http_status_code err = f_proc_reqline(
                                                (char *)httpcon->proc_buf, eol_pos, &req);

                                    if (err != LIP_HTTP_STATUS_OK) {
                                        /* неверную строку запроса счтамем крит ошибкой (возможно ввести разделение) */
                                        f_set_req_error(httpcon, err, 1);
                                    } else {
                                        if ((req.http_version.minor == 0) && (req.http_version.major == 1)) {
                                            httpcon->flags |= LIP_HTTP_CONFLAG_V1_0;
                                        }
                                        httpcon->method = req.method;

                                        lip_http_print_dbg("rx request %d for %s ver %d.%d\n",
                                                           (int)req.method, req.target, req.http_version.major, req.http_version.minor);

                                        err = httpcon->accept_cb(httpcon->socket, &req, &httpcon->proc_req_params);
                                        if (err != LIP_HTTP_STATUS_OK) {
                                            f_set_req_error(httpcon, err, 0);
                                        }
                                        httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_READ_HDR;
                                    }
                                } else if ((httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_READ_HDR) ||
                                    (httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_TRAILER)) {
                                    /* Обработка полей в заголовке пакета или псоеле передачи
                                     * chunked-данных в trailer'e выполняется одинаковым образом */
                                    /** @todo Часть полей не допустимо в trailer
                                     * (rfc7230 p37). Они должны игнорироваться или рассматриваться
                                     * ошибкой, сейчас это не проверяется, хотя это может сделать
                                     * верхний уровень, так как они не объединяются с полями заголовков */

                                    /* пустая строка означает конец заголовка или trailer'а */
                                    if (eol_pos == 0) {
                                        if (httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_TRAILER) {
                                            lip_http_print_dbg("rx chunk trailer done\n");
                                            httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_FINISH;
                                            LIP_HTTPCON_REQ_CLIENT_CB(httpcon, req_finish_cb, httpcon->resp.params.status);
                                        } else {
                                            /* после заголовка переходим к чтению тела. при chunked-кодировке
                                             * сперва читаем длину блока */
                                            httpcon->rxstate = httpcon->req_body_params.len_type == LIP_HTTP_BODYLEN_CHUNKED ?
                                                    LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_LEN : LIP_HTTP_RXCONSTATE_REQ_READ_BODY;

                                            LIP_HTTPCON_REQ_CLIENT_CB(httpcon, data_start_cb,  &httpcon->req_body_params);
                                            lip_http_print_dbg("rx hdr done. len = %d, pos = %d\n", httpcon->req_body_params.len, httpcon->req_data_pos);
                                            /* после разбора заголовка, если клиент ожидает continue
                                             * и обработка заголовка успешна и еще не начали отправлять
                                             * ответ, то переходим в состояние передачи continue */
                                            if ((httpcon->flags & LIP_HTTP_CONFLAG_EXP_CONTINUE) &&
                                                    (httpcon->resp.params.status == LIP_HTTP_STATUS_OK) &&
                                                    (httpcon->resp.state == LIP_HTTP_TXCONSTATE_WAIT)) {
                                                httpcon->resp.state = LIP_HTTP_TXCONSTATE_SND_CONTINUE;
                                            }
                                        }
                                    } else if (f_is_ws(httpcon->proc_buf[next_pos])) {
                                        /* obs-fold - многострочное поле, допустимы
                                         * только в message/http, который не поддерживаем
                                         * (см rfc7230 3.2.4) */
                                        f_set_req_error(httpcon, LIP_HTTP_STATUS_BAD_REQUEST, 1);
                                        lip_http_print_pkterr("obs fold detected\n");
                                    } else {
                                        t_lip_http_hdr_req_field_params hdrfield;
                                        t_lip_http_status_code err = f_prarse_hdrfield(
                                                    (char *)httpcon->proc_buf, eol_pos, &hdrfield);
                                        if (err == LIP_HTTP_STATUS_OK) {
                                            /* обработка стандартных полей */
                                            err = f_proc_std_hdrfields(httpcon, &hdrfield);
                                        }

                                        if (err == LIP_HTTP_STATUS_OK) {
                                            LIP_HTTPCON_REQ_CLIENT_CB(httpcon, hdr_field_cb, &hdrfield);
                                        } else {
                                            f_set_req_error(httpcon, err, 0);
                                        }
                                    }
                                } else if (httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_LEN) {
                                    /* блок начинается с числа в hex, равным размеру.
                                     * лишние пробелы тут недопустимы */
                                    const char *buf = (char *)httpcon->proc_buf;
                                    int len = f_get_hex(buf, buf + eol_pos, &buf);
                                    if (len < 0) {
                                        f_set_req_error(httpcon, LIP_HTTP_STATUS_BAD_REQUEST, 1);
                                    } else {
                                        /** @note после ; могут быть chunk-extension в виде
                                         * параметр = значение. Согласно rfc неподдерживаемые
                                         * расширения игнорируется, т.к. поддерживаемых
                                         * нет, то сейчас не анализируем вообще. */
                                        lip_http_print_dbg("chunk len = %d\n", len);
                                        if (len == 0) {
                                            /* блок нулевой длины - последний и без строки с данными.
                                             * далее начинаем прием trailer'а */
                                            httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_TRAILER;
                                        } else {
                                            /* В httpcon->req_body_params.len подсчитываем
                                             * общую длину принятых данных в запросе.
                                             * При приеме первого блока она равна -1
                                             * и становится равна размеру первого блока */
                                            if (httpcon->req_body_params.len < 0) {
                                                httpcon->req_body_params.len = len;
                                            } else {
                                                httpcon->req_body_params.len += len;
                                            }
                                            httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_READ_BODY;
                                        }
                                    }
                                } else if (httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_DATA_END) {
                                    /* за данными блока должна немедленно идти завершающая строка. */

                                    if (eol_pos != 0) {
                                        lip_http_print_pkterr("no chunk data eol\n");
                                        f_set_req_error(httpcon, LIP_HTTP_STATUS_BAD_REQUEST, 1);
                                    } else {
                                        httpcon->rxstate = LIP_HTTP_RXCONSTATE_REQ_READ_CHUNK_LEN;
                                    }
                                }


                                /* отбрасываем обработанную строку, смещая оставшиеся данные (если есть)
                                 * в начало буфера */
                                httpcon->proc_buf_pos = 0;
                                httpcon->proc_buf_end -= next_pos;
                                if (httpcon->proc_buf_end > 0) {
                                    memmove(httpcon->proc_buf, &httpcon->proc_buf[next_pos], httpcon->proc_buf_end);
                                }
                            } else {
                                httpcon->proc_buf_pos = httpcon->proc_buf_end;
                                if (httpcon->proc_buf_pos == LIP_HTTP_SERVER_MAX_RXLINE_SIZE) {
                                    /** @note слишком большой размер запроса или поля не обрабатываем.
                                     * сейчас возвращаем всегда ошибку BAD_REQUEST,
                                     * хотя теоретически для запроса можно проверить URL
                                     * или метод выходит за диапазон и возвращать другие ошибки */
                                    f_set_req_error(httpcon, LIP_HTTP_STATUS_BAD_REQUEST, 1);
                                }
                                proc_done = 1;
                            }
                        } else {
                            proc_done = 1;
                        }
                    }

                    if ((sock_state == LIP_TCP_SOCK_STATE_CLOSE_WAIT) &&
                         !LIP_HTTPCON_TX_ACTIVE(httpcon)) {
                        /* После обработки принятых данных, если удаленная сторона
                         * закрыла соединение и не идет передача, то можем закрывать
                         * со своей стороны */
                        f_close(httpcon);
                    }


                }
            }
        }
    }

#define PUT_CRLR(tx_buf, tx_pos) do { \
        tx_buf[tx_pos++] = '\r'; \
        tx_buf[tx_pos++] = '\n'; \
    } while(0)

#define LIP_HTTP_STATUS_LINE_SIZE  16

    static int f_tx_put_status(uint8_t *buf, int buf_size, int *pbuf_pos, t_lip_http_status_code status) {
        int buf_pos = *pbuf_pos;
        int ret = 0;
        if ((buf_size - buf_pos) >= LIP_HTTP_STATUS_LINE_SIZE) {
            memcpy(&buf[buf_pos], "HTTP/1.1 ", 9);
            buf_pos += 9;
            buf[buf_pos++] = status / 100 + '0';
            buf[buf_pos++] = (status % 100)/10 + '0';
            buf[buf_pos++] = status % 10 + '0';
            buf[buf_pos++] = ' ';
            PUT_CRLR(buf, buf_pos);
            *pbuf_pos = buf_pos;
            ret = 1;
        }
        return ret;
    }

    static void f_tx_resp_proc(t_lip_httpsrv_con_ctx *httpcon) {
        t_lip_http_tx_ctx *txctx = &httpcon->resp;
        t_lip_tcp_sock_state sock_state = lsock_state(httpcon->socket);

        /* обработка передачи ответа на запрос */
        if (LIP_HTTPCON_TX_ACTIVE(httpcon)) {
            if ((sock_state == LIP_TCP_SOCK_STATE_ESTABLISHED) || (sock_state == LIP_TCP_SOCK_STATE_CLOSE_WAIT)) {
                uint8_t *tx_buf = f_tx_tmp_buf;
                int proc_done = 0;
                int tx_buf_pos = 0;
                int tx_rdy = lsock_send_rdy_size(httpcon->socket);
                if (tx_rdy > LIP_HTTP_SERVER_MAX_TXLINE_SIZE)
                    tx_rdy = LIP_HTTP_SERVER_MAX_TXLINE_SIZE;


                while (!proc_done) {
                    if (txctx->state == LIP_HTTP_TXCONSTATE_SND_CONTINUE) {
                        /* Промежуточный 100-Continue состоит всегда только из статусной
                         * строки и завершающей пустой строки. Посылаем все данные всегда
                         * за один раз */
                         if ((tx_rdy - tx_buf_pos) >= (LIP_HTTP_STATUS_LINE_SIZE + 2)) {
                             f_tx_put_status(tx_buf, tx_rdy, &tx_buf_pos, LIP_HTTP_STATUS_CONTINUE);
                             PUT_CRLR(tx_buf, tx_buf_pos);
                             /* посылка 100-continue идет всегда до того, как определен
                              * финальный ответ. поэтому после передачи всегда переходим
                              * в состояние ожидания готовности ответа */
                             txctx->state = LIP_HTTP_TXCONSTATE_WAIT;
                         }
                         proc_done = 1;
                    } else if (txctx->state == LIP_HTTP_TXCONSTATE_SND_STATUS) {
                        if (f_tx_put_status(tx_buf, tx_rdy, &tx_buf_pos, txctx->params.status)) {

                            txctx->state = LIP_HTTP_TXCONSTATE_SND_HDR;
                            txctx->tx_pos = 0;
                        } else {
                            proc_done = 1;
                        }
                    } else if (txctx->state == LIP_HTTP_TXCONSTATE_SND_HDR) {
                        /* Если переслали все поля заголовка, то необходимо вставить
                         * перенос строки для признака заверешния */
                        if (txctx->tx_pos == txctx->params.hdr_cnt) {
                            if ((tx_rdy - tx_buf_pos) >= 2) {
                                PUT_CRLR(tx_buf, tx_buf_pos);
                                txctx->tx_pos = 0;
                                /* Метод head не должен возвращать данные, поэтому после
                                   передачи заголовка завершаем посылку */
                                if (httpcon->method == LIP_HTTP_METHOD_HEAD) {
                                    txctx->state = LIP_HTTP_TXCONSTATE_SND_FINISH;
                                    proc_done = 1;
                                } else {
                                    txctx->state = LIP_HTTP_TXCONSTATE_SND_BODY;
                                }
                            } else {
                                proc_done = 1;
                            }
                        } else {
                            t_lip_http_resp_field_params *hdrfield = &txctx->params.hdrs[txctx->tx_pos];
                            int req_size;

                            /* Если длина значения заголовка не определена, то пробуем
                             * ее определить через вызов callback-функции с пустым буфером
                             * для получения */
                            if (hdrfield->value_len < 0) {
                                if (hdrfield->value_cb != NULL) {
                                    hdrfield->value_len = hdrfield->value_cb(
                                                httpcon->socket, hdrfield->ctx, hdrfield->name, NULL, 0);
                                } else {
                                    hdrfield->value_len = 0;
                                }
                            }

                            req_size = hdrfield->name_len + hdrfield->value_len + 4;
                            if ((tx_rdy - tx_buf_pos) >= req_size) {
                                memcpy(&tx_buf[tx_buf_pos], hdrfield->name, hdrfield->name_len);
                                tx_buf_pos += hdrfield->name_len;
                                tx_buf[tx_buf_pos++] = ':';
                                tx_buf[tx_buf_pos++] = ' ';
                                if (hdrfield->value_len > 0) {
                                    if (hdrfield->value_cb != NULL) {
                                        hdrfield->value_cb(httpcon->socket, hdrfield->ctx,
                                                           hdrfield->name,
                                                           &tx_buf[tx_buf_pos], hdrfield->value_len);
                                    } else {
                                        memcpy(&tx_buf[tx_buf_pos], hdrfield->value, hdrfield->value_len);
                                    }
                                    tx_buf_pos += hdrfield->value_len;
                                }
                                PUT_CRLR(tx_buf, tx_buf_pos);
                                txctx->tx_pos++;
                            } else {
                                proc_done = 1;
                            }
                        }
                    } else if (txctx->state == LIP_HTTP_TXCONSTATE_SND_BODY) {
                        int data_rdy = txctx->params.data_len - txctx->tx_pos;
                         lip_http_print_dbg("send resonse data rdy %d\n", data_rdy);
                        if (data_rdy == 0) {
                            txctx->state = LIP_HTTP_TXCONSTATE_SND_FINISH;
                        } else {

                            if (data_rdy > (tx_rdy - tx_buf_pos)) {
                                data_rdy = tx_rdy - tx_buf_pos;
                            }

                            if (data_rdy > 0) {
                                int put_size;
                                if (txctx->params.data_cb != NULL) {
                                    put_size = txctx->params.data_cb(httpcon->socket, txctx->params.data_ctx,
                                                                 txctx->tx_pos, &tx_buf[tx_buf_pos], data_rdy);
                                } else if (txctx->params.data) {
                                    memcpy(&tx_buf[tx_buf_pos], &txctx->params.data[txctx->tx_pos], data_rdy);
                                    put_size = data_rdy;
                                } else {
                                    memset(&tx_buf[tx_buf_pos], 0, data_rdy);
                                    put_size = data_rdy;
                                }
                                if (put_size > data_rdy) {
                                    put_size = data_rdy;
                                }

                                if (put_size > 0) {
                                    txctx->tx_pos += put_size;
                                    tx_buf_pos += put_size;
                                } else if (put_size < 0) {
                                    /* Значение размера < 0 соответствует ошибке чтения данных.
                                     * т.к. статус и размер уже отправлены, то не совсем понятно
                                     * как корректно завершить передачу с указанием ошибки.
                                     * Сейчас аварийно разрываем соединение. Возможно стоит посмотреть
                                     * другие способы */
                                    tx_buf_pos = 0;
                                    lip_http_print_apperr("read data block error! Abort connection...\n");
                                    f_abort(httpcon);
                                }
                            }
                        }
                        proc_done = 1;
                    }
                }

                if (tx_buf_pos > 0) {
                    lsock_send(httpcon->socket, tx_buf, tx_buf_pos);
                }
            }
        }
    }




    static void f_tx_resp_start(t_lip_httpsrv_con_ctx *httpcon) {
        if (httpcon->resp.state == LIP_HTTP_TXCONSTATE_WAIT) {
            httpcon->resp.state  = LIP_HTTP_TXCONSTATE_SND_STATUS;
            httpcon->resp.tx_pos = 0;
            /* Нельзя передовать Content-Length для статусов 1xx и 204 (No Content)
             * rfc7230 3.3.2 */
            /** @todo проверить отсутствие заголовка Transfer-Encoding,
             * т.к. нельзя передавать при этом  (возможно обязать устанавливать в этом случае len < 0)*/
            /** @note не должен передоваться в 2xx ответе на CONNECT, но
             * т.к. сейчас CONNECT не поддерживается, то нет явной проверки */
            if ((httpcon->resp.params.data_len >= 0) &&
                    (httpcon->resp.params.status != LIP_HTTP_STATUS_NO_CONTENT) &&
                    !LIP_HTTP_STATUS_IS_INFORMATIONAL(httpcon->resp.params.status)) {
                lip_http_resp_add_hdrfield_intval(&httpcon->resp.params,
                                                   LIP_HTTP_HDRFIELD_CONTENTLEN,
                                                  LIP_HTTP_HDRFIELD_CONTENTLEN_SIZE,
                                                   httpcon->resp.params.data_len);
            }

            /* если после посылки ответа соединение будет закрыто, то добавляем
             * соответствующий заголовок */
            if (httpcon->flags & LIP_HTTP_CONFLAG_CON_CLOSE) {
                lip_http_resp_add_hdrfield(&httpcon->resp.params,
                                           LIP_HTTP_HDRFIELD_CONNECTION,
                                           LIP_HTTP_HDRFIELD_CONNECTION_SIZE,
                                           LIP_HTTP_CONNECTION_OPT_CLOSE,
                                           LIP_HTTP_CONNECTION_OPT_CLOSE_SIZE);
            }
        }
    }




    void lip_http_server_init(void) {
        unsigned con_idx;
        for (con_idx = 0; con_idx < LIP_HTTP_SERVER_CONNECTIONS_CNT; con_idx++) {
            f_con_list[con_idx].active = 0;
            f_con_list[con_idx].proc_buf = f_tmp_bufs[con_idx];
            f_con_list[con_idx].socket = LIP_TCP_INVALID_SOCKET_ID;
        }
    }


    void lip_http_server_pull(void) {
        unsigned con_idx;

        for (con_idx = 0; con_idx < LIP_HTTP_SERVER_CONNECTIONS_CNT; con_idx++) {
            t_lip_httpsrv_con_ctx *httpcon = &f_con_list[con_idx];
            if (httpcon->active) {
                t_lip_tcp_sock_state sock_state;
                f_rx_req_proc(httpcon);
                f_tx_resp_proc(httpcon);

                sock_state = lsock_state(httpcon->socket);

                if (sock_state == LIP_TCP_SOCK_STATE_CLOSED) {                    
                    f_start_listen(httpcon);
                } else if ((httpcon->resp.state == LIP_HTTP_TXCONSTATE_SND_FINISH) &&
                        (httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_FINISH)) {
                    if (httpcon->resp.params.finish_cb) {
                        httpcon->resp.params.finish_cb(httpcon->socket, httpcon->resp.params.finish_ctx, httpcon->resp.params.data_len);
                    }
                    if ((httpcon->flags & LIP_HTTP_CONFLAG_CON_CLOSE) ||
                            (lsock_state(httpcon->socket) == LIP_TCP_SOCK_STATE_CLOSE_WAIT)) {
                        /* Если был запрос на закрытие соединения со стороны
                         * сервера или удаленной стороны, то после завершения
                         * обмена закрываем соединение */
                        httpcon->resp.state = LIP_HTTP_TXCONSTATE_WAIT;
                        f_close(httpcon);
                    } else {
                        f_start_wait_req(httpcon);
                    }
                }
            }
        }
    }

    t_lip_errs lip_http_server_listen_start(uint16_t port, t_lip_http_server_req_cb cb, uint16_t sock_cnt)     {
        unsigned con_idx;

        for (con_idx = 0; (con_idx < LIP_HTTP_SERVER_CONNECTIONS_CNT) && (sock_cnt > 0); con_idx++) {
            t_lip_httpsrv_con_ctx *httpcon = &f_con_list[con_idx];
            if (!httpcon->active) {
                httpcon->socket = lsock_create();
                if (httpcon->socket != LIP_TCP_INVALID_SOCKET_ID) {
#ifdef LIP_TCP_USE_KEEPALIVE
                    int val = 1;
                    lsock_set_opt(httpcon->socket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
#endif
                    httpcon->active = 1;
                    lsock_set_recv_fifo(httpcon->socket, f_rx_bufs[con_idx], LIP_HTTP_SERVER_RXBUF_SIZE);
                    lsock_set_send_fifo(httpcon->socket, f_tx_bufs[con_idx], LIP_HTTP_SERVER_TXBUF_SIZE);
                    httpcon->accept_cb = cb;
                    httpcon->port = port;
                    f_start_listen(httpcon);
                    sock_cnt--;
                }
            }
        }

        return sock_cnt == 0 ? LIP_ERR_SUCCESS : LIP_ERR_HTTP_INSUF_SOCKETS;
    }

    static t_lip_errs f_get_con(t_lsock_id socket, t_lip_httpsrv_con_ctx **phttpcon)     {
        t_lip_errs err = LIP_ERR_HTTP_CONNECTION_NOT_FOUND;
        unsigned con_idx;

        for (con_idx = 0; (con_idx < LIP_HTTP_SERVER_CONNECTIONS_CNT) &&
             (err == LIP_ERR_HTTP_CONNECTION_NOT_FOUND); con_idx++) {
            t_lip_httpsrv_con_ctx *httpcon = &f_con_list[con_idx];
            if (httpcon->active && (httpcon->socket == socket)) {
                *phttpcon = httpcon;
                err = LIP_ERR_SUCCESS;
            }
        }
        return err;
    }

    static t_lip_errs f_get_con_for_response(t_lsock_id socket, t_lip_httpsrv_con_ctx **phttpcon)     {
        t_lip_httpsrv_con_ctx *httpcon;
        t_lip_errs err = f_get_con(socket, &httpcon);
        if (err == LIP_ERR_SUCCESS) {
            /* указатель на ответ можно получить, только если есть запрос
             * в обработке и если не начали уже передавать ответ.
             * При этом если хотим передать continue, то можно сформировать ответ
             * и при завершении он перепишет continue */
            if ((httpcon->rxstate == LIP_HTTP_RXCONSTATE_REQ_WAIT) ||
                    ((httpcon->resp.state != LIP_HTTP_TXCONSTATE_WAIT) &&
                     (httpcon->resp.state != LIP_HTTP_TXCONSTATE_SND_CONTINUE))) {
                err = LIP_ERR_HTTP_CONNECTION_STATE;
            } else {
                *phttpcon = httpcon;
                err = LIP_ERR_SUCCESS;
            }
        }
        return err;
    }


     t_lip_errs lip_http_srv_get_resp(t_lsock_id socket, t_lip_http_resp **resp) {
        t_lip_httpsrv_con_ctx *httpcon;
        t_lip_errs err = f_get_con_for_response(socket, &httpcon);
        if (err == LIP_ERR_SUCCESS) {
            *resp = &httpcon->resp.params;
        }
        return err;
    }

    t_lip_errs lip_http_srv_request_finish(t_lsock_id socket) {
        t_lip_httpsrv_con_ctx *httpcon;
        t_lip_errs err = f_get_con_for_response(socket, &httpcon);
        if (err == LIP_ERR_SUCCESS) {
            lip_http_print_dbg("start response (status = %d)\n", httpcon->resp.params.status);
            httpcon->flags |= LIP_HTTP_CONFLAG_REQ_DROP;
            f_tx_resp_start(httpcon);

            err = LIP_ERR_HTTP_INVALID_RESP_POINTER;
        }
        return err;
    }

    t_lip_errs lip_http_srv_close_connection(t_lsock_id socket)  {
        t_lip_httpsrv_con_ctx *httpcon;
        t_lip_errs err = f_get_con(socket, &httpcon);
        if (err == LIP_ERR_SUCCESS) {
            if (!LIP_HTTPCON_TX_ACTIVE(httpcon) && !LIP_HTTPCON_RX_ACTIVE(httpcon)) {
                f_close(httpcon);
            } else {
                httpcon->flags |= LIP_HTTP_CONFLAG_CON_CLOSE;
            }
        }
        return err;
    }
    t_lip_errs lip_http_srv_abort_connection(t_lsock_id socket)     {
        t_lip_httpsrv_con_ctx *httpcon;
        t_lip_errs err = f_get_con(socket, &httpcon);
        if (err == LIP_ERR_SUCCESS) {
            f_abort(httpcon);
        }
        return err;
    }

    static void f_resp_init(t_lip_http_resp *params) {
        params->data_len = 0;
        params->hdr_cnt = 0;
        params->status = LIP_HTTP_STATUS_OK;
        params->data_ctx = NULL;
        params->data_cb = NULL;
        params->finish_ctx = NULL;
        params->finish_cb = NULL;
    }

    void lip_http_resp_add_hdrfield(t_lip_http_resp *resp,
                                    const char *name, int name_len,
                                    const char *value, int value_len) {
        if (resp->hdr_cnt < LIP_HTTP_SERVER_MAX_RESP_HDR_FIELDS) {
            t_lip_http_resp_field_params *hdrfield = &resp->hdrs[resp->hdr_cnt];
            hdrfield->name = name;
            hdrfield->value = value;
            hdrfield->name_len = name_len >= 0 ? name_len : (int)strlen(name);
            hdrfield->value_len = value_len >= 0 ? value_len : (int)strlen(value);
            hdrfield->value_cb = NULL;
            hdrfield->ctx = NULL;
            resp->hdr_cnt++;
        }
    }

    void lip_http_resp_add_hdrfield_cbval(t_lip_http_resp *resp,
                                       const char *name, int name_len,
                                       t_lip_http_server_get_resp_hdrfield_cb cb, void *ctx) {
        if (resp->hdr_cnt < LIP_HTTP_SERVER_MAX_RESP_HDR_FIELDS) {
            t_lip_http_resp_field_params *hdrfield = &resp->hdrs[resp->hdr_cnt];
            hdrfield->name = name;
            hdrfield->value = NULL;
            hdrfield->name_len = name_len >= 0 ? name_len : (int)strlen(name);
            hdrfield->value_len = -1;
            hdrfield->value_cb = cb;
            hdrfield->ctx = ctx;
            resp->hdr_cnt++;
        }
    }

    void lip_http_resp_add_hdrfield_intval(t_lip_http_resp *resp, const char *name, int name_len, int value) {
        if (resp->hdr_cnt < LIP_HTTP_SERVER_MAX_RESP_HDR_FIELDS) {
            t_lip_http_resp_field_params *hdrfield = &resp->hdrs[resp->hdr_cnt];
            hdrfield->name = name;
            hdrfield->value = NULL;
            hdrfield->name_len = name_len >= 0 ? name_len : (int)strlen(name);
            hdrfield->value_len = -1;
            hdrfield->value_cb = f_hdrfield_int_cb;
            hdrfield->ctx = (void*)value;
            resp->hdr_cnt++;
        }
    }

    void lip_http_resp_set_data_buf(t_lip_http_resp *resp, const uint8_t *data, int data_len) {
        resp->data_cb = NULL;
        resp->data = data;
        resp->data_len = data_len;
    }

    void lip_http_resp_set_data_cb(t_lip_http_resp *resp, t_lip_http_server_get_resp_data_cb cb,
                                   int data_len,  void *ctx) {
        resp->data_cb = cb;
        resp->data_len = data_len;
        resp->data_ctx = ctx;
    }

    void lip_http_resp_set_finish_cb(t_lip_http_resp *resp, t_lip_http_server_resp_finish_cb cb, void *ctx) {
        resp->finish_cb = cb;
        resp->finish_ctx = ctx;
    }

    void lip_http_resp_set_status_code(t_lip_http_resp *resp, t_lip_http_status_code status) {
        resp->status = status;
    }

#endif








