|
@@ -32,8 +32,11 @@
|
|
|
#include "keys.h"
|
|
|
#include "socket.h"
|
|
|
|
|
|
+#define MAX_URL 256
|
|
|
+
|
|
|
static const struct option long_options[] = {
|
|
|
{"port", 1, 0, 'p'},
|
|
|
+ {"endpoint", 1, 0, 'e'},
|
|
|
{"listen", 0, 0, 'l'},
|
|
|
{"version", 0, 0, 'v'},
|
|
|
{"help", 0, 0, 'h'},
|
|
@@ -45,6 +48,7 @@ print_help(const char *name)
|
|
|
{
|
|
|
fprintf(stderr, "Usage: %s [OPTIONS] <jwkdir>\n", name);
|
|
|
fprintf(stderr, " -p, --port=PORT Specify the port to listen (default 9090)\n");
|
|
|
+ fprintf(stderr, " -e, --endpoint=ENDPOINT Specify endpoint to listen (empty by default)\n");
|
|
|
fprintf(stderr, " -l, --listen Run as a service and wait for connections\n");
|
|
|
fprintf(stderr, " -v, --version Display program version\n");
|
|
|
fprintf(stderr, " -h, --help Show this help message\n");
|
|
@@ -64,7 +68,7 @@ str_cleanup(char **str)
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-adv(enum http_method method, const char *path, const char *body,
|
|
|
+adv(http_method_t method, const char *path, const char *body,
|
|
|
regmatch_t matches[], void *misc)
|
|
|
{
|
|
|
__attribute__((cleanup(str_cleanup))) char *adv = NULL;
|
|
@@ -101,7 +105,7 @@ adv(enum http_method method, const char *path, const char *body,
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-rec(enum http_method method, const char *path, const char *body,
|
|
|
+rec(http_method_t method, const char *path, const char *body,
|
|
|
regmatch_t matches[], void *misc)
|
|
|
{
|
|
|
__attribute__((cleanup(str_cleanup))) char *enc = NULL;
|
|
@@ -184,7 +188,7 @@ rec(enum http_method method, const char *path, const char *body,
|
|
|
"\r\n%s", strlen(enc), enc);
|
|
|
}
|
|
|
|
|
|
-static struct http_dispatch dispatch[] = {
|
|
|
+static struct http_dispatch s_dispatch[] = {
|
|
|
{ adv, 1 << HTTP_GET, 2, "^/+adv/+([0-9A-Za-z_-]+)$" },
|
|
|
{ adv, 1 << HTTP_GET, 2, "^/+adv/*$" },
|
|
|
{ rec, 1 << HTTP_POST, 2, "^/+rec/+([0-9A-Za-z_-]+)$" },
|
|
@@ -193,17 +197,56 @@ static struct http_dispatch dispatch[] = {
|
|
|
|
|
|
#define DEFAULT_PORT 9090
|
|
|
|
|
|
+static size_t
|
|
|
+tang_http_parser_execute(http_parser_t *parser, const char* data, size_t len)
|
|
|
+{
|
|
|
+#ifdef USE_LLHTTP
|
|
|
+ llhttp_errno_t error;
|
|
|
+ size_t parsed_len;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Unlike http_parser, which returns the number of parsed
|
|
|
+ * bytes in the _execute() call, llhttp returns an error
|
|
|
+ * code.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (data == NULL || len == 0) {
|
|
|
+ error = llhttp_finish(parser);
|
|
|
+ } else {
|
|
|
+ error = llhttp_execute(parser, data, len);
|
|
|
+ }
|
|
|
+
|
|
|
+ parsed_len = len;
|
|
|
+ /*
|
|
|
+ * Adjust number of parsed bytes in case of error.
|
|
|
+ */
|
|
|
+ if (error != HPE_OK) {
|
|
|
+ parsed_len = llhttp_get_error_pos(parser) - data;
|
|
|
+
|
|
|
+ /* This isn't a real pause, just a way to stop parsing early. */
|
|
|
+ if (error == HPE_PAUSED_UPGRADE) {
|
|
|
+ llhttp_resume_after_upgrade(parser);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return parsed_len;
|
|
|
+#else
|
|
|
+ return http_parser_execute(parser, &http_settings, data, len);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
process_request(const char *jwkdir, int in_fileno)
|
|
|
{
|
|
|
- struct http_state state = { .dispatch = dispatch, .misc = (char*)jwkdir };
|
|
|
- struct http_parser parser = { .data = &state };
|
|
|
+ struct http_state state = { .dispatch = s_dispatch, .misc = (char*)jwkdir };
|
|
|
+ http_parser_t parser;
|
|
|
struct stat st = {};
|
|
|
char req[4096] = {};
|
|
|
size_t rcvd = 0;
|
|
|
int r = 0;
|
|
|
|
|
|
- http_parser_init(&parser, HTTP_REQUEST);
|
|
|
+ tang_http_parser_init(&parser, &http_settings);
|
|
|
+ parser.data = &state;
|
|
|
|
|
|
if (stat(jwkdir, &st) != 0) {
|
|
|
fprintf(stderr, "Error calling stat() on path: %s: %m\n", jwkdir);
|
|
@@ -224,17 +267,22 @@ process_request(const char *jwkdir, int in_fileno)
|
|
|
|
|
|
rcvd += r;
|
|
|
|
|
|
- r = http_parser_execute(&parser, &http_settings, req, rcvd);
|
|
|
- if (parser.http_errno != 0) {
|
|
|
+ r = tang_http_parser_execute(&parser, req, rcvd);
|
|
|
+ switch (tang_http_parser_errno(parser)) {
|
|
|
+ case HPE_OK:
|
|
|
+ break;
|
|
|
+ case HPE_PAUSED:
|
|
|
+ tang_http_parser_resume(&parser);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
fprintf(stderr, "HTTP Parsing Error: %s\n",
|
|
|
- http_errno_description(parser.http_errno));
|
|
|
+ tang_http_errno_description(&parser, tang_http_parser_errno(parser)));
|
|
|
return EXIT_SUCCESS;
|
|
|
}
|
|
|
|
|
|
memmove(req, &req[r], rcvd - r);
|
|
|
rcvd -= r;
|
|
|
}
|
|
|
-
|
|
|
return EXIT_SUCCESS;
|
|
|
}
|
|
|
|
|
@@ -244,9 +292,10 @@ main(int argc, char *argv[])
|
|
|
int listen = 0;
|
|
|
int port = DEFAULT_PORT;
|
|
|
const char *jwkdir = NULL;
|
|
|
+ const char *endpoint = NULL;
|
|
|
|
|
|
while (1) {
|
|
|
- int c = getopt_long(argc, argv, "lp:vh", long_options, NULL);
|
|
|
+ int c = getopt_long(argc, argv, "lp:e:vh", long_options, NULL);
|
|
|
if (c == -1)
|
|
|
break;
|
|
|
|
|
@@ -260,6 +309,9 @@ main(int argc, char *argv[])
|
|
|
case 'p':
|
|
|
port = atoi(optarg);
|
|
|
break;
|
|
|
+ case 'e':
|
|
|
+ endpoint = optarg;
|
|
|
+ break;
|
|
|
case 'l':
|
|
|
listen = 1;
|
|
|
break;
|
|
@@ -272,9 +324,24 @@ main(int argc, char *argv[])
|
|
|
}
|
|
|
jwkdir = argv[optind++];
|
|
|
|
|
|
+ char adv_thp_endpoint[MAX_URL] = {};
|
|
|
+ char adv_endpoint[MAX_URL] = {};
|
|
|
+ char rec_endpoint[MAX_URL] = {};
|
|
|
+ if (endpoint != NULL) {
|
|
|
+ char *endpoint_ptr = (char*)endpoint;
|
|
|
+ while (*endpoint_ptr == '/') {
|
|
|
+ endpoint_ptr++;
|
|
|
+ }
|
|
|
+ snprintf(adv_thp_endpoint, MAX_URL, "^/%s/+adv/+([0-9A-Za-z_-]+)$", endpoint_ptr);
|
|
|
+ snprintf(adv_endpoint, MAX_URL, "^/%s/+adv/*$", endpoint_ptr);
|
|
|
+ snprintf(rec_endpoint, MAX_URL, "^/%s/+rec/+([0-9A-Za-z_-]+)$", endpoint_ptr);
|
|
|
+ s_dispatch[0].re = adv_thp_endpoint;
|
|
|
+ s_dispatch[1].re = adv_endpoint;
|
|
|
+ s_dispatch[2].re = rec_endpoint;
|
|
|
+ }
|
|
|
if (listen == 0) { /* process one-shot query from stdin */
|
|
|
- return process_request(jwkdir, STDIN_FILENO);
|
|
|
+ return process_request(jwkdir, STDIN_FILENO);
|
|
|
} else { /* listen and process all incoming connections */
|
|
|
- return run_service(jwkdir, port, process_request);
|
|
|
+ return run_service(jwkdir, port, process_request);
|
|
|
}
|
|
|
}
|