jmjatlanta 2017-07-24 09:09:48 -05:00
commit 5507937bff
3 changed files with 339 additions and 5 deletions

View File

@ -8,11 +8,11 @@ libp2p: https://github.com/libp2p/specs <br>
<br> <br>
Prerequisites: To compile the C version you will need:<br> Prerequisites: To compile the C version you will need:<br>
lmdb https://github.com/jmjatlanta/lmdb<br> lmdb https://github.com/jmjatlanta/lmdb<br>
c-protobuf https://github.com/kenCode/c-protobuf<br> c-protobuf https://github.com/kenCode-de/c-protobuf<br>
c-multihash https://github.com/kenCode/c-multihash<br> c-multihash https://github.com/kenCode-de/c-multihash<br>
c-multiaddr https://github.com/kenCode/c-multiaddr<br> c-multiaddr https://github.com/kenCode-de/c-multiaddr<br>
c-libp2p https://github.com/kenCode/c-libp2p<br> c-libp2p https://github.com/kenCode-de/c-libp2p<br>
<br> <br>
And of course this project at https://github.com/kenCode/c-ipfs<br> And of course this project at https://github.com/kenCode-de/c-ipfs<br>
<br> <br>
The compilation at this point is simple, but not very flexible. Place all of these projects in a directory. Compile all (the order above is recommended) by going into each one and running "make all". The compilation at this point is simple, but not very flexible. Place all of these projects in a directory. Compile all (the order above is recommended) by going into each one and running "make all".

270
core/api.c Normal file
View File

@ -0,0 +1,270 @@
/**
* Methods for lightweight/specific HTTP for API communication.
*/
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <arpa/inet.h>
#include "libp2p/net/p2pnet.h"
#include "libp2p/utils/logger.h"
#include "ipfs/core/api.h"
pthread_mutex_t conns_lock;
int conns_count;
pthread_t listen_thread = 0;
struct s_list api_list;
/**
* Pthread to take care of each client connection.
* @param ptr is the connection index in api_list, integer not pointer, cast required.
* @returns nothing
*/
void *api_connection_thread (void *ptr)
{
int timeout, s, r;
const INT_TYPE i = (INT_TYPE) ptr;
char buf[MAX_READ+1], *p;
char client[INET_ADDRSTRLEN];
struct s_request req;
req.buf = NULL; // sanity.
buf[MAX_READ] = '\0';
s = api_list.conns[i]->socket;
timeout = api_list.timeout;
if (socket_read_select4(s, timeout) <= 0) {
libp2p_logger_error("api", "Client connection timeout.\n");
goto quit;
}
r = read(s, buf, sizeof buf);
if (r <= 0) {
libp2p_logger_error("api", "Read from client fail.\n");
goto quit;
}
buf[r] = '\0';
p = strstr(buf, "\r\n\r\n");
if (p) {
req.size = p - buf + 1;
req.buf = malloc(req.size);
if (!req.buf) {
// memory allocation fail.
libp2p_logger_error("api", "malloc fail.\n");
write_cstr (s, HTTP_500);
goto quit;
}
memcpy(req.buf, buf, req.size - 1);
req.buf[req.size-1] = '\0';
req.method = req.buf;
p = strchr(req.method, ' ');
if (!p) {
write_cstr (s, HTTP_400);
goto quit;
}
*p++ = '\0'; // End of method.
req.path = p;
p = strchr(req.path, ' ');
if (!p) {
write_cstr (s, HTTP_400);
goto quit;
}
*p++ = '\0'; // End of path.
req.http_ver = p;
p = strchr(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.body_size = 0;
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);
if (strcmp(req.method, "GET")==0) {
// just an error message, because it's not used.
write_cstr (s, HTTP_404);
//} else if (cstrstart(buf, "POST ")) {
// TODO: Handle chunked/gzip/form-data/json POST requests.
}
} else {
write_cstr (s, HTTP_400);
}
quit:
if (req.buf)
free(req.buf);
if (inet_ntop(AF_INET, &(api_list.conns[i]->ipv4), client, INET_ADDRSTRLEN) == NULL)
strcpy(client, "UNKNOW");
libp2p_logger_error("api", "Closing client connection %s:%d (%d).\n", client, api_list.conns[i]->port, i+1);
pthread_mutex_lock(&conns_lock);
close(s);
free (api_list.conns[i]);
api_list.conns[i] = NULL;
conns_count--;
pthread_mutex_unlock(&conns_lock);
return NULL;
}
/**
* Close all connections stopping respectives pthreads and free allocated memory.
*/
void api_connections_cleanup (void)
{
int i;
pthread_mutex_lock(&conns_lock);
if (conns_count > 0 && api_list.conns) {
for (i = 0 ; i < api_list.max_conns ; i++) {
if (api_list.conns[i]->pthread) {
pthread_cancel (api_list.conns[i]->pthread);
close (api_list.conns[i]->socket);
free (api_list.conns[i]);
api_list.conns[i] = NULL;
}
}
conns_count = 0;
}
if (api_list.conns) {
free (api_list.conns);
api_list.conns = NULL;
}
pthread_mutex_unlock(&conns_lock);
}
/**
* Pthread to keep in background dealing with client connections.
* @param ptr is not used.
* @returns nothing
*/
void *api_listen_thread (void *ptr)
{
int s;
INT_TYPE i;
uint32_t ipv4;
uint16_t port;
char client[INET_ADDRSTRLEN];
conns_count = 0;
for (;;) {
s = socket_accept4(api_list.socket, &ipv4, &port);
if (s <= 0) {
break;
}
if (conns_count >= api_list.max_conns) { // limit reached.
libp2p_logger_error("api", "Limit of connections reached (%d).\n", api_list.max_conns);
close (s);
continue;
}
pthread_mutex_lock(&conns_lock);
for (i = 0 ; i < api_list.max_conns && api_list.conns[i] ; i++);
api_list.conns[i] = malloc (sizeof (struct s_conns));
if (!api_list.conns[i]) {
libp2p_logger_error("api", "Fail to allocate memory to accept connection.\n");
close (s);
continue;
}
if (inet_ntop(AF_INET, &ipv4, client, INET_ADDRSTRLEN) == NULL)
strcpy(client, "UNKNOW");
api_list.conns[i]->socket = s;
api_list.conns[i]->ipv4 = ipv4;
api_list.conns[i]->port = port;
if (pthread_create(&(api_list.conns[i]->pthread), NULL, api_connection_thread, (void*)i)) {
libp2p_logger_error("api", "Create pthread fail.\n");
free (api_list.conns[i]);
api_list.conns[i] = NULL;
conns_count--;
close(s);
} else {
conns_count++;
}
libp2p_logger_error("api", "Accept connection %s:%d (%d/%d), pthread %d.\n", client, port, conns_count, api_list.max_conns, i+1);
pthread_mutex_unlock(&conns_lock);
}
api_connections_cleanup ();
return NULL;
}
/**
* Start API interface daemon.
* @param port.
* @param max_conns.
* @param timeout time out of client connection.
* @returns 0 when failure or 1 if success.
*/
int api_start (uint16_t port, int max_conns, int timeout)
{
int s;
size_t alloc_size = sizeof(void*) * max_conns;
api_list.ipv4 = hostname_to_ip("127.0.0.1"); // api is listening only on loopback.
api_list.port = port;
if (listen_thread != 0) {
libp2p_logger_error("api", "API already running.\n");
return 0;
}
if ((s = socket_listen(socket_tcp4(), &(api_list.ipv4), &(api_list.port))) <= 0) {
libp2p_logger_error("api", "Failed to init API. port: %d\n", port);
return 0;
}
api_list.socket = s;
api_list.max_conns = max_conns;
api_list.timeout = timeout;
api_list.conns = malloc (alloc_size);
if (!api_list.conns) {
close (s);
libp2p_logger_error("api", "Error allocating memory.\n");
return 0;
}
memset(api_list.conns, 0, alloc_size);
if (pthread_create(&listen_thread, NULL, api_listen_thread, NULL)) {
close (s);
free (api_list.conns);
api_list.conns = NULL;
listen_thread = 0;
libp2p_logger_error("api", "Error creating thread for API.\n");
return 0;
}
return 1;
}
/**
* Stop API.
* @returns 0 when failure or 1 if success.
*/
int api_stop (void)
{
if (!listen_thread) return 0;
pthread_cancel(listen_thread);
api_connections_cleanup ();
listen_thread = 0;
return 1;
}

64
include/ipfs/core/api.h Normal file
View File

@ -0,0 +1,64 @@
#pragma once
#ifdef __x86_64__
#define INT_TYPE uint64_t
#else
#define INT_TYPE uint32_t
#endif
#define MAX_READ (32*1024) // 32k
struct s_list {
int socket;
uint32_t ipv4;
uint16_t port;
int max_conns;
int timeout;
struct s_conns {
int socket;
uint32_t ipv4;
uint16_t port;
pthread_t pthread;
} **conns;
};
struct s_request {
char *buf;
size_t size;
char *method;
char *path;
char *http_ver;
char *header;
char *body;
size_t body_size;
};
#define HTTP_400 "HTTP/1.1 400 Bad Request\r\n" \
"Content-Type: text/plain\r\n" \
"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"
#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 write_cstr(f,s) write(f,s,sizeof(s)-1)
#define write_str(f,s) write(f,s,strlen(s))
#define cstrstart(a,b) (memcmp(a,b,sizeof(b)-1)==0)
#define strstart(a,b) (memcmp(a,b,strlen(b))==0)
void *api_connection_thread (void *ptr);
void api_connections_cleanup (void);
void *api_listen_thread (void *ptr);
int api_start (uint16_t port, int max_conns, int timeout);
int api_stop (void);