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