]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/src/http/request_parser.rl
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / src / http / request_parser.rl
1 /*
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.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
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
16 * under the License.
17 */
18 /*
19 * Copyright (C) 2014 Cloudius Systems, Ltd.
20 */
21
22 #pragma once
23
24 #include <seastar/core/ragel.hh>
25 #include <memory>
26 #include <unordered_map>
27 #include <seastar/http/request.hh>
28
29 namespace seastar {
30
31 using namespace httpd;
32
33 %% machine request;
34
35 %%{
36
37 access _fsm_;
38
39 action mark {
40 g.mark_start(p);
41 }
42
43 action store_method {
44 _req->_method = str();
45 }
46
47 action store_uri {
48 _req->_url = str();
49 }
50
51 action store_version {
52 _req->_version = str();
53 }
54
55 action store_field_name {
56 _field_name = str();
57 }
58
59 action store_value {
60 _value = str();
61 }
62
63 action trim_trailing_whitespace_and_store_value {
64 _value = str();
65 trim_trailing_spaces_and_tabs(_value);
66 g.mark_start(nullptr);
67 }
68
69 action assign_field {
70 auto [iter, inserted] = _req->_headers.try_emplace(_field_name, std::move(_value));
71 if (!inserted) {
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);
77 }
78 }
79
80 action extend_field {
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);
86 }
87
88 action done {
89 done = true;
90 fbreak;
91 }
92
93 crlf = '\r\n';
94 tchar = alpha | digit | '-' | '!' | '#' | '$' | '%' | '&' | '\'' | '*'
95 | '+' | '.' | '^' | '_' | '`' | '|' | '~';
96
97 sp = ' ';
98 ht = '\t';
99
100 sp_ht = sp | ht;
101
102 op_char = upper;
103
104 operation = op_char+ >mark %store_method;
105 uri = (any - sp)+ >mark %store_uri;
106 http_version = 'HTTP/' (digit '.' digit) >mark %store_version;
107
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
112 # between words.
113 # Trailing spaces are trimmed in postprocessing.
114 field_content = (field_vchar | sp_ht)*;
115
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);
123
124 }%%
125
126 class http_request_parser : public ragel_parser_base<http_request_parser> {
127 %% write data nofinal noprefix;
128 public:
129 enum class state {
130 error,
131 eof,
132 done,
133 };
134 std::unique_ptr<http::request> _req;
135 sstring _field_name;
136 sstring _value;
137 state _state;
138 public:
139 void init() {
140 init_base();
141 _req.reset(new http::request());
142 _state = state::eof;
143 %% write init;
144 }
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(); };
148 bool done = false;
149 if (p != pe) {
150 _state = state::error;
151 }
152 #ifdef __clang__
153 #pragma clang diagnostic push
154 #pragma clang diagnostic ignored "-Wmisleading-indentation"
155 #endif
156 %% write exec;
157 #ifdef __clang__
158 #pragma clang diagnostic pop
159 #endif
160 if (!done) {
161 if (p == eof) {
162 _state = state::eof;
163 } else if (p != pe) {
164 _state = state::error;
165 } else {
166 p = nullptr;
167 }
168 } else {
169 _state = state::done;
170 }
171 return p;
172 }
173 auto get_parsed_request() {
174 return std::move(_req);
175 }
176 bool eof() const {
177 return _state == state::eof;
178 }
179 bool failed() const {
180 return _state == state::error;
181 }
182 };
183
184 }