• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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