From c289690877d15ff475f2421fe0398c66caf7bbd5 Mon Sep 17 00:00:00 2001 From: Nate Choe Date: Wed, 13 Jul 2022 07:57:30 -0500 Subject: [PATCH] Accept and Content-Type headers --- documentation/sitefiles.md | 1 + site/sitefile | 4 + src/responses.c | 171 ++++++++++++++++++++++++++++++++++--- src/responseutil.c | 110 +++++++++++++++--------- src/sitefile.c | 13 ++- src/swebs/responseutil.h | 12 +-- src/swebs/sitefile.h | 1 + 7 files changed, 252 insertions(+), 60 deletions(-) diff --git a/documentation/sitefiles.md b/documentation/sitefiles.md index 4d56f9d..8d7e1ee 100644 --- a/documentation/sitefiles.md +++ b/documentation/sitefiles.md @@ -50,6 +50,7 @@ sitefiles also allow comments with # * POST * ```host``` - The hostname to respond to. Case insensitive regex, default: .* * ```port``` - The ports to respond to in a comma separated list. Default: 80 +* ```type``` - The content-type (default: text/html) # Part 4: Global variables diff --git a/site/sitefile b/site/sitefile index 137d2b3..c685a91 100644 --- a/site/sitefile +++ b/site/sitefile @@ -18,6 +18,8 @@ define library ./library.so set host localhost:8000 # The following pages will respond to the host localhost:8000 +set type text/html +# The following pages are html read / site/index.html # The path / should be read from site/index.html read /hello site/hello.html @@ -35,7 +37,9 @@ set host .* read / site/easteregg.html # The path / should be read from site/easteregg.html +set type image/png read /egg.png site/egg.png +set type text/html # The path /egg.png should be read from site/egg.png read /alldomains site/alldomains.html # The path /alldomains should be read from /site/alldomains.html diff --git a/src/responses.c b/src/responses.c index ab7fbb9..26ead4e 100644 --- a/src/responses.c +++ b/src/responses.c @@ -18,6 +18,7 @@ #include +#include #include #include @@ -31,9 +32,11 @@ #include #include -static int readResponse(Connection *conn, char *path) { +static int readResponse(Connection *conn, SiteCommand *command) { int fd = -1; struct stat statbuf; + char *path; + path = command->arg; if (stat(path, &statbuf)) { sendErrorResponse(conn->stream, ERROR_404); return 1; @@ -95,7 +98,19 @@ static int readResponse(Connection *conn, char *path) { fd = open(path, O_RDONLY); if (fd < 0) goto forbidden; - return sendSeekableFile(conn->stream, CODE_200, fd); + { + int ret; + char *contenthead, *contenttype; + const char *template = "Content-Type: %s\r\n"; + contenttype = command->contenttype; + contenthead = malloc(snprintf(NULL, 0, template, contenttype) + 1); + if (contenthead == NULL) + return 1; + sprintf(contenthead, template, contenttype); + ret = sendSeekableFile(conn->stream, CODE_200, fd, contenthead, NULL); + free(contenthead); + return ret; + } error: sendErrorResponse(conn->stream, ERROR_500); return 1; @@ -126,14 +141,14 @@ static int linkedResponse(Connection *conn, case FILE_KNOWN_LENGTH: return sendKnownPipe(conn->stream, getCode(code), response.response.file.fd, - response.response.file.len); + response.response.file.len, NULL); case FILE_UNKNOWN_LENGTH: return sendPipe(conn->stream, getCode(code), - response.response.file.fd); + response.response.file.fd, NULL); case BUFFER: case BUFFER_NOFREE: ret = sendBinaryResponse(conn->stream, getCode(code), response.response.buffer.data, - response.response.buffer.len); + response.response.buffer.len, NULL); if (response.type == BUFFER) free(response.response.buffer.data); return ret; @@ -150,16 +165,142 @@ static int fullmatch(regex_t *regex, char *str) { return match.rm_so != 0 || match.rm_eo != strlen(str); } -int sendResponse(Connection *conn, Sitefile *site) { - char *host = NULL; - int i; - for (i = 0; i < conn->fieldCount; i++) { - if (strcmp(conn->fields[i].field, "Host") == 0) { - host = conn->fields[i].value; - break; +static char *nextdirective(char *header) { + char *loc; + loc = strstr(header, "; "); + if (loc == NULL) + return NULL; + return loc + 2; +} + +static char *gettype(char *request, char **type) { + char *typeret; + char *ret; + { + char *next; + next = strstr(request, ", "); + if (next == NULL) { + typeret = strdup(request); + if (typeret == NULL) { + *type = NULL; + return NULL; + } + ret = NULL; + } + else { + size_t biglen; + biglen = next - request; + typeret = malloc(biglen + 1); + if (typeret == NULL) { + *type = NULL; + return NULL; + } + memcpy(typeret, request, biglen); + typeret[biglen] = '\0'; + ret = next + 2; } } - if (host == NULL) { + { + char *set; + set = strchr(typeret, ';'); + if (set != NULL) + set[0] = '\0'; + } + *type = typeret; + return ret; +} + +static int ismatch(char *request, char *type) { +/* Matches a single MIME type. Note that * /html is valid for request. */ + int i; + if (request[0] == '\0' || type[0] == '\0') + return request[0] != type[0]; + + if (request[0] == '*' && (request[1] == '/' || request[1] == '\0')) { + char *nexttype; + nexttype = type; + while (nexttype[0] != '/' && nexttype[0] != '\0') + ++nexttype; + if (request[1] != nexttype[0]) + return 0; + if (nexttype[0] == '\0') + return 1; + return ismatch(request + 2, type + 1); + } + + for (i = 0; request[i] == type[i] && + request[i] != '/' && request[i] != '\0'; ++i) ; + if (request[i] != type[i]) + return 0; + if (request[i] == '\0') + return 1; + return ismatch(request + i + 1, type + i + 1); +} + +static int wasasked(char *request, char *type) { +/* request is the Accept header field and type is the type of the page. */ + char *mimetype; /* the actual mime type*/ + char *checkloc; + { + char *typeptr; + mimetype = NULL; + typeptr = type; + while (mimetype == NULL) { + char *next; + if (typeptr == NULL) + return 0; + next = nextdirective(typeptr); + if (strncmp(typeptr, "charset=", 8) == 0) { + typeptr = next; + continue; + } + if (strncmp(typeptr, "boundary=", 9) == 0) { + typeptr = next; + continue; + } + if (next == NULL) { + mimetype = strdup(typeptr); + if (mimetype == NULL) + return 0; + } + else { + size_t mimelen; + mimelen = next - typeptr; + mimetype = malloc(mimelen + 1); + if (mimetype == NULL) + return 0; + memcpy(mimetype, typeptr, mimelen); + mimetype[mimelen] = '\0'; + } + } + } + + checkloc = request; + while (checkloc != NULL) { + char *check; + checkloc = gettype(checkloc, &check); + if (check == NULL) + return 0; + if (ismatch(check, mimetype)) { + free(check); + return 1; + } + free(check); + } + return 0; +} + +int sendResponse(Connection *conn, Sitefile *site) { + char *host = NULL; + char *accept = NULL; + int i; + for (i = 0; i < conn->fieldCount; i++) { + if (strcmp(conn->fields[i].field, "Host") == 0) + host = conn->fields[i].value; + else if (strcmp(conn->fields[i].field, "Accept") == 0) + accept = conn->fields[i].value; + } + if (host == NULL || accept == NULL) { sendErrorResponse(conn->stream, ERROR_400); return 1; } @@ -168,6 +309,8 @@ int sendResponse(Connection *conn, Sitefile *site) { continue; if (fullmatch(&site->content[i].host, host)) continue; + if (!wasasked(accept, site->content[i].contenttype)) + continue; { int j; const unsigned short currport = site->ports[conn->portind].num; @@ -181,7 +324,7 @@ foundport: switch (site->content[i].command) { case READ: if (readResponse(conn, - site->content[i].arg)) + site->content + i)) return 1; break; case THROW: diff --git a/src/responseutil.c b/src/responseutil.c index 564e2eb..3fe7933 100644 --- a/src/responseutil.c +++ b/src/responseutil.c @@ -68,6 +68,30 @@ static int sendStreamValist(Stream *stream, char *format, ...) { return 0; } +static int sendHeaderValist(Stream *stream, const char *status, size_t len, va_list ap) { + if (sendStreamValist(stream, + "HTTP/1.1 %s\r\n" + CONST_FIELDS + "Content-Length: %lu\r\n", status, len)) + return 1; + for (;;) { + char *header; + header = va_arg(ap, char *); + if (header == NULL) + break; + if (resilientSend(stream, header, strlen(header))) + return 1; + } + va_end(ap); + return resilientSend(stream, "\r\n", 2); +} + +int sendHeader(Stream *stream, const char *status, size_t len, ...) { + va_list ap; + va_start(ap, len); + return sendHeaderValist(stream, status, len, ap); +} + char *getCode(int code) { switch (code) { case 200: @@ -85,15 +109,14 @@ char *getCode(int code) { } } -int sendStringResponse(Stream *stream, const char *status, char *str) { - return sendStreamValist(stream, - "HTTP/1.1 %s\r\n" - CONST_FIELDS - "Content-Length: %lu\r\n" - "\r\n" - "%s" - , status, strlen(str), str - ); +int sendStringResponse(Stream *stream, const char *status, char *str, ...) { + va_list ap; + size_t len; + va_start(ap, str); + len = strlen(str); + if (sendHeaderValist(stream, status, len, ap)) + return 1; + return resilientSend(stream, str, len); } int sendErrorResponse(Stream *stream, const char *error) { @@ -106,38 +129,62 @@ int sendErrorResponse(Stream *stream, const char *error) { int len = snprintf(NULL, 0, template, error); char *response = malloc(len + 1); sprintf(response, template, error); - ret = sendStringResponse(stream, error, response); + ret = sendStringResponse(stream, error, response, + "Content-Type: text/html\r\n", NULL); free(response); return ret; } -int sendBinaryResponse(Stream *stream, const char *status, - void *data, size_t len) { - if (sendHeader(stream, status, len)) +static int sendBinaryResponseValist(Stream *stream, const char *status, + void *data, size_t len, va_list ap) { + if (sendHeaderValist(stream, status, len, ap)) return 1; return resilientSend(stream, data, len); } -int sendHeader(Stream *stream, const char *status, size_t len) { - return (sendStreamValist(stream, - "HTTP/1.1 %s\r\n" - CONST_FIELDS - "Content-Length: %lu\r\n" - "\r\n" - , status, len)); +static int sendKnownPipeValist(Stream *stream, const char *status, + int fd, size_t len, va_list ap) { + size_t totalSent = 0; + sendHeaderValist(stream, status, len, ap); + for (;;) { + char buffer[1024]; + ssize_t inBuffer = read(fd, buffer, sizeof(buffer)); + if (inBuffer < 0) + return 1; + if (inBuffer == 0) + return totalSent != len; + if (resilientSend(stream, buffer, inBuffer)) + return 1; + } } -int sendSeekableFile(Stream *stream, const char *status, int fd) { +int sendKnownPipe(Stream *stream, const char *status, int fd, size_t len, ...) { + va_list ap; + va_start(ap, len); + return sendKnownPipeValist(stream, status, fd, len, ap); +} + +int sendBinaryResponse(Stream *stream, const char *status, + void *data, size_t len, ...) { + va_list ap; + va_start(ap, len); + return sendBinaryResponseValist(stream, status, data, len, ap); +} + +int sendSeekableFile(Stream *stream, const char *status, int fd, ...) { off_t len; + va_list ap; len = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); - return sendKnownPipe(stream, status, fd, len); + va_start(ap, fd); + return sendKnownPipeValist(stream, status, fd, len, ap); } -int sendPipe(Stream *stream, const char *status, int fd) { +int sendPipe(Stream *stream, const char *status, int fd, ...) { size_t allocResponse = 1024; size_t responseLen = 0; char *response = malloc(allocResponse); + va_list ap; for (;;) { ssize_t len; if (responseLen >= allocResponse) { @@ -158,7 +205,7 @@ int sendPipe(Stream *stream, const char *status, int fd) { responseLen += len; } close(fd); - sendHeader(stream, CODE_200, responseLen); + sendHeaderValist(stream, CODE_200, responseLen, ap); if (resilientSend(stream, response, responseLen)) { free(response); return 1; @@ -171,18 +218,3 @@ error: sendErrorResponse(stream, ERROR_500); return 1; } - -int sendKnownPipe(Stream *stream, const char *status, int fd, size_t len) { - size_t totalSent = 0; - sendHeader(stream, status, len); - for (;;) { - char buffer[1024]; - ssize_t inBuffer = read(fd, buffer, sizeof(buffer)); - if (inBuffer < 0) - return 1; - if (inBuffer == 0) - return totalSent != len; - if (resilientSend(stream, buffer, inBuffer)) - return 1; - } -} diff --git a/src/sitefile.c b/src/sitefile.c index 00649bb..d699ae5 100644 --- a/src/sitefile.c +++ b/src/sitefile.c @@ -223,6 +223,7 @@ Sitefile *parseSitefile(char *path) { Sitefile *ret; unsigned short *ports; int portcount; + char *contenttype; file = fopen(path, "r"); if (file == NULL) @@ -243,6 +244,8 @@ Sitefile *parseSitefile(char *path) { ret->getResponse = NULL; #endif + contenttype = xstrdup("text/html"); + for (;;) { ReturnCode status = getCommand(file, &argc, &argv); switch (status) { @@ -274,8 +277,10 @@ Sitefile *parseSitefile(char *path) { if (respondto == INVALID) goto error; } - else if (strcmp(argv[1], "host") == 0) + else if (strcmp(argv[1], "host") == 0) { + free(host); host = xstrdup(argv[2]); + } else if (strcmp(argv[1], "port") == 0) { free(ports); if (getports(&ports, &portcount, argv[2])) { @@ -284,6 +289,10 @@ Sitefile *parseSitefile(char *path) { goto error; } } + else if (strcmp(argv[1], "type") == 0) { + free(contenttype); + contenttype = strdup(argv[2]); + } else goto error; continue; @@ -417,6 +426,8 @@ Sitefile *parseSitefile(char *path) { memcpy(ret->content[ret->size].ports, ports, portcount * sizeof *ports); ret->content[ret->size].portcount = portcount; + ret->content[ret->size].contenttype = xstrdup(contenttype); + ret->size++; } error: diff --git a/src/swebs/responseutil.h b/src/swebs/responseutil.h index c7c3a49..a84d92a 100644 --- a/src/swebs/responseutil.h +++ b/src/swebs/responseutil.h @@ -26,13 +26,13 @@ #define ERROR_500 "500 Internal Server Error" char *getCode(int code); -int sendStringResponse(Stream *stream, const char *status, char *str); +int sendStringResponse(Stream *stream, const char *status, char *str, ...); int sendBinaryResponse(Stream *stream, const char *status, - void *data, size_t len); + void *data, size_t len, ...); int sendErrorResponse(Stream *stream, const char *error); /* sendErrorResponse(conn, ERROR_404); */ -int sendHeader(Stream *stream, const char *status, size_t len); -int sendSeekableFile(Stream *stream, const char *status, int fd); -int sendPipe(Stream *stream, const char *status, int fd); -int sendKnownPipe(Stream *stream, const char *status, int fd, size_t len); +int sendHeader(Stream *stream, const char *status, size_t len, ...); +int sendSeekableFile(Stream *stream, const char *status, int fd, ...); +int sendPipe(Stream *stream, const char *status, int fd, ...); +int sendKnownPipe(Stream *stream, const char *status, int fd, size_t len, ...); #endif diff --git a/src/swebs/sitefile.h b/src/swebs/sitefile.h index c8cef22..96b9a2c 100644 --- a/src/swebs/sitefile.h +++ b/src/swebs/sitefile.h @@ -45,6 +45,7 @@ typedef struct { char *arg; unsigned short *ports; int portcount; + char *contenttype; } SiteCommand; typedef struct {