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