Initial implementation of multipart in API.
This commit is contained in:
parent
a9481631df
commit
0b113cb95d
2 changed files with 151 additions and 28 deletions
177
core/api.c
177
core/api.c
|
@ -1,6 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* Methods for lightweight/specific HTTP for API communication.
|
* Methods for lightweight/specific HTTP for API communication.
|
||||||
*/
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define __USE_GNU
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -178,6 +180,94 @@ int read_all(int fd, struct s_request *req, char *already, size_t alread_size)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a token in a string array.
|
||||||
|
* @param string array and token string.
|
||||||
|
* @returns the pointer after where the token was found or NULL if it fails.
|
||||||
|
*/
|
||||||
|
char *str_tok(char *str, char *tok)
|
||||||
|
{
|
||||||
|
char *p = strstr(str, tok);
|
||||||
|
if (p) {
|
||||||
|
p += strlen(tok);
|
||||||
|
while(*p == ' ') p++;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a token in a binary array.
|
||||||
|
* @param array, size of array, token and size of token.
|
||||||
|
* @returns the pointer after where the token was found or NULL if it fails.
|
||||||
|
*/
|
||||||
|
char *bin_tok(char *bin, size_t limit, char *tok, size_t tok_size)
|
||||||
|
{
|
||||||
|
char *p = memmem(bin, limit, tok, tok_size);
|
||||||
|
if (p) {
|
||||||
|
p += tok_size;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if header contain a especific value.
|
||||||
|
* @param request structure, header name and value to check.
|
||||||
|
* @returns the pointer where the value was found or NULL if it fails.
|
||||||
|
*/
|
||||||
|
char *header_value_cmp(struct s_request *req, char *header, char *value)
|
||||||
|
{
|
||||||
|
char *p = str_tok(req->buf + req->header, header);
|
||||||
|
if (p) {
|
||||||
|
if (strstart(p, value)) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup for boundary at buffer string.
|
||||||
|
* @param body buffer string, boundary id, filename and content-type string.
|
||||||
|
* @returns the pointer where the multipart start.
|
||||||
|
*/
|
||||||
|
char *boundary_find(char *str, char *boundary, char **filename, char **contenttype)
|
||||||
|
{
|
||||||
|
char *p = str_tok(str, "--");
|
||||||
|
while (p) {
|
||||||
|
if (strstart(p, boundary)) {
|
||||||
|
// skip to the beginning, ignoring the header for now, if there is.
|
||||||
|
// TODO: return filename and content-type
|
||||||
|
p = strstr(p, "\r\n\r\n");
|
||||||
|
if (p) {
|
||||||
|
return p + 4; // ignore 4 bytes CRLF 2x
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = str_tok(str, "--");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size of boundary.
|
||||||
|
* @param boundary buffer, boundary id.
|
||||||
|
* @returns the size of boundary or 0 if fails.
|
||||||
|
*/
|
||||||
|
size_t boundary_size(char *str, char *boundary, size_t limit)
|
||||||
|
{
|
||||||
|
char *p = bin_tok(str, limit, "\r\n--", 4);
|
||||||
|
while (p) {
|
||||||
|
if (strstart(p, boundary)) {
|
||||||
|
if (cstrstart(p + strlen(boundary), "--\r\n")) {
|
||||||
|
p -= 4;
|
||||||
|
return (size_t)(p - str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = bin_tok(p, limit, "\r\n--", 4);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pthread to take care of each client connection.
|
* Pthread to take care of each client connection.
|
||||||
* @param ptr is the connection index in api_list, integer not pointer, cast required.
|
* @param ptr is the connection index in api_list, integer not pointer, cast required.
|
||||||
|
@ -255,13 +345,8 @@ void *api_connection_thread (void *ptr)
|
||||||
req.body = req.size;
|
req.body = req.size;
|
||||||
req.body_size = 0;
|
req.body_size = 0;
|
||||||
|
|
||||||
p = strstr(req.buf + req.header, "Transfer-Encoding:");
|
if (header_value_cmp(&req, "Transfer-Encoding:", "chunked")) {
|
||||||
if (p) {
|
read_func = read_chunked;
|
||||||
p += strlen("Transfer-Encoding:");
|
|
||||||
while(*p == ' ') p++;
|
|
||||||
if (cstrstart(p, "chunked\r\n") || strcmp(p, "chunked")==0) {
|
|
||||||
read_func = read_chunked;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!read_func(s, &req, body, r - (body - buf))) {
|
if (!read_func(s, &req, body, r - (body - buf))) {
|
||||||
|
@ -270,29 +355,65 @@ void *api_connection_thread (void *ptr)
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = strstr(req.buf + req.header, "Accept-Encoding:");
|
|
||||||
libp2p_logger_error("api", "method = '%s'\n"
|
|
||||||
"path = '%s'\n"
|
|
||||||
"http_ver = '%s'\n"
|
|
||||||
"header {\n%s\n}\n"
|
|
||||||
"body_size = %d\n",
|
|
||||||
req.buf+req.method, req.buf+req.path, req.buf+req.http_ver,
|
|
||||||
req.buf+req.header, req.body_size);
|
|
||||||
|
|
||||||
snprintf(resp, sizeof(resp), "%s 200 OK\r\n" \
|
|
||||||
"Content-Type: application/json\r\n"
|
|
||||||
"Server: c-ipfs/0.0.0-dev\r\n"
|
|
||||||
"X-Chunked-Output: 1\r\n"
|
|
||||||
"Connection: close\r\n"
|
|
||||||
"Transfer-Encoding: chunked\r\n\r\n", req.buf + req.http_ver);
|
|
||||||
write_str (s, resp);
|
|
||||||
libp2p_logger_error("api", "resp = {\n%s\n}\n", resp);
|
|
||||||
|
|
||||||
if (strcmp(req.buf + req.method, "GET")==0) {
|
if (strcmp(req.buf + req.method, "GET")==0) {
|
||||||
// just an error message, because it's not used.
|
// just an error message, because it's not used yet.
|
||||||
|
// TODO: implement gateway requests and GUI (javascript) for API.
|
||||||
write_dual (s, req.buf + req.http_ver, strchr (HTTP_404, ' '));
|
write_dual (s, req.buf + req.http_ver, strchr (HTTP_404, ' '));
|
||||||
//} else if (cstrstart(buf, "POST ")) {
|
} else if (cstrstart(buf, "POST ")) {
|
||||||
// TODO: Handle chunked/gzip/form-data/json POST requests.
|
// TODO: Handle gzip/json POST requests.
|
||||||
|
|
||||||
|
p = header_value_cmp(&req, "Content-Type:", "multipart/form-data;");
|
||||||
|
if (p) {
|
||||||
|
p = str_tok(p, "boundary=");
|
||||||
|
if (p) {
|
||||||
|
char *boundary, *l;
|
||||||
|
int len;
|
||||||
|
if (*p == '"') {
|
||||||
|
p++;
|
||||||
|
l = strchr(p, '"');
|
||||||
|
} else {
|
||||||
|
l = p;
|
||||||
|
while (*l != '\r' && *l != '\0') l++;
|
||||||
|
}
|
||||||
|
len = l - p;
|
||||||
|
boundary = malloc (len+1);
|
||||||
|
if (boundary) {
|
||||||
|
memcpy(boundary, p, len);
|
||||||
|
boundary[len] = '\0';
|
||||||
|
|
||||||
|
p = boundary_find(req.buf + req.body, boundary, NULL, NULL);
|
||||||
|
if (p) {
|
||||||
|
req.boundary_size = boundary_size(p, boundary, req.size - (p - buf));
|
||||||
|
if (req.boundary_size > 0) {
|
||||||
|
req.boundary = p - req.buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free (boundary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Parse the path var and decide what to do with the received data.
|
||||||
|
if (req.boundary > 0) {
|
||||||
|
libp2p_logger_error("api", "boundary index = %d, size = %d\n", req.boundary, req.boundary_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
libp2p_logger_error("api", "method = '%s'\n"
|
||||||
|
"path = '%s'\n"
|
||||||
|
"http_ver = '%s'\n"
|
||||||
|
"header {\n%s\n}\n"
|
||||||
|
"body_size = %d\n",
|
||||||
|
req.buf+req.method, req.buf+req.path, req.buf+req.http_ver,
|
||||||
|
req.buf+req.header, req.body_size);
|
||||||
|
|
||||||
|
snprintf(resp, sizeof(resp), "%s 200 OK\r\n" \
|
||||||
|
"Content-Type: application/json\r\n"
|
||||||
|
"Server: c-ipfs/0.0.0-dev\r\n"
|
||||||
|
"X-Chunked-Output: 1\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Transfer-Encoding: chunked\r\n\r\n", req.buf + req.http_ver);
|
||||||
|
write_str (s, resp);
|
||||||
|
libp2p_logger_error("api", "resp = {\n%s\n}\n", resp);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
libp2p_logger_error("api", "fail looking for body.\n");
|
libp2p_logger_error("api", "fail looking for body.\n");
|
||||||
|
|
|
@ -32,6 +32,8 @@ struct s_request {
|
||||||
int header;
|
int header;
|
||||||
int body;
|
int body;
|
||||||
size_t body_size;
|
size_t body_size;
|
||||||
|
int boundary;
|
||||||
|
size_t boundary_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HTTP_400 "HTTP/1.1 400 Bad Request\r\n" \
|
#define HTTP_400 "HTTP/1.1 400 Bad Request\r\n" \
|
||||||
|
|
Loading…
Reference in a new issue