balsa_frame.h revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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 24namespace net { 25 26// BalsaFrame is a 'Model' of a framer (haha). 27// It exists as a proof of concept headers framer. 28class 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; 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 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 75 const BalsaHeaders* const_balsa_headers() const { return 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). 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 92 void set_balsa_visitor(BalsaVisitorInterface* visitor) { 93 visitor_ = visitor; 94 if (visitor_ == NULL) { 95 visitor_ = &do_nothing_visitor_; 96 } 97 } 98 99 void set_is_request(bool is_request) { is_request_ = is_request; } 100 101 bool is_request() const { 102 return is_request_; 103 } 104 105 void set_request_was_head(bool request_was_head) { 106 request_was_head_ = request_was_head; 107 } 108 109 bool request_was_head() const { 110 return request_was_head_; 111 } 112 113 void set_max_header_length(size_t max_header_length) { 114 max_header_length_ = max_header_length; 115 } 116 117 size_t max_header_length() const { 118 return max_header_length_; 119 } 120 121 void set_max_request_uri_length(size_t max_request_uri_length) { 122 max_request_uri_length_ = max_request_uri_length; 123 } 124 125 size_t max_request_uri_length() const { 126 return max_request_uri_length_; 127 } 128 129 130 bool MessageFullyRead() { 131 return parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ; 132 } 133 134 BalsaFrameEnums::ParseState ParseState() const { return parse_state_; } 135 136 137 bool Error() { 138 return parse_state_ == BalsaFrameEnums::PARSE_ERROR; 139 } 140 141 BalsaFrameEnums::ErrorCode ErrorCode() const { return last_error_; } 142 143 const BalsaHeaders* headers() const { return 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 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) 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 218 inline bool HeaderFramingMayBeFound() const { 219 return term_chars_ != 0; 220 } 221 222 private: 223 class DoNothingBalsaVisitor : public BalsaVisitorInterface { 224 virtual void ProcessBodyInput(const char *input, size_t size) {} 225 virtual void ProcessBodyData(const char *input, size_t size) {} 226 virtual void ProcessHeaderInput(const char *input, size_t size) {} 227 virtual void ProcessTrailerInput(const char *input, size_t size) {} 228 virtual void ProcessHeaders(const BalsaHeaders& headers) {} 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) {} 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) {} 245 virtual void ProcessChunkLength(size_t chunk_length) {} 246 virtual void ProcessChunkExtensions(const char *input, size_t size) {} 247 virtual void HeaderDone() {} 248 virtual void MessageDone() {} 249 virtual void HandleHeaderError(BalsaFrame* framer) {} 250 virtual void HandleHeaderWarning(BalsaFrame* framer) {} 251 virtual void HandleChunkingError(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