#include "stdint.h"
#include "stdio.h"
#include "string.h"
#include "lwip/opt.h"
#include "lwip/arch.h"
#include "lwip/api.h"
#include "lwip/inet.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "httpclient.h"

uint8_t httpSendBuffer[1024];
uint8_t httpRecvBuffer[512];



/**
  * @brief  从socket缓存中读取一行http数据
	* @param  sock:    已经连接到服务器的sock编号
	* @param  buf:     保存数据的buffer
	* @param  size:    buf的最大可用长度
	* @retval 读取到的数据的长度,包括两个字节的换行符
  */
int http_getLine(int sock, uint8_t *buf, int size)
{
	 int i = 0;
	 char c = '\0';
	 int n;
	
	 while((i < (size - 1)) && (c != '\n'))
	 {
			n = recv(sock, &c, 1, 0);
		  if(n <= 0) c = '\n';
			buf[i++] = c;
	 }
	 buf[i] = '\0';
 
	 return i;  //返回读取的到的数据长度
}

/**
  * @brief  解析http响应行
	* @param  pbuf:  响应行的数据
* @retval 其他值: 返回http请求状态   -1: 解析失败
  */
int http_parseRequestLine(uint8_t *pbuf)
{
	int b, s, g;
	
	if((strncmp((char *)pbuf, "HTTP/1.1 ", strlen("HTTP/1.1 ")) == 0) || (strncmp((char *)pbuf, "http/1.1 ", strlen("http/1.1 ")) == 0))
	{
		pbuf += strlen("HTTP/1.1 ");
		b = pbuf[0] - '0';
		s = pbuf[1] - '0';
		g = pbuf[2] - '0';	

		return (b * 100 + s * 10 + g);
	}
	
	return -1;
}

/**
  * @brief  DNS解析回调函数
  * @note   在解析域名成功后,会调用这个函数,然后可以读取到对应的IP地址
  * @param  name:         域名
  * @param  host_ip:      域名对应的ip地址
  * @param  callback_arg: 传递的参数
  * @retval None
  */
void http_dns_found(const char *name, ip_addr_t *host_ip, void *callback_arg)
{
	*(ip_addr_t *)callback_arg = *host_ip;
	HTTP_PRINTF("%s:%s\r\n",name, ipaddr_ntoa(host_ip));
}

/**
  * @brief  连接到http服务器的函数
	* @note   连接到http服务器
  * @param  host:     服务器的域名或者ip地址
	* @param  port:     服务器端口号
	* @param  hostIsIp: host代表的是 域名,还是ip地址  0: host为域名  1: host为ip地址
	* @retval -1:连接服务器失败  -2: 域名解析失败  >=0: 连接成功,返回值为 sock编号
  */
int http_clientConnectToServer(char *host, int port, int hostIsIp)
{
	int timeout;
	struct sockaddr_in serverAddr;
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if(sock < 0) return -2;	

	//如果传入的host参数是域名,解析域名
	if(hostIsIp == 0)
	{
		ip_addr_t addr;	
		
    addr.addr = 0;
		dns_gethostbyname(host, &addr, http_dns_found, &addr);
		//等待dns解析完成
		timeout = 0;
		while((addr.addr == 0) && (timeout < 2000)) 
		{
			vTaskDelay(100);  
			timeout += 10;
		}
		if(timeout >= 2000) 
		{
			HTTP_PRINTF(("dns get failure \n"));
			return -2;
		}
		serverAddr.sin_addr.s_addr = inet_addr(inet_ntoa(addr));
	}
	else serverAddr.sin_addr.s_addr = inet_addr((char*)host);
	serverAddr.sin_len = sizeof(serverAddr);
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(port);	
  memset(&serverAddr.sin_zero, 0, sizeof(serverAddr.sin_zero));
	
	//连接服务器
	if(connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) != 0)
	{
		HTTP_PRINTF("connect server error \r\n");
		return -1;
	}
	
	//设置接收数据超时时间
	timeout = 3000;
	setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(int));	
	
	HTTP_PRINTF("connect server success \r\n");
	
	return sock;
}

/**
  * @brief  关闭socket端口
	* @param  sock:    已经连接到服务器的sock编号
  */
void http_clientClose(int sock)
{
	close(sock);
}


/**
  * @brief  连接到发送GET请求
	* @note   组装GET数据包,并将GET请求发送出去
	* @param  sock:    已经连接到服务器的sock编号
  * @param  host:    服务器的域名或者ip地址
	* @param  url:     请求资源的地址
	* @retval -1: 发送请求失败 1:发送成功
  */
int http_clientPacketRequest_GET(int sock, char *host, char *url)
{
	int len;
	
	memset(httpSendBuffer, 0, sizeof(httpSendBuffer));
	//组建请求行
	sprintf((char *)httpSendBuffer, "GET ");
	if(url == NULL) strcat((char *)httpSendBuffer, "/");
	else            strcat((char *)httpSendBuffer, url); 
	strcat((char *)httpSendBuffer, " HTTP/1.1\r\n");
	//组建请求头部 
	strcat((char *)httpSendBuffer, "Host: ");
	strcat((char *)httpSendBuffer, host);	
	strcat((char *)httpSendBuffer, "\r\n");
	strcat((char *)httpSendBuffer, "Connection: close\r\n");	
	strcat((char *)httpSendBuffer, "Accept: application/json\r\n");
	strcat((char *)httpSendBuffer, "User-Agent: stm32f207\r\n");		
	strcat((char *)httpSendBuffer, "Cache-Control: no-cache\r\n");	
	//添加一个空白行
	strcat((char *)httpSendBuffer, "\r\n"); 
	len = strlen((char *)httpSendBuffer);
	HTTP_PRINTF("%s", (char *)httpSendBuffer);	 //
	
	//发送请求报文
	len = write(sock, httpSendBuffer, len);  
	if(len <= 0) 
	{
		return -1;
	}
	
	return 1;
}

/**
  * @brief  http_clientReadResponse_GET
	* @note   等待服务器返回GET响应
	* @param  sock:    已经连接到服务器的sock编号
	* @retval -1: 发送请求失败 1:发送成功
  */
int http_clientReadResponse_GET(int sock, uint8_t *pbuf, int *datlen)
{
	int len, ret;
	int length = 0;
	
	//读取响应行
	len = http_getLine(sock, httpRecvBuffer, sizeof(httpRecvBuffer));	
	if(len <= 0) return -1;
	HTTP_PRINTF("%s", (char *)httpRecvBuffer);
	
	ret = http_parseRequestLine(httpRecvBuffer);
	
	//读取响应头
	do
	{
		len = http_getLine(sock, httpRecvBuffer, sizeof(httpRecvBuffer));		
		HTTP_PRINTF("%s", (char *)httpRecvBuffer);
		if(len <= 2) 
		{
			if(len == 2) break;     //读取到了空行
			else         return -1;
		}
	}while(len > 0);
	
	//读取响应主体内容
	length = 0;
	do
	{
		 len = recv(sock, httpRecvBuffer, sizeof(httpRecvBuffer), 0);
		 if(len > 0) 
		 {
			 memcpy(pbuf + length, httpRecvBuffer, len);
			 length += len;
		 }
	}while(len > 0);
	
	*datlen = length;
	
	return ret;
}

/*
* @brief  http_clientPacketRequest_POST
* @note   组建和发送POST请求头数据包
* @param  sock:     已经连接到服务器的sock编号
* @param  host:     服务器域名或者IP地址
* @param  url:      请求的资源位置 
* @param  pbuf:     需要post的数据缓存
* @param  datalen   需要post的数据长度
* @retval           -1: 发送失败 1:发送成功
*/

char httpTmpBuffer[64];
int http_clientPacketRequest_POST(int sock, char *host, char *url, int datalen)
{
	int len;
	
	memset(httpSendBuffer, 0, sizeof(httpSendBuffer));
	//组建请求行
	sprintf((char *)httpSendBuffer, "POST ");
	if(url == NULL) strcat((char *)httpSendBuffer, "/");
	else            strcat((char *)httpSendBuffer, url); 
	strcat((char *)httpSendBuffer, " HTTP/1.1\r\n");
	
	//组建请求头部
	memset(httpTmpBuffer, 0, sizeof(httpTmpBuffer));
	sprintf(httpTmpBuffer, "Host: %s\r\n", host);
	strcat((char *)httpSendBuffer, httpTmpBuffer);
	strcat((char *)httpSendBuffer, "Connection: close\r\n");	
	strcat((char *)httpSendBuffer, "Accept: application/json\r\n");		 //
	strcat((char *)httpSendBuffer, "User-Agent: stm32f207\r\n");			
	strcat((char *)httpSendBuffer, "Cache-Control: no-cache\r\n");	
	strcat((char *)httpSendBuffer, "Content-Type: application/json\r\n");
	memset(httpTmpBuffer, 0, sizeof(httpTmpBuffer));
	sprintf(httpTmpBuffer, "Content-Length: %d\r\n", datalen);   
	strcat((char *)httpSendBuffer, httpTmpBuffer);
	//添加一个空白行
	strcat((char *)httpSendBuffer, "\r\n"); 
	
	len = strlen((char *)httpSendBuffer);
	HTTP_PRINTF("%s", (char *)httpSendBuffer);	

	//发送请求报文
	len = write(sock, httpSendBuffer, len);  
	if(len <= 0) 
	{
		return -1;
	}

	return 1;
}


/*
* @brief  http_clientPacketBody_POST
* @note   发送post数据
* @param  sock:     已经连接到服务器的sock编号
* @param  pbuf:     需要post的数据缓存
* @param  datalen   需要post的数据长度
* @retval           -1: 发送失败 1:发送成功
*/
int http_clientPacketBody_POST(int sock, uint8_t *pbuf, int datalen)
{
	int len;
	
	while(datalen > 1000)
	{
		len = write(sock, pbuf, 1000);  
		if(len <= 0) 
		{
			return -1;
		}
		
	  datalen -= 1000;
		pbuf += 1000;
	}
	
	if(datalen > 0)
	{
		len = write(sock, pbuf, datalen);  
		if(len <= 0) 
		{
			return -1;
		}
	}	
	
	return 1;
}

/*
* @brief  http_clientReadResponse_POST
* @note   读取和解析POST返回的响应
* @param  sock:     已经连接到服务器的sock编号
* @retval 响应的返回值
*/
int http_clientReadResponse_POST(int sock, uint8_t *pbuf, int *datlen)
{
	int len, ret;
	int length = 0;
	
	//读取响应行
	len = http_getLine(sock, httpRecvBuffer, sizeof(httpRecvBuffer));	
	if(len <= 0) return -1;
	HTTP_PRINTF("%s", (char *)httpRecvBuffer);
	
	ret = http_parseRequestLine(httpRecvBuffer);
	
	//读取响应头
	do
	{
		len = http_getLine(sock, httpRecvBuffer, sizeof(httpRecvBuffer));		
		HTTP_PRINTF("%s", (char *)httpRecvBuffer);
		if(len <= 2) 
		{
			if(len == 2) break;     //读取到了空行
			else         return -1;
		}
	}while(len > 0);	
	
	//读取响应主体内容
	length = 0;
	do
	{
		 len = recv(sock, httpRecvBuffer, sizeof(httpRecvBuffer), 0);
		 if(len > 0) 
		 {
			 memcpy(pbuf + length, httpRecvBuffer, len);
			 length += len;
		 }
	}while(len > 0);
	
	*datlen = length;
	
	return ret;	
}


/*
* @brief  http_clientGet
* @note   客户端发送GET请求
* @param  host:     服务器的域名或者IP地址
* @param  url:      访问服务器资源的位置
* @param  port:     服务器的端口号
* @param  hostIsIp: 第一个参数是域名还是ip地址   0:域名  1: ip地址
* @param  pbuf:     接收服务器响应的数据
* @param  datalen:  服务器响应数据的长度
* @retval 负值: GET请求异常, 正值: http协议的返回码
*/
int http_clientGet(char *host, char *url, uint16_t port, uint8_t hostIsIp, uint8_t *pbuf, int *datalen)
{
	int sock = -1, ret;
	
	sock = http_clientConnectToServer(host, port, hostIsIp);
	if(sock < 0) goto __httpError;
	
	ret = http_clientPacketRequest_GET(sock, host, url);
	if(sock < 0) goto __httpError;
	
	ret = http_clientReadResponse_GET(sock, pbuf, datalen);
	if(sock < 0) goto __httpError;

__httpError:	
	if(sock >= 0)
		http_clientClose(sock);

	return ret;
}

/*
* @brief  http_clientGet
* @note   客户端发送GET请求
* @param  host:     服务器的域名或者IP地址
* @param  url:      访问服务器资源的位置
* @param  port:     服务器的端口号
* @param  hostIsIp: 第一个参数是域名还是ip地址   0:域名  1: ip地址
* @param  postbuf:  需要发往服务器的数据
* @param  postlen:  发送数据的长度
* @param  rtnbuf:     接收服务器响应的数据
* @param  rtnlen:  服务器响应数据的长度
* @retval 负值: GET请求异常, 正值: http协议的返回码
*/
int http_clientPost(char *host, char *url, uint16_t port, uint8_t hostIsIp, uint8_t *postbuf, int postlen, uint8_t *rtnbuf, int *rtnlen)
{
	int sock, ret;
	
	sock = http_clientConnectToServer(host, port, hostIsIp);
	if(sock < 0) goto __httpError;

	ret = http_clientPacketRequest_POST(sock, host, url, postlen);
	if(ret < 0) goto __httpError;
	
	ret = http_clientPacketBody_POST(sock, postbuf, postlen);
  if(ret < 0) goto __httpError;
	
	ret = http_clientReadResponse_POST(sock, rtnbuf, rtnlen);

__httpError:	
	if(sock >= 0)
		http_clientClose(sock);
	
	return ret;
}