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 const_balsa_headers()55 const BalsaHeaders* const_balsa_headers() const { return headers_; } balsa_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). set_balsa_headers(BalsaHeaders * headers)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 set_balsa_visitor(BalsaVisitorInterface * visitor)72 void set_balsa_visitor(BalsaVisitorInterface* visitor) { 73 visitor_ = visitor; 74 if (visitor_ == NULL) { 75 visitor_ = &do_nothing_visitor_; 76 } 77 } 78 set_is_request(bool is_request)79 void set_is_request(bool is_request) { is_request_ = is_request; } 80 is_request()81 bool is_request() const { 82 return is_request_; 83 } 84 set_request_was_head(bool request_was_head)85 void set_request_was_head(bool request_was_head) { 86 request_was_head_ = request_was_head; 87 } 88 request_was_head()89 bool request_was_head() const { 90 return request_was_head_; 91 } 92 set_max_header_length(size_t max_header_length)93 void set_max_header_length(size_t max_header_length) { 94 max_header_length_ = max_header_length; 95 } 96 max_header_length()97 size_t max_header_length() const { 98 return max_header_length_; 99 } 100 set_max_request_uri_length(size_t max_request_uri_length)101 void set_max_request_uri_length(size_t max_request_uri_length) { 102 max_request_uri_length_ = max_request_uri_length; 103 } 104 max_request_uri_length()105 size_t max_request_uri_length() const { 106 return max_request_uri_length_; 107 } 108 109 MessageFullyRead()110 bool MessageFullyRead() { 111 return parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ; 112 } 113 ParseState()114 BalsaFrameEnums::ParseState ParseState() const { return parse_state_; } 115 116 Error()117 bool Error() { 118 return parse_state_ == BalsaFrameEnums::PARSE_ERROR; 119 } 120 ErrorCode()121 BalsaFrameEnums::ErrorCode ErrorCode() const { return last_error_; } 122 headers()123 const BalsaHeaders* headers() const { return headers_; } mutable_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 LineFramingFound(char current_char)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) HeaderFramingFound(char current_char)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 HeaderFramingMayBeFound()198 inline bool HeaderFramingMayBeFound() const { 199 return term_chars_ != 0; 200 } 201 202 private: 203 class DoNothingBalsaVisitor : public BalsaVisitorInterface { ProcessBodyInput(const char * input,size_t size)204 virtual void ProcessBodyInput(const char *input, size_t size) OVERRIDE {} ProcessBodyData(const char * input,size_t size)205 virtual void ProcessBodyData(const char *input, size_t size) OVERRIDE {} ProcessHeaderInput(const char * input,size_t size)206 virtual void ProcessHeaderInput(const char *input, size_t size) OVERRIDE {} ProcessTrailerInput(const char * input,size_t size)207 virtual void ProcessTrailerInput(const char *input, size_t size) OVERRIDE {} ProcessHeaders(const BalsaHeaders & headers)208 virtual void ProcessHeaders(const BalsaHeaders& headers) OVERRIDE {} 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)209 virtual 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 {} 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)217 virtual 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 {} ProcessChunkLength(size_t chunk_length)225 virtual void ProcessChunkLength(size_t chunk_length) OVERRIDE {} ProcessChunkExtensions(const char * input,size_t size)226 virtual void ProcessChunkExtensions(const char *input, 227 size_t size) OVERRIDE {} HeaderDone()228 virtual void HeaderDone() OVERRIDE {} MessageDone()229 virtual void MessageDone() OVERRIDE {} HandleHeaderError(BalsaFrame * framer)230 virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {} HandleHeaderWarning(BalsaFrame * framer)231 virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {} HandleChunkingError(BalsaFrame * framer)232 virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {} HandleBodyError(BalsaFrame * framer)233 virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {} 234 }; 235 236 bool last_char_was_slash_r_; 237 bool saw_non_newline_char_; 238 bool start_was_space_; 239 bool chunk_length_character_extracted_; 240 bool is_request_; // This is not reset in Reset() 241 bool request_was_head_; // This is not reset in Reset() 242 size_t max_header_length_; // This is not reset in Reset() 243 size_t max_request_uri_length_; // This is not reset in Reset() 244 BalsaVisitorInterface* visitor_; 245 size_t chunk_length_remaining_; 246 size_t content_length_remaining_; 247 const char* last_slash_n_loc_; 248 const char* last_recorded_slash_n_loc_; 249 size_t last_slash_n_idx_; 250 uint32 term_chars_; 251 BalsaFrameEnums::ParseState parse_state_; 252 BalsaFrameEnums::ErrorCode last_error_; 253 254 Lines lines_; 255 256 BalsaHeaders* headers_; // This is not reset to NULL in Reset(). 257 DoNothingBalsaVisitor do_nothing_visitor_; 258 }; 259 260 } // namespace net 261 262 #endif // NET_TOOLS_BALSA_BALSA_FRAME_H_ 263 264