diff --git a/core/api.c b/core/api.c index dde67fd..7aff2e0 100644 --- a/core/api.c +++ b/core/api.c @@ -19,6 +19,59 @@ pthread_t listen_thread = 0; struct s_list api_list; +/** + * Write two strings on one write. + * @param fd file descriptor to write. + * @param str1 first string to write. + * @param str2 second string to write. + */ +size_t write_dual(int fd, char *str1, char *str2) +{ + struct iovec iov[2]; + + iov[0].iov_base = str1; + iov[0].iov_len = strlen(str1); + iov[1].iov_base = str2; + iov[1].iov_len = strlen(str2); + + return writev(fd, iov, 2); +} + +int read_all(int fd, struct s_request *req, char *already, size_t alread_size) +{ + char buf[MAX_READ], *p; + size_t size = 0; + + if (alread_size > 0) { + p = realloc(req->buf, req->size + alread_size); + if (!p) { + return 0; + } + req->buf = p; + req->size += alread_size; + memcpy(req->buf + req->body + req->body_size, already, alread_size); + req->body_size += alread_size; + } + for(;;) { + if (socket_read_select4(fd, 5) <= 0) { + break; + } + size = read(fd, buf, sizeof buf); + if (size <= 0) { + break; + } + p = realloc(req->buf, req->size + size); + if (!p) { + return 0; + } + req->buf = p; + req->size += size; + memcpy(req->buf + req->body + req->body_size, buf, size); + req->body_size += size; + } + return 1; +} + /** * Pthread to take care of each client connection. * @param ptr is the connection index in api_list, integer not pointer, cast required. @@ -28,9 +81,10 @@ void *api_connection_thread (void *ptr) { int timeout, s, r; const INT_TYPE i = (INT_TYPE) ptr; - char buf[MAX_READ+1], *p; + char buf[MAX_READ+1], *p, *body; char client[INET_ADDRSTRLEN]; struct s_request req; + int (*read_func)(int, struct s_request*, char*, size_t) = read_all; req.buf = NULL; // sanity. @@ -53,6 +107,8 @@ void *api_connection_thread (void *ptr) p = strstr(buf, "\r\n\r\n"); if (p) { + body = p + 4; + req.size = p - buf + 1; req.buf = malloc(req.size); if (!req.buf) { @@ -64,42 +120,58 @@ void *api_connection_thread (void *ptr) memcpy(req.buf, buf, req.size - 1); req.buf[req.size-1] = '\0'; - req.method = req.buf; - p = strchr(req.method, ' '); + req.method = 0; + p = strchr(req.buf + req.method, ' '); if (!p) { write_cstr (s, HTTP_400); goto quit; } *p++ = '\0'; // End of method. - req.path = p; - p = strchr(req.path, ' '); + req.path = p - req.buf; + p = strchr(p, ' '); if (!p) { write_cstr (s, HTTP_400); goto quit; } *p++ = '\0'; // End of path. - req.http_ver = p; - p = strchr(req.http_ver, '\r'); + req.http_ver = p - req.buf; + p = strchr(req.buf + req.http_ver, '\r'); if (!p) { write_cstr (s, HTTP_400); goto quit; } *p++ = '\0'; // End of http version. while (*p == '\r' || *p == '\n') p++; - req.header = p; - req.body = req.buf + req.size; + req.header = p - req.buf; + req.body = req.size; req.body_size = 0; + p = strstr(req.buf + req.header, "Transfer-Encoding:"); + if (p) { + p += strlen("Transfer-Encoding:"); + while(*p == ' ') p++; + if (cstrstart(p, "chunked\r\n") || strcmp(p, "chunked")==0) { + write_cstr (s, HTTP_501); + goto quit; + } + } + + if (!read_func(s, &req, body, r - (body - buf))) { + write_cstr (s, HTTP_500); + goto quit; + } + libp2p_logger_error("api", "method = '%s'\n" "path = '%s'\n" "http_ver = '%s'\n" "header {\n%s\n}\n" "body_size = %d\n", - req.method, req.path, req.http_ver, req.header, req.body_size); + req.buf+req.method, req.buf+req.path, req.buf+req.http_ver, + req.buf+req.header, req.body_size); - if (strcmp(req.method, "GET")==0) { + if (strcmp(req.buf + req.method, "GET")==0) { // just an error message, because it's not used. - write_cstr (s, HTTP_404); + write_dual (s, req.buf + req.http_ver, strchr (HTTP_404, ' ')); //} else if (cstrstart(buf, "POST ")) { // TODO: Handle chunked/gzip/form-data/json POST requests. } diff --git a/include/ipfs/core/api.h b/include/ipfs/core/api.h index 663f797..b5597c4 100644 --- a/include/ipfs/core/api.h +++ b/include/ipfs/core/api.h @@ -26,11 +26,11 @@ struct s_request { char *buf; size_t size; - char *method; - char *path; - char *http_ver; - char *header; - char *body; + int method; + int path; + int http_ver; + int header; + int body; size_t body_size; }; @@ -39,18 +39,21 @@ struct s_request { "Connection: close\r\n\r\n" \ "400 Bad Request" - #define HTTP_404 "HTTP/1.1 404 Not Found\r\n" \ - "Content-Type: text/plain; charset=utf-8\r\n" \ - "X-Content-Type-Options: nosniff\r\n" \ - "Content-Length: 19\r\n\r\n" \ - "404 page not found\n" + "Content-Type: text/plain\r\n" \ + "Connection: close\r\n\r\n" \ + "404 page not found" #define HTTP_500 "HTTP/1.1 500 Internal server error\r\n" \ "Content-Type: text/plain\r\n" \ "Connection: close\r\n\r\n" \ "500 Internal server error" +#define HTTP_501 "HTTP/1.1 501 Not Implemented\r\n" \ + "Content-Type: text/plain\r\n" \ + "Connection: close\r\n\r\n" \ + "501 Not Implemented" + #define write_cstr(f,s) write(f,s,sizeof(s)-1) #define write_str(f,s) write(f,s,strlen(s))