/***************************************************************************************
 *
 *  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
 *
 *  By downloading, copying, installing or using the software you agree to this license.
 *  If you do not agree to this license, do not download, install, 
 *  copy or use the software.
 *
 *  Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
 *
 *  Redistribution and use in binary forms, with or without modification, are permitted.
 *
 *  Unless required by applicable law or agreed to in writing, software distributed 
 *  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 *  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
 *  language governing permissions and limitations under the License.
 *
****************************************************************************************/

#include "sys_inc.h"
#include "http_test.h"
#include "http.h"
#include "http_cln.h"
#include "http_parse.h"


BOOL http_test_req(HTTPREQ * p_http)
{
    int ret;
    int offset = 0;
	char bufs[1024];
	char cookie[100];
	int buflen = sizeof(bufs);

    snprintf(cookie, sizeof(cookie), "%x%x%x", rand(), rand(), sys_os_get_ms());
	
	offset += snprintf(bufs+offset, buflen-offset, "GET %s HTTP/1.1\r\n", p_http->url);
	offset += snprintf(bufs+offset, buflen-offset, "Host: %s:%u\r\n", p_http->host, p_http->port);
	offset += snprintf(bufs+offset, buflen-offset, "Accept: */*\r\n");
	offset += snprintf(bufs+offset, buflen-offset, "x-sessioncookie: %s\r\n", cookie);
	offset += snprintf(bufs+offset, buflen-offset, "User-Agent: happytimesoft/1.0\r\n");

    if (p_http->need_auth)
    {
        offset += http_build_auth_msg(p_http, "GET", bufs+offset, buflen-offset);        
    }

    offset += snprintf(bufs+offset, buflen-offset, "\r\n");
    
	bufs[offset] = '\0';

    log_print(HT_LOG_DBG, "TX >> %s\r\n", bufs);

    ret = http_cln_tx(p_http, bufs, offset);
    
	return (ret == offset) ? TRUE : FALSE;
}

BOOL http_test_rx_timeout(HTTPREQ * p_http, int timeout)
{
	int sret, rlen;
	BOOL ret = FALSE;
	fd_set fdr;
	struct timeval tv;
	
	while (1)
	{
#ifdef HTTPS
        if (p_http->https && SSL_pending(p_http->ssl) > 0)
    	{
    	    // There is data to read 
    	}
    	else
#endif
        {
    		tv.tv_sec = timeout / 1000;
    		tv.tv_usec = (timeout % 1000) * 1000;
    		
    		FD_ZERO(&fdr);
    		FD_SET(p_http->cfd, &fdr);

    		sret = select((int)(p_http->cfd+1), &fdr, NULL, NULL, &tv);
    		if (sret == 0)
    		{
		        log_print(HT_LOG_WARN, "%s, timeout!!!\r\n", __FUNCTION__);
		        break;
    		}
    		else if (sret < 0)
    		{
    			log_print(HT_LOG_ERR, "%s, select err[%s], sret[%d]!!!\r\n", 
    			    __FUNCTION__, sys_os_get_socket_error(), sret);
    			break;
    		}
    		else if (!FD_ISSET(p_http->cfd, &fdr))
    		{
    		    continue;
    		}
		}

        if (p_http->rbuf == NULL)
    	{
    		p_http->rbuf = p_http->rcv_buf;
    		p_http->mlen = sizeof(p_http->rcv_buf)-4;
    		p_http->rcv_dlen = 0;
    		p_http->ctt_len = 0;
    		p_http->hdr_len = 0;
    	}
		
#ifdef HTTPS
    	if (p_http->https)
    	{
    	    sys_os_mutex_enter(p_http->ssl_mutex);
    		rlen = SSL_read(p_http->ssl, p_http->rbuf+p_http->rcv_dlen, p_http->mlen-p_http->rcv_dlen);
    		sys_os_mutex_leave(p_http->ssl_mutex);
    	}
    	else 
#endif	
        rlen = recv(p_http->cfd, p_http->rbuf+p_http->rcv_dlen, p_http->mlen-p_http->rcv_dlen, 0);

    	if (rlen < 0)
    	{
    		log_print(HT_LOG_INFO, "%s, recv return = %d, dlen[%d], mlen[%d]\r\n", 
    		    __FUNCTION__, rlen, p_http->rcv_dlen, p_http->mlen);	
    		return FALSE;
    	}

    	p_http->rcv_dlen += rlen;
    	p_http->rbuf[p_http->rcv_dlen] = '\0';

        if (p_http->rcv_dlen < 16)
    	{
    		continue;
    	}
    	
    	if (http_is_http_msg(p_http->rbuf) == FALSE)
    	{
    		break;
    	}

    	if (p_http->hdr_len == 0)
    	{
    		int parse_len;
    		int http_pkt_len;

    		http_pkt_len = http_pkt_find_end(p_http->rbuf);
    		if (http_pkt_len == 0) 
    		{
    			continue;
    		}
    		p_http->hdr_len = http_pkt_len;

            HTTPMSG * rx_msg = http_get_msg_buf(http_pkt_len+1);
    		if (rx_msg == NULL)
    		{
    			log_print(HT_LOG_ERR, "%s, get msg buf failed\r\n", __FUNCTION__);
    			return FALSE;
    		}

    		memcpy(rx_msg->msg_buf, p_http->rbuf, http_pkt_len);
    		rx_msg->msg_buf[http_pkt_len] = '\0';
    		
    		log_print(HT_LOG_DBG, "RX from %s << %s\r\n", p_http->host, rx_msg->msg_buf);
    		
    		parse_len = http_msg_parse_part1(rx_msg->msg_buf, http_pkt_len, rx_msg);
    		if (parse_len != http_pkt_len)
    		{
    			log_print(HT_LOG_ERR, "%s, http_msg_parse_part1=%d, http_pkt_len=%d!!!\r\n", 
    			    __FUNCTION__, parse_len, http_pkt_len);

    			http_free_msg(rx_msg);
    			return FALSE;
    		}
    		
    		p_http->ctt_len = rx_msg->ctt_len;
    		p_http->rx_msg = rx_msg;

    		ret = TRUE;
		    break;
    	}
	}

	return ret;
}

BOOL http_test(const char * url, const char * user, const char * pass, HTTPCTT * ctt, int timeout)
{
    BOOL ret = FALSE;
    int  port, https=0;
    char proto[32], username[64], password[64], host[100], path[200];

    url_split(url, proto, sizeof(proto), username, sizeof(username), 
        password, sizeof(password), host, sizeof(host), &port, path, sizeof(path));

    if (strcasecmp(proto, "https") == 0)
    {
        https = 1;
        port = (port == -1) ? 443 : port;
    }
    else if (strcasecmp(proto, "http") == 0)
    {
        port = (port == -1) ? 80 : port;
    }
    else
    {
        return FALSE;
    }

    if (host[0] == '\0')
    {
	    return FALSE;
    }

    HTTPREQ hreq;
    memset(&hreq, 0, sizeof(hreq));
    
    strncpy(hreq.host, host, sizeof(hreq.host) - 1);
    
    if (username[0] != '\0')
	{
	    strcpy(hreq.auth_info.auth_name, username);
	}
	else if (user && user[0] != '\0')
	{
	    strcpy(hreq.auth_info.auth_name, user);
	}

	if (password[0] != '\0')
	{
		strcpy(hreq.auth_info.auth_pwd, password);
	}
	else if (pass && pass[0] != '\0')
	{
	    strcpy(hreq.auth_info.auth_pwd, pass);
	}
	
	if (path[0] != '\0')
	{
		strcpy(hreq.url, path);
		strcpy(hreq.auth_info.auth_uri, path);
	}
    
	hreq.port = port;
	hreq.https = https;

RETRY:

	hreq.cfd = tcp_connect_timeout(inet_addr(hreq.host), hreq.port, timeout);
	if (hreq.cfd <= 0)
	{
	    log_print(HT_LOG_ERR, "%s, tcp_connect_timeout\r\n", __FUNCTION__);
		return FALSE;
    }

	if (hreq.https)
	{
#ifdef HTTPS	
		if (!http_cln_ssl_conn(&hreq, timeout))
		{
		    return FALSE;
		}
#else
        log_print(HT_LOG_ERR, "%s, the server require ssl connection, unsupport!\r\n", __FUNCTION__);
		return FALSE;
#endif		
	}

    if (!http_test_req(&hreq))
    {
        return FALSE;
    }
    
	if (!http_test_rx_timeout(&hreq, timeout))
	{
	    return FALSE;
	}
    
	if (hreq.rx_msg->msg_sub_type == 200)
	{
	    if (ctt)
    	{
    	    *ctt = hreq.rx_msg->ctt_type;
    	}

    	ret = TRUE;
	}
	else if (hreq.rx_msg->msg_sub_type == 401)
	{
	    if (!hreq.need_auth)
	    {
	        http_cln_auth_set(&hreq, NULL, NULL);

	        http_cln_free_req(&hreq);

			goto RETRY;
	    }
	}
	
	http_cln_free_req(&hreq);

	return ret;
}



