pion  5.0.6
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