Skip to content

Commit a7c8711

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 abdc1e2 commit a7c8711

File tree

10 files changed

+423
-99
lines changed

10 files changed

+423
-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
@@ -72,6 +72,7 @@ subdir('kvdb')
7272
subdir('wal')
7373
subdir('mpool')
7474
subdir('pidfile')
75+
subdir('rest')
7576

7677
hse_sources = [
7778
build_config_h,
@@ -108,6 +109,7 @@ hse_dependencies = [
108109
libbsd_dep,
109110
libcurl_dep,
110111
libhse_pidfile_dep,
112+
libhse_rest_dep,
111113
libmicrohttpd_dep,
112114
liburcu_bp_dep,
113115
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: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
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+
11+
#include <hse_util/compiler.h>
812
#include <hse_util/hse_err.h>
913

1014
#ifndef HSE_UTIL_MAP_H
@@ -57,7 +61,7 @@ map_remove_ptr(struct map *map, uint64_t key)
5761
void
5862
map_reset(struct map *map);
5963

60-
uint
64+
unsigned int
6165
map_count_get(struct map *map);
6266

6367
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)