Skip to content

Commit bed9f4b

Browse files
committed
Reimplement REST support
The REST support in HSE has fallen off the wagon of late. PUT requests don't even work. The code isn't exactly the cleanest either. This should improve REST support for the most part. Signed-off-by: Tristan Partin <[email protected]>
1 parent 513db23 commit bed9f4b

File tree

10 files changed

+424
-99
lines changed

10 files changed

+424
-99
lines changed

lib/binding/kvdb_interface.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
#include <hse/version.h>
2727

2828
#include <hse_util/platform.h>
29-
#include <hse_util/rest_api.h>
3029
#include <hse_util/logging.h>
3130
#include <hse_util/vlb.h>
3231

3332
#include <bsd/libutil.h>
3433
#include <bsd/string.h>
3534
#include <pidfile/pidfile.h>
35+
#include <rest/rest.h>
3636

3737
/* clang-format off */
3838

lib/meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ subdir('kvdb')
7373
subdir('wal')
7474
subdir('mpool')
7575
subdir('pidfile')
76+
subdir('rest')
7677

7778
hse_sources = [
7879
build_config_h,
@@ -110,6 +111,7 @@ hse_dependencies = [
110111
libcurl_dep,
111112
libhse_error_dep,
112113
libhse_pidfile_dep,
114+
libhse_rest_dep,
113115
libmicrohttpd_dep,
114116
liburcu_bp_dep,
115117
libyaml_dep,

lib/rest/include/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rest_includes = include_directories('.')

lib/rest/include/rest/rest.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* SPDX-License-Identifier: Apache-2.0 */
2+
/*
3+
* Copyright (C) 2022 Micron Technology, Inc. All rights reserved.
4+
*/
5+
6+
#ifndef HSE_REST_REST_H
7+
#define HSE_REST_REST_H
8+
9+
#include <hse_util/hse_err.h>
10+
11+
#define REST_ROUTE_USES_KV (1 << 1)
12+
#define REST_ROUTE_PRODUCES_JSON (1 << 2)
13+
#define REST_ROUTE_ACCEPTS_JSON (1 << 3)
14+
15+
struct map;
16+
17+
typedef int (*rest_route_cb)
18+
(const char *url, const void *data, size_t data_len, struct map *kv, char **buf, size_t *buf_len, void *ctx);
19+
20+
enum rest_method {
21+
REST_METHOD_GET,
22+
REST_METHOD_PUT,
23+
};
24+
25+
struct rest_route {
26+
const char rr_path[PATH_MAX];
27+
enum rest_method rr_method;
28+
unsigned int rr_flags;
29+
rest_route_cb rr_cb;
30+
void *rr_ctx;
31+
};
32+
33+
merr_t
34+
rest_register_routes(size_t routec, const struct rest_route *routev);
35+
36+
merr_t
37+
rest_server_start(const char *socket_path);
38+
39+
void
40+
rest_server_stop(void);
41+
42+
#endif

lib/rest/lib/meson.build

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
rest_sources = files(
2+
'rest.c'
3+
)

lib/rest/lib/rest.c

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/* SPDX-License-Identifier: Apache-2.0 */
2+
/*
3+
* Copyright (C) 2022 Micron Technology, Inc. All rights reserved.
4+
*/
5+
6+
#include <assert.h>
7+
#include <errno.h>
8+
#include <stdbool.h>
9+
#include <stddef.h>
10+
#include <sys/socket.h>
11+
#include <sys/un.h>
12+
#include <unistd.h>
13+
14+
#include <bsd/string.h>
15+
#include <microhttpd.h>
16+
17+
#include <rest/rest.h>
18+
19+
#include <hse_util/assert.h>
20+
#include <hse_util/compiler.h>
21+
#include <hse_util/event_counter.h>
22+
#include <hse_util/hse_err.h>
23+
#include <hse_util/logging.h>
24+
#include <hse_util/table.h>
25+
26+
struct server {
27+
struct MHD_Daemon *daemon;
28+
bool initialized;
29+
int socket_fd;
30+
char socket_path[sizeof(((struct sockaddr_un *)NULL)->sun_path)];
31+
};
32+
33+
struct context {
34+
struct MHD_Connection *connection;
35+
enum rest_method method;
36+
};
37+
38+
static struct server server;
39+
40+
static struct table *route_map;
41+
42+
merr_t
43+
rest_register_routes(size_t routec, const struct rest_route *routev)
44+
{
45+
if (routec == 0)
46+
return 0;
47+
48+
if (routec > 0 && !routev)
49+
return merr(EINVAL);
50+
51+
for (size_t i = 0; i < routec; i++) {
52+
const void *elem = table_append_object(route_map, (void *)(routev + i));
53+
if (ev(!elem))
54+
return merr(ENOMEM);
55+
}
56+
57+
return 0;
58+
}
59+
60+
static merr_t
61+
create_socket(const char *const socket_path, int *const socket_fd)
62+
{
63+
struct sockaddr_un sock;
64+
int tmp_fd;
65+
int rc;
66+
size_t n;
67+
68+
INVARIANT(socket_path);
69+
INVARIANT(socket_fd);
70+
71+
/* In case the program exited inadvertently on the last run,
72+
* remove the socket.
73+
*/
74+
unlink(socket_path);
75+
76+
tmp_fd = socket(AF_UNIX, SOCK_STREAM, 0);
77+
if (tmp_fd == -1)
78+
return merr(errno);
79+
80+
sock.sun_family = AF_UNIX;
81+
n = strlcpy(sock.sun_path, socket_path, sizeof(sock.sun_path));
82+
if (n >= sizeof(sock.sun_path))
83+
return merr(EINVAL);
84+
85+
rc = bind(tmp_fd, (const struct sockaddr *)&sock, sizeof(sock));
86+
if (rc == -1) {
87+
close(tmp_fd);
88+
89+
return merr(errno);
90+
}
91+
92+
rc = listen(tmp_fd, 1);
93+
if (rc == -1) {
94+
close(tmp_fd);
95+
96+
return merr(errno);
97+
}
98+
99+
*socket_fd = tmp_fd;
100+
101+
return 0;
102+
}
103+
104+
static enum MHD_Result
105+
response(
106+
void *const cls,
107+
struct MHD_Connection *const connection,
108+
const char *const url,
109+
const char *const method,
110+
const char *const version,
111+
const char *const upload_data,
112+
unsigned long *const upload_data_size,
113+
void **const con_cls)
114+
{
115+
struct context *ctx;
116+
117+
log_debug("REST request received: %s %s %s", version, method, url);
118+
119+
if (!*con_cls) {
120+
ctx = calloc(1, sizeof(*ctx));
121+
if (ev(!ctx))
122+
return MHD_NO;
123+
124+
ctx->connection = connection;
125+
126+
if (strcmp(method, "GET") == 0) {
127+
ctx->method = REST_METHOD_GET;
128+
} else if (strcmp(method, "PUT") == 0) {
129+
ctx->method = REST_METHOD_PUT;
130+
}
131+
132+
*con_cls = ctx;
133+
134+
return MHD_YES;
135+
}
136+
137+
ctx = *con_cls;
138+
139+
switch (ctx->method) {
140+
case REST_METHOD_GET:
141+
break;
142+
case REST_METHOD_PUT:
143+
break;
144+
default:
145+
abort();
146+
}
147+
148+
return MHD_NO;
149+
}
150+
151+
static void *
152+
completed(
153+
void *const cls,
154+
struct MHD_Connection *const connection,
155+
void **const con_cls,
156+
const enum MHD_RequestTerminationCode toe)
157+
{
158+
struct context *ctx = *con_cls;
159+
160+
free(ctx);
161+
162+
return NULL;
163+
}
164+
165+
static void
166+
panic(
167+
void *const cls,
168+
const char *const file,
169+
const unsigned int line,
170+
const char *const reason)
171+
{
172+
log_err("REST server paniced at %s:%u: %s", file, line, reason);
173+
}
174+
175+
merr_t
176+
rest_server_start(const char *socket_path)
177+
{
178+
merr_t err;
179+
size_t n HSE_MAYBE_UNUSED;
180+
181+
if (server.initialized)
182+
return 0;
183+
184+
route_map = table_create(8, sizeof(struct rest_route), false);
185+
if (ev(!route_map))
186+
return merr(ENOMEM);
187+
188+
/* Keep a copy of the socket path so we can unlink it when closing the
189+
* rest server.
190+
*/
191+
n = strlcpy(server.socket_path, socket_path, sizeof(server.socket_path));
192+
assert(n < sizeof(server.socket_path));
193+
194+
err = create_socket(socket_path, &server.socket_fd);
195+
if (ev(err))
196+
goto out;
197+
198+
/* The REST server panicing should not abort the entire program */
199+
MHD_set_panic_func(panic, NULL);
200+
201+
server.daemon = MHD_start_daemon(
202+
MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_DEBUG,
203+
0,
204+
NULL,
205+
NULL,
206+
response,
207+
NULL,
208+
MHD_OPTION_LISTEN_SOCKET,
209+
server.socket_fd,
210+
MHD_OPTION_CONNECTION_LIMIT,
211+
1,
212+
MHD_OPTION_CONNECTION_TIMEOUT,
213+
120,
214+
MHD_OPTION_NOTIFY_COMPLETED,
215+
completed,
216+
NULL,
217+
MHD_OPTION_END);
218+
if (ev(!server.daemon)) {
219+
err = merr(ENOANO);
220+
goto out;
221+
}
222+
223+
server.initialized = true;
224+
225+
out:
226+
if (err) {
227+
table_destroy(route_map);
228+
if (server.socket_fd)
229+
unlink(server.socket_path);
230+
}
231+
232+
return 0;
233+
}
234+
235+
void
236+
rest_server_stop(void)
237+
{
238+
if (!server.initialized)
239+
return;
240+
241+
unlink(server.socket_path);
242+
table_destroy(route_map);
243+
MHD_stop_daemon(server.daemon);
244+
245+
memset(&server, 0, sizeof(server));
246+
}

lib/rest/meson.build

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
subdir('include')
2+
subdir('lib')
3+
4+
libhse_rest = static_library(
5+
'hse-rest',
6+
rest_sources,
7+
include_directories: [
8+
component_root_includes,
9+
public_includes,
10+
rest_includes,
11+
util_includes,
12+
],
13+
dependencies: [
14+
cjson_dep,
15+
libbsd_dep,
16+
rbtree_dep,
17+
],
18+
gnu_symbol_visibility: 'hidden'
19+
)
20+
21+
libhse_rest_dep = declare_dependency(
22+
link_with: libhse_rest,
23+
include_directories: [
24+
rest_includes,
25+
]
26+
)

lib/util/include/hse_util/map.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
* A map library to store key-value pairs. This is not a thread safe library.
66
*/
77

8+
#include <inttypes.h>
9+
#include <stdbool.h>
10+
811
#include <error/merr.h>
912

13+
#include <hse_util/compiler.h>
14+
1015
#ifndef HSE_UTIL_MAP_H
1116
#define HSE_UTIL_MAP_H
1217

@@ -57,7 +62,7 @@ map_remove_ptr(struct map *map, uint64_t key)
5762
void
5863
map_reset(struct map *map);
5964

60-
uint
65+
unsigned int
6166
map_count_get(struct map *map);
6267

6368
struct map_iter {

lib/util/include/hse_util/rest_api.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ rest_put_t(
7171
struct kv_iter * iter,
7272
void * context);
7373

74-
merr_t
75-
rest_server_start(const char *sock_path);
74+
// merr_t
75+
// rest_server_start(const char *sock_path);
7676

77-
void
78-
rest_server_stop(void);
77+
// void
78+
// rest_server_stop(void);
7979

8080
void
8181
rest_init(void);

0 commit comments

Comments
 (0)