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