2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
19 * Copyright (C) 2014 Cloudius Systems, Ltd.
24 #include <seastar/core/ragel.hh>
26 #include <unordered_map>
27 #include <seastar/http/request.hh>
31 using namespace httpd;
44 _req->_method = str();
51 action store_version {
52 _req->_version = str();
55 action store_field_name {
63 action trim_trailing_whitespace_and_store_value {
65 trim_trailing_spaces_and_tabs(_value);
66 g.mark_start(nullptr);
70 auto [iter, inserted] = _req->_headers.try_emplace(_field_name, std::move(_value));
72 // RFC 7230, section 3.2.2. Field Parsing:
73 // A recipient MAY combine multiple header fields with the same field name into one
74 // "field-name: field-value" pair, without changing the semantics of the message,
75 // by appending each subsequent field value to the combined field value in order, separated by a comma.
76 iter->second += sstring(",") + std::move(_value);
81 // RFC 7230, section 3.2.4. Field Order:
82 // A server that receives an obs-fold in a request message that is not
83 // within a message/http container MUST either reject the message [...]
84 // or replace each received obs-fold with one or more SP octets [...]
85 _req->_headers[_field_name] += sstring(" ") + std::move(_value);
94 tchar = alpha | digit | '-' | '!' | '#' | '$' | '%' | '&' | '\'' | '*'
95 | '+' | '.' | '^' | '_' | '`' | '|' | '~';
104 operation = op_char+ >mark %store_method;
105 uri = (any - sp)+ >mark %store_uri;
106 http_version = 'HTTP/' (digit '.' digit) >mark %store_version;
108 obs_text = 0x80..0xFF; # defined in RFC 7230, Section 3.2.6.
109 field_vchar = (graph | obs_text);
110 # RFC 9110, Section 5.5 allows single ' '/'\t' separators between
111 # field_vchar words. We are less strict and allow any number of spaces
113 # Trailing spaces are trimmed in postprocessing.
114 field_content = (field_vchar | sp_ht)*;
116 field = tchar+ >mark %store_field_name;
117 value = field_content >mark %trim_trailing_whitespace_and_store_value;
118 start_line = ((operation sp uri sp http_version) -- crlf) crlf;
119 header_1st = (field ':' sp_ht* <: value crlf) %assign_field;
120 header_cont = (sp_ht+ <: value crlf) %extend_field;
121 header = header_1st header_cont*;
122 main := start_line header* (crlf @done);
126 class http_request_parser : public ragel_parser_base<http_request_parser> {
127 %% write data nofinal noprefix;
134 std::unique_ptr<http::request> _req;
141 _req.reset(new http::request());
145 char* parse(char* p, char* pe, char* eof) {
146 sstring_builder::guard g(_builder, p, pe);
147 [[maybe_unused]] auto str = [this, &g, &p] { g.mark_end(p); return get_str(); };
150 _state = state::error;
153 #pragma clang diagnostic push
154 #pragma clang diagnostic ignored "-Wmisleading-indentation"
158 #pragma clang diagnostic pop
163 } else if (p != pe) {
164 _state = state::error;
169 _state = state::done;
173 auto get_parsed_request() {
174 return std::move(_req);
177 return _state == state::eof;
179 bool failed() const {
180 return _state == state::error;