Index: EcnlProtoTool/trunk/ntshell/webserver/httpd.c
===================================================================
--- EcnlProtoTool/trunk/ntshell/webserver/httpd.c	(revision 278)
+++ EcnlProtoTool/trunk/ntshell/webserver/httpd.c	(revision 279)
@@ -54,11 +54,45 @@
 #include <string.h>
 #include <stdlib.h>
+#include "kernel_cfg.h"
+#include "tinet_cfg.h"
 #include "syssvc/syslog.h"
 #include "http-strings.h"
 #include "netinet/in.h"
-#include "arduino.h"
+#include "netinet/in_var.h"
+#include "httpd.h"
+#include "httpd-fs.h"
+#include "http-strings.h"
+#include "base64.h"
+#include "sha1.h"
+#include "ntstdio.h"
 
 #define TRUE 1
 #define FALSE 0
+
+extern ntstdio_t ntstdio;
+SYSTIM httpd_time;
+struct httpd_state *uploding;
+extern char command[256];
+
+extern int execute_command(int wait);
+
+/*  TCP éåä¿¡ã¦ã£ã³ããããã¡  */
+uint8_t tcp_swbuf1[TCP_SWBUF_SIZE];
+uint8_t tcp_rwbuf1[TCP_RWBUF_SIZE];
+uint8_t tcp_swbuf2[TCP_SWBUF_SIZE];
+uint8_t tcp_rwbuf2[TCP_RWBUF_SIZE];
+
+#define ISO_nl      0x0a
+#define ISO_space   0x20
+#define ISO_bang    0x21
+#define ISO_percent 0x25
+#define ISO_period  0x2e
+#define ISO_slash   0x2f
+#define ISO_colon   0x3a
+
+struct httpd_state httpd_state[2] = {
+	{ HTTPD1_TASK, TCP_CEPID1 },
+	{ HTTPD2_TASK, TCP_CEPID2 },
+};
 
 #ifndef _MSC_VER
@@ -147,6 +181,4 @@
 	return ncpy;
 }
-
-extern struct httpd_state *uploding;
 
 int websvr_message_begin(http_parser *p)
@@ -306,5 +338,5 @@
 ã¯SDã«ã¼ã
 			s->filename[0] = '1';
-			syslog(LOG_NOTICE, "create:    %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
+			ntstdio_printf(&ntstdio, "create:    %s.%d %s\n", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
 			if (!httpd_fs_create(s->filename, &s->file)) {
 				goto error;
@@ -314,5 +346,5 @@
 		}
 		else if (strcmp(s->filename, uploding->filename) == 0) {
-			syslog(LOG_NOTICE, "collision: %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
+			ntstdio_printf(&ntstdio, "collision: %s.%d %s\n", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
 			goto error;
 		}
@@ -359,9 +391,10 @@
 
 	if (s->message.body_is_final) {
-		syslog(LOG_NOTICE, "close:     %s.%d %s", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
+		ntstdio_printf(&ntstdio, "close:     %s.%d %s\n", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
 		httpd_fs_close(&s->file);
 		memset(&s->file, 0, sizeof(s->file));
 
-		strcpy_s(RubyFilename, sizeof(RubyFilename), s->filename);
+		strcpy_s(command, sizeof(command), "mruby -b ");
+		strcat_s(command, sizeof(command), s->filename);
 		s->reset = 1;
 
@@ -413,2 +446,552 @@
 	websvr_message_complete,
 };
+
+/*
+ *  ãããã¯ã¼ã¯å±¤ã®é¸æ
+ */
+
+#ifdef SUPPORT_INET6
+
+#define TCP_ACP_CEP(c,r,d,t)	tcp6_acp_cep(c,r,d,t)
+#define IP2STR(s,a)		ipv62str(s,a)
+
+#else	/* of #ifdef SUPPORT_INET6 */
+
+#ifdef SUPPORT_INET4
+
+#define TCP_ACP_CEP(c,r,d,t)	tcp_acp_cep(c,r,d,t)
+#define IP2STR(s,a)		ip2str(s,a)
+
+#endif	/* of #ifdef SUPPORT_INET4 */
+
+#endif	/* of #ifdef SUPPORT_INET6 */
+
+struct httpd_state *get_httpd(ID cepid)
+{
+	for (int i = 0; i < 2; i++) {
+		if (httpd_state[i].cepid != cepid)
+			continue;
+
+		return &httpd_state[i];
+	}
+	return NULL;
+}
+
+struct websocket *websocket_getws(ID wbsid)
+{
+	for (int i = 0; i < 2; i++) {
+		if (httpd_state[i].websocket.wbsid != wbsid)
+			continue;
+
+		return &httpd_state[i].websocket;
+	}
+	return NULL;
+}
+
+void send_file(struct httpd_state *s)
+{
+	char *buf;
+	int len, slen;
+
+	while (s->file.len > 0) {
+		slen = tcp_get_buf(s->cepid, (void **)&buf, TMO_FEVR);
+		if (slen < 0) {
+			syslog(LOG_ERROR, "send_file#tcp_get_buf(%s.%d) => %d", s->addr, ((T_IPV4EP *)s->dst)->portno, slen);
+			s->state = STATE_CLOSING;
+			break;
+		}
+		if (slen == 0)
+			return;
+
+		len = s->file.len;
+		if (len > slen)
+			len = slen;
+
+		len = httpd_fs_read(&s->file, buf, len);
+		if (len <= 0) {
+			syslog(LOG_ERROR, "send_file#httpd_fs_read(%s.%d) => %d", s->addr, ((T_IPV4EP *)s->dst)->portno, len);
+			break;
+		}
+
+		s->file.len -= len;
+		s->file.pos += len;
+
+		if ((slen = tcp_snd_buf(s->cepid, len)) != E_OK) {
+			syslog(LOG_ERROR, "send_file#tcp_snd_buf(%s.%d) => %d", s->addr, ((T_IPV4EP *)s->dst)->portno, slen);
+			s->state = STATE_CLOSING;
+			break;
+		}
+	}
+
+	ntstdio_printf(&ntstdio, "close:     %s.%d %s\n", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
+	httpd_fs_close(&s->file);
+	s->file.len = 0;
+	s->file.pos = 0;
+
+	s->out.state = OUT_STATE_SEND_END;
+}
+
+void send_data(struct httpd_state *s)
+{
+	char *buf;
+	int len, slen;
+
+	while (s->response_len > 0) {
+		slen = tcp_get_buf(s->cepid, (void **)&buf, TMO_FEVR);
+		if (slen < 0) {
+			syslog(LOG_ERROR, "send_data#tcp_get_buf(%s.%d) => %d", s->addr, ((T_IPV4EP *)s->dst)->portno, slen);
+			s->state = STATE_CLOSING;
+			break;
+		}
+		if (slen == 0)
+			return;
+
+		len = s->response_len;
+		if (len > slen)
+			len = slen;
+
+		memcpy(buf, &s->response_body[s->response_pos], len);
+
+		s->response_len -= len;
+		s->response_pos += len;
+
+		if ((slen = tcp_snd_buf(s->cepid, len)) != E_OK) {
+			syslog(LOG_ERROR, "send_data#tcp_snd_buf(%s.%d) => %d", s->addr, ((T_IPV4EP *)s->dst)->portno, slen);
+			s->state = STATE_CLOSING;
+			break;
+		}
+	}
+
+	s->response_body = NULL;
+	s->response_len = 0;
+	s->response_pos = 0;
+
+	s->out.state = OUT_STATE_SEND_END;
+}
+
+void send_headers(struct httpd_state *s, const char *statushdr)
+{
+	int len;
+	char *ptr;
+
+	len = strlen(statushdr);
+	tcp_snd_dat(s->cepid, (void *)statushdr, len, TMO_FEVR);
+
+	if ((s->filename[0] == '0') && (s->file.len > 0)) {
+		len = sizeof(http_content_encoding_gzip) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_encoding_gzip, len, TMO_FEVR);
+	}
+
+	if (s->file.redirect) {
+		len = sizeof(http_location) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_location, len, TMO_FEVR);
+		if (s->filename[0] == '1') {
+			len = 2;
+			tcp_snd_dat(s->cepid, "/~", len, TMO_FEVR);
+		}
+		len = strlen(s->filename);
+		tcp_snd_dat(s->cepid, s->filename, len, TMO_FEVR);
+		if (s->query != NULL) {
+			tcp_snd_dat(s->cepid, "?", 1, TMO_FEVR);
+			len = strlen(s->query);
+			tcp_snd_dat(s->cepid, s->query, len, TMO_FEVR);
+		}
+		len = 2;
+		tcp_snd_dat(s->cepid, "\r", len, TMO_FEVR);
+	}
+
+	ptr = strrchr(s->filename, ISO_period);
+	if (ptr == NULL) {
+		len = sizeof(http_content_type_binary) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_binary, len, TMO_FEVR);
+	}
+	else if (strncmp(http_html, ptr, sizeof(http_html) - 1) == 0 ||
+		strncmp(http_htm, ptr, sizeof(http_htm) - 1) == 0) {
+		len = sizeof(http_content_type_html) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_html, len, TMO_FEVR);
+	}
+	else if (strncmp(http_css, ptr, sizeof(http_css) - 1) == 0) {
+		len = sizeof(http_content_type_css) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_css, len, TMO_FEVR);
+	}
+	else if (strncmp(http_js, ptr, sizeof(http_js) - 1) == 0) {
+		len = sizeof(http_content_type_js) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_js, len, TMO_FEVR);
+	}
+	else if (strncmp(http_json, ptr, sizeof(http_json) - 1) == 0) {
+		len = sizeof(http_content_type_json) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_json, len, TMO_FEVR);
+	}
+	else if (strncmp(http_png, ptr, sizeof(http_png) - 1) == 0) {
+		len = sizeof(http_content_type_png) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_png, len, TMO_FEVR);
+	}
+	else if (strncmp(http_gif, ptr, sizeof(http_gif) - 1) == 0) {
+		len = sizeof(http_content_type_gif) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_gif, len, TMO_FEVR);
+	}
+	else if (strncmp(http_jpg, ptr, sizeof(http_jpg) - 1) == 0) {
+		len = sizeof(http_content_type_jpg) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_jpg, len, TMO_FEVR);
+	}
+	else if (strncmp(http_svg, ptr, sizeof(http_svg) - 1) == 0) {
+		len = sizeof(http_content_type_svg) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_svg, len, TMO_FEVR);
+	}
+	else if (strncmp(http_text, ptr, sizeof(http_text) - 1) == 0) {
+		len = sizeof(http_content_type_text) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_text, len, TMO_FEVR);
+	}
+	else if (strncmp(http_eot, ptr, sizeof(http_eot) - 1) == 0) {
+		len = sizeof(http_content_type_eot) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_eot, len, TMO_FEVR);
+	}
+	else if (strncmp(http_ttf, ptr, sizeof(http_ttf) - 1) == 0) {
+		len = sizeof(http_content_type_ttf) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_ttf, len, TMO_FEVR);
+	}
+	else if (strncmp(http_woff, ptr, sizeof(http_woff) - 1) == 0) {
+		len = sizeof(http_content_type_woff) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_woff, len, TMO_FEVR);
+	}
+	else if (strncmp(http_woff2, ptr, sizeof(http_woff2) - 1) == 0) {
+		len = sizeof(http_content_type_woff2) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_woff2, len, TMO_FEVR);
+	}
+	else if (strncmp(http_ico, ptr, sizeof(http_ico) - 1) == 0) {
+		len = sizeof(http_content_type_ico) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_ico, len, TMO_FEVR);
+	}
+	else {
+		len = sizeof(http_content_type_plain) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_type_plain, len, TMO_FEVR);
+	}
+
+	if (s->file.len > 0) {
+		len = sizeof(http_content_length) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_content_length, len, TMO_FEVR);
+		ntstdio_snprintf(s->temp, sizeof(s->temp), "%d\r\n", s->file.len);
+		tcp_snd_dat(s->cepid, (void *)s->temp, strlen(s->temp), TMO_FEVR);
+	}
+
+	if (s->message.should_keep_alive && s->reset == 0) {
+		len = sizeof(http_connection_keep_alive) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_connection_keep_alive, len, TMO_FEVR);
+	}
+	else {
+		len = sizeof(http_connection_close) - 1;
+		tcp_snd_dat(s->cepid, (void *)http_connection_close, len, TMO_FEVR);
+	}
+
+	tcp_snd_dat(s->cepid, (void *)http_crnl, 2, TMO_FEVR);
+
+	if (s->filename != NULL) {
+		s->out.state = OUT_STATE_SEND_FILE;
+	}
+	else {
+		s->out.state = OUT_STATE_SEND_DATA;
+	}
+}
+
+void handle_output(struct httpd_state *s)
+{
+	s->out.wait = false;
+
+	switch (s->out.state) {
+	case OUT_STATE_WAIT_REQUEST:
+		s->out.wait = true;
+		break;
+	case OUT_STATE_OPEN_GET_FILE:
+		ntstdio_printf(&ntstdio, "open:      %s.%d %s\n", s->addr, ((T_IPV4EP *)s->dst)->portno, s->filename);
+		if (!httpd_fs_open(s->filename, sizeof(s->message.request_url), &s->file)) {
+			s->filename = NULL;
+			s->response_body = http_content_404;
+			s->response_pos = 0;
+			s->response_len = sizeof(http_content_403) - 1;
+			s->out.statushdr = http_header_404;
+		}
+		else {
+			s->out.statushdr = s->file.redirect ? http_header_301 : http_header_200;
+		}
+		s->out.state = OUT_STATE_SEND_HEADER;
+		break;
+	case OUT_STATE_WAIT_POST_BODY:
+		s->out.wait = true;
+		break;
+	case OUT_STATE_BODY_RECEIVED:
+		s->out.statushdr = http_header_200;
+		s->out.state = OUT_STATE_SEND_HEADER;
+		break;
+	case OUT_STATE_SEND_HEADER:
+		send_headers(s, s->out.statushdr);
+		break;
+	case OUT_STATE_SEND_FILE:
+		send_file(s);
+		break;
+	case OUT_STATE_SEND_DATA:
+		send_data(s);
+		break;
+	case OUT_STATE_SEND_END:
+		s->out.wait = true;
+		if (s->message.should_keep_alive && s->reset == 0) {
+			s->out.state = OUT_STATE_WAIT_REQUEST;
+		}
+		else {
+			s->state = STATE_CLOSING;
+		}
+		break;
+	}
+}
+
+void send_ws_headers(struct httpd_state *s, const char *statushdr)
+{
+	int len;
+
+	len = strlen(statushdr);
+	tcp_snd_dat(s->cepid, (void *)statushdr, len, TMO_FEVR);
+
+	len = sizeof(http_upgrade) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_upgrade, len, TMO_FEVR);
+	len = strlen(s->message.upgrade);
+	tcp_snd_dat(s->cepid, s->message.upgrade, len, TMO_FEVR);
+	len = sizeof(http_crnl) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_crnl, len, TMO_FEVR);
+
+	len = sizeof(http_connection) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_connection, len, TMO_FEVR);
+	len = strlen(s->message.connection);
+	tcp_snd_dat(s->cepid, s->message.connection, len, TMO_FEVR);
+	len = sizeof(http_crnl) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_crnl, len, TMO_FEVR);
+
+	len = sizeof(http_sec_websocket_accept) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_sec_websocket_accept, len, TMO_FEVR);
+	len = strlen(s->message.response_key);
+	tcp_snd_dat(s->cepid, s->message.response_key, len, TMO_FEVR);
+	len = sizeof(http_crnl) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_crnl, len, TMO_FEVR);
+
+	len = sizeof(http_sec_websocket_protocol) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_sec_websocket_protocol, len, TMO_FEVR);
+	len = strlen(s->message.sec_websocket_protocol);
+	tcp_snd_dat(s->cepid, s->message.sec_websocket_protocol, len, TMO_FEVR);
+	len = sizeof(http_crnl) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_crnl, len, TMO_FEVR);
+
+	len = sizeof(http_crnl) - 1;
+	tcp_snd_dat(s->cepid, (void *)http_crnl, len, TMO_FEVR);
+}
+
+void send_ws_data(struct httpd_state *s)
+{
+	char *buf;
+	int slen;
+
+	slen = tcp_get_buf(s->cepid, (void **)&buf, TMO_FEVR);
+	if (slen < 0) {
+		syslog(LOG_ERROR, "send_ws_data#tcp_get_buf(%s.%d) => %d", s->addr, ((T_IPV4EP *)s->dst)->portno, slen);
+		return;
+	}
+
+	websocket_output(&s->websocket, buf, slen);
+}
+
+void handle_ws_output(struct httpd_state *s)
+{
+	char shaHash[20];
+	SHA_CTX sha1;
+	int len;
+
+	strlncat(s->message.response_key, sizeof(s->message.response_key),
+		s->message.sec_websocket_key, sizeof(s->message.sec_websocket_key));
+	len = strlncat(s->message.response_key, sizeof(s->message.response_key),
+		http_websocket_guid, sizeof(http_websocket_guid));
+	memset(shaHash, 0, sizeof(shaHash));
+	SHA1_Init(&sha1);
+	SHA1_Update(&sha1, (sha1_byte *)s->message.response_key, len);
+	SHA1_Final((sha1_byte *)shaHash, &sha1);
+	base64_encode((unsigned char *)s->message.response_key,
+		sizeof(s->message.response_key), (unsigned char *)shaHash, sizeof(shaHash));
+
+	send_ws_headers(s, http_header_101);
+
+	s->message.response_key[0] = '\0';
+
+	do {
+		while (!websocket_newdata(&s->websocket))
+			slp_tsk();
+
+		send_ws_data(s);
+	} while ((s->state == STATE_CONNECTED) && (!s->close_req));
+	s->state = STATE_DISCONNECTED;
+	websocket_destroy(&s->websocket);
+	s->close_req = 0;
+
+	s->state = STATE_CLOSING;
+}
+
+void handle_input(struct httpd_state *s)
+{
+	size_t done;
+	int len;
+
+	s->in.wait = false;
+
+	switch (s->in.state) {
+	case IN_STATE_START:
+		http_parser_init(&s->parser, HTTP_REQUEST);
+		s->in.state = IN_STATE_REQUEST;
+		break;
+	case IN_STATE_REQUEST:
+	case IN_STATE_RESPONSE:
+	case IN_STATE_UPLOAD:
+		if ((len = tcp_rcv_buf(s->cepid, (void **)&s->in.data, TMO_POL)) <= 0) {
+			if ((len == E_TMOUT) || (len == 0)) {
+				// 3ç§ã¯å¾
+ã¤
+				//if (httpd_time - s->in.timer < 30000000) {
+					s->in.wait = true;
+					break;
+				//}
+			}
+			syslog(LOG_ERROR, "handle_input#tcp_rcv_buf#%d(%s.%d) => %d", s->in.state, s->addr, ((T_IPV4EP *)s->dst)->portno, len);
+			uploding = NULL;
+			s->state = STATE_CLOSING;
+			return;
+		}
+		done = http_parser_execute(&s->parser, &websvr_settings, s->in.data, len);
+		tcp_rel_buf(s->cepid, done);
+		if (s->parser.http_errno != HPE_OK) {
+			syslog(LOG_ERROR, "http_parser error %s.%d => %d", s->addr, ((T_IPV4EP *)s->dst)->portno, s->parser.http_errno);
+			uploding = NULL;
+			s->state = STATE_CLOSING;
+			return;
+		}
+
+		s->parse_pos = done;
+		s->parse_len = len - done;
+		break;
+	case IN_STATE_UPLOAD_WAIT:
+		if (uploding != NULL) {
+			s->in.wait = true;
+		}
+		else {
+			uploding = s;
+			s->in.state = IN_STATE_UPLOAD;
+		}
+		break;
+	case IN_STATE_WEBSOCKET:
+		if (s->parse_len <= 0) {
+			if ((len = tcp_rcv_buf(s->cepid, (void **)&s->in.data, TMO_POL)) <= 0) {
+				if ((len == E_TMOUT) || (len == 0)) {
+					s->in.wait = true;
+					break;
+				}
+				syslog(LOG_ERROR, "handle_input#tcp_rcv_buf#%d(%s.%d) => %d", s->in.state, s->addr, ((T_IPV4EP *)s->dst)->portno, len);
+				s->state = STATE_CLOSING;
+				break;
+			}
+
+			s->parse_pos = 0;
+			s->parse_len = len;
+		}
+		else
+			len = s->parse_len;
+		done = websocket_input(&s->websocket, (void *)s->in.data, s->parse_len);
+		tcp_rel_buf(s->cepid, done);
+		if ((done != 0) || (s->websocket.rstate.opecode == connection_close)) {
+			s->close_req = 1;
+			s->state = STATE_CLOSING;
+			break;
+		}
+		s->parse_pos = done;
+		s->parse_len -= done;
+		break;
+	case IN_STATE_END:
+		s->in.wait = true;
+		break;
+	default:
+		s->state = STATE_CLOSING;
+		break;
+	}
+}
+
+/*
+ *  ãã³ãã­ãã­ã³ã°ã³ã¼ã«ã®ã³ã¼ã«ããã¯é¢æ°
+ */
+ER
+callback_nblk_tcp(ID cepid, FN fncd, void *p_parblk)
+{
+	struct httpd_state *s = get_httpd(cepid);
+
+	if (s == NULL)
+		ntstdio_printf(&ntstdio, "callback_nblk_tcp(%d, %d)\n", fncd, cepid);
+	else
+		ntstdio_printf(&ntstdio, "callback_nblk_tcp(%d, %s.%d)\n", fncd, s->addr, ((T_IPV4EP *)s->dst)->portno);
+
+	return E_PAR;
+}
+
+/*
+ * HTTPãµã¼ãã¼ã¿ã¹ã¯
+ */
+void httpd_task(intptr_t exinf)
+{
+	ER ret, ret2;
+	struct httpd_state *s = &httpd_state[exinf];
+
+	for (;;) {
+		ret2 = get_tim(&httpd_time);
+		if (ret2 != E_OK) {
+			syslog(LOG_ERROR, "get_tim");
+			return;
+		}
+
+		switch (s->state) {
+		case STATE_DISCONNECTED:
+			memset(&s->dst, 0, sizeof(s->dst));
+			if ((ret = TCP_ACP_CEP(s->cepid, TCP_REPID, (T_IPV4EP *)s->dst, TMO_FEVR)) != E_OK) {
+				syslog(LOG_ERROR, "tcp_acp_cep(%d) => %d", s->cepid, ret);
+				tslp_tsk(100);	// TODO
+				s->state = STATE_CLOSING;
+				break;
+			}
+			IP2STR(s->addr, &((T_IPV4EP *)s->dst)->ipaddr);
+			ntstdio_printf(&ntstdio, "connected: %s.%d\n", s->addr, ((T_IPV4EP *)s->dst)->portno);
+			memset(&s->in, 0, sizeof(s->in));
+			memset(&s->out, 0, sizeof(s->out));
+			s->in.timer = httpd_time;
+			s->state = STATE_CONNECTED;
+			break;
+		case STATE_CONNECTED:
+			handle_input(s);
+			handle_output(s);
+			break;
+		case STATE_WEBSOCKET:
+			handle_input(s);
+			handle_ws_output(s);
+			break;
+		case STATE_CLOSING:
+			ntstdio_printf(&ntstdio, "close:     %s.%d\n", s->addr, ((T_IPV4EP *)s->dst)->portno);
+			tcp_sht_cep(s->cepid);
+			tcp_cls_cep(s->cepid, TMO_FEVR);
+
+			if (s->reset) {
+				s->reset = 0;
+				s->state = STATE_RESET;
+			}
+			else {
+				s->state = STATE_DISCONNECTED;
+			}
+			break;
+		case STATE_RESET:
+			execute_command(0);
+			s->state = STATE_DISCONNECTED;
+			break;
+		}
+
+		if (s->in.wait && s->out.wait) {
+			tslp_tsk(100);
+		}
+	}
+}
