123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
- /*
- * Copyright (c) 2016 Red Hat, Inc.
- * Author: Nathaniel McCallum <npmccallum@redhat.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "http.h"
- #undef http_reply
- #include <errno.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- static const char *METHOD_NAMES[] = {
- #define XX(num, name, string) [num] = # string,
- HTTP_METHOD_MAP(XX)
- #undef XX
- NULL
- };
- static int
- on_url(http_parser *parser, const char *at, size_t length)
- {
- struct http_state *state = parser->data;
- if (state->req.status == 0) {
- if (strlen(state->req.path) + length >= sizeof(state->req.path))
- state->req.status = HTTP_STATUS_URI_TOO_LONG;
- else
- strncat(state->req.path, at, length);
- }
- return 0;
- }
- static int
- on_body(http_parser *parser, const char *at, size_t length)
- {
- struct http_state *state = parser->data;
- if (state->req.status == 0) {
- if (strlen(state->req.body) + length >= sizeof(state->req.body))
- state->req.status = HTTP_STATUS_PAYLOAD_TOO_LARGE;
- else
- strncat(state->req.body, at, length);
- }
- return 0;
- }
- static int
- on_message_complete(http_parser *parser)
- {
- struct http_state *state = parser->data;
- const char *addr = NULL;
- bool pathmatch = false;
- bool methmatch = false;
- int r = 0;
- if (state->req.status != 0)
- goto error;
- addr = getenv("REMOTE_ADDR");
- fprintf(stderr, "%s %s %s",
- addr ? addr : "<unknown>",
- METHOD_NAMES[parser->method],
- state->req.path);
- for (size_t i = 0; state->dispatch[i].re && r == 0; i++) {
- const struct http_dispatch *d = &state->dispatch[i];
- regmatch_t match[d->nmatches];
- regex_t re = {};
- memset(match, 0, sizeof(match));
- r = regcomp(&re, d->re, REG_EXTENDED) == 0 ? 0 : -EINVAL;
- if (r != 0) {
- state->req.status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
- goto error;
- }
- if (regexec(&re, state->req.path, d->nmatches, match, 0) == 0) {
- pathmatch = true;
- if (((1 << parser->method) & d->methods) != 0) {
- methmatch = true;
- r = d->func(parser->method, state->req.path,
- state->req.body, match, state->misc);
- }
- }
- regfree(&re);
- }
- if (r > 0)
- goto egress;
- if (r == 0) {
- if (!pathmatch)
- state->req.status = HTTP_STATUS_NOT_FOUND;
- else if (!methmatch)
- state->req.status = HTTP_STATUS_METHOD_NOT_ALLOWED;
- else
- state->req.status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
- } else {
- state->req.status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
- }
- error:
- http_reply(__FILE__, __LINE__, state->req.status, NULL);
- egress:
- memset(&state->req, 0, sizeof(state->req));
- return 0;
- }
- const http_parser_settings http_settings = {
- .on_url = on_url,
- .on_body = on_body,
- .on_message_complete = on_message_complete,
- };
- int
- http_reply(const char *file, int line,
- enum http_status code, const char *fmt, ...)
- {
- const char *msg = NULL;
- va_list ap;
- int a;
- int b;
- switch (code) {
- #define XX(num, name, string) case num: msg = # string; break;
- HTTP_STATUS_MAP(XX)
- #undef XX
- default:
- return http_reply(file, line, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
- }
- fprintf(stderr, " => %d (%s:%d)\n", code, file, line);
- a = dprintf(STDOUT_FILENO, "HTTP/1.1 %d %s\r\n", code, msg);
- if (a < 0)
- return a;
- va_start(ap, fmt);
- b = vdprintf(STDOUT_FILENO, fmt ? fmt : "Content-Length: 0\r\n\r\n", ap);
- va_end(ap);
- return b < 0 ? b : a + b;
- }
|