summaryrefslogtreecommitdiff
path: root/chromium/net/tools/flip_server/balsa_frame.h
blob: 0d3b372ef9f6dd887cfd7d9c3d459fdbbf57d389 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_TOOLS_FLIP_SERVER_BALSA_FRAME_H_
#define NET_TOOLS_FLIP_SERVER_BALSA_FRAME_H_

#include <strings.h>

#include <utility>
#include <vector>

#include "base/compiler_specific.h"
#include "base/port.h"
#include "net/tools/flip_server/balsa_enums.h"
#include "net/tools/flip_server/balsa_headers.h"
#include "net/tools/flip_server/balsa_visitor_interface.h"
#include "net/tools/flip_server/buffer_interface.h"
#include "net/tools/flip_server/http_message_constants.h"
#include "net/tools/flip_server/simple_buffer.h"

// For additional debug output, uncomment the following:
// #define DEBUGFRAMER 1

namespace net {

// BalsaFrame is a 'Model' of a framer (haha).
// It exists as a proof of concept headers framer.
class BalsaFrame {
 public:
  typedef std::vector<std::pair<size_t, size_t> > Lines;

  typedef BalsaHeaders::HeaderLineDescription HeaderLineDescription;
  typedef BalsaHeaders::HeaderLines HeaderLines;
  typedef BalsaHeaders::HeaderTokenList HeaderTokenList;

  // TODO(fenix): get rid of the 'kValidTerm*' stuff by using the 'since last
  // index' strategy.  Note that this implies getting rid of the HeaderFramed()

  static const uint32 kValidTerm1  = '\n' << 16 |
                                     '\r' <<  8 |
                                     '\n';
  static const uint32 kValidTerm1Mask = 0xFF << 16 |
                                        0xFF <<  8 |
                                        0xFF;
  static const uint32 kValidTerm2      = '\n' << 8 |
                                         '\n';
  static const uint32 kValidTerm2Mask = 0xFF << 8 |
                                        0xFF;
  BalsaFrame();
  ~BalsaFrame();

  // Reset reinitializes all the member variables of the framer and clears the
  // attached header object (but doesn't change the pointer value headers_).
  void Reset();

  const BalsaHeaders* const_balsa_headers() const { return headers_; }
  BalsaHeaders* balsa_headers() { return headers_; }
  // The method set_balsa_headers clears the headers provided and attaches them
  // to the framer.  This is a required step before the framer will process any
  // input message data.
  // To detach the header object from the framer, use set_balsa_headers(NULL).
  void set_balsa_headers(BalsaHeaders* headers) {
    if (headers_ != headers) {
      headers_ = headers;
    }
    if (headers_) {
      // Clear the headers if they are non-null, even if the new headers are
      // the same as the old.
      headers_->Clear();
    }
  }

  void set_balsa_visitor(BalsaVisitorInterface* visitor) {
    visitor_ = visitor;
    if (visitor_ == NULL) {
      visitor_ = &do_nothing_visitor_;
    }
  }

  void set_is_request(bool is_request) { is_request_ = is_request; }

  bool is_request() const {
    return is_request_;
  }

  void set_request_was_head(bool request_was_head) {
    request_was_head_ = request_was_head;
  }

  bool request_was_head() const {
    return request_was_head_;
  }

  void set_max_header_length(size_t max_header_length) {
    max_header_length_ = max_header_length;
  }

  size_t max_header_length() const {
    return max_header_length_;
  }

  void set_max_request_uri_length(size_t max_request_uri_length) {
    max_request_uri_length_ = max_request_uri_length;
  }

  size_t max_request_uri_length() const {
    return max_request_uri_length_;
  }


  bool MessageFullyRead() {
    return parse_state_ == BalsaFrameEnums::MESSAGE_FULLY_READ;
  }

  BalsaFrameEnums::ParseState ParseState() const { return parse_state_; }


  bool Error() {
    return parse_state_ == BalsaFrameEnums::PARSE_ERROR;
  }

  BalsaFrameEnums::ErrorCode ErrorCode() const { return last_error_; }

  const BalsaHeaders* headers() const { return headers_; }
  BalsaHeaders* mutable_headers() { return headers_; }

  size_t BytesSafeToSplice() const;
  void BytesSpliced(size_t bytes_spliced);

  size_t ProcessInput(const char* input, size_t size);

  // Parses input and puts the key, value chunk extensions into extensions.
  // TODO(phython): Find a better data structure to put the extensions into.
  static void ProcessChunkExtensions(const char* input, size_t size,
                                     BalsaHeaders* extensions);

 protected:
  // The utils object needs access to the ParseTokenList in order to do its
  // job.
  friend class BalsaHeadersTokenUtils;

  inline void ProcessContentLengthLine(
      size_t line_idx,
      BalsaHeadersEnums::ContentLengthStatus* status,
      size_t* length);

  inline void ProcessTransferEncodingLine(size_t line_idx);

  void ProcessFirstLine(const char* begin,
                        const char* end);

  void CleanUpKeyValueWhitespace(
      const char* stream_begin,
      const char* line_begin,
      const char* current,
      const char* line_end,
      HeaderLineDescription* current_header_line);

  void FindColonsAndParseIntoKeyValue();

  void ProcessHeaderLines();

  inline size_t ProcessHeaders(const char* message_start,
                               size_t message_length);

  void AssignParseStateAfterHeadersHaveBeenParsed();

  inline bool LineFramingFound(char current_char) {
    return current_char == '\n';
  }

  // TODO(fenix): get rid of the following function and its uses (and
  // replace with something more efficient)
  inline bool HeaderFramingFound(char current_char) {
    // Note that the 'if (current_char == '\n' ...)' test exists to ensure that
    // the HeaderFramingMayBeFound test works properly. In benchmarking done on
    // 2/13/2008, the 'if' actually speeds up performance of the function
    // anyway..
    if (current_char == '\n' || current_char == '\r') {
      term_chars_ <<= 8;
      // This is necessary IFF architecture has > 8 bit char.  Alas, I'm
      // paranoid.
      term_chars_ |= current_char & 0xFF;

      if ((term_chars_ & kValidTerm1Mask) == kValidTerm1) {
        term_chars_ = 0;
        return true;
      }
      if ((term_chars_ & kValidTerm2Mask) == kValidTerm2) {
        term_chars_ = 0;
        return true;
      }
    } else {
      term_chars_ = 0;
    }
    return false;
  }

  inline bool HeaderFramingMayBeFound() const {
    return term_chars_ != 0;
  }

 private:
  class DoNothingBalsaVisitor : public BalsaVisitorInterface {
    virtual void ProcessBodyInput(const char *input, size_t size) OVERRIDE {}
    virtual void ProcessBodyData(const char *input, size_t size) OVERRIDE {}
    virtual void ProcessHeaderInput(const char *input, size_t size) OVERRIDE {}
    virtual void ProcessTrailerInput(const char *input, size_t size) OVERRIDE {}
    virtual void ProcessHeaders(const BalsaHeaders& headers) OVERRIDE {}
    virtual void 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) OVERRIDE {}
    virtual void 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) OVERRIDE {}
    virtual void ProcessChunkLength(size_t chunk_length) OVERRIDE {}
    virtual void ProcessChunkExtensions(const char *input,
                                        size_t size) OVERRIDE {}
    virtual void HeaderDone() OVERRIDE {}
    virtual void MessageDone() OVERRIDE {}
    virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {}
    virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {}
    virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {}
    virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {}
  };

  bool last_char_was_slash_r_;
  bool saw_non_newline_char_;
  bool start_was_space_;
  bool chunk_length_character_extracted_;
  bool is_request_;                // This is not reset in Reset()
  bool request_was_head_;          // This is not reset in Reset()
  size_t max_header_length_;       // This is not reset in Reset()
  size_t max_request_uri_length_;  // This is not reset in Reset()
  BalsaVisitorInterface* visitor_;
  size_t chunk_length_remaining_;
  size_t content_length_remaining_;
  const char* last_slash_n_loc_;
  const char* last_recorded_slash_n_loc_;
  size_t last_slash_n_idx_;
  uint32 term_chars_;
  BalsaFrameEnums::ParseState parse_state_;
  BalsaFrameEnums::ErrorCode last_error_;

  Lines lines_;

  BalsaHeaders* headers_;  // This is not reset to NULL in Reset().
  DoNothingBalsaVisitor do_nothing_visitor_;
};

}  // namespace net

#endif  // NET_TOOLS_FLIP_SERVER_BALSA_FRAME_H_