http.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
  2. /*
  3. * Copyright (c) 2016 Red Hat, Inc.
  4. * Author: Nathaniel McCallum <npmccallum@redhat.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "http.h"
  20. #undef http_reply
  21. #include <errno.h>
  22. #include <stdarg.h>
  23. #include <stdbool.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <unistd.h>
  28. static const char *METHOD_NAMES[] = {
  29. #define XX(num, name, string) [num] = # string,
  30. HTTP_METHOD_MAP(XX)
  31. #undef XX
  32. NULL
  33. };
  34. static int
  35. on_url(http_parser *parser, const char *at, size_t length)
  36. {
  37. struct http_state *state = parser->data;
  38. if (state->req.status == 0) {
  39. if (strlen(state->req.path) + length >= sizeof(state->req.path))
  40. state->req.status = HTTP_STATUS_URI_TOO_LONG;
  41. else
  42. strncat(state->req.path, at, length);
  43. }
  44. return 0;
  45. }
  46. static int
  47. on_body(http_parser *parser, const char *at, size_t length)
  48. {
  49. struct http_state *state = parser->data;
  50. if (state->req.status == 0) {
  51. if (strlen(state->req.body) + length >= sizeof(state->req.body))
  52. state->req.status = HTTP_STATUS_PAYLOAD_TOO_LARGE;
  53. else
  54. strncat(state->req.body, at, length);
  55. }
  56. return 0;
  57. }
  58. static int
  59. on_message_complete(http_parser *parser)
  60. {
  61. struct http_state *state = parser->data;
  62. const char *addr = NULL;
  63. bool pathmatch = false;
  64. bool methmatch = false;
  65. int r = 0;
  66. if (state->req.status != 0)
  67. goto error;
  68. addr = getenv("REMOTE_ADDR");
  69. fprintf(stderr, "%s %s %s",
  70. addr ? addr : "<unknown>",
  71. METHOD_NAMES[parser->method],
  72. state->req.path);
  73. for (size_t i = 0; state->dispatch[i].re && r == 0; i++) {
  74. const struct http_dispatch *d = &state->dispatch[i];
  75. regmatch_t match[d->nmatches];
  76. regex_t re = {};
  77. memset(match, 0, sizeof(match));
  78. r = regcomp(&re, d->re, REG_EXTENDED) == 0 ? 0 : -EINVAL;
  79. if (r != 0) {
  80. state->req.status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
  81. goto error;
  82. }
  83. if (regexec(&re, state->req.path, d->nmatches, match, 0) == 0) {
  84. pathmatch = true;
  85. if (((1 << parser->method) & d->methods) != 0) {
  86. methmatch = true;
  87. r = d->func(parser->method, state->req.path,
  88. state->req.body, match, state->misc);
  89. }
  90. }
  91. regfree(&re);
  92. }
  93. if (r > 0)
  94. goto egress;
  95. if (r == 0) {
  96. if (!pathmatch)
  97. state->req.status = HTTP_STATUS_NOT_FOUND;
  98. else if (!methmatch)
  99. state->req.status = HTTP_STATUS_METHOD_NOT_ALLOWED;
  100. else
  101. state->req.status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
  102. } else {
  103. state->req.status = HTTP_STATUS_INTERNAL_SERVER_ERROR;
  104. }
  105. error:
  106. http_reply(__FILE__, __LINE__, state->req.status, NULL);
  107. egress:
  108. memset(&state->req, 0, sizeof(state->req));
  109. return 0;
  110. }
  111. const http_parser_settings http_settings = {
  112. .on_url = on_url,
  113. .on_body = on_body,
  114. .on_message_complete = on_message_complete,
  115. };
  116. int
  117. http_reply(const char *file, int line,
  118. enum http_status code, const char *fmt, ...)
  119. {
  120. const char *msg = NULL;
  121. va_list ap;
  122. int a;
  123. int b;
  124. switch (code) {
  125. #define XX(num, name, string) case num: msg = # string; break;
  126. HTTP_STATUS_MAP(XX)
  127. #undef XX
  128. default:
  129. return http_reply(file, line, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
  130. }
  131. fprintf(stderr, " => %d (%s:%d)\n", code, file, line);
  132. a = dprintf(STDOUT_FILENO, "HTTP/1.1 %d %s\r\n", code, msg);
  133. if (a < 0)
  134. return a;
  135. va_start(ap, fmt);
  136. b = vdprintf(STDOUT_FILENO, fmt ? fmt : "Content-Length: 0\r\n\r\n", ap);
  137. va_end(ap);
  138. return b < 0 ? b : a + b;
  139. }