Accept and Content-Type headers

This commit is contained in:
2022-07-13 07:57:30 -05:00
parent 45aa67062f
commit c289690877
7 changed files with 252 additions and 60 deletions

View File

@@ -50,6 +50,7 @@ sitefiles also allow comments with #
* POST * POST
* ```host``` - The hostname to respond to. Case insensitive regex, default: .* * ```host``` - The hostname to respond to. Case insensitive regex, default: .*
* ```port``` - The ports to respond to in a comma separated list. Default: 80 * ```port``` - The ports to respond to in a comma separated list. Default: 80
* ```type``` - The content-type (default: text/html)
# Part 4: Global variables # Part 4: Global variables

View File

@@ -18,6 +18,8 @@ define library ./library.so
set host localhost:8000 set host localhost:8000
# The following pages will respond to the 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 read / site/index.html
# The path / should be read from site/index.html # The path / should be read from site/index.html
read /hello site/hello.html read /hello site/hello.html
@@ -35,7 +37,9 @@ set host .*
read / site/easteregg.html read / site/easteregg.html
# The path / should be read from site/easteregg.html # The path / should be read from site/easteregg.html
set type image/png
read /egg.png site/egg.png read /egg.png site/egg.png
set type text/html
# The path /egg.png should be read from site/egg.png # The path /egg.png should be read from site/egg.png
read /alldomains site/alldomains.html read /alldomains site/alldomains.html
# The path /alldomains should be read from /site/alldomains.html # The path /alldomains should be read from /site/alldomains.html

View File

@@ -18,6 +18,7 @@
#include <features.h> #include <features.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -31,9 +32,11 @@
#include <swebs/responses.h> #include <swebs/responses.h>
#include <swebs/responseutil.h> #include <swebs/responseutil.h>
static int readResponse(Connection *conn, char *path) { static int readResponse(Connection *conn, SiteCommand *command) {
int fd = -1; int fd = -1;
struct stat statbuf; struct stat statbuf;
char *path;
path = command->arg;
if (stat(path, &statbuf)) { if (stat(path, &statbuf)) {
sendErrorResponse(conn->stream, ERROR_404); sendErrorResponse(conn->stream, ERROR_404);
return 1; return 1;
@@ -95,7 +98,19 @@ static int readResponse(Connection *conn, char *path) {
fd = open(path, O_RDONLY); fd = open(path, O_RDONLY);
if (fd < 0) if (fd < 0)
goto forbidden; 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: error:
sendErrorResponse(conn->stream, ERROR_500); sendErrorResponse(conn->stream, ERROR_500);
return 1; return 1;
@@ -126,14 +141,14 @@ static int linkedResponse(Connection *conn,
case FILE_KNOWN_LENGTH: case FILE_KNOWN_LENGTH:
return sendKnownPipe(conn->stream, getCode(code), return sendKnownPipe(conn->stream, getCode(code),
response.response.file.fd, response.response.file.fd,
response.response.file.len); response.response.file.len, NULL);
case FILE_UNKNOWN_LENGTH: case FILE_UNKNOWN_LENGTH:
return sendPipe(conn->stream, getCode(code), return sendPipe(conn->stream, getCode(code),
response.response.file.fd); response.response.file.fd, NULL);
case BUFFER: case BUFFER_NOFREE: case BUFFER: case BUFFER_NOFREE:
ret = sendBinaryResponse(conn->stream, getCode(code), ret = sendBinaryResponse(conn->stream, getCode(code),
response.response.buffer.data, response.response.buffer.data,
response.response.buffer.len); response.response.buffer.len, NULL);
if (response.type == BUFFER) if (response.type == BUFFER)
free(response.response.buffer.data); free(response.response.buffer.data);
return ret; return ret;
@@ -150,16 +165,142 @@ static int fullmatch(regex_t *regex, char *str) {
return match.rm_so != 0 || match.rm_eo != strlen(str); return match.rm_so != 0 || match.rm_eo != strlen(str);
} }
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;
}
}
{
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) { int sendResponse(Connection *conn, Sitefile *site) {
char *host = NULL; char *host = NULL;
char *accept = NULL;
int i; int i;
for (i = 0; i < conn->fieldCount; i++) { for (i = 0; i < conn->fieldCount; i++) {
if (strcmp(conn->fields[i].field, "Host") == 0) { if (strcmp(conn->fields[i].field, "Host") == 0)
host = conn->fields[i].value; host = conn->fields[i].value;
break; else if (strcmp(conn->fields[i].field, "Accept") == 0)
accept = conn->fields[i].value;
} }
} if (host == NULL || accept == NULL) {
if (host == NULL) {
sendErrorResponse(conn->stream, ERROR_400); sendErrorResponse(conn->stream, ERROR_400);
return 1; return 1;
} }
@@ -168,6 +309,8 @@ int sendResponse(Connection *conn, Sitefile *site) {
continue; continue;
if (fullmatch(&site->content[i].host, host)) if (fullmatch(&site->content[i].host, host))
continue; continue;
if (!wasasked(accept, site->content[i].contenttype))
continue;
{ {
int j; int j;
const unsigned short currport = site->ports[conn->portind].num; const unsigned short currport = site->ports[conn->portind].num;
@@ -181,7 +324,7 @@ foundport:
switch (site->content[i].command) { switch (site->content[i].command) {
case READ: case READ:
if (readResponse(conn, if (readResponse(conn,
site->content[i].arg)) site->content + i))
return 1; return 1;
break; break;
case THROW: case THROW:

View File

@@ -68,6 +68,30 @@ static int sendStreamValist(Stream *stream, char *format, ...) {
return 0; 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) { char *getCode(int code) {
switch (code) { switch (code) {
case 200: case 200:
@@ -85,15 +109,14 @@ char *getCode(int code) {
} }
} }
int sendStringResponse(Stream *stream, const char *status, char *str) { int sendStringResponse(Stream *stream, const char *status, char *str, ...) {
return sendStreamValist(stream, va_list ap;
"HTTP/1.1 %s\r\n" size_t len;
CONST_FIELDS va_start(ap, str);
"Content-Length: %lu\r\n" len = strlen(str);
"\r\n" if (sendHeaderValist(stream, status, len, ap))
"%s" return 1;
, status, strlen(str), str return resilientSend(stream, str, len);
);
} }
int sendErrorResponse(Stream *stream, const char *error) { 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); int len = snprintf(NULL, 0, template, error);
char *response = malloc(len + 1); char *response = malloc(len + 1);
sprintf(response, template, error); sprintf(response, template, error);
ret = sendStringResponse(stream, error, response); ret = sendStringResponse(stream, error, response,
"Content-Type: text/html\r\n", NULL);
free(response); free(response);
return ret; return ret;
} }
int sendBinaryResponse(Stream *stream, const char *status, static int sendBinaryResponseValist(Stream *stream, const char *status,
void *data, size_t len) { void *data, size_t len, va_list ap) {
if (sendHeader(stream, status, len)) if (sendHeaderValist(stream, status, len, ap))
return 1; return 1;
return resilientSend(stream, data, len); return resilientSend(stream, data, len);
} }
int sendHeader(Stream *stream, const char *status, size_t len) { static int sendKnownPipeValist(Stream *stream, const char *status,
return (sendStreamValist(stream, int fd, size_t len, va_list ap) {
"HTTP/1.1 %s\r\n" size_t totalSent = 0;
CONST_FIELDS sendHeaderValist(stream, status, len, ap);
"Content-Length: %lu\r\n" for (;;) {
"\r\n" char buffer[1024];
, status, len)); 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; off_t len;
va_list ap;
len = lseek(fd, 0, SEEK_END); len = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET); 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 allocResponse = 1024;
size_t responseLen = 0; size_t responseLen = 0;
char *response = malloc(allocResponse); char *response = malloc(allocResponse);
va_list ap;
for (;;) { for (;;) {
ssize_t len; ssize_t len;
if (responseLen >= allocResponse) { if (responseLen >= allocResponse) {
@@ -158,7 +205,7 @@ int sendPipe(Stream *stream, const char *status, int fd) {
responseLen += len; responseLen += len;
} }
close(fd); close(fd);
sendHeader(stream, CODE_200, responseLen); sendHeaderValist(stream, CODE_200, responseLen, ap);
if (resilientSend(stream, response, responseLen)) { if (resilientSend(stream, response, responseLen)) {
free(response); free(response);
return 1; return 1;
@@ -171,18 +218,3 @@ error:
sendErrorResponse(stream, ERROR_500); sendErrorResponse(stream, ERROR_500);
return 1; 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;
}
}

View File

@@ -223,6 +223,7 @@ Sitefile *parseSitefile(char *path) {
Sitefile *ret; Sitefile *ret;
unsigned short *ports; unsigned short *ports;
int portcount; int portcount;
char *contenttype;
file = fopen(path, "r"); file = fopen(path, "r");
if (file == NULL) if (file == NULL)
@@ -243,6 +244,8 @@ Sitefile *parseSitefile(char *path) {
ret->getResponse = NULL; ret->getResponse = NULL;
#endif #endif
contenttype = xstrdup("text/html");
for (;;) { for (;;) {
ReturnCode status = getCommand(file, &argc, &argv); ReturnCode status = getCommand(file, &argc, &argv);
switch (status) { switch (status) {
@@ -274,8 +277,10 @@ Sitefile *parseSitefile(char *path) {
if (respondto == INVALID) if (respondto == INVALID)
goto error; goto error;
} }
else if (strcmp(argv[1], "host") == 0) else if (strcmp(argv[1], "host") == 0) {
free(host);
host = xstrdup(argv[2]); host = xstrdup(argv[2]);
}
else if (strcmp(argv[1], "port") == 0) { else if (strcmp(argv[1], "port") == 0) {
free(ports); free(ports);
if (getports(&ports, &portcount, argv[2])) { if (getports(&ports, &portcount, argv[2])) {
@@ -284,6 +289,10 @@ Sitefile *parseSitefile(char *path) {
goto error; goto error;
} }
} }
else if (strcmp(argv[1], "type") == 0) {
free(contenttype);
contenttype = strdup(argv[2]);
}
else else
goto error; goto error;
continue; continue;
@@ -417,6 +426,8 @@ Sitefile *parseSitefile(char *path) {
memcpy(ret->content[ret->size].ports, ports, portcount * sizeof *ports); memcpy(ret->content[ret->size].ports, ports, portcount * sizeof *ports);
ret->content[ret->size].portcount = portcount; ret->content[ret->size].portcount = portcount;
ret->content[ret->size].contenttype = xstrdup(contenttype);
ret->size++; ret->size++;
} }
error: error:

View File

@@ -26,13 +26,13 @@
#define ERROR_500 "500 Internal Server Error" #define ERROR_500 "500 Internal Server Error"
char *getCode(int code); 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, int sendBinaryResponse(Stream *stream, const char *status,
void *data, size_t len); void *data, size_t len, ...);
int sendErrorResponse(Stream *stream, const char *error); int sendErrorResponse(Stream *stream, const char *error);
/* sendErrorResponse(conn, ERROR_404); */ /* sendErrorResponse(conn, ERROR_404); */
int sendHeader(Stream *stream, const char *status, size_t len); int sendHeader(Stream *stream, const char *status, size_t len, ...);
int sendSeekableFile(Stream *stream, const char *status, int fd); int sendSeekableFile(Stream *stream, const char *status, int fd, ...);
int sendPipe(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 sendKnownPipe(Stream *stream, const char *status, int fd, size_t len, ...);
#endif #endif

View File

@@ -45,6 +45,7 @@ typedef struct {
char *arg; char *arg;
unsigned short *ports; unsigned short *ports;
int portcount; int portcount;
char *contenttype;
} SiteCommand; } SiteCommand;
typedef struct { typedef struct {