diff options
Diffstat (limited to 'src/http.cc')
| -rw-r--r-- | src/http.cc | 154 |
1 files changed, 147 insertions, 7 deletions
diff --git a/src/http.cc b/src/http.cc index 26911cb..3b8a67b 100644 --- a/src/http.cc +++ b/src/http.cc @@ -34,6 +34,28 @@ inline char upper_ascii(char c) { return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c; } +inline bool is_lws(char c) { + return c == ' ' || c == '\t'; +} + +inline bool is_char(char c) { + return !(c & 0x80); +} + +inline bool is_ctl(char c) { + return c < ' ' || c == 0x7f; +} + +inline bool is_separator(char c) { + return is_lws(c) || c == '(' || c == ')' || c == '<' || c == '>' || c == '@' + || c == ',' || c == ';' || c == ':' || c == '\\' || c == '\"' || c == '/' + || c == '[' || c == ']' || c == '?' || c == '=' || c == '{' || c == '}'; +} + +inline bool is_token(char c) { + return is_char(c) && !is_ctl(c) && !is_separator(c); +} + bool lower_equal(char const* data, size_t start, size_t end, std::string const& str) { assert(start <= end); @@ -134,6 +156,122 @@ private: std::string const filter_; }; +class HeaderTokenIteratorImpl : public HeaderTokenIterator { +public: + HeaderTokenIteratorImpl(std::unique_ptr<HeaderIterator>&& header) + : header_(std::move(header)), start_(0), middle_(0), end_(0) { + check_token(); + } + + bool valid() const override { + return header_->valid(); + } + + std::string token() const override { + return make_string(header_->value().data(), start_, middle_); + } + + bool token_equal(std::string const& token) const override { + return lower_equal(header_->value().data(), start_, middle_, token); + } + + void next() override { + start_ = end_; + check_token(); + } + +private: + static size_t skip_lws(std::string const& str, size_t pos) { + while (pos < str.size() && is_lws(str[pos])) ++pos; + return pos; + } + + static size_t skip_token(std::string const& str, size_t pos) { + assert(is_token(str[pos])); + ++pos; + while (pos < str.size() && is_token(str[pos])) ++pos; + return pos; + } + + static size_t skip_quoted(std::string const& str, size_t pos) { + assert(str[pos] == '"'); + ++pos; + while (pos < str.size()) { + if (str[pos] == '\\') { + pos += 2; + } else if (str[pos] == '\"') { + ++pos; + break; + } else { + ++pos; + } + } + return pos; + } + + void check_token() { + while (true) { + if (!header_->valid()) return; + auto const& value = header_->value(); + start_ = skip_lws(value, start_); + if (start_ >= value.size()) { + header_->next(); + start_ = 0; + continue; + } + if (!is_token(value[start_])) { + if (value[start_] != ';') { + ++start_; + while (start_ < value.size() + && !(is_lws(value[start_]) || value[start_] == ',' + || value[start_] == ';')) { + ++start_; + } + if (start_ < value.size() && value[start_] != ';') { + continue; + } + } + // This will cause us to loop again after paramters + // are read + middle_ = start_; + } else { + middle_ = skip_token(value, start_); + } + end_ = middle_; + while (true) { + end_ = skip_lws(value, end_); + if (end_ == value.size() || value[end_] != ';') break; + end_ = skip_lws(value, end_ + 1); + if (!is_token(value[end_])) { + while (end_ < value.size() && !is_separator(value[end_])) ++end_; + continue; + } + end_ = skip_token(value, end_); + end_ = skip_lws(value, end_); + if (end_ == value.size() || value[end_] != '=') break; + end_ = skip_lws(value, end_ + 1); + if (end_ < value.size() && value[end_] == '"') { + end_ = skip_quoted(value, end_); + } else { + if (!is_token(value[end_])) { + while (end_ < value.size() && !is_separator(value[end_])) ++end_; + continue; + } + end_ = skip_token(value, end_); + } + } + if (end_ < value.size() && value[end_] == ',') ++end_; + if (start_ < middle_) return; + start_ = end_; + } + } + + std::unique_ptr<HeaderIterator> header_; + size_t start_; + size_t middle_; + size_t end_; +}; + class AbstractHttp : public virtual Http { public: AbstractHttp(char const* data, size_t size) @@ -166,6 +304,12 @@ public: new FilterHeaderIteratorImpl(data_, &headers_, name)); } + std::unique_ptr<HeaderTokenIterator> header_tokens(std::string const& name) + const override { + return std::unique_ptr<HeaderTokenIterator>( + new HeaderTokenIteratorImpl(header(name))); + } + size_t size() const override { return content_start_; } @@ -209,7 +353,7 @@ protected: data_ = data; } - char const* data() const { + char const* data() const override { return data_; } @@ -225,10 +369,6 @@ protected: return std::string::npos; } - static bool is_lws(char c) { - return c == ' ' || c == '\t'; - } - size_t skip_lws(size_t start, size_t end) const { assert(start <= end); while (start < end && is_lws(data_[start])) ++start; @@ -601,7 +741,7 @@ HttpResponse* HttpResponse::parse(char const* data, size_t len, bool copy) { auto ret = std::unique_ptr<UniqueHttpResponse>( new UniqueHttpResponse(data, len)); if (ret->parse() == INCOMPLETE) return nullptr; - if (copy) ret->copy(); + if (copy && ret->good()) ret->copy(); return ret.release(); } @@ -633,7 +773,7 @@ HttpRequest* HttpRequest::parse(char const* data, size_t len, bool copy) { auto ret = std::unique_ptr<UniqueHttpRequest>( new UniqueHttpRequest(data, len)); if (ret->parse() == INCOMPLETE) return nullptr; - if (copy) ret->copy(); + if (copy && ret->good()) ret->copy(); return ret.release(); } |
