Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added response parser

  • Loading branch information...
commit 903187ddf8b7d071e155f5c10ebfb96f2cf5139f 1 parent ce6de44
@tokuhirom tokuhirom authored
Showing with 214 additions and 49 deletions.
  1. +119 −49 picohttpparser.c
  2. +6 −0 picohttpparser.h
  3. +89 −0 test_response.c
View
168 picohttpparser.c
@@ -49,13 +49,68 @@ static int is_complete(const char* buf, const char* buf_end, size_t last_len)
return -2;
}
+static int parse_headers(const char *buf, const char *_buf, const char *buf_end, struct phr_header* headers, size_t* num_headers) {
+ size_t 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;
+}
+
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 */
@@ -104,61 +159,76 @@ int phr_parse_request(const char* _buf, size_t len, const char** method,
return -1;
}
- /* parse headers */
- max_headers = *num_headers;
- for (*num_headers = 0; ; ++*num_headers) {
+ return parse_headers(buf, _buf, buf_end, headers, num_headers);
+}
+
+int phr_parse_response(const char* _buf, size_t len, int *minor_version,
+ int *status, const char **msg, size_t *msg_len,
+ struct phr_header* headers, size_t* num_headers,
+ size_t last_len)
+{
+ const char * buf = _buf, * buf_end = buf + len;
+
+ /* if last_len != 0, check if the response 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) */
+ /* is this needed for response parser? -- tokuhirom */
+ CHECK_EOF();
+ if (*buf == '\r') {
+ ++buf;
+ EXPECT('\n');
+ } else if (*buf == '\n') {
+ ++buf;
+ }
+
+ /* parse request line */
+ EXPECT('H'); EXPECT('T'); EXPECT('T'); EXPECT('P'); EXPECT('/'); EXPECT('1');
+ EXPECT('.');
+ *minor_version = 0;
+ for (; ; ++buf) {
CHECK_EOF();
- if (*buf == '\r') {
- ++buf;
- EXPECT('\n');
- break;
- } else if (*buf == '\n') {
- ++buf;
+ if ('0' <= *buf && *buf <= '9') {
+ *minor_version = *minor_version * 10 + *buf - '0';
+ } else {
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;
- }
- }
+ }
+ ++buf; /* skip space */
+ *status = 0;
+ for (; ; ++buf) {
+ CHECK_EOF();
+ if ('0' <= *buf && *buf <= '9') {
+ *status = *status * 10 + *buf - '0';
} else {
- headers[*num_headers].name = NULL;
- headers[*num_headers].name_len = 0;
+ break;
}
- 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;
- }
+ }
+ ++buf; /* skip space */
+ *msg = buf;
+ for (; ; ++buf) {
+ CHECK_EOF();
+ if (*buf == '\r' || *buf == '\n') {
+ break;
}
}
-
- return buf - _buf;
+ *msg_len = buf - *msg;
+ if (*buf == '\r') {
+ ++buf;
+ EXPECT('\n');
+ } else if (*buf == '\n') {
+ ++buf;
+ } else {
+ return -1;
+ }
+
+ return parse_headers(buf, _buf, buf_end, headers, num_headers);
}
#undef CHECK_EOF
View
6 picohttpparser.h
@@ -18,4 +18,10 @@ int phr_parse_request(const char* buf, size_t len, const char** method,
struct phr_header* headers, size_t* num_headers,
size_t last_len);
+/* ditto */
+int phr_parse_response(const char* _buf, size_t len, int *minor_version,
+ int *status, const char **msg, size_t *msg_len,
+ struct phr_header* headers, size_t* num_headers,
+ size_t last_len);
+
#endif
View
89 test_response.c
@@ -0,0 +1,89 @@
+#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)
+{
+ int minor_version;
+ int status;
+ const char *msg;
+ size_t msg_len;
+ struct phr_header headers[4];
+ size_t num_headers;
+
+ tests(33);
+
+#define PARSE(s, last_len, exp, comment) \
+ num_headers = sizeof(headers) / sizeof(headers[0]); \
+ ok(phr_parse_response(s, strlen(s), &minor_version, &status, \
+ &msg, &msg_len, headers, \
+ &num_headers, last_len) \
+ == (exp == 0 ? strlen(s) : exp), \
+ comment)
+
+ PARSE("HTTP/1.0 200 OK\r\n\r\n", 0, 0, "simple");
+ ok(num_headers == 0, "# of headers");
+ ok(status == 200, "http status code");
+ ok(minor_version = 1, "method");
+ ok(strrcmp(msg, msg_len, "OK"), "msg");
+
+ PARSE("HTTP/1.0 200 OK\r\n\r", 0, -2, "partial");
+
+ PARSE("HTTP/1.1 200 OK\r\nHost: example.com\r\nCookie: \r\n\r\n", 0, 0,
+ "parse headers");
+ ok(num_headers == 2, "# of headers");
+ ok(minor_version == 1, "minor_version");
+ ok(status == 200, "status");
+ ok(strrcmp(msg, msg_len, "OK"), "msg");
+ 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("HTTP/1.0 200 OK\r\nfoo: \r\nfoo: b\r\n \tc\r\n\r\n", 0, 0,
+ "parse multiline");
+ ok(num_headers == 3, "# of headers");
+ ok(minor_version == 0, "minor_version");
+ ok(status == 200, "status");
+ ok(strrcmp(msg, msg_len, "OK"), "msg");
+ 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("HTTP/1.0 500 Internal Server Error\r\n\r\n", 0, 0,
+ "internal server error");
+ ok(num_headers == 0, "# of headers");
+ ok(minor_version == 0, "minor_version");
+ ok(status == 500, "status");
+ ok(strrcmp(msg, msg_len, "Internal Server Error"), "msg");
+
+ PARSE("HTTP/1.0 200 OK\r\n\r", strlen("GET /hoge HTTP/1.0\r\n\r") - 1,
+ -2, "slowloris (incomplete)");
+ PARSE("HTTP/1.0 200 OK\r\n\r\n", strlen("HTTP/1.0 200 OK\r\n\r\n") - 1,
+ 0, "slowloris (complete)");
+
+#undef PARSE
+
+ return 0;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.