From 28c814674072fa18e86c57e6f9fa2ec4d0d300bd Mon Sep 17 00:00:00 2001 From: zhaojh329 Date: Mon, 7 Sep 2020 10:03:32 +0800 Subject: [PATCH] Optimize code and fix gzip encoding Signed-off-by: zhaojh329 --- example/example.c | 14 +++--- src/connection.c | 113 +++++++++++++++++----------------------------- src/connection.h | 37 ++++++--------- src/file.c | 64 +++++++++++++++++--------- src/mimetypes.c | 4 +- 5 files changed, 108 insertions(+), 124 deletions(-) diff --git a/example/example.c b/example/example.c index 9f70bf6..378ec68 100644 --- a/example/example.c +++ b/example/example.c @@ -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); diff --git a/src/connection.c b/src/connection.c index 52b2182..95f2b3d 100644 --- a/src/connection.c +++ b/src/connection.c @@ -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)); diff --git a/src/connection.h b/src/connection.h index d998372..8a02f91 100644 --- a/src/connection.h +++ b/src/connection.h @@ -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); diff --git a/src/file.c b/src/file.c index 70151e3..74d096e 100644 --- a/src/file.c +++ b/src/file.c @@ -32,11 +32,12 @@ #include #include #include +#include #include #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"); diff --git a/src/mimetypes.c b/src/mimetypes.c index 5c3f243..929090a 100644 --- a/src/mimetypes.c +++ b/src/mimetypes.c @@ -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--;