summaryrefslogtreecommitdiff
path: root/src/http.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/http.cc')
-rw-r--r--src/http.cc154
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();
}