接收带有 "Connection: close\r\n" 将会发送ok失败
libghttp用gethostbyname()解析域名 线程不安全。 可改用 getaddrinfo
libhttp 文件多 代码短,涉及功能更多 里面的test等样例不是最新 要改造 https://www.libhttp.org/
--makefile
CC:=gcc
MAKE:=make
CFLAGS:=-O -g -Wall
BIN=../bin/
LIB=../lib/
SRC=../src/
RM:= rm -f
HIDE=@
##
libghttp_la_FNAMES=ghttp \
http_date \
http_hdrs \
http_req \
http_resp \
http_trans \
http_base64 \
http_uri
include_HEADERS = ghttp.h ghttp_constants.h
libghttp_la_OBJECTS= ghttp.o http_date.o http_hdrs.o http_req.o http_resp.o http_trans.o http_base64.o http_uri.o
EXTRA_DIST += \
http_date.h \
http_global.h \
http_hdrs.h \
http_req.h \
http_resp.h \
http_trans.h \
http_uri.h \
http_base64.h
##
ALLFN=mmsS $(libghttp_la_FNAMES)
ALLOBJ=mmsS.o $(libghttp_la_OBJECTS)
##################dynamic static
LDFLAGS= -lpthread -lrt -L../lib/
CPPFLAGS:= -I. -I../src/
##../lib/name.o
pOBJCB= $(foreach aobj,$(ALLOBJ),../lib/$(aobj))
###########链接规则
$(BIN)mms:$(pOBJCB)
$(CC) $(CFLAGS) $(pOBJCB) $(LDFLAGS) $(CPPFLAGS) -o $@
###########自动生成编译规则########################
##../lib/name.o:../src/name.call
## $(CC) -c $(CFLAGS) $(CPPFLAGS) $^ -o $@
###################################################
define Function_prec
$2:$1
$$(CC) -c $$(CFLAGS) $$(CPPFLAGS) $$^ -o $$@
endef
$(foreach one,$(ALLFN),\
$(eval $(call Function_prec,../src/$(one).c,../lib/$(one).o))\
)
###########清空
clear:
$(RM) $(LIB)*.o
$(RM) $(BIN)mms
--mmsS.c
#include<time.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include "ghttp.h"
#include "http_uri.h"
#include "http_hdrs.h"
#include "http_trans.h"
#include "http_req.h"
#include "http_resp.h"
#include "http_date.h"
#include "http_global.h"
#include "http_base64.h"
#define DEBUG_PRINTF
#define http_resq_read_body(a,b,c) http_resp_read_body(a,b,c)
typedef http_resp mmsSreq;
struct _ghttp_request
{
http_uri *uri;
http_uri *proxy;
http_req *req;
http_resp *resp;
http_trans_conn *conn;
const char *errstr;
int connected;
ghttp_proc proc;
char *username;
char *password;
char *authtoken;
char *proxy_username;
char *proxy_password;
char *proxy_authtoken;
};
typedef enum header_state_tag
{
reading_header = 0,
reading_value,
reading_sep,
reading_eol
} header_state;
char *memmem(char *haystack, size_t hlen, char *needle, size_t nlen) {
if (nlen == 0) return haystack; /* degenerate edge case */
if (hlen < nlen) return 0; /* another degenerate edge case */
char *hlimit = haystack + hlen - nlen + 1;
while (NULL!=(haystack = memchr(haystack, needle[0], hlimit - haystack))) {
if (!memcmp(haystack, needle, nlen)) return haystack;
haystack++;
}
return 0;
}
int http_trans_accept(http_trans_conn *conn,int sockServer)
{
socklen_t len = sizeof(struct sockaddr);
//会阻塞进程,直到有客户端连接上来为止
conn->sock = accept(sockServer, (struct sockaddr*)&(conn->saddr), &len);
if (conn->sock < 0)
{
printf("accept errno\n");
return -1;
}
printf("get client\n");
return conn->sock;
}
int http_trans_listen(http_trans_conn *a_conn,int *psockServer,const int port)
{
struct sockaddr_in addrServer;
(*psockServer) = socket(AF_INET, SOCK_STREAM, 0);
if (*psockServer == -1)
{
a_conn->error_type = http_trans_err_type_errno;
a_conn->error = errno;
return -1;
}
addrServer.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY表示任何IP
addrServer.sin_family = AF_INET;
addrServer.sin_port = htons(port);//绑定端口
bind((*psockServer), (struct sockaddr*)&addrServer, sizeof(struct sockaddr));
//Listen监听端
listen((*psockServer), 5);//5为等待连接数目
printf("server:服务器已启动:\n监听中...\n");
return 1;
}
double XX_httplib_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before) {
return ((double)(ts_now->tv_nsec - ts_before->tv_nsec)) * 1.0E-9 +
((double)(ts_now->tv_sec - ts_before->tv_sec));
} /* XX_httplib_difftimespec */
int XX_httplib_pull(int scfd, char *buf, int len, double timeout)
{
int err;
int nread;
struct timespec start;
struct timespec now;
if (timeout > 0) {
memset(&start, 0, sizeof(start));
memset(&now, 0, sizeof(now));
clock_gettime(CLOCK_MONOTONIC, &start);
}
do {
nread = (int)read(scfd, buf, (size_t)len);
err = (nread < 0) ? errno : 0;
if (nread > 0 || (nread == 0 && len == 0))
{
return nread;
}
if (nread < 0)
{
/*
* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases,
* if the timeout is reached and if the socket was set to non-
* blocking in close_socket_gracefully, so we can not distinguish
* here. We have to wait for the timeout in both cases for now.
*/
if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) {
/*
* EAGAIN/EWOULDBLOCK:
* standard case if called from close_socket_gracefully
* => should return -1
* or timeout occured
* => the code must stay in the while loop
*
* EINTR can be generated on a socket with a timeout set even
* when SA_RESTART is effective for all relevant signals
* (see signal(7)).
* => stay in the while loop
*/
}
else return -1;
}
if (timeout > 0) clock_gettime(CLOCK_MONOTONIC, &now);
} while (timeout <= 0 || XX_httplib_difftimespec(&now, &start) <= timeout);
return nread;
}
static int
http_trans_buf_free(http_trans_conn *a_conn)
{
return (a_conn->io_buf_len - a_conn->io_buf_alloc);
}
int
http_trans_read_into_buf2(http_trans_conn *a_conn)
{
int l_read = 0;
int l_bytes_to_read = 0;
/* set the length if this is the first time */
if (a_conn->io_buf_io_left == 0)
{
a_conn->io_buf_io_left = a_conn->io_buf_chunksize;
a_conn->io_buf_io_done = 0;
}
/* make sure there's enough space */
if (http_trans_buf_free(a_conn) < a_conn->io_buf_io_left)
{
a_conn->io_buf = realloc(a_conn->io_buf,
a_conn->io_buf_len + a_conn->io_buf_io_left);
a_conn->io_buf_len += a_conn->io_buf_io_left;
}
/* check to see how much we should try to read */
if (a_conn->io_buf_io_left > a_conn->io_buf_chunksize)
l_bytes_to_read = a_conn->io_buf_chunksize;
else
l_bytes_to_read = a_conn->io_buf_io_left;
/* read in some data */
if((a_conn->last_read = l_read = XX_httplib_pull(a_conn->sock, &a_conn->io_buf[a_conn->io_buf_alloc], l_bytes_to_read, 0.5))
<0)
//
{
if (errno == EINTR)
l_read = 0;
else
return HTTP_TRANS_ERR;
}
else if (l_read == 0)
return HTTP_TRANS_DONE;
/* mark the buffer */
a_conn->io_buf_io_left -= l_read;
a_conn->io_buf_io_done += l_read;
a_conn->io_buf_alloc += l_read;
/* generate the result */
if (a_conn->io_buf_io_left == 0)
return HTTP_TRANS_DONE;
return HTTP_TRANS_NOT_DONE;
}
int
http_resq_read_headers(mmsSreq*a_req, http_trans_conn *a_conn)
{
char *l_start_body = NULL;
int l_rv = 0;
int l_done = 0;
int l_return = HTTP_TRANS_DONE;
char *l_start_header = NULL;
int l_header_len = 0;
char *l_last_header = NULL;
int l_last_header_len = 0;
char *l_start_value = NULL;
int l_value_len = 0;
char *l_cur_ptr = NULL;
char *l_ptr = NULL;
header_state l_state = reading_header;
/* check to see if we need to jump in somewhere */
if (a_conn->sync == HTTP_TRANS_ASYNC)
{
if (a_req->header_state == http_resp_reading_header)
goto http_req_reading_header_jump;
}
/* start reading headers */
do
{
a_req->header_state = http_resp_reading_header;
http_req_reading_header_jump:
/* read in the buffer */
l_rv = http_trans_read_into_buf2(a_conn);
/* check for an error */
if (l_rv == HTTP_TRANS_ERR)
{
a_conn->errstr = "Failed to read http request line";
l_return = HTTP_TRANS_ERR;
goto ec;
}
/* check to see if the end of headers string is in the buffer */
l_start_body = memmem(a_conn->io_buf, a_conn->io_buf_alloc,"\r\n\r\n" , 4);
if (l_start_body != NULL)
{
l_done = 1;//头結束
break;
}
if ((l_done == 0) && (a_conn->sync == HTTP_TRANS_ASYNC) && (l_rv == HTTP_TRANS_NOT_DONE))
return HTTP_TRANS_NOT_DONE;
/* yes, that !l_done is ther because in the case of a 100
continue we well get back up to this loop without doing a
successful read. */
if ((!l_done) && (l_rv == HTTP_TRANS_DONE) && (a_conn->last_read == 0))
{
printf("<%s>", a_conn->io_buf);
a_conn->errstr = "Short read while reading http response headers";
return HTTP_TRANS_ERR;
}
} while (l_done == 0);
/* parse out the header 解析头部*/
#ifdef DEBUG_PRINTF
printf("%s", a_conn->io_buf);
#endif
//**************
l_ptr = a_conn->io_buf;
l_cur_ptr = l_ptr;
while (*l_cur_ptr != '\r')
l_cur_ptr++;
/* see if there are any headers. If there aren't any headers
then the end of the reason phrase is the same as the start body
as above. If that's the case then skip reading any headers. */
if (l_cur_ptr == l_start_body)
l_done = 1;
else
l_done = 0;
while ((*l_cur_ptr == '\r') ||
(*l_cur_ptr == '\n'))
l_cur_ptr++;
/* start parsing out the headers */
/* start at the beginning */
l_start_header = l_cur_ptr;
while (l_done == 0)
{
/* check to see if we're at the end of the
headers as determined above by the _patt() call */
if (l_cur_ptr == (l_start_body + 1))
break;
/* reading the header name */
if (l_state == reading_header)
{
/* check to see if there's leading whitespace.
If that's the case then it needs to be combined
from the previous header */
if (l_header_len == 0)
{
if ((*l_cur_ptr == ' ') || (*l_cur_ptr == '\t'))
{
/* bomb if it's the first header. That's not valid */
if ((l_last_header == NULL) || (l_last_header_len == 0))
{
a_conn->errstr = "The first http request header began with whitespace";
l_return = HTTP_TRANS_ERR;
goto ec;
}
l_cur_ptr++;
/* set it reading sep. sep will eat all of the write space */
l_state = reading_sep;
continue;
}
}
if (*l_cur_ptr == ':')
{
/* make sure that there's some header there */
if (l_header_len == 0)
{
a_conn->errstr = "An http request header was zero length";
l_return = HTTP_TRANS_ERR;
goto ec;
}
/* start reading the seperator */
l_state = reading_sep;
l_cur_ptr++;
}
/* make sure there's a seperator in
there somewhere */
else if (*l_cur_ptr == '\r')
{
a_conn->errstr = "Failed to find seperator in http request headers";
l_return = HTTP_TRANS_ERR;
goto ec;
}
else
{
l_cur_ptr++;
l_header_len++;
}
}
/* read the seperator */
else if (l_state == reading_sep)
{
/* walk until you get a non-whitespace character */
if ((*l_cur_ptr == ' ') || (*l_cur_ptr == '\t'))
l_cur_ptr++;
else
{
l_state = reading_value;
l_start_value = l_cur_ptr;
l_value_len = 0;
}
}
/* read the value */
else if (l_state == reading_value)
{
/* check to see if we've reached the end of the
value */
if ((*l_cur_ptr == '\r') || (*l_cur_ptr == '\n'))
{
/* check to see if this is a continuation of the last
header. If the header len is 0 and we've gotten to
this point then that's the case */
if (l_header_len == 0)
{
http_hdr_set_value_no_nts(a_req->headers,
l_last_header,
l_last_header_len,
l_start_value,
l_value_len);
}
else
{
http_hdr_set_value_no_nts(a_req->headers,
l_start_header,
l_header_len,
l_start_value,
l_value_len);
/* set the last header and the length so that a new line
that starts with spaces can figure out what header it
applies to */
l_last_header = l_start_header;
l_last_header_len = l_header_len;
}
/* start eating the end of line */
l_state = reading_eol;
}
else
{
l_cur_ptr++;
l_value_len++;
}
}
/* read the eof */
else if (l_state == reading_eol)
{
/* eat the eol */
if ((*l_cur_ptr == '\r') || (*l_cur_ptr == '\n'))
l_cur_ptr++;
else
{
/* start reading a new header again. */
l_state = reading_header;
l_start_header = l_cur_ptr;
l_header_len = 0;
}
}
/* what state is this? */
else
{
a_conn->errstr = "Unknown state while reading http request headers";
l_return = HTTP_TRANS_ERR;
goto ec;
}
}
/* clip the buffer */
http_trans_buf_clip(a_conn, l_start_body + 4);
ec:
a_req->header_state = http_resp_header_start;
return l_return;
}
ghttp_status ServerTrans(ghttp_request*a_request)
{
int l_rv = 0;
//****
//ENTITY HEADERS
//****
if (a_request->proc == ghttp_proc_none)
{
l_rv = http_resq_read_headers(a_request->resp, a_request->conn);
if (l_rv == HTTP_TRANS_ERR)
{
a_request->errstr = a_request->conn->errstr;
return ghttp_error;
}
if (l_rv == HTTP_TRANS_NOT_DONE)
return ghttp_not_done;
if (l_rv == HTTP_TRANS_DONE)
{
a_request->proc = ghttp_proc_request;
if (a_request->conn->sync == HTTP_TRANS_ASYNC)
return ghttp_not_done;
}
}
//****
//body
//****
if (a_request->proc == ghttp_proc_request)
{
l_rv = http_resq_read_body(a_request->resp,
a_request->req,
a_request->conn);
if (l_rv == HTTP_TRANS_ERR)
{
/* make sure that the connected flag is fixed and stuff */
if (a_request->conn->sock == -1)
a_request->connected = 0;
return ghttp_error;
}
if (l_rv == HTTP_TRANS_NOT_DONE)
return ghttp_not_done;
if (l_rv == HTTP_TRANS_DONE)
{
/* make sure that the connected flag is fixed and stuff */
if (a_request->conn->sock == -1)
a_request->connected = 0;
a_request->proc = ghttp_proc_none;
return ghttp_done;
}
//只調用body部分 request 的body 到response body
}
a_request->errstr = a_request->conn->errstr;
return ghttp_error;
}
int serverGet(int port)
{
char *temresult = NULL;
ghttp_request *request = NULL;
ghttp_status status;
request = ghttp_request_new();//uri目标uri 代理uri 请求 响应 sock连接 malloc
if (-1 == ghttp_set_sync(request, ghttp_sync))
{
printf("ghttp_set_sync error\n");
ghttp_request_destroy(request);
return -1;
}
int sockServer;
http_trans_listen(request->conn, &sockServer,port);//结束需要close(sockeServer)
if (sockServer < 0)
{
printf("http_trans_listen error\n");
ghttp_request_destroy(request);
return -1;
}
http_trans_accept(request->conn, sockServer);
close(sockServer);//此处监听只收一条,这里就关闭
//--把接收的请求写道resp的结构中 没有写回信息 对方unknow errno
#ifdef DEBUG_PRINTF
printf("---head---\n");
#endif
status = ServerTrans(request);
if (status == ghttp_error)
{
printf("trans:%s\n", ghttp_get_error(request));
ghttp_request_destroy(request);
return -1;
}
temresult = ghttp_get_body(request);
printf("---body---\n");
printf("%s", temresult);
//==
char senbuf[200] = "HTTP/1.1 200 ok\r\nContent-Type:application/json\r\nContent-Length:9\r\n\r\n{receive}";
write(request->conn->sock,senbuf,strlen(senbuf));
//==
ghttp_request_destroy(request);
return 0;
}
int main(int argc, char*argv[])
{
serverGet(8080);
return 0;
}