OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef NET_TOOLS_BALSA_BALSA_FRAME_H_ | |
6 #define NET_TOOLS_BALSA_BALSA_FRAME_H_ | |
7 | |
8 #include <utility> | |
9 #include <vector> | |
10 | |
11 #include "base/compiler_specific.h" | |
12 #include "base/port.h" | |
13 #include "net/tools/balsa/balsa_enums.h" | |
14 #include "net/tools/balsa/balsa_headers.h" | |
15 #include "net/tools/balsa/balsa_visitor_interface.h" | |
16 #include "net/tools/balsa/buffer_interface.h" | |
17 #include "net/tools/balsa/http_message_constants.h" | |
18 #include "net/tools/balsa/simple_buffer.h" | |
19 | |
20 // For additional debug output, uncomment the following: | |
21 // #define DEBUGFRAMER 1 | |
22 | |
23 namespace net { | |
24 | |
25 // BalsaFrame is a 'Model' of a framer (haha). | |
26 // It exists as a proof of concept headers framer. | |
27 class BalsaFrame { | |
28 public: | |
29 typedef std::vector<std::pair<size_t, size_t> > Lines; | |
30 | |
31 typedef BalsaHeaders::HeaderLineDescription HeaderLineDescription; | |
32 typedef BalsaHeaders::HeaderLines HeaderLines; | |
33 typedef BalsaHeaders::HeaderTokenList HeaderTokenList; | |
34 | |
35 // TODO(fenix): get rid of the 'kValidTerm*' stuff by using the 'since last | |
36 // index' strategy. Note that this implies getting rid of the HeaderFramed() | |
37 | |
38 static const uint32 kValidTerm1 = '\n' << 16 | | |
39 '\r' << 8 | | |
40 '\n'; | |
41 static const uint32 kValidTerm1Mask = 0xFF << 16 | | |
42 0xFF << 8 | | |
43 0xFF; | |
44 static const uint32 kValidTerm2 = '\n' << 8 | | |
45 '\n'; | |
46 static const uint32 kValidTerm2Mask = 0xFF << 8 | | |
47 0xFF; | |
48 BalsaFrame(); | |
49 ~BalsaFrame(); | |
50 | |
51 // Reset reinitializes all the member variables of the framer and clears the | |
52 // attached header object (but doesn't change the pointer value headers_). | |
53 void Reset(); | |
54 | |
55 const BalsaHeaders* const_balsa_headers() const { return headers_; } | |
56 BalsaHeaders* balsa_headers() { return headers_; } | |
57 // The method set_balsa_headers clears the headers provided and attaches them | |
58 // to the framer. This is a required step before the framer will process any | |
59 // input message data. | |
60 // To detach the header object from the framer, use set_balsa_headers(NULL). | |
61 void set_balsa_headers(BalsaHeaders* headers) { | |
62 if (headers_ != headers) { | |
63 headers_ = headers; | |
64 } | |
65 if (headers_) { | |
66 // Clear the headers if they are non-null, even if the new headers are | |
67 // the same as the old. | |
68 headers_->Clear(); | |
69 } | |
70 } | |
71 | |
72 void set_balsa_visitor(BalsaVisitorInterface* visitor) { | |
73 visitor_ = visitor; | |
74 if (visitor_ == NULL) { | |
75 visitor_ = &do_nothing_visitor_; | |
76 } | |
77 } | |
78 | |
79 void set_is_request(bool is_request) { is_request_ = is_request; } | |
80 | |
81 bool is_request() const { | |
82 return is_request_; | |
83 } | |
84 | |
85 void set_request_was_head(bool request_was_head) { | |
86 request_was_head_ = request_was_head; | |
87 } | |
88 | |
89 bool request_was_head() const { | |
90 return request_was_head_; | |
91 } | |
92 | |
93 void set_max_header_length(size_t max_header_length) { | |
94 max_header_length_ = max_header_length; | |
95 } | |
96 | |
97 size_t max_header_length() const { | |
98 return max_header_length_; | |
99 } | |
100 | |
101 void set_max_request_uri_length(size_t max_request_uri_length) { | |
102 max_request_uri_length_ = max_request_uri_length; | |
103 } | |
104 | |
105 size_t max_request_uri_length() const { | |
106 return max_request_uri_length_; | |
107 } | |
108 | |
109 | |
110 bool MessageFullyRead() { | |
111 return parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ; | |
112 } | |
113 | |
114 BalsaFrameEnums::ParseState ParseState() const { return parse_state_; } | |
115 | |
116 | |
117 bool Error() { | |
118 return parse_state_ == BalsaFrameEnums::PARSE_ERROR; | |
119 } | |
120 | |
121 BalsaFrameEnums::ErrorCode ErrorCode() const { return last_error_; } | |
122 | |
123 const BalsaHeaders* headers() const { return headers_; } | |
124 BalsaHeaders* mutable_headers() { return headers_; } | |
125 | |
126 size_t BytesSafeToSplice() const; | |
127 void BytesSpliced(size_t bytes_spliced); | |
128 | |
129 size_t ProcessInput(const char* input, size_t size); | |
130 | |
131 // Parses input and puts the key, value chunk extensions into extensions. | |
132 // TODO(phython): Find a better data structure to put the extensions into. | |
133 static void ProcessChunkExtensions(const char* input, size_t size, | |
134 BalsaHeaders* extensions); | |
135 | |
136 protected: | |
137 // The utils object needs access to the ParseTokenList in order to do its | |
138 // job. | |
139 friend class BalsaHeadersTokenUtils; | |
140 | |
141 inline void ProcessContentLengthLine( | |
142 size_t line_idx, | |
143 BalsaHeadersEnums::ContentLengthStatus* status, | |
144 size_t* length); | |
145 | |
146 inline void ProcessTransferEncodingLine(size_t line_idx); | |
147 | |
148 void ProcessFirstLine(const char* begin, | |
149 const char* end); | |
150 | |
151 void CleanUpKeyValueWhitespace( | |
152 const char* stream_begin, | |
153 const char* line_begin, | |
154 const char* current, | |
155 const char* line_end, | |
156 HeaderLineDescription* current_header_line); | |
157 | |
158 void FindColonsAndParseIntoKeyValue(); | |
159 | |
160 void ProcessHeaderLines(); | |
161 | |
162 inline size_t ProcessHeaders(const char* message_start, | |
163 size_t message_length); | |
164 | |
165 void AssignParseStateAfterHeadersHaveBeenParsed(); | |
166 | |
167 inline bool LineFramingFound(char current_char) { | |
168 return current_char == '\n'; | |
169 } | |
170 | |
171 // TODO(fenix): get rid of the following function and its uses (and | |
172 // replace with something more efficient) | |
173 inline bool HeaderFramingFound(char current_char) { | |
174 // Note that the 'if (current_char == '\n' ...)' test exists to ensure that | |
175 // the HeaderFramingMayBeFound test works properly. In benchmarking done on | |
176 // 2/13/2008, the 'if' actually speeds up performance of the function | |
177 // anyway.. | |
178 if (current_char == '\n' || current_char == '\r') { | |
179 term_chars_ <<= 8; | |
180 // This is necessary IFF architecture has > 8 bit char. Alas, I'm | |
181 // paranoid. | |
182 term_chars_ |= current_char & 0xFF; | |
183 | |
184 if ((term_chars_ & kValidTerm1Mask) == kValidTerm1) { | |
185 term_chars_ = 0; | |
186 return true; | |
187 } | |
188 if ((term_chars_ & kValidTerm2Mask) == kValidTerm2) { | |
189 term_chars_ = 0; | |
190 return true; | |
191 } | |
192 } else { | |
193 term_chars_ = 0; | |
194 } | |
195 return false; | |
196 } | |
197 | |
198 inline bool HeaderFramingMayBeFound() const { | |
199 return term_chars_ != 0; | |
200 } | |
201 | |
202 private: | |
203 class DoNothingBalsaVisitor : public BalsaVisitorInterface { | |
204 void ProcessBodyInput(const char* input, size_t size) override {} | |
205 void ProcessBodyData(const char* input, size_t size) override {} | |
206 void ProcessHeaderInput(const char* input, size_t size) override {} | |
207 void ProcessTrailerInput(const char* input, size_t size) override {} | |
208 void ProcessHeaders(const BalsaHeaders& headers) override {} | |
209 void ProcessRequestFirstLine(const char* line_input, | |
210 size_t line_length, | |
211 const char* method_input, | |
212 size_t method_length, | |
213 const char* request_uri_input, | |
214 size_t request_uri_length, | |
215 const char* version_input, | |
216 size_t version_length) override {} | |
217 void ProcessResponseFirstLine(const char* line_input, | |
218 size_t line_length, | |
219 const char* version_input, | |
220 size_t version_length, | |
221 const char* status_input, | |
222 size_t status_length, | |
223 const char* reason_input, | |
224 size_t reason_length) override {} | |
225 void ProcessChunkLength(size_t chunk_length) override {} | |
226 void ProcessChunkExtensions(const char* input, size_t size) override {} | |
227 void HeaderDone() override {} | |
228 void MessageDone() override {} | |
229 void HandleHeaderError(BalsaFrame* framer) override {} | |
230 void HandleHeaderWarning(BalsaFrame* framer) override {} | |
231 void HandleChunkingError(BalsaFrame* framer) override {} | |
232 void HandleBodyError(BalsaFrame* framer) override {} | |
233 }; | |
234 | |
235 bool last_char_was_slash_r_; | |
236 bool saw_non_newline_char_; | |
237 bool start_was_space_; | |
238 bool chunk_length_character_extracted_; | |
239 bool is_request_; // This is not reset in Reset() | |
240 bool request_was_head_; // This is not reset in Reset() | |
241 size_t max_header_length_; // This is not reset in Reset() | |
242 size_t max_request_uri_length_; // This is not reset in Reset() | |
243 BalsaVisitorInterface* visitor_; | |
244 size_t chunk_length_remaining_; | |
245 size_t content_length_remaining_; | |
246 const char* last_slash_n_loc_; | |
247 const char* last_recorded_slash_n_loc_; | |
248 size_t last_slash_n_idx_; | |
249 uint32 term_chars_; | |
250 BalsaFrameEnums::ParseState parse_state_; | |
251 BalsaFrameEnums::ErrorCode last_error_; | |
252 | |
253 Lines lines_; | |
254 | |
255 BalsaHeaders* headers_; // This is not reset to NULL in Reset(). | |
256 DoNothingBalsaVisitor do_nothing_visitor_; | |
257 }; | |
258 | |
259 } // namespace net | |
260 | |
261 #endif // NET_TOOLS_BALSA_BALSA_FRAME_H_ | |
262 | |
OLD | NEW |