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