/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#define STREAM_LIB

#include	<fcntl.h>

#include	"machine/include.h"
#include	"lock_level.h"
#include	"memory_debug.h"
#include	"stream.h"
#include	"task.h"
#include	"utils.h"
#include	"XLoHTTP.h"
#include	"change_endian.h"
#include	"machine/err.h"
#include	"client.h"
#include	"s_buf.h"

#define DONT_KEEP_ALIVE_INTERVAL	15

typedef struct get_buf {
	struct get_buf *	next;
	char *			str;
} GET_BUF;


typedef struct CHUNK_READ_LIST {
	struct CHUNK_READ_LIST * next;
	STREAM * stream;
	int rest;
} CHUNK_READ_LIST;

CHUNK_READ_LIST * chunk_read_list;
SEM chunk_read_lock;

extern SEM stream_lock;
SEM XLoHTTP_lock,scan_lock;

int s_close_XLoHTTP();
int s_write_XLoHTTP();
int s_read_XLoHTTP();
int s_flush_XLoHTTP();
STREAM * s_error_stream();
int s_error();
int s_get_socketip_XLoHTTP();

int off_keep_alive_mode = 0;
int dont_keep_alive_mode;

void XLoHTTP_task();

S_TABLE s_XLoHTTP_table = {
	'x',
	0,
	{0,0},
	0,
	s_error_stream,
	s_close_XLoHTTP,
	s_write_XLoHTTP,
	s_read_XLoHTTP,
	s_flush_XLoHTTP,
	s_error,
	s_error_stream,
	s_get_socketip_XLoHTTP
};

void
init_XLoHTTP_stream()
{
	XLoHTTP_lock = new_lock(LL_XLoHTTP);
	scan_lock = new_lock(LL_XLoHTTP);
	chunk_read_lock = new_lock(LL_XLoHTTP);
	insert_s_table(&s_XLoHTTP_table);
}

CHUNK_READ_LIST *
get_chunk_read(STREAM * s)
{
CHUNK_READ_LIST * list;
	lock_task(chunk_read_lock);
	for ( list = chunk_read_list ; list ; list = list->next )
		if ( list->stream == s )
			break;
	unlock_task(chunk_read_lock, "get_chunk_read");
	return list;
}

int
read_chunk_size(CHUNK_READ_LIST * list)
{
STREAM * s = list->stream;
int chunk_len = 0,er;
char buf;
	for ( ; ; ) {

		er = s_read(s,&buf,1);
		if ( er <= 0 ) {
			return -1;
		}
		if ( buf >= '0' && buf <= '9' ) {
			chunk_len *= 0x10;
			chunk_len += buf - '0';
		} else if ( buf >= 'a' && buf <= 'f' ) {
			chunk_len *= 0x10;
			chunk_len += buf - 'a' + 0xa;
		} else if ( buf >= 'A' && buf <= 'F' ) {
			chunk_len *= 0x10;
			chunk_len += buf - 'A' + 0xa;
		} else {
			break;
		}
	}
	
	do {
		if ( s_read(s,&buf,1) <= 0 )
			return -1;
	} while ( buf != '\n' );

	list->rest = chunk_len;
	return chunk_len;
}

void
chunk_read_start(STREAM * s)
{
CHUNK_READ_LIST * list;
	list = d_alloc(sizeof(CHUNK_READ_LIST));
	lock_task(chunk_read_lock);
	list->next = chunk_read_list;
	chunk_read_list = list;
	unlock_task(chunk_read_lock, "chunk_read_start");
	list->stream = s;
	read_chunk_size(list);
}

void
chunk_read_end(STREAM * s)
{
CHUNK_READ_LIST * list, * prev;
	lock_task(chunk_read_lock);
	for ( prev = NULL, list = chunk_read_list ; list ; prev = list, list = list->next) {
		if ( list->stream == s) {
			if ( prev )
				prev->next = list->next;
			else
				chunk_read_list = list->next;
			d_f_ree(list);
			break;
		}
	}
	unlock_task(chunk_read_lock, "chunk_read_end");
}

int
s_chunk_read(STREAM * s,void * data,int len)
{
int result, _len, total_len = 0;
char crlf[24];
CHUNK_READ_LIST * crl;

	crl = get_chunk_read(s);
	if ( crl == 0 ) {
		return s_read(s,data,len);
	}

	for ( ; ; ) {		
		if ( len - total_len <  crl->rest ) {
			result = en_do(&_len,s_read,s,(char*)data+total_len,len - total_len);
			if ( result <= 0 )
				break;
			total_len += _len;
			crl->rest -= _len;
			result = total_len;
			break;
		}
		
		result = en_do(&_len,s_read,s,(char*)data+total_len,crl->rest);
		if ( result <= 0 )
			break;
		total_len += _len;
		result = en_do(&_len,s_read,s,crlf,2);
		if ( result <= 0 )
			break;

		result = read_chunk_size(crl);
		if ( result < 0 )
			break;
		if ( result == 0 ) {
			result = en_do(&_len,s_read,s,crlf,2);
			if ( result <= 0 ) {
				if (total_len>0) {
					printf("### read_chunk_size timeout : len=%d\n",total_len);
					result = total_len;
				}
				break;
			}
			result = total_len;
			break;
		}

		if ( len == total_len ) {
			result = total_len;
			break;
		}
	}
	return result;
}



void
change_endian_XLoHTTP_HEADER(XLoHTTP_HEADER * h)
{
	change_endian_s(h->seq);
	change_endian_i(h->cockie[0]);
	change_endian_i(h->cockie[1]);
}

int
XoH_checksum(XLoHTTP_HEADER * h)
{
char * ptr;
int i;
int ret;
	ret = 0;
	for ( ptr = (char*)h , i = 0 ; i < sizeof(*h) ; i ++ )
		ret += *ptr++;
	return 0x100 - (ret & 0x0ff);
}

void
s_close_XLoHTTP_func(STREAM * s)
{
	s_close(s);
}

void
s_safe_close(STREAM * s)
{
	new_tick(s_close_XLoHTTP_func,0,(int)s);
}

void
free_con_list(S_XLoHTTP_INFO * info)
{
XoH_REQUEST * r;
	for ( ; info->con_head ; ) {
		r = info->con_head;
		info->con_head = r->next;
		s_safe_close(r->con);
		d_f_ree(r);
	}
	info->con_tail = 0;
}

void
free_GET_BUF(GET_BUF * b)
{
GET_BUF * b1;
	for ( ; b ; ) {
		b1 = b->next;
		if ( b->str )
			d_f_ree(b->str);
		d_f_ree(b);
		b = b1;
	}
}

GET_BUF *
step_GET_BUF(GET_BUF * b)
{
GET_BUF * ret;
	ret = b->next;
	if ( b->str )
		d_f_ree(b->str);
	d_f_ree(b);
	return ret;
}


void
free_HTTP_info(HTTP_INFO * info)
{
	if ( info->dir )
		d_f_ree(info->dir);
	if ( info->method )
		d_f_ree(info->method);
}

int
scan_HTTP_one_line(GET_BUF ** retp,STREAM * con)
{
GET_BUF * ret, ** rp, * b;
char * str;
int er,p;
int start;
	ret = 0;
	rp = &ret;
	for ( ; ; ) {
		str = d_alloc(1);
		p = 0;
		start = 1;
		for ( ; ; ) {
			er = s_read(con,&str[p],1);
			if ( er < 1 )
				return -1;
/*
printf("%c",str[p]);
*/
			switch ( str[p] ) {
			case '\r':
				continue;
			case '\n':
				str[p] = 0;
				if ( p ) {
					b = d_alloc(sizeof(*b));
					b->str = str;
					b->next = 0;
					*rp = b;
				}
				else	d_f_ree(str);

				*retp = ret;
				return 0;
			case ' ':
			case '\t':
				if ( start )
					continue;
				goto next;
			}
			start = 0;
			p ++;
			str = d_re_alloc(str,p+1);
		}
	next:
		str[p] = 0;
		b = d_alloc(sizeof(*b));
		b->str = str;
		b->next = 0;
		*rp = b;
		rp = &b->next;
	}
}

int
scan_HTTP(HTTP_INFO * info,STREAM * con,int flag)
{
GET_BUF * ret, * rp;
char * v, * r;
	info->http_ver = info->http_rev = 0;
	info->method = 0;
	info->dir = 0;
	info->content_length = -1;
	info->err = 200;
	info->flags = 0;


	if ( flag == 0 )
		goto next;

	if ( scan_HTTP_one_line(&ret,con) < 0 )
		return -1;
	if ( ret == 0 )
		return -1;
	info->method = ret->str;
	ret->str = 0;
	ret = step_GET_BUF(ret);
	if ( ret == 0 ) {
		info->dir = 0;
		info->http_ver = 0;
		info->http_rev = 9;
		return 0;
	}
	info->dir = ret->str;
	ret->str = 0;
	ret = step_GET_BUF(ret);
	if ( ret == 0 ) {
		info->http_ver = 0;
		info->http_rev = 9;
		return 0;
	}
	for ( v = ret->str ; *v && *v != '/' ; v ++ );
	if ( *v == 0 ) {
		info->http_ver = 0;
		info->http_rev = 9;
		return 0;
	}
	v ++;
	for ( r = v ; *r && *r != '.' ; r ++ );
	if ( *r == 0 ) {
		info->http_ver = atoi(v);
		info->http_rev = 0;
	}
	else {
		*r = 0;
		r ++;
		info->http_ver = atoi(v);
		info->http_rev = atoi(r);
	}
	free_GET_BUF(ret);

next:
	for ( ; ; ) {
		if ( scan_HTTP_one_line(&ret,con) < 0 )
			return -1;
		if ( ret == 0 )
			break;
		if ( strcasecmp(ret->str,"Content-Length:") == 0 ) {
			ret = step_GET_BUF(ret);
			info->content_length = atoi(ret->str);
			free_GET_BUF(ret);
		}
		else if ( strcasecmp(ret->str,"Connection:") == 0 ||
			strcasecmp(ret->str,"Proxy-Connection:") == 0 ) {
			ret = step_GET_BUF(ret);
			if ( strcasecmp(ret->str,"Keep-Alive") == 0 )
				info->flags |= XoHINF_F_KEEP_ALIVE;
			free_GET_BUF(ret);
		}
		else if ( strcasecmp(ret->str,"Transfer-Encoding:") == 0 ) {
			ret = step_GET_BUF(ret);
			if ( strcasecmp(ret->str,"chunked") == 0 )
				info->flags |= XoHINF_F_CHUNKED;
			free_GET_BUF(ret);
		}
		else if ( memcmp(ret->str,"HTTP",4) == 0 ) {
			ret = step_GET_BUF(ret);
			sscanf(ret->str,"%i",&info->err);
			free_GET_BUF(ret);
		}
		else {
			free_GET_BUF(ret);
		}
	}
	return 0;
}


void
output_client_header(STREAM * con,int flags,char * server,int port,int length)
{
static int counter;
	if ( flags & XoH_F_PROXY )
		s_printf(con,
			"POST http://%s:%i/cgi-bin/xlsv%i.cgi HTTP/1.1\r\n",
			server,port,(counter++)&0x0ff);
	else	s_printf(con,"POST /cgi-bin/xlsv.cgi HTTP/1.0\r\n");
	switch ( flags & (XoH_F_PROXY|XoH_F_KEEP_ALIVE) ) {
	case 0:
		break;
	case XoH_F_PROXY:
		break;
	case XoH_F_KEEP_ALIVE:
		s_printf(con,"Connection: keep-alive\r\n");
		break;
	case (XoH_F_PROXY|XoH_F_KEEP_ALIVE):
		s_printf(con,"Proxy-Connection: keep-alive\r\n");
		break;
	}
	s_printf(con,"Pragma: no-cache\r\n");
	s_printf(con,"Content-Type: application/xlohttp\r\n");
	s_printf(con,"Host: %s:%i\r\n",server,port);
	s_printf(con,"Content-Length: %i\r\n\r\n",length);
}


void
open_timeout(STREAM * con)
{
	s_close(con);
}

STREAM *
s_connect_XLoHTTP(
	int * cerr,
	char * target_server,
	int target_ip,
	short target_port,
	char * proxy_server,
	int proxy_ip,
	short proxy_port,
	int flags)
{
STREAM * ret, * con;
HTTP_INFO info;
char cmd[10];
unsigned int cockie[2];
XLoHTTP_HEADER h;
int _len;
int length, result;


	if ( !(flags & XoH_F_KEEP_ALIVE) )
		dont_keep_alive_mode = 1;
	if ( dont_keep_alive_mode ) {
		flags &= ~XoH_F_KEEP_ALIVE;
		if ( off_keep_alive_mode == 0 )
			dont_keep_alive_mode = 0;
	}

	if ( proxy_server ) {
		con = new_connection(
			cerr,
			proxy_server,
			proxy_ip,
			proxy_port,
			0,0);
		if ( con == 0 ) {
			return 0;
		}
	}
	else {
		con = new_connection(
			cerr,
			target_server,
			target_ip,
			target_port,
			0,0);
		if ( con == 0 ) {
			return 0;
		}
	}
	proxy_ip = s_get_socketip(con);
	if ( proxy_server )
		flags |= XoH_F_PROXY;

	output_client_header(con,flags,
		target_server,target_port,sizeof(h));

	h.type = XoH_T_OPEN;
	h.sum = 0;
	h.seq = 0;
	h.cockie[0] = h.cockie[1] = 0;
	change_endian_XLoHTTP_HEADER(&h);
	h.sum = XoH_checksum(&h);
	if ( en_do(&_len,s_write,con,&h,sizeof(h)) <= 0 ) {
		s_close(con);
		*cerr = ESYS_CREFUSED;

		return 0;
	}
	new_tick(open_timeout,-30,(int)con);
	if ( scan_HTTP(&info,con,0) < 0 ) {
		*cerr = ESYS_CTIMEOUT;
		del_tick_with_data(open_timeout,(int)con);
		s_close(con);
		return 0;
	}
	del_tick_with_data(open_timeout,(int)con);
	if ( info.err != 200 ) {
		dont_keep_alive_mode = 1;
		free_HTTP_info(&info);
		s_close(con);
		*cerr = ESYS_CREFUSED;
		return 0;
	}
	if ( info.flags & XoHINF_F_CHUNKED ) {
		flags |= XoH_F_CHUNKED;
		chunk_read_start(con);
	}
	free_HTTP_info(&info);
	if ( flags & XoH_F_CHUNKED )
		result = en_do(&_len,s_chunk_read,con,&h,sizeof(h));
	else
		result = en_do(&_len,s_read,con,&h,sizeof(h));
	if ( result <= 0 ) {
		if ( flags & XoH_F_CHUNKED )
			chunk_read_end(con);
		s_close(con);
		*cerr = ESYS_CREFUSED;
		return 0;
	}
	h.sum = XoH_checksum(&h);
	change_endian_XLoHTTP_HEADER(&h);
	if ( h.sum ) {
		if ( flags & XoH_F_CHUNKED )
			chunk_read_end(con);
		s_close(con);
		*cerr = ESYS_CREFUSED;
		return 0;
	}

	switch ( h.type ) {
	case XoH_T_DATA:
		flags &= ~XoH_F_KEEP_ALIVE;
		break;
	case XoH_T_KEEP_ALIVE:
		h.type = XoH_T_DATA;
		break;
	}

	ret = d_alloc(sizeof(*ret));
	ret->h.tbl = &s_XLoHTTP_table;
	ret->xoh.info = d_alloc(sizeof(*ret->xoh.info));

	ret->xoh.info->con_head = ret->xoh.info->con_tail = 0;
	ret->xoh.info->flags = flags & XoH_F_MASK;
	ret->xoh.info->send_seq = 1;
	ret->xoh.info->recv_seq = h.seq;
	init_sbuf(&ret->xoh.info->recv,0);
	init_sbuf(&ret->xoh.info->send,0);
	ret->xoh.info->target_server = copy_str(target_server);
	ret->xoh.info->target_ip = target_ip;
	ret->xoh.info->target_port = target_port;
	if ( proxy_server ) {
		ret->xoh.info->proxy_server = copy_str(proxy_server);
		ret->xoh.info->proxy_ip = proxy_ip;
		ret->xoh.info->proxy_port = proxy_port;
		ret->xoh.info->flags |= XoH_F_PROXY;
	}
	else {
		ret->xoh.info->proxy_server = copy_str(target_server);
		ret->xoh.info->proxy_ip = proxy_ip;
		ret->xoh.info->proxy_port = target_port;
	}
	ret->xoh.info->last_polling_time = get_xltime();
	ret->xoh.info->polling_interval = XLoHTTP_POLLING_MAX;
	ret->xoh.info->target_con = 0;
	ret->xoh.info->st = ret;
	memcpy(ret->xoh.info->cockie,h.cockie,sizeof(h.cockie));

	length = info.content_length - sizeof(h);
	free_HTTP_info(&info);
	if ( flags & XoH_F_CHUNKED ) {
		if ( copy_in_sbuf_stream(&ret->xoh.info->recv,con,length,0) < 0 )
			ret->xoh.info->flags |= XoH_F_ERR;
	} else {
		if ( length > 0 &&
				copy_in_sbuf_stream(&ret->xoh.info->recv,con,length,0) < 0 )
			ret->xoh.info->flags |= XoH_F_ERR;
	}
	if ( h.type == XoH_T_LAST )
		ret->xoh.info->flags |= XoH_F_RECV_LAST;

	if ( ret->xoh.info->flags & XoH_F_KEEP_ALIVE )
		ret->xoh.info->keep_con = con;
	else {
		ret->xoh.info->keep_con = 0;
		if ( flags & XoH_F_CHUNKED )
			chunk_read_end(con);
		s_close(con);
	}
	create_task(XLoHTTP_task,(int)ret->xoh.info,1);
	lock_task(stream_lock);
	_s_open(ret,O_RDWR);
	unlock_task(stream_lock,"s_connect_XLoHTTP");

	return ret;
}


int
__s_flush_XLoHTTP(STREAM * s,S_XLoHTTP_INFO * s_info,
	unsigned char type,int zero_check)
{
XLoHTTP_HEADER h;
XoH_REQUEST * r;
int cerr;
SBUF_DATA * d;
int _len,total;
char * buf;
char * server;
int ip;
short port;
S_XLoHTTP_INFO * info;
STREAM * keep_con;
int flags;

retry:

	lock_task(XLoHTTP_lock);
	if ( s == 0 ) {
		info = s_info;
	}
	else {
		info = s->xoh.info;
		if ( info == 0 ) {
			unlock_task(XLoHTTP_lock,"__s_flush_");
			return -1;
		}
	}

	if ( zero_check && get_sbuf_data_size(&info->send,0) == 0 ) {
		unlock_task(XLoHTTP_lock,"__s_flush_");
		return 0;
	}
	if ( info->flags & XoH_F_FLUSH ) {
		sleep_task((int)info,XLoHTTP_lock);
		goto retry;
	}

	info->flags |= XoH_F_FLUSH;

	r = d_alloc(sizeof(*r));
	r->seq = 0;
	r->next = 0;

	total = get_sbuf_data_size(&info->send,0);
	buf = d_alloc(total+1);
	copy_out_sbuf(&info->send,buf,total,0);
	server = copy_str(info->proxy_server);
	ip = info->proxy_ip;
	port = info->proxy_port;
	h.type = type;
	h.sum = 0;
	memcpy(h.cockie,info->cockie,sizeof(h.cockie));
	r->seq = h.seq = info->send_seq ++;

	flags = info->flags & XoH_F_KEEP_ALIVE;
	keep_con = info->keep_con;

	unlock_task(XLoHTTP_lock,"__s_flush_XLoHTTP");


	if ( flags ) {
		r->con = keep_con;
	}
	else	r->con = new_connection(
			&cerr,
			server,
			ip,
			port,
			0,0);

	d_f_ree(server);

	if ( r->con == 0 ) {
		goto err;
	}

	output_client_header(r->con,info->flags,
		info->target_server,info->target_port,
		sizeof(h)+total);

	change_endian_XLoHTTP_HEADER(&h);
	h.sum = XoH_checksum(&h);

	if ( en_do(&_len,s_write,r->con,&h,sizeof(h)) <= 0 ) {
		goto err;
	}
	if ( en_do(&_len,s_write,r->con,buf,total) <= 0 ) {
		goto err;
	}
	d_f_ree(buf);

	lock_task(XLoHTTP_lock);

	info->flags &= ~XoH_F_FLUSH;

	if ( info->con_head ) {
		info->con_tail->next = r;
		info->con_tail = r;
	}
	else 	info->con_head = info->con_tail = r;
	wakeup_task((int)info);

	unlock_task(XLoHTTP_lock,"__s_flush_XLoHTTP");

	return 0;

err:
	lock_task(XLoHTTP_lock);

	info->flags &= ~XoH_F_FLUSH;

	d_f_ree(buf);
	s_safe_close(r->con);
	info->flags |= XoH_F_ERR;
	d_f_ree(r);
	wakeup_task((int)info);

	unlock_task(XLoHTTP_lock,"__s_flush_XLoHTTP");

	return -1;
}


void
XLoHTTP_tick_func(S_XLoHTTP_INFO * info)
{
	lock_task(XLoHTTP_lock);
	wakeup_task((int)info);
	unlock_task(XLoHTTP_lock,"XLoHTTP_tick_func");
}


void
XLoHTTP_task(TKEY d)
{
S_XLoHTTP_INFO * s_info;
XoH_REQUEST * r;
XLoHTTP_HEADER h;
int er,ret,result;
HTTP_INFO info;
int tim;
int zero_cnt;

	s_info = (S_XLoHTTP_INFO*)GET_TKEY(d);
	zero_cnt = 0;
	for ( ; ; ) {
		lock_task(XLoHTTP_lock);
		if ( zero_cnt != 1 && s_info->con_head == 0 ) {
			s_info->last_polling_time = get_xltime();
			s_info->polling_interval = 0;
		}
	retry:
		for ( ; s_info->con_head == 0 &&
				(tim = s_info->polling_interval 
					+ s_info->last_polling_time
					- get_xltime() )
				> 0 &&
				(s_info->flags & XoH_F_CLOSE) == 0 ; ) {

			new_tick(XLoHTTP_tick_func,-tim,(int)s_info);
			sleep_task((int)s_info,XLoHTTP_lock);
			lock_task(XLoHTTP_lock);
		}
		if ( s_info->flags & XoH_F_CLOSE ) {
			unlock_task(XLoHTTP_lock,"XLoHTTP_task");
			break;
		}
		zero_cnt = 0;
		if ( s_info->con_head == 0 ) {
			r = 0;
			unlock_task(XLoHTTP_lock,"task");
			zero_cnt = 1;
			if ( __s_flush_XLoHTTP(0,s_info,XoH_T_DATA,0)
					< 0 ) {
				lock_task(XLoHTTP_lock);
				goto err;
			}
			lock_task(XLoHTTP_lock);

			s_info->last_polling_time = get_xltime();
			s_info->polling_interval *= 2;
			if ( s_info->polling_interval == 0 )
				s_info->polling_interval = 1;
			if ( s_info->polling_interval >
					XLoHTTP_POLLING_MAX )
				s_info->polling_interval
					= XLoHTTP_POLLING_MAX;
		}
		r = s_info->con_head;
		s_info->con_head = r->next;
		if ( s_info->con_head == 0 )
			s_info->con_tail = 0;
		s_info->target_con = r->con;
		unlock_task(XLoHTTP_lock,"XLoHTTP_task");


		scan_HTTP(&info,r->con,0);
		if ( info.err != 200 ) {
			dont_keep_alive_mode = 1;
			lock_task(XLoHTTP_lock);
			s_info->flags |= XoH_F_ERR|XoH_F_RECV_LAST;
			free_HTTP_info(&info);
printf("ERR (%i) 1\n",get_tid());
			goto err;
		}

		if ( info.flags & XoHINF_F_CHUNKED ) {
			s_info->flags |= XoH_F_CHUNKED;
			chunk_read_start(r->con);
		}
		if ( s_info->flags & XoH_F_CHUNKED )
			result = en_do(&er,s_chunk_read,r->con,&h,sizeof(h));
		else
			result = en_do(&er,s_read,r->con,&h,sizeof(h));

		if ( result <= 0 )	{
			free_HTTP_info(&info);
			lock_task(XLoHTTP_lock);

			s_info->flags |= XoH_F_ERR|XoH_F_RECV_LAST;

printf("ERR (%i) 2\n",get_tid());
			goto err;
		}
		h.sum = XoH_checksum(&h);
		change_endian_XLoHTTP_HEADER(&h);
		if ( h.sum ) {
			free_HTTP_info(&info);
			lock_task(XLoHTTP_lock);
			s_info->flags |= XoH_F_ERR|XoH_F_RECV_LAST;

printf("ERR (%i) 3\n",get_tid());
			goto err;
		}
		if ( h.seq != r->seq ) {

			free_HTTP_info(&info);

			lock_task(XLoHTTP_lock);

			s_info->flags |= XoH_F_ERR|XoH_F_RECV_LAST;
printf("ERR (%i) 4\n",get_tid());
			goto err;
		}
		if ( info.content_length == sizeof(h) ) {
			lock_task(XLoHTTP_lock);
			goto last_check;
		}
		ret = copy_in_sbuf_stream(
				&s_info->recv,
				r->con,
				info.content_length - sizeof(h),
				XLoHTTP_lock);

		free_HTTP_info(&info);

		lock_task(XLoHTTP_lock);

		switch ( ret ) {
		case -1:
		case 0:
			s_info->flags |= XoH_F_ERR|XoH_F_RECV_LAST;
printf("ERR (%i) 5 %i %i\n",get_tid(),ret,errno);
perror("EEE");
			break;
		case 1:
			break;
		}
	last_check:
		switch ( h.type ) {
		case XoH_T_LAST:
			s_info->flags |= XoH_F_RECV_LAST;
			break;
		case XoH_T_ERROR:
printf("ERR (%i) 6\n",get_tid());
			s_info->flags |= XoH_F_ERR;
			break;
		}

	err:

		s_info->target_con = 0;
		wakeup_task((int)s_info);

		unlock_task(XLoHTTP_lock,"task");

		if ( s_info->flags & XoH_F_CLOSE )
			s_info->flags |= XoH_F_TASK_CLOSE;
		if ( r ) {
			if ( s_info->flags & XoH_F_CHUNKED )
				chunk_read_end(r->con);
			if ( !(s_info->flags & XoH_F_KEEP_ALIVE ) )
				s_close(r->con);
			d_f_ree(r);
		}

		if ( s_info->flags & (XoH_F_CLOSE|XoH_F_ERR) )
			break;
	}
	__s_flush_XLoHTTP(0,s_info,XoH_T_LAST,0);

	lock_task(XLoHTTP_lock);

	wakeup_task((int)s_info);

	for ( ; ; ) {
		if ( s_info->st == 0 )
			break;
		if ( s_info->st->h.tbl == 0 )
			break;
		sleep_task((int)s_info,XLoHTTP_lock);
		lock_task(XLoHTTP_lock);
	}
	s_info->flags |= XoH_F_TASK_CLOSE;


	if ( s_info->st )
		s_info->st->xoh.info = 0;

	free_sbuf(&s_info->recv,0);
	free_sbuf(&s_info->send,0);
	free_con_list(s_info);

	d_f_ree(s_info->proxy_server);
	d_f_ree(s_info->target_server);
	d_f_ree(s_info);

	unlock_task(XLoHTTP_lock,"task");

}



int
s_close_XLoHTTP(STREAM * s)
{

STREAM * con;
int key;

	lock_task(XLoHTTP_lock);
	for ( ; s->h.thread ; ) {

		if ( s->xoh.info )
			wakeup_task((int)s->xoh.info);
		wakeup_task((int)s);
		unlock_task(XLoHTTP_lock,"s_close_XLoHTTP");
		unlock_task(stream_lock,"s_close_XLoHTTP");
		sleep_sec(1);
		lock_task(stream_lock);
		lock_task(XLoHTTP_lock);
	}

printf("close XLoHTTP %x ",s);
if ( s->xoh.info )
printf("keep %x target %x\n",s->xoh.info->keep_con,s->xoh.info->target_con);
else printf("\n");

	if ( s->xoh.info ) {

		s->xoh.info->flags |= XoH_F_CLOSE;
		if ( s->xoh.info->target_con )
			s_safe_close(s->xoh.info->target_con);
		if ( s->xoh.info->keep_con ) {
			if ( s->xoh.info->flags & XoH_F_CHUNKED )
				chunk_read_end(s->xoh.info->keep_con);
			s_safe_close(s->xoh.info->keep_con);
		}

		key = (int)s->xoh.info;
		s->xoh.info->st = 0;
		s->xoh.info = 0;
		wakeup_task(key);
	}
	unlock_task(XLoHTTP_lock,"s_close_XLoHTTP");
	
	return 0;
}

int
s_write_XLoHTTP(STREAM * s,void * data,int len)
{
int ret;


	lock_task(XLoHTTP_lock);
	if ( s->xoh.info == 0 ) {
		ret = -1;
		goto err;
	}
	if ( s->xoh.info->flags & (XoH_F_ERR|XoH_F_CLOSE) ) {
		ret = -1;
		goto err;
	}
	copy_in_sbuf(&s->xoh.info->send,data,len,0);
	ret = len;
err:
	unlock_task(XLoHTTP_lock,"s_write_XLoHTTP");

	return ret;
}

int
s_read_XLoHTTP(STREAM * s,void * data,int len)
{
int ret;


retry:
	lock_task(XLoHTTP_lock);
	if ( s->h.tbl == 0 ) {
		ret = -1;
		goto end;
	}
	if ( s->xoh.info == 0 ) {
		ret = -1;
		goto end;
	}
	if ( get_sbuf_data_size(&s->xoh.info->recv,0) == 0 ) {
		if ( s->xoh.info->flags & XoH_F_ERR ) {
			ret = -1;
			goto end;
		}
		if ( s->xoh.info->flags & XoH_F_RECV_LAST ) {
			ret = 0;
			goto end;
		}
		sleep_task((int)s->xoh.info,XLoHTTP_lock);
		goto retry;
	}
	unlock_task(XLoHTTP_lock,"s_read_XLoHTTP");
	ret = copy_out_sbuf(&s->xoh.info->recv,data,len,0);

//xdump(data,ret);
	return ret;
end:

	unlock_task(XLoHTTP_lock,"s_read_XLoHTTP");

//xdump(data,ret);
	return ret;
}

int
s_flush_XLoHTTP(STREAM * s)
{
int ret;


	ret = __s_flush_XLoHTTP(s,0,XoH_T_DATA,1);


	return ret;
}



int
s_get_socketip_XLoHTTP(STREAM * s)
{
	if ( s->xoh.info )
		return s->xoh.info->proxy_ip;
	else	return -1;
}
