forked from trealla-prolog/trealla
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathspin.c
196 lines (169 loc) · 5.62 KB
/
spin.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#ifdef WASI_TARGET_SPIN
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "cdebug.h"
#include "trealla.h"
#include "prolog.h"
#include "spin-http.h"
#include "stringbuf.h"
#include "skiplist.h"
#include "internal.h"
#include "spin.h"
const char* SPIN_METHODS[] = {
[SPIN_HTTP_METHOD_GET] = "get",
[SPIN_HTTP_METHOD_POST] = "post",
[SPIN_HTTP_METHOD_PUT] = "put",
[SPIN_HTTP_METHOD_DELETE] = "delete",
[SPIN_HTTP_METHOD_PATCH] = "patch",
[SPIN_HTTP_METHOD_HEAD] = "head",
[SPIN_HTTP_METHOD_OPTIONS] = "options"
};
bool spin_http_method_lookup(const char *name, uint8_t *id)
{
for (uint8_t i = 0; i < SPIN_HTTP_METHODS_MAX; i++) {
if (!strcmp(name, SPIN_METHODS[i])) {
*id = i;
return true;
}
}
return false;
}
// The Spin HTTP component handler.
// Using the pre-initialized global interpreter, it asserts a bunch of HTTP-related info
// and then calls spin:http_handle_request/2.
// The response body is read from a memory stream with the alias "http_body".
// The response headers are read from a map stream with the alias "http_headers".
extern void spin_http_handle_http_request(spin_http_request_t *request, spin_http_response_t *response) {
pl_global_init();
prolog *pl = pl_global();
// We need to consult the init file before we run the query,
// so it has a chance to run term_expansion/2 first.
char *init = getenv("INIT");
if (init == NULL)
init = "init";
if (!pl_consult(pl, init)) {
fprintf(stderr, "Error: failed to load init file: %s. Do you need to set the INIT environment variable?\n", init);
char msg[256];
int msg_len = snprintf(msg, sizeof(msg), "Failed to initialize server: failed to consult '%s'.", init);
response->status = 503;
response->body.is_some = true;
response->body.val.ptr = (uint8_t*)strdup(msg);
response->body.val.len = msg_len;
return;
}
// s will be the query sent to Prolog
SB(s);
SB_strcat(s, "use_module(library(spin)), spin:init, spin:assertz(current_http_uri(\"");
SB_strcatn(s, request->uri.ptr, request->uri.len);
SB_strcat(s, "\")), ");
if (request->method >= SPIN_HTTP_METHODS_MAX) {
BADMETHOD:
SB_free(s);
fprintf(stderr, "Unhandled method: %d\n", request->method);
response->status = 405;
return;
}
const char *method = SPIN_METHODS[request->method];
if (!method) goto BADMETHOD;
for (size_t i = 0; i < request->params.len; i++) {
spin_http_tuple2_string_string_t param = request->params.ptr[i];
SB_strcat(s, "spin:assertz(current_http_param(\"");
SB_strcat_and_free(s, formatted(param.f0.ptr, param.f0.len, true, false));
SB_strcat(s, "\",\"");
SB_strcat_and_free(s, formatted(param.f1.ptr, param.f1.len, true, false));
SB_strcat(s, "\")), ");
}
for (size_t i = 0; i < request->headers.len; i++) {
spin_http_tuple2_string_string_t header = request->headers.ptr[i];
SB_strcat(s, "spin:assertz(current_http_header(\"");
SB_strcat_and_free(s, formatted(header.f0.ptr, header.f0.len, true, false));
SB_strcat(s, "\",\"");
SB_strcat_and_free(s, formatted(header.f1.ptr, header.f1.len, true, false));
SB_strcat(s, "\")), ");
}
if (request->body.is_some && request->body.val.len > 0) {
SB_strcat(s, "spin:assertz(current_http_body(\"");
SB_strcat_and_free(s, formatted((const char*)request->body.val.ptr,
request->body.val.len, true, false));
SB_strcat(s, "\")), ");
}
SB_strcat(s, "spin:http_handle_request(\"");
SB_strcatn(s, request->uri.ptr, request->uri.len);
SB_strcat(s, "\",");
SB_strcat(s, method);
SB_strcat(s, ").");
bool ok = pl_eval(pl, SB_cstr(s), false);
if (!ok || !get_status(pl)) {
fprintf(stderr, "Error: query failed (ok = %d, status = %d): %s\n",
ok, get_status(pl), SB_cstr(s));
SB_free(s);
int n = pl->current_error;
stream *str = &pl->streams[n];
const char *src = SB_cstr(str->sb);
size_t len = SB_strlen(str->sb);
char* body = (len > 0) ? strdup(src) :
strdup("Internal server error: query failed.\n");
int32_t body_length = strlen(body);
response->status = 500;
response->body.is_some = true;
response->body.val.ptr = (uint8_t *)body;
response->body.val.len = body_length;
return;
}
SB_free(s);
int status = 0;
const char *body_string;
size_t body_len = 0;
int n = get_named_stream(pl, "http_body", strlen("http_body"));
stream *str = &pl->streams[n];
if (str->is_memory) {
body_string = SB_cstr(str->sb);
body_len = SB_strlen(str->sb);
}
n = get_named_stream(pl, "http_headers", strlen("http_headers"));
str = &pl->streams[n];
spin_http_headers_t response_headers = {0};
if (str->is_map) {
skiplist *m = str->keyval;
const char *sch;
if (sl_get(m, "status", (const void **)&sch)) {
status = atoi(sch);
sl_del(m, "status");
}
size_t count = sl_count(m);
response_headers.len = count;
spin_http_tuple2_string_string_t *headers = calloc(count,
sizeof(spin_http_tuple2_string_string_t));
response_headers.ptr = headers;
if (count > 0)
response->headers.is_some = true;
sliter *iter = sl_first(m);
const char *value;
size_t i = 0;
while (sl_next(iter, (void **)&value)) {
spin_http_tuple2_string_string_t *header = &headers[i++];
const char *key = sl_key(iter);
spin_http_string_t header_name, header_value;
spin_http_string_dup(&header_name, key);
spin_http_string_dup(&header_value, value);
header->f0 = header_name;
header->f1 = header_value;
}
sl_done(iter);
}
response->headers.val = response_headers;
if (!status)
status = 500;
response->status = status;
if (body_len > 0) {
uint8_t *body = malloc(body_len);
memcpy(body, body_string, body_len);
response->body.is_some = true;
response->body.val.ptr = body;
response->body.val.len = body_len;
}
}
#endif