From 7ba2670512c5819a74a590c121f254dff1161e7d Mon Sep 17 00:00:00 2001 From: Godmar Back Date: Wed, 14 Apr 2021 00:31:37 -0400 Subject: [PATCH] various fixes for Spring 2021 - support for 206 return code - fixes to sendfile - add URL variable to testloginapi.sh --- src/bufio.c | 5 ++++- src/bufio.h | 2 +- src/http.c | 22 +++++++++++++++++----- src/http.h | 1 + src/testloginapi.sh | 13 +++++++++---- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/bufio.c b/src/bufio.c index 9fc2970..308907a 100644 --- a/src/bufio.c +++ b/src/bufio.c @@ -108,6 +108,9 @@ read_more(struct bufio *self) /* Given an offset into the buffer, return a char *. * This pointer will be valid only until the next call * to any of the bufio_read* function. + * + * The returned pointer is not guaranteed to point to a + * zero-terminated string. */ char * bufio_offset2ptr(struct bufio *self, size_t offset) @@ -194,7 +197,7 @@ bufio_read(struct bufio *self, size_t count, size_t *buf_offset) * See sendfile(2) for return value. */ ssize_t -bufio_sendfile(struct bufio *self, int fd, off_t *off, int filesize) +bufio_sendfile(struct bufio *self, int fd, off_t *off, size_t filesize) { return sendfile(self->socket, fd, off, filesize); } diff --git a/src/bufio.h b/src/bufio.h index 3b21ef3..c4c8432 100644 --- a/src/bufio.h +++ b/src/bufio.h @@ -13,7 +13,7 @@ ssize_t bufio_readline(struct bufio *self, size_t *line_offset); ssize_t bufio_read(struct bufio *self, size_t count, size_t *buf_offset); char * bufio_offset2ptr(struct bufio *self, size_t offset); size_t bufio_ptr2offset(struct bufio *self, char *ptr); -ssize_t bufio_sendfile(struct bufio *self, int fd, off_t *off, int filesize); +ssize_t bufio_sendfile(struct bufio *self, int fd, off_t *off, size_t filesize); ssize_t bufio_sendbuffer(struct bufio *self, buffer_t *response); #endif /* _BUFIO_H */ diff --git a/src/http.c b/src/http.c index 1560d56..23b9655 100644 --- a/src/http.c +++ b/src/http.c @@ -28,6 +28,7 @@ // Need macros here because of the sizeof #define CRLF "\r\n" +#define CR "\r" #define STARTS_WITH(field_name, header) \ (!strncasecmp(field_name, header, sizeof(header) - 1)) @@ -41,6 +42,7 @@ http_parse_request(struct http_transaction *ta) return false; char *request = bufio_offset2ptr(ta->client->bufio, req_offset); + request[len-2] = '\0'; // replace LF with 0 to ensure zero-termination char *endptr; char *method = strtok_r(request, " ", &endptr); if (method == NULL) @@ -59,10 +61,11 @@ http_parse_request(struct http_transaction *ta) ta->req_path = bufio_ptr2offset(ta->client->bufio, req_path); - char *http_version = strtok_r(NULL, CRLF, &endptr); + char *http_version = strtok_r(NULL, CR, &endptr); if (http_version == NULL) // would be HTTP 0.9 return false; + // record client's HTTP version in request if (!strcmp(http_version, "HTTP/1.1")) ta->req_version = HTTP_1_1; else if (!strcmp(http_version, "HTTP/1.0")) @@ -97,7 +100,7 @@ http_process_headers(struct http_transaction *ta) char *field_name = strtok_r(header, ":", &endptr); char *field_value = strtok_r(NULL, " \t", &endptr); // skip leading & trailing OWS - if (field_name == NULL) + if (field_name == NULL || field_value == NULL) return false; // printf("Header: %s: %s\n", field_name, field_value); @@ -147,6 +150,9 @@ start_response(struct http_transaction * ta, buffer_t *res) case HTTP_OK: buffer_appends(res, "200 OK"); break; + case HTTP_PARTIAL_CONTENT: + buffer_appends(res, "206 Partial Content"); + break; case HTTP_BAD_REQUEST: buffer_appends(res, "400 Bad Request"); break; @@ -295,20 +301,26 @@ handle_static_asset(struct http_transaction *ta, char *basedir) } ta->resp_status = HTTP_OK; - add_content_length(&ta->resp_headers, st.st_size); http_add_header(&ta->resp_headers, "Content-Type", "%s", guess_mime_type(fname)); + off_t from = 0, to = st.st_size - 1; + + off_t content_length = to + 1 - from; + add_content_length(&ta->resp_headers, content_length); bool success = send_response_header(ta); if (!success) goto out; - success = bufio_sendfile(ta->client->bufio, filefd, NULL, st.st_size) == st.st_size; + // sendfile may send fewer bytes than requested, hence the loop + while (success && from <= to) + success = bufio_sendfile(ta->client->bufio, filefd, &from, to + 1 - from) > 0; + out: close(filefd); return success; } -static int +static bool handle_api(struct http_transaction *ta) { return send_error(ta, HTTP_NOT_FOUND, "API not implemented"); diff --git a/src/http.h b/src/http.h index f0092b0..fae6024 100644 --- a/src/http.h +++ b/src/http.h @@ -20,6 +20,7 @@ enum http_version { enum http_response_status { HTTP_OK = 200, + HTTP_PARTIAL_CONTENT = 206, HTTP_BAD_REQUEST = 400, HTTP_PERMISSION_DENIED = 403, HTTP_NOT_FOUND = 404, diff --git a/src/testloginapi.sh b/src/testloginapi.sh index 32911cc..5cc419a 100644 --- a/src/testloginapi.sh +++ b/src/testloginapi.sh @@ -2,8 +2,13 @@ # change this as per instruction to avoid conflicts. PORT=10000 +# to test against a working implementation (and see the intended responses) +# change this URL=http://theta.cs.vt.edu:3000/ +URL=http://localhost:${PORT} + COOKIEJAR=cookies.txt + # clear cookies /bin/rm ${COOKIEJAR} @@ -12,20 +17,20 @@ curl -v -H "Content-Type: application/json" \ -c ${COOKIEJAR} \ -X POST \ -d '{"username":"user0","password":"thepassword"}' \ - http://localhost:${PORT}/api/login + ${URL}/api/login # this should succeed if the password is correct curl -v \ -b ${COOKIEJAR} \ - http://localhost:${PORT}/api/login + ${URL}/api/login # create a 'private' folder first. # this should fail since credentials were not presented curl -v \ - http://localhost:${PORT}/private/secret.txt + ${URL}/private/secret.txt # this should succeed since credentials were presented curl -v \ -b ${COOKIEJAR} \ - http://localhost:${PORT}/private/secret.txt + ${URl}/private/secret.txt