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