]> git.sesse.net Git - cubemap/blobdiff - parse.cpp
Tabs are seemingly accepted as linear white space in HTTP headers.
[cubemap] / parse.cpp
index 0747afb9e495334e41fec2bd639695b7bb172a29..37b5436bd6d7bc82cd7890e0082ad6a26543f727 100644 (file)
--- a/parse.cpp
+++ b/parse.cpp
@@ -1,11 +1,9 @@
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
 #include <ctype.h>
-#include <assert.h>
-#include <vector>
+#include <string.h>
 #include <string>
+#include <vector>
 
+#include "log.h"
 #include "parse.h"
 
 using namespace std;
@@ -58,86 +56,55 @@ vector<string> split_lines(const string &str)
        return ret;
 }
 
-vector<ConfigLine> parse_config(const string &filename)
+multimap<string, string> extract_headers(const vector<string> &lines, const string &log_context)
 {
-       vector<ConfigLine> ret;
-
-       FILE *fp = fopen(filename.c_str(), "r");
-       if (fp == NULL) {
-               perror(filename.c_str());
-               exit(1);
-       }
-
-       char buf[4096];
-       while (!feof(fp)) {
-               if (fgets(buf, sizeof(buf), fp) == NULL) {
-                       break;
+       multimap<string, string> parameters;
+       for (size_t i = 1; i < lines.size(); ++i) {
+               size_t split = lines[i].find(":");
+               if (split == string::npos) {
+                       log(WARNING, "[%s] Ignoring malformed HTTP response line '%s'",
+                               log_context.c_str(), lines[i].c_str());
+                       continue;
                }
 
-               // Chop off the string at the first #, \r or \n.
-               buf[strcspn(buf, "#\r\n")] = 0;
-
-               // Remove all whitespace from the end of the string.
-               size_t len = strlen(buf);
-               while (len > 0 && isspace(buf[len - 1])) {
-                       buf[--len] = 0;
-               }
+               string key(lines[i].begin(), lines[i].begin() + split);
 
-               // If the line is now all blank, ignore it.
-               if (len == 0) {
-                       continue;
-               }
+               // Skip any spaces after the colon.
+               do {
+                       ++split;
+               } while (split < lines[i].size() && (lines[i][split] == ' ' || lines[i][split] == '\t'));
 
-               vector<string> tokens = split_tokens(buf);
-               assert(!tokens.empty());
-               
-               ConfigLine line;
-               line.keyword = tokens[0];
-
-               for (size_t i = 1; i < tokens.size(); ++i) {
-                       // foo=bar is a parameter; anything else is an argument.
-                       size_t equals_pos = tokens[i].find_first_of('=');
-                       if (equals_pos == string::npos) {
-                               line.arguments.push_back(tokens[i]);
-                       } else {
-                               string key = tokens[i].substr(0, equals_pos);
-                               string value = tokens[i].substr(equals_pos + 1, string::npos);
-                               line.parameters.insert(make_pair(key, value));
-                       }
-               }
+               string value(lines[i].begin() + split, lines[i].end());
 
-               ret.push_back(line);
+               parameters.insert(make_pair(key, value));
        }
 
-       fclose(fp);
-       return ret;
+       return parameters;
 }
 
-int fetch_config_int(const vector<ConfigLine> &config, const string &keyword, int min_limit, int max_limit)
+#define MAX_REQUEST_SIZE 16384  /* 16 kB. */
+
+RequestParseStatus wait_for_double_newline(string *existing_data, const char *new_data, size_t new_data_size)
 {
-       bool value_found = false;
-       int value = -1;
-       for (unsigned i = 0; i < config.size(); ++i) {
-               if (config[i].keyword != keyword) {
-                       continue;
-               }
-               if (config[i].parameters.size() > 0 ||
-                   config[i].arguments.size() != 1) {
-                       fprintf(stderr, "ERROR: '%s' takes one argument and no parameters\n", keyword.c_str());
-                       exit(1);
-               }
-               value_found = true;
-               value = atoi(config[i].arguments[0].c_str());  // TODO: verify int validity.
-       }
-       if (!value_found) {
-               fprintf(stderr, "ERROR: Missing '%s' statement in config file.\n",
-                       keyword.c_str());
-               exit(1);
+       // Guard against overlong requests gobbling up all of our space.
+       if (existing_data->size() + new_data_size > MAX_REQUEST_SIZE) {
+               return RP_OUT_OF_SPACE;
+       }       
+
+       // See if we have \r\n\r\n anywhere in the request. We start three bytes
+       // before what we just appended, in case we just got the final character.
+       size_t existing_data_bytes = existing_data->size();
+       existing_data->append(string(new_data, new_data + new_data_size));
+       
+       const size_t start_at = (existing_data_bytes >= 3 ? existing_data_bytes - 3 : 0);
+       const char *ptr = reinterpret_cast<char *>(
+               memmem(existing_data->data() + start_at, existing_data->size() - start_at,
+                      "\r\n\r\n", 4));
+       if (ptr == NULL) {
+               return RP_NOT_FINISHED_YET;
        }
-       if (value < min_limit || value > max_limit) {
-               fprintf(stderr, "ERROR: '%s' is set to %d, must be in [%d,%d]\n",
-                       keyword.c_str(), value, min_limit, max_limit);
-               exit(1);
+       if (ptr != existing_data->data() + existing_data->size() - 4) {
+               return RP_EXTRA_DATA;
        }
-       return value;
+       return RP_FINISHED;
 }