/
helpers.c
114 lines (92 loc) · 3.62 KB
/
helpers.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sched.h>
#include <sys/wait.h>
#include <err.h>
#include <dynamic.h>
#include <reactor.h>
#include <clo.h>
#define JSON_PREAMBLE "HTTP/1.1 200 OK\r\n"\
"Server: L\r\n"\
"Content-Type: application/json\r\n"
#define TEXT_PREAMBLE "HTTP/1.1 200 OK\r\n"\
"Server: L\r\n"\
"Content-Type: text/plain\r\n"
// Returns the full header and trailing \r\n
static segment http_date_header()
{
static __thread char date_header[38] = "Date: Thu, 01 Jan 1970 00:00:00 GMT\r\n";
segment date = http_date(0);
memcpy(date_header + 6, date.base, date.size);
return (segment) {date_header, 37};
}
// Returns the full header and trailing \r\n
// Also includes the final \r\n separating the headers from response body
static segment http_content_length_header(uint32_t n)
{
// Max content length limited by uint32_t which is 4294967296 (4GB) or 10 chars when written out.
// 16 (header name) + 10 (header value) + 4 (newlines) + 1 (null terminator) = 31
static __thread char header[32] = "Content-Length: ";
size_t length = utility_u32_len(n);
utility_u32_sprint(n, header + length + 16);
memcpy(header + length + 16, "\r\n\r\n", 4);
return (segment) {header, length + 16 + 4};
}
static void write_response(stream *stream, segment preamble, segment body)
{
segment date_header = http_date_header(0); // includes header name, value, and \r\n
segment content_length_header = http_content_length_header(body.size); // includes header name, value, and \r\n\r\n
size_t response_size = preamble.size + date_header.size + content_length_header.size + body.size;
// Reserves additional space in the stream's output buffer (if needed) and updates the buffer size
// stream_allocate returns a segment which we convert to a char * pointer
char *output_buffer_ptr = (char *) (stream_allocate(stream, response_size)).base;
// memcpy the response directly to the output stream buffer
memcpy(output_buffer_ptr, preamble.base, preamble.size);
memcpy(output_buffer_ptr + preamble.size, date_header.base, date_header.size);
memcpy(output_buffer_ptr + preamble.size + date_header.size, content_length_header.base, content_length_header.size);
memcpy(output_buffer_ptr + preamble.size + date_header.size + content_length_header.size, body.base, body.size);
}
void plaintext(server_context *context, char *response)
{
static const segment text_preamble = { .base = TEXT_PREAMBLE, .size = sizeof(TEXT_PREAMBLE) - 1 };
write_response(&context->session->stream, text_preamble, segment_string(response));
}
void json(server_context *context, clo *json_object)
{
static const segment json_preamble = { .base = JSON_PREAMBLE, .size = sizeof(JSON_PREAMBLE) - 1 };
static char json_string[4096];
(void) clo_encode(json_object, json_string, sizeof(json_string));
write_response(&context->session->stream, json_preamble, segment_string(json_string));
}
void setup()
{
int e;
pid_t pid;
cpu_set_t available_cpus, cpu;
signal(SIGPIPE, SIG_IGN);
CPU_ZERO(&available_cpus);
sched_getaffinity(0, sizeof(available_cpus), &available_cpus); // Get set of all available CPUs
for (int i = 0; i < CPU_SETSIZE; i++)
{
if (CPU_ISSET(i, &available_cpus))
{
pid = fork();
if (pid == -1)
err(1, "fork");
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);
}