Permalink
Browse files

moved from coderepos

  • Loading branch information...
0 parents commit ce6de4453c40a7c6590831bb46f04dedc66643a2 @kazuho kazuho committed Oct 8, 2009
Showing with 270 additions and 0 deletions.
  1. +166 −0 picohttpparser.c
  2. +21 −0 picohttpparser.h
  3. +83 −0 test.c
@@ -0,0 +1,166 @@
+#include <stddef.h>
+#include "picohttpparser.h"
+
+#define CHECK_EOF() \
+ if (buf == buf_end) { \
+ return -2; \
+ }
+
+#define EXPECT(ch) \
+ CHECK_EOF(); \
+ if (*buf++ != ch) { \
+ return -1; \
+ }
+
+#define ADVANCE_TOKEN() \
+ for (; ; ++buf) { \
+ CHECK_EOF(); \
+ if (*buf == ' ') { \
+ break; \
+ } else if (*buf == '\r' || *buf == '\n') { \
+ return -1; \
+ } \
+ }
+
+static int is_complete(const char* buf, const char* buf_end, size_t last_len)
+{
+ int ret_cnt = 0;
+ buf = last_len < 3 ? buf : buf + last_len - 3;
+
+ while (1) {
+ CHECK_EOF();
+ if (*buf == '\r') {
+ ++buf;
+ CHECK_EOF();
+ EXPECT('\n');
+ ++ret_cnt;
+ } else if (*buf == '\n') {
+ ++buf;
+ ++ret_cnt;
+ } else {
+ ++buf;
+ ret_cnt = 0;
+ }
+ if (ret_cnt == 2) {
+ return 0;
+ }
+ }
+
+ return -2;
+}
+
+int phr_parse_request(const char* _buf, size_t len, const char** method,
+ size_t* method_len, const char** path, size_t* path_len,
+ int* minor_version, struct phr_header* headers,
+ size_t* num_headers, size_t last_len)
+{
+ const char * buf = _buf, * buf_end = buf + len;
+ size_t max_headers;
+
+ /* if last_len != 0, check if the request is complete (a fast countermeasure
+ againt slowloris */
+ if (last_len != 0) {
+ int r = is_complete(buf, buf_end, last_len);
+ if (r != 0) {
+ return r;
+ }
+ }
+
+ /* skip first empty line (some clients add CRLF after POST content) */
+ CHECK_EOF();
+ if (*buf == '\r') {
+ ++buf;
+ EXPECT('\n');
+ } else if (*buf == '\n') {
+ ++buf;
+ }
+
+ /* parse request line */
+ *method = buf;
+ ADVANCE_TOKEN();
+ *method_len = buf - *method;
+ ++buf;
+ *path = buf;
+ ADVANCE_TOKEN();
+ *path_len = buf - *path;
+ ++buf;
+ EXPECT('H'); EXPECT('T'); EXPECT('T'); EXPECT('P'); EXPECT('/'); EXPECT('1');
+ EXPECT('.');
+ *minor_version = 0;
+ for (; ; ++buf) {
+ CHECK_EOF();
+ if ('0' <= *buf && *buf <= '9') {
+ *minor_version = *minor_version * 10 + *buf - '0';
+ } else {
+ break;
+ }
+ }
+ if (*buf == '\r') {
+ ++buf;
+ EXPECT('\n');
+ } else if (*buf == '\n') {
+ ++buf;
+ } else {
+ return -1;
+ }
+
+ /* parse headers */
+ max_headers = *num_headers;
+ for (*num_headers = 0; ; ++*num_headers) {
+ CHECK_EOF();
+ if (*buf == '\r') {
+ ++buf;
+ EXPECT('\n');
+ break;
+ } else if (*buf == '\n') {
+ ++buf;
+ break;
+ }
+ if (*num_headers == max_headers) {
+ return -1;
+ }
+ if (*num_headers == 0 || ! (*buf == ' ' || *buf == '\t')) {
+ /* parsing name */
+ headers[*num_headers].name = buf;
+ for (; ; ++buf) {
+ CHECK_EOF();
+ if (*buf == ':') {
+ break;
+ } else if (*buf < ' ') {
+ return -1;
+ }
+ }
+ headers[*num_headers].name_len = buf - headers[*num_headers].name;
+ ++buf;
+ for (; ; ++buf) {
+ CHECK_EOF();
+ if (! (*buf == ' ' || *buf == '\t')) {
+ break;
+ }
+ }
+ } else {
+ headers[*num_headers].name = NULL;
+ headers[*num_headers].name_len = 0;
+ }
+ headers[*num_headers].value = buf;
+ for (; ; ++buf) {
+ CHECK_EOF();
+ if (*buf == '\r') {
+ headers[*num_headers].value_len = buf - headers[*num_headers].value;
+ ++buf;
+ EXPECT('\n');
+ break;
+ } else if (*buf == '\n') {
+ headers[*num_headers].value_len = buf - headers[*num_headers].value;
+ ++buf;
+ break;
+ }
+ }
+ }
+
+ return buf - _buf;
+}
+
+#undef CHECK_EOF
+#undef EXPECT
+#undef ADVACE_TOKEN
@@ -0,0 +1,21 @@
+#ifndef picohttpparser_h
+#define picohttpparser_h
+
+/* contains name and value of a header (name == NULL if is a continuing line
+ * of a multiline header */
+struct phr_header {
+ const char* name;
+ size_t name_len;
+ const char* value;
+ size_t value_len;
+};
+
+/* returns number of bytes cosumed if successful, -2 if request is partial,
+ * -1 if failed */
+int phr_parse_request(const char* buf, size_t len, const char** method,
+ size_t* method_len, const char** path,
+ size_t* path_len, int* minor_version,
+ struct phr_header* headers, size_t* num_headers,
+ size_t last_len);
+
+#endif
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <string.h>
+#include "picohttpparser.h"
+
+void tests(int num)
+{
+ printf("1..%d\n", num);
+}
+
+void ok(int ok, const char* msg)
+{
+ static int testnum = 0;
+ printf("%s %d - %s\n", ok ? "ok" : "not ok", ++testnum, msg);
+}
+
+int strrcmp(const char* s, size_t l, const char* t)
+{
+ return strlen(t) == l && memcmp(s, t, l) == 0;
+}
+
+int main(void)
+{
+ const char* method;
+ size_t method_len;
+ const char* path;
+ size_t path_len;
+ int minor_version;
+ struct phr_header headers[4];
+ size_t num_headers;
+
+ tests(28);
+
+#define PARSE(s, last_len, exp, comment) \
+ num_headers = sizeof(headers) / sizeof(headers[0]); \
+ ok(phr_parse_request(s, strlen(s), &method, &method_len, &path, \
+ &path_len, &minor_version, headers, \
+ &num_headers, last_len) \
+ == (exp == 0 ? strlen(s) : exp), \
+ comment)
+
+ PARSE("GET / HTTP/1.0\r\n\r\n", 0, 0, "simple");
+ ok(num_headers == 0, "# of headers");
+ ok(strrcmp(method, method_len, "GET"), "method");
+ ok(strrcmp(path, path_len, "/"), "path");
+ ok(minor_version == 0, "minor version");
+
+ PARSE("GET / HTTP/1.0\r\n\r", 0, -2, "partial");
+
+ PARSE("GET /hoge HTTP/1.1\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0,
+ "parse headers");
+ ok(num_headers == 2, "# of headers");
+ ok(strrcmp(method, method_len, "GET"), "method");
+ ok(strrcmp(path, path_len, "/hoge"), "path");
+ ok(minor_version == 1, "minor version");
+ ok(strrcmp(headers[0].name, headers[0].name_len, "Host"), "host");
+ ok(strrcmp(headers[0].value, headers[0].value_len, "example.com"),
+ "host value");
+ ok(strrcmp(headers[1].name, headers[1].name_len, "Cookie"), "cookie");
+ ok(strrcmp(headers[1].value, headers[1].value_len, ""), "cookie value");
+
+ PARSE("GET / HTTP/1.0\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0,
+ "parse multiline");
+ ok(num_headers == 3, "# of headers");
+ ok(strrcmp(method, method_len, "GET"), "method");
+ ok(strrcmp(path, path_len, "/"), "path");
+ ok(minor_version == 0, "minor version");
+ ok(strrcmp(headers[0].name, headers[0].name_len, "foo"), "header #1 name");
+ ok(strrcmp(headers[0].value, headers[0].value_len, ""), "header #1 value");
+ ok(strrcmp(headers[1].name, headers[1].name_len, "foo"), "header #2 name");
+ ok(strrcmp(headers[1].value, headers[1].value_len, "b"), "header #2 value");
+ ok(headers[2].name == NULL, "header #3");
+ ok(strrcmp(headers[2].value, headers[2].value_len, " \tc"),
+ "header #3 value");
+
+ PARSE("GET /hoge HTTP/1.0\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1,
+ -2, "slowloris (incomplete)");
+ PARSE("GET /hoge HTTP/1.0\r\n\r\n", strlen("GET /hoge HTTP/1.0\r\n\r\n") - 1,
+ 0, "slowloris (complete)");
+
+#undef PARSE
+
+ return 0;
+}

0 comments on commit ce6de44

Please sign in to comment.