/*
    zhttpd.c - Simple http daemon
    Copyright (C) 2011-2020 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/


#include <zhttpd.h>

#include "libziaint.h"

//#include "eprintf.h"
#include <zdebug.h>
#include <zdump.h>
#include <zerror.h>
#include <zfile.h>
#include <zglib.h>
#include <zgptrarray.h>
#include <zpath.h>
#include <zselect.h>
#include <zsha1.h>
#include <zsock.h>
#include <zstr.h>
#include <string.h>

#ifdef Z_HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif

#ifdef Z_MSC
#pragma warning(disable : 4996)
#endif



struct zhttpd *zhttpd_init(struct zselect *zsel, int port, int loopback){
    struct zhttpd *zhttpd;
    struct sockaddr_in sin;
    char errbuf[100];

    zhttpd = g_new0(struct zhttpd, 1);
    zhttpd->zsel = zsel; 
    zhttpd->port = port; 

    zhttpd->sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (zhttpd->sock < 0){
        zinternal("Can't create zhttpd socket");
        goto x;
    } 

    if (z_sock_reuse(zhttpd->sock, 1)){
        zinternal("Can't set SO_REUSEADDR\n");
        goto x;
    }
    
    if (z_sock_nonblock(zhttpd->sock, 1)){
        zinternal("Can't set O_NONBLOCK\n");
        goto x;
    }
        
    memset(&sin, 0, sizeof(struct sockaddr_in));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(zhttpd->port);                              
    if (loopback)    
        sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    else
        sin.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(zhttpd->sock, (struct sockaddr *)&sin, sizeof(sin))) {
		int err = z_sock_errno;
		if (err == EACCES || err == 10013){
			sin.sin_port = htons(zhttpd->port == 80 ? 8080 : 1024 + zhttpd->port);
			if (bind(zhttpd->sock, (struct sockaddr *)&sin, sizeof(sin))) {
				int err = z_sock_errno;
				zinternal("Can't bind port %d or %d, %s\n", zhttpd->port, ntohs(sin.sin_port), z_sock_strerror());
				goto x;
			}
		}else{
			int err = z_sock_errno;
			zinternal("Can't bind port %d, %s\n", zhttpd->port, z_sock_strerror());
			goto x;
		}
    }
    
    if (listen(zhttpd->sock, 10)){
        zinternal("Can't listen on socket %d, tcp port %d \n", zhttpd->sock, zhttpd->port);
        goto x;
    }
    
    zselect_set(zsel, zhttpd->sock, zhttpd_accept_handler, NULL, NULL, zhttpd);
    zhttpd->conns = g_ptr_array_new();
	g_ptr_array_set_free_func(zhttpd->conns, (GDestroyNotify)zhttpd_free_conn);
    zhttpd->bindings = g_ptr_array_new();
	g_ptr_array_set_free_func(zhttpd->bindings, (GDestroyNotify)zhttpd_free_binding);
	dbg("zhttpd active on TCP port %d\n", ntohs(sin.sin_port));

    return zhttpd;
x:;
    if (zhttpd->sock >= 0) closesocket(zhttpd->sock);
    zhttpd->sock = -1;
    g_free(zhttpd);
    return NULL;
}

void zhttpd_handlers(struct zhttpd *httpd, void(*conn_new)(struct zhttpconn *conn), void(*conn_close)(struct zhttpconn *conn)){
	httpd->conn_new = conn_new;
	httpd->conn_close = conn_close;
}


void zhttpd_free(struct zhttpd *zhttpd){
    if (!zhttpd) return;

    if (zhttpd->sock >= 0) {
        zselect_set(zhttpd->zsel, zhttpd->sock, NULL, NULL, NULL, NULL);
        closesocket(zhttpd->sock);
    }

	g_ptr_array_free(zhttpd->conns, TRUE);
	g_ptr_array_free(zhttpd->bindings, TRUE);

    g_free(zhttpd);
}

void zhttpd_free_conn(struct zhttpconn *conn){
	zhttpd_close_conn(conn);
	if (conn->ws_ping_timer_id > 0) zselect_timer_kill(conn->zhttpd->zsel, conn->ws_ping_timer_id);

    g_string_free(conn->request, TRUE);
	MUTEX_LOCK(conn->response);
    if (conn->response) zbinbuf_free(conn->response);
	MUTEX_UNLOCK(conn->response);
	MUTEX_FREE(conn->response);

	g_free(conn->req_path);
	g_free(conn->req_args);
	g_ptr_array_free(conn->memlist, TRUE);
    if (conn->wsbuf) zbinbuf_free(conn->wsbuf);
    g_free(conn);
}

void zhttpd_close_conn(struct zhttpconn *conn){
	if (conn->sock >= 0) {
		zselect_set(conn->zhttpd->zsel, conn->sock, NULL, NULL, NULL, NULL);
		closesocket(conn->sock);
		conn->sock = -1;
        if (conn->zhttpd->conn_close) conn->zhttpd->conn_close(conn);
	}
}


void zhttpd_free_binding(struct zhttpdbinding *b){

	g_regex_unref(b->regex);
	g_free(b->file_docroot);
	g_free(b);
}


void zhttpd_accept_handler(void *arg){
    int sock;
    socklen_t socklen;
    struct zhttpconn *conn;
	struct zhttpd *zhttpd = (struct zhttpd*)arg;
    
    conn = g_new0(struct zhttpconn, 1);
	conn->zhttpd = zhttpd;
	conn->memlist = g_ptr_array_new_with_free_func(g_free);
	conn->response_headers = g_ptr_array_new_with_free_func((GDestroyNotify)zhttpd_free_header);
	MUTEX_INIT(conn->response);

    socklen = sizeof(conn->peer);
    sock = accept(zhttpd->sock, (struct sockaddr *)&conn->peer, &socklen);
    if (!socklen || sock < 0) {
        g_free(conn);
        return;
    }

    //dbg("Accepted socket %d %s:%d\n", sock, inet_ntoa(conn->peer.sin_addr), ntohs(conn->peer.sin_port));

    conn->sock = sock;
    conn->request = g_string_sized_new(500);
	conn->response = zbinbuf_init();
    
    zselect_set(zhttpd->zsel, conn->sock, zhttpd_read_handler, NULL, NULL, conn);
    g_ptr_array_add(zhttpd->conns, conn);
    if (zhttpd->conn_new) zhttpd->conn_new(conn);
}

void zhttpd_read_handler(void *arg){
    struct zhttpconn *conn;
    char s[1030], *c, lf;
    int ret;

    conn = (struct zhttpconn*)arg;
    ret = recv(conn->sock, s, 1024, 0);
    //err=z_sock_errno;
    if (ret <= 0){
        g_ptr_array_remove(conn->zhttpd->conns, conn);
        return;
    }
    s[ret] = '\0';
    g_string_append(conn->request, s);

    lf = 0;
    for (c = conn->request->str; *c != '\0'; c++){
        if (*c == '\r') continue;
        if (*c != '\n') {
            lf = 0;
            continue;
        }
        lf++;
        if (lf < 2) continue;
        break;
    }
    if (lf < 2) return;
    
    conn->req_data = c + 1;
    g_strlcpy(s, conn->request->str, sizeof(s));
    c = strchr(s, '\r');
    if (c != NULL) *c = '\0';
    if (c != NULL && *c=='\n') c++;
    dbg("HTTP %s: %s\n", inet_ntoa(conn->peer.sin_addr), s);
	zselect_set_read(conn->zhttpd->zsel, conn->sock, NULL, arg);

    if (strncasecmp(conn->request->str, "GET ", 4) == 0){
        zhttpd_get(conn);
    }else if (strncasecmp(conn->request->str, "POST ", 5) == 0){
        zhttpd_post(conn);
    }else{
        zhttpd_response(conn, 400, NULL);
		MUTEX_LOCK(conn->response); // v dobe cteni asi nemusi byt
		zbinbuf_sprintfa(conn->response, "<html><body>Bad request</body></html>");
		MUTEX_UNLOCK(conn->response);

    }
/*    for (i = 0; i < 1000; i++){
        g_string_append_c(conn->response, '0' + (i / 100));
    }*/


	zhttpd_write_response_header(conn);
	zselect_set_write(conn->zhttpd->zsel, conn->sock, zhttpd_write_handler, conn);
}

void zhttpd_write_handler(void *arg){
    struct zhttpconn *conn;
    int ret, len;
	int remove_after = 0;

    conn = (struct zhttpconn*)arg;

    
	MUTEX_LOCK(conn->response);
	len = conn->response->len - conn->response_i;
    //printf("len=%d\n", len);
    if (len > 1400) len = 1400;
	if (len > 0)
	{
		ret = send(conn->sock, conn->response->buf + conn->response_i, len, 0);
		int err = z_sock_errno;
        //printf("send(%d) = %d\n", len, ret);
		if (ret <= 0){	
			if (err != EWOULDBLOCK){
				if (conn->is_stream){
					zhttpd_close_conn(conn);
				}
				else{
					g_ptr_array_remove(conn->zhttpd->conns, conn);
				}
			}
		}else{
			if (conn->is_stream){
				zbinbuf_erase(conn->response, 0, ret); 
			}else{
				conn->response_i += ret;
			}
		}
	}else{
		void *read_handler = zselect_get(conn->zhttpd->zsel, conn->sock, H_READ);
		if (read_handler == NULL) {
			if (conn->is_stream){
				zselect_set_write(conn->zhttpd->zsel, conn->sock, NULL, conn);
			}else{
				remove_after = 1;
			}
		}else{
			zselect_set_write(conn->zhttpd->zsel, conn->sock, NULL, conn);
		}
    }

	MUTEX_UNLOCK(conn->response);
	if (remove_after) g_ptr_array_remove(conn->zhttpd->conns, conn);


}

/*int zhttpd_flush(struct zhttpconn *conn){
    int ret, len;
    len = conn->response->len - conn->response_i;
	ret = send(conn->sock, conn->response->buf + conn->response_i, len, 0);
	if (ret > 0) conn->response_i += ret;
	return ret;
} */

void zhttpd_write_response_header(struct zhttpconn *conn){
	int i;
    char *ss;
	GString *sb;

    switch (conn->status){
        case 101: ss = "Switching Protocols"; break;
        case 200: ss = "OK"; break;
        case 405: ss = "Not Found"; break;
        case 500: ss = "Internal server error"; break;
        default: ss = "Unknown status"; break;
    }

	
	sb = g_string_sized_new(1024);
	g_string_append_printf(sb, "HTTP/1.1 %d %s\r\n", conn->status, ss);

	for (i = 0; i < (int)conn->response_headers->len; i++){
		struct zhttpheader *h = (struct zhttpheader *)g_ptr_array_index(conn->response_headers, i);
		g_string_append_printf(sb, "%s: %s\r\n", h->key, h->value);
	}
	g_string_append(sb, "\r\n");
	MUTEX_LOCK(conn->response);
	zbinbuf_prepend(conn->response, sb->str, sb->len);
	MUTEX_UNLOCK(conn->response);
	g_string_free(sb, TRUE);
}

void zhttpd_response(struct zhttpconn *conn, int status, char *contenttype){
	char *server;

	conn->status = status;

	server = zhttpd_get_header(conn, "Server", NULL);
	if (server != NULL) zhttpd_add_header(conn, "Server", debug_msg_title);

	zhttpd_add_header(conn, "Connection", "close");
	if (contenttype) zhttpd_add_header(conn, "Content-Type", contenttype);
}

void zhttpd_add_header(struct zhttpconn *conn, char *key, char *value){
	int i;
	struct zhttpheader *h;
	for (i = 0; i < (int)conn->response_headers->len; i++){
		 h = (struct zhttpheader *)g_ptr_array_index(conn->response_headers, i);
		 if (strcasecmp(h->key, key) == 0) {   
			 g_free(h->value);
			 h->value = g_strdup(value);
			 return;
		 }
	}

	h = g_new0(struct zhttpheader, 1);
	h->key = g_strdup(key);
	h->value = g_strdup(value);
	g_ptr_array_add(conn->response_headers, h);
}

void zhttpd_free_header(struct zhttpheader *header){
	if (!header) return;
	g_free(header->key);
	g_free(header->value);
	g_free(header);
}



void zhttpd_get(struct zhttpconn *conn){
    char *c, *page;
	int i;

	//char dir[256];
	//GetCurrentDirectory(256, dir);

    page = g_strdup(conn->request->str + strlen("GET") + 1);
    while (*page == ' ') page++;
    c = strchr(page, ' ');
    if (c != NULL) *c = '\0';

	g_free(conn->req_path);
	g_free(conn->req_args);
	z_split2(page, '?', &conn->req_path, &conn->req_args, ZSPL_NSTRIP);
	if (!conn->req_path) conn->req_path = g_strdup(page);

	if (strcmp(conn->req_path, "/") == 0){
		g_free(conn->req_path);
		conn->req_path = g_strdup("/index.html");
    }

	g_free(page);
	

	for (i = 0; i < (int)conn->zhttpd->bindings->len; i++){
		//GMatchInfo *mi;
		struct zhttpdbinding *b = (struct zhttpdbinding *)g_ptr_array_index(conn->zhttpd->bindings, i);

		if (g_regex_match(b->regex, conn->req_path, (GRegexMatchFlags)0, NULL))
		{
			conn->binding = b;
			b->handler(conn);
			return;
		};
	}

    
	zhttpd_response(conn, 404, "text/plain");
	MUTEX_LOCK(conn->response);
	zbinbuf_sprintfa(conn->response, "Not found");
	MUTEX_UNLOCK(conn->response);
}

void zhttpd_post(struct zhttpconn *conn){
    char *c, *page;
	int i;

    page = g_strdup(conn->request->str + strlen("POST") + 1);
    while (*page == ' ') page++;
    c = strchr(page, ' ');
    if (c != NULL) *c = '\0';

	g_free(conn->req_path);
	g_free(conn->req_args);
	z_split2(page, '?', &conn->req_path, &conn->req_args, ZSPL_NSTRIP);
	if (!conn->req_path) conn->req_path = g_strdup(page);

	/*if (strcmp(conn->req_path, "/") == 0){
		g_free(conn->req_path);
		conn->req_path = g_strdup("/index.html");
    } */

	g_free(page);
	

	for (i = 0; i < (int)conn->zhttpd->bindings->len; i++){
		//GMatchInfo *mi;
		struct zhttpdbinding *b = (struct zhttpdbinding *)g_ptr_array_index(conn->zhttpd->bindings, i);
        //dbg("binding %d '%s' '%s'\n", i, b->regex, conn->req_path);

		if (g_regex_match(b->regex, conn->req_path, (GRegexMatchFlags)0, NULL))
		{
			conn->binding = b;
			b->handler(conn);
			return;
		};
	}

    
	zhttpd_response(conn, 404, "text/plain");
	MUTEX_LOCK(conn->response);
	zbinbuf_sprintfa(conn->response, "Not found");
	MUTEX_UNLOCK(conn->response);
}

struct zhttpdbinding *zhttpd_add_binding(struct zhttpd *zhttpd, char *regex,
	void (*handler)(struct zhttpconn *conn))
{
	struct zhttpdbinding *b = g_new0(struct zhttpdbinding, 1);
	b->zhttpd = zhttpd;
	b->regex = g_regex_new(regex, (GRegexCompileFlags)0, (GRegexMatchFlags)0, NULL);
	b->handler = handler;
	g_ptr_array_add(zhttpd->bindings, b);
	return b;
}

char *zhttpd_arg(struct zhttpconn *conn, char *key, char *def){
	char *args, *c, *token_ptr = NULL, *k, *v;

	if (!conn->req_args) return def;

	args = g_strdup(conn->req_args);
	for (c = strtok_r(args, "&", &token_ptr); 
         c!=NULL; 
         c = strtok_r(NULL, "&", &token_ptr)){

		 z_split2(c, '=', &k, &v, 0);
		 if (!key) continue;

		 if (strcasecmp(key, k) == 0) {
    		 g_free(k);
			 g_ptr_array_add(conn->memlist, v);
			 return v;
		 }
		 g_free(k);
		 g_free(v);
	}
	g_free(args);
	return def;
}

int zhttpd_arg_int(struct zhttpconn *conn, char *key, int def){
	int ret;
	 
	char *s = zhttpd_arg(conn, key, NULL);
	if (s == NULL) return def;

	ret = atoi(s);
	return ret;
}

char *zhttpd_get_header(struct zhttpconn *conn, char *header, char *def){
	char *v;
	char *c = conn->request->str;
	char *lf;
	while(1){
		c = strchr(c, '\n');
		if (c == NULL) break;

		c++;
		if (strncasecmp(c, header, strlen(header)) != 0) continue;

		v = c + strlen(header);
		if (*v != ':') continue;

		v++;
		lf = strchr(c, '\n');
		v = g_strndup(v, lf - v);
		g_ptr_array_add(conn->memlist, v);
		v = z_trim(v);
		return v;
	}
	return NULL;
}

const char *zhttpd_get_mime(char *path){
	const char *ext = z_extension(path);

	if (strcasecmp(ext, ".html") == 0) return "text/html";
	if (strcasecmp(ext, ".css") == 0) return "text/css";
	if (strcasecmp(ext, ".png") == 0)  return "image/png";
	if (strcasecmp(ext, ".json") == 0) return "application/json";
	if (strcasecmp(ext, ".js") == 0) return "text/javascript";

	return "text/plain";
}

void zhttpd_file_handler(struct zhttpconn *conn){
	char *filename, *file, *contenttype;

	filename = g_strdup_printf("%s/%s", "../www", conn->req_path);
	file = zfile_read_textfile(filename);
	g_free(filename);

	if (file == NULL)
	{
		zhttpd_response(conn, 404, "text/plain");
		MUTEX_LOCK(conn->response);
		zbinbuf_sprintfa(conn->response, "Not found");
		MUTEX_UNLOCK(conn->response);
	}
	else{
		contenttype = g_strdup(zhttpd_get_mime(conn->req_path));
		zhttpd_response(conn, 200, contenttype);
		MUTEX_LOCK(conn->response);
		zbinbuf_append(conn->response, file);
		MUTEX_UNLOCK(conn->response);
		g_free(contenttype);
		g_free(file);
	}

}

void zhttpd_ws_handshake_handler(struct zhttpconn *conn){
	GString *gs = g_string_sized_new(100);
//	char *upgrade = zhttpd_get_header(conn, "Upgrade", NULL);
	char *wskey = zhttpd_get_header(conn, "Sec-WebSocket-Key", NULL);
	char *protocol = zhttpd_get_header(conn, "Sec-WebSocket-Protocol", NULL);
	char reply[21];

	//wskey = "dGhlIHNhbXBsZSBub25jZQ==";

	g_string_append(gs, wskey);
	g_string_append(gs, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
	zsha1(reply, gs->str, gs->len);
	zg_string_eprintf("b", gs, "%b", reply, 20);

	zhttpd_response(conn, 101, NULL);
	zhttpd_add_header(conn, "Upgrade", "websocket");
	zhttpd_add_header(conn, "Connection", "Upgrade");
	zhttpd_add_header(conn, "Sec-WebSocket-Accept", gs->str);
	g_string_free(gs, TRUE);
	if (protocol != NULL) zhttpd_add_header(conn, "Sec-WebSocket-Protocol", protocol);
	zselect_set_read(conn->zhttpd->zsel, conn->sock, zhttpd_ws_read_handler, conn);
	conn->is_ws = 1;
	conn->ws_ping_timer_id = zselect_timer_new(conn->zhttpd->zsel, 30000, zhttpd_ws_ping_timer, conn);

}

void zhttpd_ws_read_handler(void *arg)
{
	struct zhttpconn *conn;
    char s[1030];
    int ret;
	int opcode, len, ptr, i;
	char *key;
	char *packet;

    conn = (struct zhttpconn*)arg;
    ret = recv(conn->sock, s, 1024, 0);
    //err=z_sock_errno;
    if (ret <= 0){
        g_ptr_array_remove(conn->zhttpd->conns, conn);
        return;
    }

	if (!conn->wsbuf) conn->wsbuf = zbinbuf_init();
	zbinbuf_append_bin(conn->wsbuf, s, ret);

	while (conn->wsbuf->len >= 6) // have enough data
	{
		ptr = 2;
		opcode = conn->wsbuf->buf[0] & 0x0f; // ZWS_xxx
		if ((conn->wsbuf->buf[1] & 0x80) == 0) {
			g_ptr_array_remove(conn->zhttpd->conns, conn); // RFC6455 5.1
			return;
		}

		if ((conn->wsbuf->buf[1] & 0x7f) <= 125) {
			len = conn->wsbuf->buf[1] & 0x7f;
		}else if ((conn->wsbuf->buf[1] & 0x7f) == 126) {
			len = ((unsigned char)conn->wsbuf->buf[2]) * 256 + ((unsigned char)conn->wsbuf->buf[3]);
			ptr += 2;
		}else if ((conn->wsbuf->buf[1] & 0x7f) == 127) {
			zinternal("Websocket messages > 64kB not supported");
			return;
		}

		if (ptr	+ 4 + len > conn->wsbuf->len) return; // not full packet

		key = conn->wsbuf->buf + ptr;
		ptr += 4;

		packet = (char *)g_malloc(len + 1);

		for (i = 0; i < len; i++){
			packet[i] = conn->wsbuf->buf[ptr + i] ^ key[i % 4];
		}
		packet[i] = '\0';
		switch(opcode){
			case ZWS_CONTINUE:
				zinternal("Websocket fragmentation not supported");
				break;
			case ZWS_TEXT: 
			case ZWS_BINARY:
				if (conn->binding->ws_onmessage) conn->binding->ws_onmessage(conn, opcode, packet, len);
				break;
			case ZWS_CLOSE:
				zhttpd_ws_send(conn, ZWS_CLOSE, packet, len);
				break;
			case ZWS_PING:
				zhttpd_ws_send(conn, ZWS_PONG, packet, len);
				break;
			case ZWS_PONG:
				break;
			default:
				zinternal("Websocket opcode %d not supported");
				break;
		}
		g_free(packet);

		zbinbuf_erase(conn->wsbuf, 0, ptr + len);
	}
}

void zhttpd_ws_send(struct zhttpconn *conn, int opcode, char *s, int len){
	unsigned char b;
	uint16_t u16;

	if (len < 0) len = strlen(s);

	MUTEX_LOCK(conn->response);
	b = opcode | 0x80; // FIN
	zbinbuf_append_bin(conn->response, &b, 1);
	if (len <= 125){
		b = len;
		zbinbuf_append_bin(conn->response, &b, 1);
	}else if (len < 65536){
		b = 126;
		zbinbuf_append_bin(conn->response, &b, 1);
	    u16 = htons(len);
		zbinbuf_append_bin(conn->response, &u16, 2);
	}else{
		zinternal("Websocket messages > 64KB not supported");
	}	

	zbinbuf_append_bin(conn->response, s, len);
	MUTEX_UNLOCK(conn->response);

	zselect_set_write(conn->zhttpd->zsel, conn->sock, zhttpd_write_handler, conn);
}


void zhttpd_ws_send_all(struct zhttpd *zhttpd, int opcode, char *s, int len){
	int i;

	for (i = 0; i < (int)zhttpd->conns->len; i++){
		struct zhttpconn *conn = (struct zhttpconn *)g_ptr_array_index(zhttpd->conns, i);
		if (!conn->is_ws) continue;

		zhttpd_ws_send(conn, opcode, s, len);
	}
}

void zhttpd_ws_ping_timer(void *arg){
	struct zhttpconn *conn = (struct zhttpconn*)arg;

	MUTEX_LOCK(conn->response);
	zbinbuf_append_bin(conn->response, "\x89\0", 2);
	MUTEX_UNLOCK(conn->response);

	conn->ws_ping_timer_id = zselect_timer_new(conn->zhttpd->zsel, 30000, zhttpd_ws_ping_timer, conn);
	zselect_set_write(conn->zhttpd->zsel, conn->sock, zhttpd_write_handler, conn);

}



void zhttpd_write(struct zhttpconn *conn, const void *data, int len){
    if (conn->sock == -1) return;
	MUTEX_LOCK(conn->response);
	zbinbuf_append_bin(conn->response, data, len);
	MUTEX_UNLOCK(conn->response);
	zselect_set_write(conn->zhttpd->zsel, conn->sock, zhttpd_write_handler, conn);
}

int http_response_buf_len(struct zhttpconn *conn){
	MUTEX_LOCK(conn->response);
	int len = conn->response->len;
	MUTEX_UNLOCK(conn->response);
	return len;
}
