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