Home | History | Annotate | Download | only in flip_server
      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 
     24 namespace net {
     25 
     26 // BalsaFrame is a 'Model' of a framer (haha).
     27 // It exists as a proof of concept headers framer.
     28 class 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