_value = str();
}
+action trim_trailing_whitespace_and_store_value {
+ _value = str();
+ trim_trailing_spaces_and_tabs(_value);
+ g.mark_start(nullptr);
+}
+
action assign_field {
- _rsp->_headers[_field_name] = std::move(_value);
+ auto [iter, inserted] = _rsp->_headers.try_emplace(_field_name, std::move(_value));
+ if (!inserted) {
+ // RFC 7230, section 3.2.2. Field Parsing:
+ // A recipient MAY combine multiple header fields with the same field name into one
+ // "field-name: field-value" pair, without changing the semantics of the message,
+ // by appending each subsequent field value to the combined field value in order, separated by a comma.
+ iter->second += sstring(",") + std::move(_value);
+ }
}
action extend_field {
+ // RFC 7230, section 3.2.4. Field Order:
+ // A server that receives an obs-fold in a request message that is not
+ // within a message/http container MUST either reject the message [...]
+ // or replace each received obs-fold with one or more SP octets [...]
_rsp->_headers[_field_name] += sstring(" ") + std::move(_value);
}
http_version = 'HTTP/' (digit '.' digit) >mark %store_version;
+obs_text = 0x80..0xFF; # defined in RFC 7230, Section 3.2.6.
+field_vchar = (graph | obs_text);
+# RFC 9110, Section 5.5 allows single ' '/'\t' separators between
+# field_vchar words. We are less strict and allow any number of spaces
+# between words.
+# Trailing spaces are trimmed in postprocessing.
+field_content = (field_vchar | sp_ht)*;
+
field = tchar+ >mark %store_field_name;
-value = any* >mark %store_value;
+value = field_content >mark %trim_trailing_whitespace_and_store_value;
status_code = (digit digit digit) >mark %store_status;
start_line = http_version space status_code space (any - cr - lf)* crlf;
-header_1st = (field sp_ht* ':' value :> crlf) %assign_field;
-header_cont = (sp_ht+ value sp_ht* crlf) %extend_field;
+header_1st = (field ':' sp_ht* <: value crlf) %assign_field;
+header_cont = (sp_ht+ <: value crlf) %extend_field;
header = header_1st header_cont*;
-main := start_line header* :> (crlf @done);
+main := start_line header* (crlf @done);
}%%
#pragma clang diagnostic pop
#endif
if (!done) {
- p = nullptr;
+ if (p == eof) {
+ _state = state::eof;
+ } else if (p != pe) {
+ _state = state::error;
+ } else {
+ p = nullptr;
+ }
} else {
_state = state::done;
}
bool eof() const {
return _state == state::eof;
}
+ bool failed() const {
+ return _state == state::error;
+ }
};
}