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
23namespace net {
24
25// BalsaFrame is a 'Model' of a framer (haha).
26// It exists as a proof of concept headers framer.
27class 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
55  const BalsaHeaders* const_balsa_headers() const { return 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).
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
72  void set_balsa_visitor(BalsaVisitorInterface* visitor) {
73    visitor_ = visitor;
74    if (visitor_ == NULL) {
75      visitor_ = &do_nothing_visitor_;
76    }
77  }
78
79  void set_is_request(bool is_request) { is_request_ = is_request; }
80
81  bool is_request() const {
82    return is_request_;
83  }
84
85  void set_request_was_head(bool request_was_head) {
86    request_was_head_ = request_was_head;
87  }
88
89  bool request_was_head() const {
90    return request_was_head_;
91  }
92
93  void set_max_header_length(size_t max_header_length) {
94    max_header_length_ = max_header_length;
95  }
96
97  size_t max_header_length() const {
98    return max_header_length_;
99  }
100
101  void set_max_request_uri_length(size_t max_request_uri_length) {
102    max_request_uri_length_ = max_request_uri_length;
103  }
104
105  size_t max_request_uri_length() const {
106    return max_request_uri_length_;
107  }
108
109
110  bool MessageFullyRead() {
111    return parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ;
112  }
113
114  BalsaFrameEnums::ParseState ParseState() const { return parse_state_; }
115
116
117  bool Error() {
118    return parse_state_ == BalsaFrameEnums::PARSE_ERROR;
119  }
120
121  BalsaFrameEnums::ErrorCode ErrorCode() const { return last_error_; }
122
123  const BalsaHeaders* headers() const { return 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
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)
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
198  inline bool HeaderFramingMayBeFound() const {
199    return term_chars_ != 0;
200  }
201
202 private:
203  class DoNothingBalsaVisitor : public BalsaVisitorInterface {
204    virtual void ProcessBodyInput(const char *input, size_t size) OVERRIDE {}
205    virtual void ProcessBodyData(const char *input, size_t size) OVERRIDE {}
206    virtual void ProcessHeaderInput(const char *input, size_t size) OVERRIDE {}
207    virtual void ProcessTrailerInput(const char *input, size_t size) OVERRIDE {}
208    virtual void ProcessHeaders(const BalsaHeaders& headers) OVERRIDE {}
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 {}
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 {}
225    virtual void ProcessChunkLength(size_t chunk_length) OVERRIDE {}
226    virtual void ProcessChunkExtensions(const char *input,
227                                        size_t size) OVERRIDE {}
228    virtual void HeaderDone() OVERRIDE {}
229    virtual void MessageDone() OVERRIDE {}
230    virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {}
231    virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {}
232    virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {}
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