在之前的课程里,我们已经实现了报文从客户端发送到服务端,然后服务端接收后再返回给客户端。这样其实已经基本具备一个服务器的雏形,但如果需要实现HTTP服务器,我们还需要将接收到的报文按照HTTP协议进行解析,然后交由业务处理后,将业务返回的结果按照HTTP协议再封装,返回给客户端。所以本小节,我们主要介绍如何处理HTTP请求。
1. HTTP请求处理流程
当前我们实现的位置,即是HTTP Conn
接收到客户端发送的报文,接下来我们需要将报文交由HTTP Request
模块处理。
2. HTTP缓冲区模块
在将报文送往请求处理之前,我们需要考虑一个问题,就是有可能我们一次所接收的报文可能是不完整的,也就是假定客户端发出的报文为2KB大小,但是由于我们服务端接受缓冲区只有1KB,我们需要接收2次才能将客户端发送的报文接收完全。这个时候我们就需要有一个缓冲区去存放第一次和第二次接收的报文,待接收完全之后将这个缓冲区内的数据统一交给请求模块解析。那么对于这个缓冲区的实现就有如下要求:
- 不能是固定大小。因为我们无法预知客户端发来的请求大小有多大;
- 可以动态扩张。需要根据存储的数据大小扩展。
据此我们实现出如下缓冲区模块,达成如上的目标。老规矩先看接口:
typedef struct http_buf http_buf_t;
http_buf_t *http_buf_new(void);
int http_buf_append(http_buf_t *buf, const uint8_t *data, size_t len);
int http_buf_get_data(const http_buf_t *buf, uint8_t **data);
void http_buf_free(http_buf_t *buf);
如上接口实现HTTP
缓冲区的创建和销毁,以及向缓冲区中追加数据/获取数据。具体的实现如下:
#define DEFAULT_CAPACITY 512 /* 默认容量512字节 */
typedef struct http_buf {
uint8_t *p;
size_t capacity;
size_t cur_len;
} http_buf_t;
http_buf_t *http_buf_new(void)
{
http_buf_t *buf = malloc(sizeof(*buf));
if (buf == NULL) {
return NULL;
}
*buf = (http_buf_t) { 0 };
buf->capacity = DEFAULT_CAPACITY;
buf->cur_len = 0;
buf->p = malloc(buf->capacity);
if (buf->p == NULL) {
free(buf);
return NULL;
}
return buf;
}
int http_buf_append(http_buf_t *buf, const uint8_t *data, size_t len)
{
if ((buf == NULL) || (data == NULL)) {
return -1;
}
if (buf->cur_len + len <= buf->capacity) {
memcpy(buf->p + buf->cur_len, data, len);
buf->cur_len += len;
return 0;
}
size_t new_len = buf->capacity * 2; /* 按照2倍扩展内存大小 */
void *new_p = malloc(new_len);
if (new_p == NULL) {
return -1;
}
memcpy(new_p, buf->p, buf->cur_len);
memcpy(new_p + buf->cur_len, data, len);
free(buf->p);
buf->capacity = new_len;
buf->cur_len += len;
buf->p = new_p;
return 0;
}
int http_buf_get_data(const http_buf_t *buf, uint8_t **data)
{
if ((buf == NULL) || (data == NULL)) {
return -1;
}
*data = buf->p;
return (int)buf->cur_len;
}
void http_buf_free(http_buf_t *buf)
{
if (buf == NULL) {
return;
}
if (buf->p != NULL) {
free(buf->p);
buf->p = NULL;
}
free(buf);
}