diff --git a/README.md b/README.md
index 45d7f4e..55123e1 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,11 @@ libp2p: https://github.com/libp2p/specs
Prerequisites: To compile the C version you will need:
lmdb https://github.com/jmjatlanta/lmdb
-c-protobuf https://github.com/kenCode/c-protobuf
-c-multihash https://github.com/kenCode/c-multihash
-c-multiaddr https://github.com/kenCode/c-multiaddr
-c-libp2p https://github.com/kenCode/c-libp2p
+c-protobuf https://github.com/kenCode-de/c-protobuf
+c-multihash https://github.com/kenCode-de/c-multihash
+c-multiaddr https://github.com/kenCode-de/c-multiaddr
+c-libp2p https://github.com/kenCode-de/c-libp2p
-And of course this project at https://github.com/kenCode/c-ipfs
+And of course this project at https://github.com/kenCode-de/c-ipfs
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".
diff --git a/core/api.c b/core/api.c
new file mode 100644
index 0000000..dde67fd
--- /dev/null
+++ b/core/api.c
@@ -0,0 +1,270 @@
+/**
+ * Methods for lightweight/specific HTTP for API communication.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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;
+}
diff --git a/include/ipfs/core/api.h b/include/ipfs/core/api.h
new file mode 100644
index 0000000..663f797
--- /dev/null
+++ b/include/ipfs/core/api.h
@@ -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);