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  ~BalsaFrame();
52
53  // Reset reinitializes all the member variables of the framer and clears the
54  // attached header object (but doesn't change the pointer value headers_).
55  void Reset();
56
57  const BalsaHeaders* const_balsa_headers() const { return headers_; }
58  BalsaHeaders* balsa_headers() { return headers_; }
59  // The method set_balsa_headers clears the headers provided and attaches them
60  // to the framer.  This is a required step before the framer will process any
61  // input message data.
62  // To detach the header object from the framer, use set_balsa_headers(NULL).
63  void set_balsa_headers(BalsaHeaders* headers) {
64    if (headers_ != headers) {
65      headers_ = headers;
66    }
67    if (headers_) {
68      // Clear the headers if they are non-null, even if the new headers are
69      // the same as the old.
70      headers_->Clear();
71    }
72  }
73
74  void set_balsa_visitor(BalsaVisitorInterface* visitor) {
75    visitor_ = visitor;
76    if (visitor_ == NULL) {
77      visitor_ = &do_nothing_visitor_;
78    }
79  }
80
81  void set_is_request(bool is_request) { is_request_ = is_request; }
82
83  bool is_request() const {
84    return is_request_;
85  }
86
87  void set_request_was_head(bool request_was_head) {
88    request_was_head_ = request_was_head;
89  }
90
91  bool request_was_head() const {
92    return request_was_head_;
93  }
94
95  void set_max_header_length(size_t max_header_length) {
96    max_header_length_ = max_header_length;
97  }
98
99  size_t max_header_length() const {
100    return max_header_length_;
101  }
102
103  void set_max_request_uri_length(size_t max_request_uri_length) {
104    max_request_uri_length_ = max_request_uri_length;
105  }
106
107  size_t max_request_uri_length() const {
108    return max_request_uri_length_;
109  }
110
111
112  bool MessageFullyRead() {
113    return parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ;
114  }
115
116  BalsaFrameEnums::ParseState ParseState() const { return parse_state_; }
117
118
119  bool Error() {
120    return parse_state_ == BalsaFrameEnums::PARSE_ERROR;
121  }
122
123  BalsaFrameEnums::ErrorCode ErrorCode() const { return last_error_; }
124
125  const BalsaHeaders* headers() const { return headers_; }
126  BalsaHeaders* mutable_headers() { return headers_; }
127
128  size_t BytesSafeToSplice() const;
129  void BytesSpliced(size_t bytes_spliced);
130
131  size_t ProcessInput(const char* input, size_t size);
132
133  // Parses input and puts the key, value chunk extensions into extensions.
134  // TODO(phython): Find a better data structure to put the extensions into.
135  static void ProcessChunkExtensions(const char* input, size_t size,
136                                     BalsaHeaders* extensions);
137
138 protected:
139  // The utils object needs access to the ParseTokenList in order to do its
140  // job.
141  friend class BalsaHeadersTokenUtils;
142
143  inline void ProcessContentLengthLine(
144      size_t line_idx,
145      BalsaHeadersEnums::ContentLengthStatus* status,
146      size_t* length);
147
148  inline void ProcessTransferEncodingLine(size_t line_idx);
149
150  void ProcessFirstLine(const char* begin,
151                        const char* end);
152
153  void CleanUpKeyValueWhitespace(
154      const char* stream_begin,
155      const char* line_begin,
156      const char* current,
157      const char* line_end,
158      HeaderLineDescription* current_header_line);
159
160  void FindColonsAndParseIntoKeyValue();
161
162  void ProcessHeaderLines();
163
164  inline size_t ProcessHeaders(const char* message_start,
165                               size_t message_length);
166
167  void AssignParseStateAfterHeadersHaveBeenParsed();
168
169  inline bool LineFramingFound(char current_char) {
170    return current_char == '\n';
171  }
172
173  // TODO(fenix): get rid of the following function and its uses (and
174  // replace with something more efficient)
175  inline bool HeaderFramingFound(char current_char) {
176    // Note that the 'if (current_char == '\n' ...)' test exists to ensure that
177    // the HeaderFramingMayBeFound test works properly. In benchmarking done on
178    // 2/13/2008, the 'if' actually speeds up performance of the function
179    // anyway..
180    if (current_char == '\n' || current_char == '\r') {
181      term_chars_ <<= 8;
182      // This is necessary IFF architecture has > 8 bit char.  Alas, I'm
183      // paranoid.
184      term_chars_ |= current_char & 0xFF;
185
186      if ((term_chars_ & kValidTerm1Mask) == kValidTerm1) {
187        term_chars_ = 0;
188        return true;
189      }
190      if ((term_chars_ & kValidTerm2Mask) == kValidTerm2) {
191        term_chars_ = 0;
192        return true;
193      }
194    } else {
195      term_chars_ = 0;
196    }
197    return false;
198  }
199
200  inline bool HeaderFramingMayBeFound() const {
201    return term_chars_ != 0;
202  }
203
204 private:
205  class DoNothingBalsaVisitor : public BalsaVisitorInterface {
206    virtual void ProcessBodyInput(const char *input, size_t size) {}
207    virtual void ProcessBodyData(const char *input, size_t size) {}
208    virtual void ProcessHeaderInput(const char *input, size_t size) {}
209    virtual void ProcessTrailerInput(const char *input, size_t size) {}
210    virtual void ProcessHeaders(const BalsaHeaders& headers) {}
211    virtual void ProcessRequestFirstLine(const char* line_input,
212                                         size_t line_length,
213                                         const char* method_input,
214                                         size_t method_length,
215                                         const char* request_uri_input,
216                                         size_t request_uri_length,
217                                         const char* version_input,
218                                         size_t version_length) {}
219    virtual void ProcessResponseFirstLine(const char *line_input,
220                                          size_t line_length,
221                                          const char *version_input,
222                                          size_t version_length,
223                                          const char *status_input,
224                                          size_t status_length,
225                                          const char *reason_input,
226                                          size_t reason_length) {}
227    virtual void ProcessChunkLength(size_t chunk_length) {}
228    virtual void ProcessChunkExtensions(const char *input, size_t size) {}
229    virtual void HeaderDone() {}
230    virtual void MessageDone() {}
231    virtual void HandleHeaderError(BalsaFrame* framer) {}
232    virtual void HandleHeaderWarning(BalsaFrame* framer) {}
233    virtual void HandleChunkingError(BalsaFrame* framer) {}
234    virtual void HandleBodyError(BalsaFrame* framer) {}
235  };
236
237  bool last_char_was_slash_r_;
238  bool saw_non_newline_char_;
239  bool start_was_space_;
240  bool chunk_length_character_extracted_;
241  bool is_request_;                // This is not reset in Reset()
242  bool request_was_head_;          // This is not reset in Reset()
243  size_t max_header_length_;       // This is not reset in Reset()
244  size_t max_request_uri_length_;  // This is not reset in Reset()
245  BalsaVisitorInterface* visitor_;
246  size_t chunk_length_remaining_;
247  size_t content_length_remaining_;
248  const char* last_slash_n_loc_;
249  const char* last_recorded_slash_n_loc_;
250  size_t last_slash_n_idx_;
251  uint32 term_chars_;
252  BalsaFrameEnums::ParseState parse_state_;
253  BalsaFrameEnums::ErrorCode last_error_;
254
255  Lines lines_;
256
257  BalsaHeaders* headers_;  // This is not reset to NULL in Reset().
258  DoNothingBalsaVisitor do_nothing_visitor_;
259};
260
261}  // namespace net
262
263#endif  // NET_TOOLS_FLIP_SERVER_BALSA_FRAME_H_
264
265