Optimize code and fix gzip encoding

Signed-off-by: zhaojh329 <zhaojh329@gmail.com>
main
zhaojh329 2020-09-07 10:03:32 +08:00 committed by Jianhui Zhao
parent 87a209c3b8
commit 28c8146740
5 changed files with 108 additions and 124 deletions

View File

@ -36,16 +36,18 @@ static const char *index_page = "index.html";
static void on_request(struct uh_connection *conn)
{
if (!serve_file) {
int body_len;
const char *body = conn->get_body(conn, &body_len);
const struct uh_str path = conn->get_path(conn);
const struct uh_str query = conn->get_query(conn);
const struct uh_str ua = conn->get_header(conn, "User-Agent");
const struct uh_str body = conn->get_body(conn);
conn->send_head(conn, HTTP_STATUS_OK, -1, NULL);
conn->chunk_printf(conn, "I'm Libuhttpd: %s\n", UHTTPD_VERSION_STRING);
conn->chunk_printf(conn, "Method: %s\n", conn->get_method_str(conn));
conn->chunk_printf(conn, "Path: %s\n", conn->get_path(conn));
conn->chunk_printf(conn, "Query: %s\n", conn->get_query(conn));
conn->chunk_printf(conn, "User-Agent: %s\n", conn->get_header(conn, "User-Agent"));
conn->chunk_printf(conn, "Body: %.*s\n", body_len, body);
conn->chunk_printf(conn, "Path: %.*s\n", path.len ,path.p);
conn->chunk_printf(conn, "Query: %.*s\n", query.len, query.p);
conn->chunk_printf(conn, "User-Agent: %.*s\n", ua.len, ua.p);
conn->chunk_printf(conn, "Body: %.*s\n", body.len, body.p);
conn->chunk_end(conn);
} else {
conn->serve_file(conn, docroot, index_page);

View File

@ -169,75 +169,58 @@ static const char *conn_get_method_str(struct uh_connection *conn)
return http_method_str(conn->parser.method);
}
/* offset of the request field */
#define ROF(c, a) (a - (const char *)c->rb.data)
/* data of the request field */
#define O2D(c, o) ((const char *)c->rb.data + o)
static const char *conn_get_path(struct uh_connection *conn)
static const struct uh_str conn_get_path(struct uh_connection *conn)
{
struct http_parser_url *u = &conn->url_parser;
struct uh_request *req = &conn->req;
struct uh_str path;
if (!req->url.path)
req->url.path = strndup(O2D(conn, u->field_data[UF_PATH].off) + req->url.offset, u->field_data[UF_PATH].len);
return req->url.path;
path.p = req->url.p + u->field_data[UF_PATH].off;
path.len = u->field_data[UF_PATH].len;
return path;
}
static const char *conn_get_query(struct uh_connection *conn)
static const struct uh_str conn_get_query(struct uh_connection *conn)
{
struct http_parser_url *u = &conn->url_parser;
struct uh_request *req = &conn->req;
struct uh_str query = {};
if (!(u->field_set & (1 << UF_QUERY)))
return "";
return query;
if (!req->url.query)
req->url.query = strndup(O2D(conn, u->field_data[UF_QUERY].off) + req->url.offset, u->field_data[UF_QUERY].len);
query.p = req->url.p + u->field_data[UF_QUERY].off;
query.len = u->field_data[UF_QUERY].len;
return req->url.query;
return query;
}
static const char *conn_get_header(struct uh_connection *conn, const char *name)
static const struct uh_str conn_get_header(struct uh_connection *conn, const char *name)
{
struct uh_request *req = &conn->req;
int name_len = strlen(name);
int i, j;
struct uh_str *field;
struct uh_str value = {};
int i;
for (i = 0; i < UHTTPD_MAX_HEADER_NUM; i++) {
if (!req->headers[i].name)
break;
if (!strcasecmp(req->headers[i].name, name))
return req->headers[i].value;
}
field = &req->headers[i].field;
if (!field->p)
return value;
if (i == UHTTPD_MAX_HEADER_NUM)
return NULL;
for (j = 0; j < UHTTPD_MAX_HEADER_NUM; j++) {
if (req->headers_info[j].name_offset > 0) {
const char *p = O2D(conn, req->headers_info[j].name_offset);
if (name_len == req->headers_info[j].name_len && !strncasecmp(p, name, req->headers_info[j].name_len)) {
req->headers[i].name = strndup(p, req->headers_info[j].name_len);
req->headers[i].value = strndup(O2D(conn, req->headers_info[j].value_offset), req->headers_info[j].value_len);
req->headers_info[j].name_len = 0;
return req->headers[i].value;
}
if (name_len == field->len && !strncasecmp(field->p, name, name_len)) {
value.p = req->headers[i].value.p;
value.len = req->headers[i].value.len;
}
}
return NULL;
return value;
}
static const char *conn_get_body(struct uh_connection *conn, int *len)
static const struct uh_str conn_get_body(struct uh_connection *conn)
{
struct uh_request *req = &conn->req;
const char *at = O2D(conn, req->body.offset);
*len = req->body.len;
return at;
return conn->req.body;
}
static int on_message_begin_cb(struct http_parser *parser)
@ -247,20 +230,19 @@ static int on_message_begin_cb(struct http_parser *parser)
req->last_was_header_value = true;
http_parser_url_init(&conn->url_parser);
return 0;
}
static int on_url_cb(struct http_parser *parser, const char *at, size_t length)
{
struct uh_connection *conn = (struct uh_connection *)parser->data;
struct uh_request *req = &conn->req;
struct uh_str *url = &conn->req.url;
if (req->url.offset == 0) {
req->url.offset = ROF(conn, at);
http_parser_url_init(&conn->url_parser);
}
req->url.length += length;
if (!url->p)
url->p = at;
url->len += length;
return 0;
}
@ -279,10 +261,10 @@ static int on_header_field_cb(struct http_parser *parser, const char *at, size_t
return 1;
}
req->headers_info[req->header_num - 1].name_offset = ROF(conn, at);
req->headers[req->header_num - 1].field.p = at;
}
req->headers_info[req->header_num - 1].name_len += length;
req->headers[req->header_num - 1].field.len += length;
return 0;
}
@ -294,10 +276,10 @@ static int on_header_value_cb(struct http_parser *parser, const char *at, size_t
if (!req->last_was_header_value) {
req->last_was_header_value = true;
req->headers_info[req->header_num - 1].value_offset = ROF(conn, at);
req->headers[req->header_num - 1].value.p = at;
}
req->headers_info[req->header_num - 1].value_len += length;
req->headers[req->header_num - 1].value.len += length;
return 0;
}
@ -307,8 +289,8 @@ static int on_body_cb(struct http_parser *parser, const char *at, size_t length)
struct uh_connection *conn = (struct uh_connection *)parser->data;
struct uh_request *req = &conn->req;
if (req->body.offset == 0)
req->body.offset = ROF(conn, at);
if (!req->body.p)
req->body.p = at;
req->body.len += length;
return 0;
@ -317,9 +299,10 @@ static int on_body_cb(struct http_parser *parser, const char *at, size_t length)
static bool run_plugins(struct uh_connection *conn)
{
struct uh_plugin *p = conn->srv->plugins;
struct uh_str path = conn->get_path(conn);
while (p) {
if (!strcmp(conn->get_path(conn), p->path)) {
if (strlen(p->path) == path.len && !strncmp(path.p, p->path, path.len)) {
p->handler(conn);
return true;
}
@ -332,9 +315,8 @@ static int on_message_complete_cb(struct http_parser *parser)
{
struct uh_connection *conn = (struct uh_connection *)parser->data;
struct uh_request *req = &conn->req;
int i;
http_parser_parse_url(O2D(conn, req->url.offset), req->url.length, false, &conn->url_parser);
http_parser_parse_url(req->url.p, req->url.len, false, &conn->url_parser);
if (!run_plugins(conn)) {
if (conn->srv->on_request)
@ -345,19 +327,6 @@ static int on_message_complete_cb(struct http_parser *parser)
buffer_pull(&conn->rb, NULL, buffer_length(&conn->rb));
if (req->url.path)
free(req->url.path);
if (req->url.query)
free(req->url.query);
for (i = 0; i < UHTTPD_MAX_HEADER_NUM; i++) {
if (req->headers[i].name)
free(req->headers[i].name);
if (req->headers[i].value)
free(req->headers[i].value);
}
memset(req, 0, sizeof(struct uh_request));
return 0;
@ -441,7 +410,7 @@ static void conn_write_cb(struct ev_loop *loop, struct ev_io *w, int revents)
if (buffer_length(&conn->wb) == 0) {
if (conn->file.fd > 0) {
ssize_t ret = sendfile(w->fd, conn->file.fd, NULL, conn->file.size);
ret = sendfile(w->fd, conn->file.fd, NULL, conn->file.size);
if (ret < 0) {
if (errno != EAGAIN) {
uh_log_err("write error: %s\n", strerror(errno));

View File

@ -41,31 +41,22 @@
struct uh_server;
struct uh_request {
struct {
int length;
int offset;
char *path;
char *query;
} url;
struct uh_str {
const char *p;
size_t len;
};
struct uh_request {
struct uh_str url;
struct {
int name_offset;
int name_len;
int value_offset;
int value_len;
} headers_info[UHTTPD_MAX_HEADER_NUM];
int header_num;
bool last_was_header_value;
struct {
char *name;
char *value;
struct uh_str field;
struct uh_str value;
} headers[UHTTPD_MAX_HEADER_NUM];
struct {
int offset;
int len;
} body;
struct uh_str body;
};
struct uh_connection {
@ -107,10 +98,10 @@ struct uh_connection {
void (*chunk_end)(struct uh_connection *conn);
enum http_method (*get_method)(struct uh_connection *conn);
const char *(*get_method_str)(struct uh_connection *conn);
const char *(*get_path)(struct uh_connection *conn);
const char *(*get_query)(struct uh_connection *conn);
const char *(*get_header)(struct uh_connection *conn, const char *name);
const char *(*get_body)(struct uh_connection *conn, int *len);
const struct uh_str (*get_path)(struct uh_connection *conn);
const struct uh_str (*get_query)(struct uh_connection *conn);
const struct uh_str (*get_header)(struct uh_connection *conn, const char *name);
const struct uh_str (*get_body)(struct uh_connection *conn);
};
struct uh_connection *uh_new_connection(struct uh_server *srv, int sock, struct sockaddr_in *addr);

View File

@ -32,11 +32,12 @@
#include <string.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <inttypes.h>
#include "connection.h"
#include "mimetypes.h"
#include "log.h"
static const char *file_mktag(struct stat *s, char *buf, int len)
{
@ -55,13 +56,16 @@ static char *unix2date(time_t ts, char *buf, int len)
return buf;
}
static time_t date2unix(const char *date)
static time_t date2unix(const struct uh_str date)
{
struct tm t;
char buf[128] = "";
memset(&t, 0, sizeof(t));
if (strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) != NULL)
strncpy(buf, date.p, date.len);
if (strptime(buf, "%a, %d %b %Y %H:%M:%S %Z", &t) != NULL)
return timegm(&t);
return 0;
@ -88,8 +92,8 @@ static void file_response_304(struct uh_connection *conn, struct stat *s)
static bool file_if_modified_since(struct uh_connection *conn, struct stat *s)
{
const char *hdr = conn->get_header(conn, "If-Modified-Since");
if (!hdr)
const struct uh_str hdr = conn->get_header(conn, "If-Modified-Since");
if (!hdr.p)
return true;
if (date2unix(hdr) >= s->st_mtime) {
@ -102,7 +106,8 @@ static bool file_if_modified_since(struct uh_connection *conn, struct stat *s)
static bool file_if_range(struct uh_connection *conn, struct stat *s)
{
if (conn->get_header(conn, "If-Range")) {
const struct uh_str hdr = conn->get_header(conn, "If-Range");
if (hdr.p) {
conn->error(conn, HTTP_STATUS_PRECONDITION_FAILED, NULL);
return false;
}
@ -112,8 +117,8 @@ static bool file_if_range(struct uh_connection *conn, struct stat *s)
static bool file_if_unmodified_since(struct uh_connection *conn, struct stat *s)
{
const char *hdr = conn->get_header(conn, "If-Modified-Since");
if (hdr && date2unix(hdr) <= s->st_mtime) {
const struct uh_str hdr = conn->get_header(conn, "If-Modified-Since");
if (hdr.p && date2unix(hdr) <= s->st_mtime) {
conn->error(conn, HTTP_STATUS_PRECONDITION_FAILED, NULL);
return false;
}
@ -121,22 +126,37 @@ static bool file_if_unmodified_since(struct uh_connection *conn, struct stat *s)
return true;
}
static void file_if_gzip(struct uh_connection *conn, const char *path)
static void file_if_gzip(struct uh_connection *conn, const char *path, const char *mime)
{
const char *hdr = conn->get_header(conn, "Accept-Encoding");
const char *extn = rindex(path, '.');
const struct uh_str hdr = conn->get_header(conn, "Accept-Encoding");
uint8_t magic[2] = {};
int fd;
if (!hdr || !strstr(hdr, "gzip"))
if (!hdr.p || !memmem(hdr.p, hdr.len, "gzip", 4))
return;
if (extn && !strcmp(extn, ".gz"))
conn->printf(conn, "Content-Encoding: gzip\r\n");
if (strcmp(mime, "text/css") && strcmp(mime, "text/javascript") && strcmp(mime, "text/html"))
return;
fd = open(path, O_RDONLY);
if (read(fd, magic, 2) != 2) {
close(fd);
return;
}
close(fd);
/* gzip magic */
if (magic[0] != 0x1f || magic[1] != 0x8b)
return;
conn->printf(conn, "Content-Encoding: gzip\r\n");
}
void serve_file(struct uh_connection *conn, const char *docroot, const char *index_page)
{
const char *path = conn->get_path(conn);
const struct uh_str path = conn->get_path(conn);
static char fullpath[512];
const char *mime;
struct stat st;
if (!docroot || !docroot[0])
@ -147,12 +167,12 @@ void serve_file(struct uh_connection *conn, const char *docroot, const char *ind
strcpy(fullpath, docroot);
if (!strcmp(path, "/")) {
if (!strncmp(path.p, "/", path.len)) {
strcat(fullpath, "/");
path = index_page;
strcat(fullpath, index_page);
} else {
strncat(fullpath, path.p, path.len);
}
strcat(fullpath, path);
if (stat(fullpath, &st) < 0) {
int code;
@ -196,10 +216,12 @@ void serve_file(struct uh_connection *conn, const char *docroot, const char *ind
conn->send_status_line(conn, HTTP_STATUS_OK, NULL);
file_response_ok_hdrs(conn, &st);
conn->printf(conn, "Content-Type: %s\r\n", file_mime_lookup(path));
mime = file_mime_lookup(fullpath);
conn->printf(conn, "Content-Type: %s\r\n", mime);
conn->printf(conn, "Content-Length: %" PRIu64 "\r\n", st.st_size);
file_if_gzip(conn, path);
file_if_gzip(conn, fullpath, mime);
conn->printf(conn, "\r\n");

View File

@ -102,8 +102,8 @@ const char *file_mime_lookup(const char *path)
while (m->extn) {
e = &path[strlen(path)-1];
while (e >= path) {
if ((*e == '.' || *e == '/') && !strcasecmp(&e[1], m->extn))
while (e >= path && *e != '/') {
if (*e == '.' && !strcasecmp(&e[1], m->extn))
return m->mime;
e--;