- Main Page
- Classes
- Files

- File List

# src/spdy_parser.cpp

    00001 // ---------------------------------------------------------------------
    00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
    00003 // ---------------------------------------------------------------------
    00004 // Copyright (C) 2007-2012 Cloudmeter, Inc.  (http://www.cloudmeter.com)
    00005 //
    00006 // Distributed under the Boost Software License, Version 1.0.
    00007 // See http://www.boost.org/LICENSE_1_0.txt
    00008 //
    00009
    00010 #include <cstdlib>
    00011 #include <boost/regex.hpp>
    00012 #include <boost/logic/tribool.hpp>
    00013 #include <boost/asio/detail/socket_ops.hpp>
    00014 #include <pion/algorithm.hpp>
    00015 #include <pion/spdy/parser.hpp>
    00016 #include <pion/spdy/decompressor.hpp>
    00017 #include <pion/spdy/types.hpp>
    00018
    00019
    00020 namespace pion {    // begin namespace pion
    00021 namespace spdy {    // begin namespace spdy
    00022
    00023
    00024 parser::error_category_t *  parser::m_error_category_ptr = NULL;
    00025 boost::once_flag            parser::m_instance_flag = BOOST_ONCE_INIT;
    00026
    00027 // parser member functions
    00028
    00029 parser::parser()
    00030     : m_read_ptr(NULL),
    00031     m_uncompressed_ptr(NULL),
    00032     m_current_data_chunk_ptr(NULL),
    00033     m_last_data_chunk_ptr(NULL),
    00034     m_logger(PION_GET_LOGGER("pion.spdy.parser"))
    00035 {}
    00036
    00037 boost::tribool parser::parse(http_protocol_info& http_info,
    00038                              boost::system::error_code& ec,
    00039                              decompressor_ptr& decompressor,
    00040                              const char *packet_ptr,
    00041                              boost::uint32_t& length_packet,
    00042                              boost::uint32_t current_stream_count)
    00043 {
    00044     // initialize read position
    00045     set_read_ptr(packet_ptr);
    00046
    00047     // Parse the frame
    00048     return parse_spdy_frame(ec, decompressor, http_info, length_packet, current_stream_count);
    00049 }
    00050
    00051 bool parser::is_spdy_control_frame(const char *ptr)
    00052 {
    00053     // Parse further for higher accuracy
    00054
    00055     // Get the control bit
    00056     boost::uint8_t control_bit;
    00057     boost::uint16_t version, type;
    00058     boost::uint16_t byte_value = algorithm::to_uint16(ptr);
    00059     control_bit = byte_value >> (sizeof(short) * CHAR_BIT - 1);
    00060
    00061     if (!control_bit) return false;
    00062
    00063     // Control bit is set; This is a control frame
    00064
    00065     // Get the version number
    00066     boost::uint16_t two_bytes = algorithm::to_uint16(ptr);
    00067     version = two_bytes & 0x7FFF;
    00068
    00069     if(version < 1 || version > 3){
    00070         // SPDY does not have a version higher than 3 and lower than 1 at the moment
    00071         return false;
    00072     }
    00073
    00074     // Increment the read pointer
    00075     ptr += 2;
    00076
    00077     type = algorithm::to_uint16(ptr);
    00078
    00079     if (type >= SPDY_INVALID) {
    00080         // Not among the recognized SPDY types
    00081         return false;
    00082     }
    00083
    00084     return true;
    00085 }
    00086
    00087 spdy_frame_type parser::get_spdy_frame_type(const char *ptr)
    00088 {
    00089     // Determine if this a SPDY frame
    00090     BOOST_ASSERT(ptr);
    00091
    00092     /*
    00093      * The first byte of a SPDY frame must be either 0 or
    00094      * 0x80. If it's not, assume that this is not SPDY.
    00095      * (In theory, a data frame could have a stream ID
    00096      * >= 2^24, in which case it won't have 0 for a first
    00097      * byte, but this is a pretty reliable heuristic for
    00098      * now.)
    00099      */
    00100
    00101     spdy_frame_type spdy_frame;
    00102     boost::uint8_t first_byte = *((unsigned char *)ptr);
    00103     if(first_byte == 0x80){
    00104         spdy_frame = spdy_control_frame;
    00105     }else if(first_byte == 0x0){
    00106         spdy_frame = spdy_data_frame;
    00107     }else{
    00108         spdy_frame = spdy_invalid_frame;
    00109     }
    00110     return spdy_frame;
    00111 }
    00112
    00113 boost::uint32_t parser::get_control_frame_stream_id(const char *ptr)
    00114 {
    00115     // The stream ID for control frames is at a 8 bit offser from start
    00116     ptr += 8;
    00117
    00118     boost::uint32_t four_bytes = algorithm::to_uint32(ptr);
    00119     return four_bytes & 0x7FFFFFFF;
    00120 }
    00121
    00122 boost::tribool parser::parse_spdy_frame(boost::system::error_code& ec,
    00123                                         decompressor_ptr& decompressor,
    00124                                         http_protocol_info& http_info,
    00125                                         boost::uint32_t& length_packet,
    00126                                         boost::uint32_t current_stream_count)
    00127 {
    00128     boost::tribool rc = true;
    00129
    00130     // Verify that this is a spdy frame
    00131
    00132     BOOST_ASSERT(m_read_ptr);
    00133     boost::uint8_t first_byte = (boost::uint8_t)*m_read_ptr;
    00134     if (first_byte != 0x80 && first_byte != 0x0) {
    00135         // This is not a SPDY frame, throw an error
    00136         PION_LOG_ERROR(m_logger, "Invalid SPDY Frame");
    00137         set_error(ec, ERROR_INVALID_SPDY_FRAME);
    00138         return false;
    00139     }
    00140
    00141     boost::uint8_t              control_bit;
    00142     spdy_control_frame_info     frame;
    00143     boost::uint32_t             stream_id = 0;
    00144
    00145     ec.clear();
    00146
    00147     // Populate the frame
    00148     bool populate_frame_result = populate_frame(ec, frame, length_packet, stream_id, http_info);
    00149
    00150     if(!populate_frame_result){
    00152         return false;
    00153     }
    00154
    00155     BOOST_ASSERT(stream_id != 0);
    00156
    00157     control_bit = (boost::uint8_t)frame.control_bit;
    00158
    00159     // There is a possibility that there are more than one SPDY frames in one TCP frame
    00160     if(length_packet > frame.length){
    00161         m_current_data_chunk_ptr = m_read_ptr + frame.length;
    00162         length_packet -= frame.length;
    00163         rc = boost::indeterminate;
    00164     }
    00165
    00166     if (!control_bit) {
    00167         // Parse the data packet
    00168         parse_spdy_data(ec, frame, stream_id, http_info);
    00169     }
    00170
    00171     /* Abort here if the version is too low. */
    00172
    00173     if (frame.version > MIN_SPDY_VERSION) {
    00174         // Version less that min SPDY version, throw an error
    00175         PION_LOG_ERROR(m_logger, "Invalid SPDY Version Number");
    00176         set_error(ec, ERROR_INVALID_SPDY_VERSION);
    00177         return false;
    00178     }
    00179
    00180     if(frame.type ==  SPDY_SYN_STREAM){
    00181         http_info.http_type = HTTP_REQUEST;
    00182     }else if (frame.type == SPDY_SYN_REPLY){
    00183         http_info.http_type = HTTP_RESPONSE;
    00184     }else if (frame.type == SPDY_DATA){
    00185         http_info.http_type = HTTP_DATA;
    00186     }
    00187
    00188     switch (frame.type) {
    00189         case SPDY_SYN_STREAM:
    00190         case SPDY_SYN_REPLY:
    00191         case SPDY_HEADERS:
    00192             parse_header_payload(ec, decompressor, frame, http_info, current_stream_count);
    00193             break;
    00194
    00195         case SPDY_RST_STREAM:
    00196             parse_spdy_rst_stream(ec, frame);
    00197             http_info.http_type = SPDY_CONTROL;
    00198             break;
    00199
    00200         case SPDY_SETTINGS:
    00201             parse_spdy_settings_frame(ec, frame);
    00202             http_info.http_type = SPDY_CONTROL;
    00203             break;
    00204
    00205         case SPDY_PING:
    00206             parse_spdy_ping_frame(ec, frame);
    00207             http_info.http_type = SPDY_CONTROL;
    00208             break;
    00209
    00210         case SPDY_GOAWAY:
    00211             parse_spdy_goaway_frame(ec, frame);
    00212             http_info.http_type = SPDY_CONTROL;
    00213             break;
    00214
    00215         case SPDY_WINDOW_UPDATE:
    00216             parse_spdy_window_update_frame(ec, frame);
    00217             http_info.http_type = SPDY_CONTROL;
    00218             break;
    00219
    00220         case SPDY_CREDENTIAL:
    00221             // We dont need to parse this for now
    00222             http_info.http_type = SPDY_CONTROL;
    00223             break;
    00224
    00225         default:
    00226             break;
    00227     }
    00228
    00229     if (ec)
    00230         return false;
    00231
    00232     m_last_data_chunk_ptr = m_read_ptr;
    00233     m_read_ptr = m_current_data_chunk_ptr;
    00234
    00235     return rc;
    00236 }
    00237
    00238 void parser::create_error_category(void)
    00239 {
    00240     static error_category_t UNIQUE_ERROR_CATEGORY;
    00241     m_error_category_ptr = &UNIQUE_ERROR_CATEGORY;
    00242 }
    00243
    00244 bool parser::populate_frame(boost::system::error_code& ec,
    00245                             spdy_control_frame_info& frame,
    00246                             boost::uint32_t& length_packet,
    00247                             boost::uint32_t& stream_id,
    00248                             http_protocol_info& http_info)
    00249 {
    00250     // Get the control bit
    00251     boost::uint8_t control_bit;
    00252     boost::uint16_t byte_value = algorithm::to_uint16(m_read_ptr);
    00253     control_bit = byte_value >> (sizeof(short) * CHAR_BIT - 1);
    00254
    00255     frame.control_bit = (bool)control_bit;
    00256
    00257     if(control_bit){
    00258
    00259         // Control bit is set; This is a control frame
    00260
    00261         // Get the version number
    00262         boost::uint16_t two_bytes = algorithm::to_uint16(m_read_ptr);
    00263         frame.version = two_bytes & 0x7FFF;
    00264
    00265         // Increment the read pointer
    00266         m_read_ptr += 2;
    00267         length_packet -= 2;
    00268         http_info.data_offset +=2;
    00269
    00270         // Get the type
    00271         frame.type = algorithm::to_uint16(m_read_ptr);
    00272
    00273         if (frame.type >= SPDY_INVALID) {
    00274             // SPDY Frame is invalid
    00275
    00276             // This is not a SPDY frame, throw an error
    00277             PION_LOG_ERROR(m_logger, "Invalid SPDY Frame");
    00278             set_error(ec, ERROR_INVALID_SPDY_FRAME);
    00279             return false;
    00280         }
    00281     }else {
    00282
    00283         // Control bit is not set; This is a data frame
    00284
    00285         frame.type = SPDY_DATA;
    00286         frame.version = 0; /* Version doesn't apply to DATA. */
    00287         // Get the stream id
    00288         boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
    00289         stream_id = four_bytes & 0x7FFFFFFF;
    00290
    00291         http_info.stream_id = stream_id;
    00292
    00293         m_read_ptr +=2;
    00294         http_info.data_offset +=2;
    00295         length_packet -= 2;
    00296
    00297     }
    00298
    00299     // Increment the read pointer
    00300     m_read_ptr += 2;
    00301     length_packet -= 2;
    00302     http_info.data_offset +=2;
    00303
    00304     // Get the flags
    00305     frame.flags = (boost::uint8_t)*m_read_ptr;
    00306
    00307     // Increment the read pointer
    00308
    00309     // Get the length
    00310     boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
    00311     frame.length = four_bytes & 0xFFFFFF;
    00312
    00313     // Increment the read pointer
    00314     m_read_ptr += 4;
    00315     length_packet -= 4;
    00316     http_info.data_offset +=4;
    00317
    00318     http_info.data_size = frame.length;
    00319
    00320     if(control_bit){
    00321         four_bytes = algorithm::to_uint32(m_read_ptr);
    00322         stream_id = four_bytes & 0x7FFFFFFF;
    00323     }
    00324
    00325     return true;
    00326 }
    00327
    00328 void parser::parse_header_payload(boost::system::error_code &ec,
    00329                                   decompressor_ptr& decompressor,
    00330                                   const spdy_control_frame_info& frame,
    00331                                   http_protocol_info& http_info,
    00332                                   boost::uint32_t current_stream_count)
    00333 {
    00334     boost::uint32_t stream_id = 0;
    00335     boost::uint32_t associated_stream_id;
    00336     boost::uint32_t header_block_length = frame.length;
    00337
    00338     // Get the 31 bit stream id
    00339
    00340     boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
    00341     stream_id = four_bytes & 0x7FFFFFFF;
    00342
    00343     m_read_ptr += 4;
    00344
    00345     http_info.stream_id = stream_id;
    00346
    00347     // Get SYN_STREAM-only fields.
    00348
    00349     if (frame.type == SPDY_SYN_STREAM) {
    00350
    00351         // Get associated stream ID.
    00352
    00353         boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
    00354         associated_stream_id = four_bytes & 0x7FFFFFFF;
    00355
    00356         m_read_ptr += 4;
    00357
    00358         // The next bits are priority, unused, and slot.
    00359         // Disregard these for now as we dont need them
    00360
    00361         m_read_ptr +=2 ;
    00362
    00363     } else if( frame.type == SPDY_SYN_REPLY || frame.type == SPDY_HEADERS ) {
    00364
    00365         // Unused bits
    00366         m_read_ptr +=2 ;
    00367     }
    00368
    00369     // Get our header block length.
    00370
    00371     switch (frame.type) {
    00372         case SPDY_SYN_STREAM:
    00373             header_block_length -= 10;
    00374             break;
    00375         case SPDY_SYN_REPLY:
    00376         case SPDY_HEADERS:
    00377             // This is a very important distinction.
    00378             // It should be 6 bytes for SPDYv2 and 4 bytes for SPDYv3.
    00379             header_block_length -= 6;
    00380             break;
    00381         default:
    00382             // Unhandled case. This should never happen.
    00383             PION_LOG_ERROR(m_logger, "Invalid SPDY Frame Type");
    00384             set_error(ec, ERROR_INVALID_SPDY_FRAME);
    00385             return;
    00386     }
    00387
    00388     // Decompress header block as necessary.
    00389     m_uncompressed_ptr = decompressor->decompress(m_read_ptr,
    00390                                                   stream_id,
    00391                                                   frame,
    00392                                                   header_block_length);
    00393
    00394     if (!m_uncompressed_ptr) {
    00395         set_error(ec, ERROR_DECOMPRESSION);
    00396         return;
    00397     }
    00398
    00399     // Now parse the name/value pairs
    00400
    00401     // The number of name/value pairs is 16 bit SPDYv2
    00402     // and it is 32 bit in SPDYv3
    00403
    00404     // TBD : Add support for SPDYv3
    00405     boost::uint16_t num_name_val_pairs = algorithm::to_uint16(m_uncompressed_ptr);
    00406
    00407     m_uncompressed_ptr += 2;
    00408
    00409     std::string content_type = "";
    00410     std::string content_encoding = "";
    00411
    00412     for(boost::uint16_t count = 0; count < num_name_val_pairs; ++count){
    00413
    00414
    00415         // Get the length of the name
    00416         boost::uint16_t length_name = algorithm::to_uint16(m_uncompressed_ptr);
    00417         std::string name = "";
    00418
    00419         m_uncompressed_ptr += 2;
    00420
    00421         {
    00422             for(boost::uint16_t count = 0; count < length_name; ++count){
    00423                 name.push_back(*(m_uncompressed_ptr+count));
    00424             }
    00425             m_uncompressed_ptr += length_name;
    00426         }
    00427
    00428         // Get the length of the value
    00429         boost::uint16_t length_value = algorithm::to_uint16(m_uncompressed_ptr);
    00430         std::string value = "";
    00431
    00432         m_uncompressed_ptr += 2;
    00433
    00434         {
    00435             for(boost::uint16_t count = 0; count < length_value; ++count){
    00436                 value.push_back(*(m_uncompressed_ptr+count));
    00437             }
    00438             m_uncompressed_ptr += length_value;
    00439         }
    00440
    00441         // Save these headers
    00442         http_info.http_headers.insert(std::make_pair(name, value));
    00443     }
    00444 }
    00445
    00446 void parser::parse_spdy_data(boost::system::error_code &ec,
    00447                              const spdy_control_frame_info& frame,
    00448                              boost::uint32_t stream_id,
    00449                              http_protocol_info& http_info)
    00450 {
    00451     // This marks the finish flag
    00452     if (frame.flags & SPDY_FLAG_FIN){
    00453         http_info.last_chunk = true;
    00454     }
    00455 }
    00456
    00457 void parser::parse_spdy_rst_stream(boost::system::error_code &ec,
    00458                                    const spdy_control_frame_info& frame)
    00459 {
    00460     boost::uint32_t stream_id = 0;
    00461     boost::uint32_t status_code = 0;
    00462
    00463     // First complete the check for size and flag
    00464     // The flag for RST frame should be 0, The length should be 8
    00465     if(frame.flags != 0 || frame.length != 8 ){
    00466         return;
    00467     }
    00468
    00469     // Get the 31 bit stream id
    00470
    00471     boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
    00472     stream_id = four_bytes & 0x7FFFFFFF;
    00473
    00474     m_read_ptr += 4;
    00475
    00476     // Get the status code
    00477
    00478     status_code = algorithm::to_uint32(m_read_ptr);
    00479
    00480     // Check the index before using the stream status array
    00481
    00482     if(status_code >=1 && status_code <= 12){
    00483
    00484         PION_LOG_INFO(m_logger,
    00485                       "SPDY " << "Status Code is : "
    00486                       << rst_stream_status_names[status_code].str);
    00487     }else{
    00488         PION_LOG_INFO(m_logger, "SPDY RST Invalid status code : " << status_code);
    00489     }
    00490 }
    00491
    00492 void parser::parse_spdy_ping_frame(boost::system::error_code &ec,
    00493                                    const spdy_control_frame_info& frame)
    00494 {
    00495     // First complete the check for size
    00496     // The length should be 4 always
    00497     if(frame.length != 4){
    00498         return;
    00499     }
    00500
    00501     boost::uint32_t ping_id = 0;
    00502
    00503     // Get the 32 bit ping id
    00504
    00505     ping_id = algorithm::to_uint32(m_read_ptr);
    00506
    00507     m_read_ptr += 4;
    00508
    00509     PION_LOG_INFO(m_logger, "SPDY " << "Ping ID is : " << ping_id);
    00510 }
    00511
    00512 void parser::parse_spdy_settings_frame(boost::system::error_code &ec,
    00513                                        const spdy_control_frame_info& frame)
    00514 {
    00515     // Can ignore this frame for our purposes
    00516 }
    00517
    00518 void parser::parse_spdy_goaway_frame(boost::system::error_code &ec,
    00519                                      const spdy_control_frame_info& frame)
    00520 {
    00521     // First complete the check for size
    00522     // The length should be 4 always
    00523     if(frame.length != 4){
    00524         return;
    00525     }
    00526
    00527     boost::uint32_t last_good_stream_id = 0;
    00528     boost::uint32_t status_code = 0;
    00529
    00530     // Get the 31 bit stream id
    00531
    00532     boost::uint32_t four_bytes = algorithm::to_uint32(m_read_ptr);
    00533     last_good_stream_id = four_bytes & 0x7FFFFFFF;
    00534
    00535     m_read_ptr += 4;
    00536
    00537     // Get the status code
    00538
    00539     status_code = algorithm::to_uint32(m_read_ptr);
    00540
    00541     // Chek if there was an error
    00542     if(status_code == 1){
    00543
    00544         PION_LOG_ERROR(m_logger, "There was a Protocol Error");
    00545         set_error(ec, ERROR_PROTOCOL_ERROR);
    00546         return;
    00547     }else if (status_code == 11) {
    00548
    00549         PION_LOG_ERROR(m_logger, "There was an Internal Error");
    00550         set_error(ec, ERROR_INTERNAL_SPDY_ERROR);
    00551         return;
    00552     }
    00553
    00554     PION_LOG_INFO(m_logger, "SPDY " << "Status Code is : " << status_code);
    00555
    00556 }
    00557
    00558 void parser::parse_spdy_window_update_frame(boost::system::error_code &ec,
    00559                                             const spdy_control_frame_info& frame)
    00560 {
    00561     // TBD : Do we really need this for our purpose
    00562 }
    00563
    00564 }   // end namespace spdy
    00565 }   // end namespace pion