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