Browse Source

Libreactor Refactor (#5771)

* libreactor - Upgrade to ubunut 20.04 and gcc 9

* libreactor - Refactor response handling

* libreactor - Fix getaddrinfo() error handling

* libreactor - Simplify setup.c
Marc Richards 5 years ago
parent
commit
b362d0f77c

+ 2 - 2
frameworks/C/libreactor/Makefile

@@ -1,7 +1,7 @@
 PROG    = libreactor
 PROG    = libreactor
 OBJS    = src/setup.o src/helpers.o src/main.o
 OBJS    = src/setup.o src/helpers.o src/main.o
-CFLAGS  = -std=gnu11 -Wall -Wextra -Wpedantic -O3 -g
-LDADD   = -lreactor -ldynamic -lclo -flto
+CFLAGS  = -std=gnu11 -Wall -Wextra -Wpedantic -O3 -g -flto
+LDADD   = -lreactor -ldynamic -lclo
 
 
 $(PROG): $(OBJS)
 $(PROG): $(OBJS)
 	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LDADD)
 	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LDADD)

+ 4 - 4
frameworks/C/libreactor/libreactor.dockerfile

@@ -1,11 +1,11 @@
-FROM ubuntu:18.04 as builder
+FROM ubuntu:20.04 as builder
 
 
 RUN apt-get update -yqq
 RUN apt-get update -yqq
-RUN apt-get install -yqq wget make automake libtool file gcc-8 g++-8
+RUN apt-get install -yqq wget make automake libtool file gcc-9 g++-9
 
 
 WORKDIR /libreactor
 WORKDIR /libreactor
 
 
-ENV CC=gcc-8 AR=gcc-ar-8 NM=gcc-nm-8 RANLIB=gcc-ranlib-8
+ENV CC=gcc-9 AR=gcc-ar-9 NM=gcc-nm-9 RANLIB=gcc-ranlib-9
 
 
 RUN wget -q https://github.com/akheron/jansson/archive/v2.12.tar.gz -O jansson-2.12.tar.gz && \
 RUN wget -q https://github.com/akheron/jansson/archive/v2.12.tar.gz -O jansson-2.12.tar.gz && \
     tar xfz jansson-2.12.tar.gz && \
     tar xfz jansson-2.12.tar.gz && \
@@ -38,7 +38,7 @@ COPY Makefile /libreactor/Makefile
 RUN make
 RUN make
 
 
 
 
-FROM ubuntu:18.04
+FROM ubuntu:20.04
 
 
 WORKDIR /libreactor
 WORKDIR /libreactor
 COPY --from=builder /libreactor .
 COPY --from=builder /libreactor .

+ 45 - 19
frameworks/C/libreactor/src/helpers.c

@@ -16,32 +16,58 @@
 #include <reactor.h>
 #include <reactor.h>
 #include <clo.h>
 #include <clo.h>
 
 
-static char date_header[] = "Date: Thu, 01 Jan 1970 00:00:00 GMT\r\n";
-static size_t date_header_size = sizeof(date_header) - 1;
-
-
-// Updates the date string
-void update_date()
+// Returns the full header and trailing \r\n
+reactor_vector http_date_header(int update)
 {
 {
+  time_t t;
+  struct tm tm;
   static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
   static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
   static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
   static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+  static __thread char date_header[38] = "Date: Thu, 01 Jan 1970 00:00:00 GMT\r\n";
 
 
-  time_t t;
-  struct tm tm;
+  if (update)
+    {
+      (void) time(&t);
+      (void) gmtime_r(&t, &tm);
+      (void) strftime(date_header, 38, "Date: ---, %d --- %Y %H:%M:%S GMT\r\n", &tm);
+      memcpy(date_header + 6, days[tm.tm_wday], 3);
+      memcpy(date_header + 14, months[tm.tm_mon], 3);
+    }
 
 
-  (void) time(&t);
-  (void) gmtime_r(&t, &tm);
-  (void) strftime(date_header, date_header_size, "Date: ---, %d --- %Y %H:%M:%S GMT\r\n", &tm);
-  memcpy(date_header + 6, days[tm.tm_wday], 3);
-  memcpy(date_header + 14, months[tm.tm_mon], 3);
+  return (reactor_vector) {date_header, 37};
 }
 }
 
 
-// Copies the date string to the preamble
-void copy_date(char *target, size_t position)
+// Returns the full header and trailing \r\n
+// Also includes the final \r\n separating the headers from response body
+reactor_vector http_content_length_header(uint32_t n)
 {
 {
-  memcpy(target + position, date_header, date_header_size);
+  static __thread char header[32] = "Content-Length: "; // full 32 elements still allocated
+  size_t length = reactor_utility_u32len(n);
+
+  reactor_utility_u32sprint(n, header + length + 16);
+  memcpy(header + length + 16, "\r\n\r\n", 4);
+
+  return (reactor_vector){header, length + 16 + 4};
 }
 }
 
 
+// memcpy the response directly to the output buffer and adjust the buffer size accordingly
+void write_response(reactor_stream *stream, reactor_vector preamble, reactor_vector body)
+{
+  char *output_stream_ptr;
+  reactor_vector date_header = http_date_header(0); // includes header name and \r\n
+  reactor_vector content_length_header = http_content_length_header(body.size); // includes header name and \r\n\r\n
+  size_t response_size = preamble.size + date_header.size + content_length_header.size + body.size;
+
+  buffer_reserve(&stream->output, response_size);
+  output_stream_ptr = (char *) buffer_data(&stream->output);
+  memcpy(output_stream_ptr, preamble.base, preamble.size);
+  memcpy(output_stream_ptr + preamble.size, date_header.base, date_header.size);
+  memcpy(output_stream_ptr + preamble.size + date_header.size, content_length_header.base, content_length_header.size);
+  memcpy(output_stream_ptr + preamble.size + date_header.size + content_length_header.size, body.base, body.size);
+  stream->output.size += response_size;
+}
+
+
 // Custom version of reactor_net_bind that doesn't use reactor_resolver/reactor_pool to resolve the address
 // Custom version of reactor_net_bind that doesn't use reactor_resolver/reactor_pool to resolve the address
 // We just call getaddrinfo here directly instead. It is called at start we don't need to worry about it blocking
 // We just call getaddrinfo here directly instead. It is called at start we don't need to worry about it blocking
 // By not using reactor_net_bind we can avoid reactor_pool altogether and compile without -pthread. This seems to
 // By not using reactor_net_bind we can avoid reactor_pool altogether and compile without -pthread. This seems to
@@ -58,10 +84,10 @@ reactor_status custom_reactor_net_bind(reactor_net *net, char *node, char *servi
   net->options |= REACTOR_NET_OPTION_PASSIVE;
   net->options |= REACTOR_NET_OPTION_PASSIVE;
 
 
   hints = (struct addrinfo) {.ai_family = 0, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE};
   hints = (struct addrinfo) {.ai_family = 0, .ai_socktype = SOCK_STREAM, .ai_flags = AI_PASSIVE};
-  (void) getaddrinfo(node, service, &hints, &ai);
+  e = getaddrinfo(node, service, &hints, &ai);
 
 
-  if (!ai)
-    err(1, "error resolving address");
+  if (e)
+    err(1, "unable to resolve %s:%s %s\n", node, service, gai_strerror(e));
 
 
   fileno = socket(ai->ai_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
   fileno = socket(ai->ai_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
   reactor_assert_int_not_equal(fileno, -1);
   reactor_assert_int_not_equal(fileno, -1);

+ 4 - 2
frameworks/C/libreactor/src/helpers.h

@@ -1,9 +1,11 @@
 #ifndef HELPERS_H_INCLUDED
 #ifndef HELPERS_H_INCLUDED
 #define HELPERS_H_INCLUDED
 #define HELPERS_H_INCLUDED
 
 
-void update_date();
+reactor_vector http_date_header(int update);
 
 
-void copy_date(char *target, size_t position);
+reactor_vector http_content_length_header(uint32_t n);
+
+void write_response(reactor_stream *stream, reactor_vector preamble, reactor_vector body);
 
 
 reactor_status custom_reactor_net_bind(reactor_net *net, char *node, char *service);
 reactor_status custom_reactor_net_bind(reactor_net *net, char *node, char *service);
 
 

+ 29 - 51
frameworks/C/libreactor/src/main.c

@@ -13,12 +13,13 @@
 #include "setup.h"
 #include "setup.h"
 #include "helpers.h"
 #include "helpers.h"
 
 
-enum response_type
-{
- JSON = 0,
- PLAINTEXT = 1
-};
-typedef enum response_type response_type;
+#define JSON_PREAMBLE "HTTP/1.1 200 OK\r\n"\
+                      "Server: libreactor\r\n"\
+                      "Content-Type: application/json\r\n"
+
+#define TEXT_PREAMBLE "HTTP/1.1 200 OK\r\n"\
+                      "Server: libreactor\r\n"\
+                      "Content-Type: text/plain\r\n"
 
 
 struct http_server
 struct http_server
 {
 {
@@ -26,59 +27,44 @@ struct http_server
   list           connections;
   list           connections;
 };
 };
 
 
-static char json_preamble[] = "HTTP/1.1 200 OK\r\n"
-                              "Server: libreactor\r\n"
-                              "Content-Type: application/json\r\n"
-                              "Date: Thu, 01 Jan 1970 00:00:00 GMT\r\n"
-                              "Content-Length: ";
-
-static char plaintext_preamble[] = "HTTP/1.1 200 OK\r\n"
-                                   "Server: libreactor\r\n"
-                                   "Content-Type: text/plain\r\n"
-                                   "Date: Thu, 01 Jan 1970 00:00:00 GMT\r\n"
-                                   "Content-Length: ";
-
-static size_t json_preamble_size = sizeof(json_preamble) - 1;
-static size_t plaintext_preamble_size = sizeof(plaintext_preamble) - 1;
-
-
-static int send_response(reactor_http *http_connection, char *response, response_type type)
+static void plaintext(reactor_stream *stream, char *response)
 {
 {
-  reactor_vector body = reactor_vector_string(response);
-  reactor_vector content_length = reactor_utility_u32tov(body.size);
+  static const reactor_vector text_preamble = { .base = TEXT_PREAMBLE, .size = sizeof(TEXT_PREAMBLE) - 1 };
+  write_response(stream, text_preamble, reactor_vector_string(response));
+}
 
 
-  if (type == JSON){
-    reactor_stream_write(&http_connection->stream, json_preamble, json_preamble_size);
-  }
-  else {
-    reactor_stream_write(&http_connection->stream, plaintext_preamble, plaintext_preamble_size);
-  }
+static void json(reactor_stream *stream, clo *json_object)
+{
+  static const reactor_vector json_preamble = { .base = JSON_PREAMBLE, .size = sizeof(JSON_PREAMBLE) - 1 };
+  static char json_string[4096];
 
 
-  reactor_stream_write(&http_connection->stream, content_length.base, content_length.size);
-  reactor_stream_write(&http_connection->stream, "\r\n\r\n", 4);
-  reactor_stream_write(&http_connection->stream, body.base, body.size);
-  return REACTOR_OK;
+  (void) clo_encode(json_object, json_string, sizeof(json_string));
+  write_response(stream, json_preamble, reactor_vector_string(json_string));
 }
 }
 
 
 static reactor_status http_handler(reactor_event *event)
 static reactor_status http_handler(reactor_event *event)
 {
 {
+  static char hello_string[] = "Hello, World!";
+  static char default_string[] = "Hello from libreactor!\n";
+  static clo_pair json_pair[] = {{ .string = "message", .value = { .type = CLO_STRING, .string = "Hello, World!" }}};
+  static clo json_object[] = {{ .type = CLO_OBJECT, .object = json_pair }};
+
   reactor_http *http_connection = event->state;
   reactor_http *http_connection = event->state;
   reactor_http_request *request;
   reactor_http_request *request;
-  char json_msg[4096];
 
 
   if (event->type == REACTOR_HTTP_EVENT_REQUEST){
   if (event->type == REACTOR_HTTP_EVENT_REQUEST){
     request = (reactor_http_request *) event->data;
     request = (reactor_http_request *) event->data;
 
 
     if (reactor_vector_equal(request->target, reactor_vector_string("/json"))){
     if (reactor_vector_equal(request->target, reactor_vector_string("/json"))){
-      (void) clo_encode((clo[]) {clo_object({"message", clo_string("Hello, World!")})}, json_msg, sizeof(json_msg));
-      return send_response(http_connection, json_msg, JSON);
+      json(&http_connection->stream, json_object);
     }
     }
     else if (reactor_vector_equal(request->target, reactor_vector_string("/plaintext"))){
     else if (reactor_vector_equal(request->target, reactor_vector_string("/plaintext"))){
-      return send_response(http_connection, "Hello, World!", PLAINTEXT);
+      plaintext(&http_connection->stream, hello_string);
     }
     }
     else{
     else{
-      return send_response(http_connection, "Hello from libreactor!\n", PLAINTEXT);
+      plaintext(&http_connection->stream, default_string);
     }
     }
+    return REACTOR_OK;
   }
   }
   else {
   else {
     reactor_http_destruct(http_connection);
     reactor_http_destruct(http_connection);
@@ -107,21 +93,13 @@ static reactor_status net_handler(reactor_event *event)
     }
     }
 }
 }
 
 
-static void set_date()
-{
-  update_date();
-  copy_date(json_preamble, 69);
-  copy_date(plaintext_preamble, 63);
-}
-
 static reactor_status http_date_timer_handler(reactor_event *event)
 static reactor_status http_date_timer_handler(reactor_event *event)
 {
 {
   (void) event;
   (void) event;
-  set_date();
+  http_date_header(1); // update the date header
   return REACTOR_OK;
   return REACTOR_OK;
 }
 }
 
 
-
 int main()
 int main()
 {
 {
   struct http_server server = {0};
   struct http_server server = {0};
@@ -131,8 +109,8 @@ int main()
   list_construct(&server.connections);
   list_construct(&server.connections);
   reactor_core_construct();
   reactor_core_construct();
 
 
-  // set the correct date in the preambles before the server starts. Timer then updates them every second
-  set_date();
+  // Set the correct date before the server starts. Timer then updates it every second
+  http_date_header(1);
   reactor_timer_construct(&timer, http_date_timer_handler, &timer);
   reactor_timer_construct(&timer, http_date_timer_handler, &timer);
   reactor_timer_set(&timer, 1, 1000000000);
   reactor_timer_set(&timer, 1, 1000000000);
 
 

+ 22 - 67
frameworks/C/libreactor/src/setup.c

@@ -4,85 +4,40 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdint.h>
 #include <unistd.h>
 #include <unistd.h>
-#include <fcntl.h>
 #include <sched.h>
 #include <sched.h>
-#include <sys/resource.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
 #include <err.h>
 #include <err.h>
 
 
-#include <dynamic.h>
-
-struct cpu
-{
-  int       id;
-  pid_t     pid;
-  cpu_set_t set;
-};
-
-static void cpu_list(vector *list, int use_all_cores)
-{
-  struct cpu cpu;
-  char path[1024], buf[1024];
-  size_t i, id;
-  ssize_t n;
-  int fd;
-
-  vector_construct(list, sizeof cpu);
-  for (i = 0;; i ++)
-    {
-      snprintf(path, sizeof path, "/sys/devices/system/cpu/cpu%lu/topology/thread_siblings_list", i);
-      fd = open(path, O_RDONLY);
-      if (fd == -1)
-        break;
-      n = read(fd, buf, sizeof buf - 1);
-      if (n == -1)
-        err(1, "read");
-      buf[n] = 0;
-      close(fd);
-
-      id = strtoul(buf, NULL, 0);
-
-      if (use_all_cores){
-        id = i; /* add every cpu (whether physical or logical) to the list */
-      }
-      else if (id != i){
-        continue; /* ignore sibling CPUs */
-      }
-
-      cpu = (struct cpu) {0};
-      cpu.id = id;
-      CPU_ZERO(&cpu.set);
-      CPU_SET(id, &cpu.set);
-      vector_push_back(list, &cpu);
-    }
-}
 
 
 void setup()
 void setup()
 {
 {
-  vector list;
-  struct cpu *cpu;
-  size_t i;
   int e;
   int e;
-  pid_t cpid;
+  pid_t pid;
+  cpu_set_t available_cpus, cpu;
 
 
   signal(SIGPIPE, SIG_IGN);
   signal(SIGPIPE, SIG_IGN);
-  cpu_list(&list, 1);
+  CPU_ZERO(&available_cpus);
+  sched_getaffinity(0, sizeof(available_cpus), &available_cpus); // Get set of all available CPUs
 
 
-  for (i = 0; i < vector_size(&list); i++)
+  for (int i = 0; i < CPU_SETSIZE; i++)
+  {
+    if (CPU_ISSET(i, &available_cpus))
     {
     {
-      cpu = vector_at(&list, i);
-      cpid = fork();
-      if (cpid == -1)
+      pid = fork();
+      if (pid == -1)
         err(1, "fork");
         err(1, "fork");
-      if (cpid == 0)
-        {
-          e = sched_setaffinity(0, sizeof cpu->set, &cpu->set);
-          if (e == -1)
-            err(1, "sched_setaffinity");
-          return;
-        }
-      cpu->pid = cpid;
+
+      if (pid == 0)
+      {
+       CPU_ZERO(&cpu);
+       CPU_SET(i, &cpu);
+        e = sched_setaffinity(0, sizeof cpu, &cpu);
+        if (e == -1)
+          err(1, "sched_setaffinity");
+
+        return;
+      }
     }
     }
+  }
   wait(NULL);
   wait(NULL);
-  vector_destruct(&list, NULL);
-}
+}