pion  5.0.6
src/spdy_decompressor.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 <zlib.h>
00012 #include <iostream>
00013 #include <fstream>
00014 #include <boost/asio/detail/socket_ops.hpp>
00015 #include <pion/spdy/decompressor.hpp>
00016 
00017 
00018 namespace pion {    // begin namespace pion
00019 namespace spdy {    // begin namespace spdy 
00020 
00021 
00022 // decompressor static members
00023     
00024 const char decompressor::SPDY_ZLIB_DICTIONARY[] =
00025     "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
00026     "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
00027     "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
00028     "-agent10010120020120220320420520630030130230330430530630740040140240340440"
00029     "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
00030     "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
00031     "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
00032     "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
00033     "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
00034     "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
00035     "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
00036     "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
00037     ".1statusversionurl";
00038     
00039 
00040 // decompressor member functions
00041 
00042 decompressor::decompressor()
00043     : m_request_zstream(NULL), m_response_zstream(NULL)
00044 {
00045     m_request_zstream = (z_streamp)malloc(sizeof(z_stream));
00046     BOOST_ASSERT(m_request_zstream);
00047 
00048     m_request_zstream->zalloc = Z_NULL;
00049     m_request_zstream->zfree = Z_NULL;
00050     m_request_zstream->opaque = Z_NULL;
00051     m_request_zstream->next_in = Z_NULL;
00052     m_request_zstream->next_out = Z_NULL;
00053     m_request_zstream->avail_in = 0;
00054     m_request_zstream->avail_out = 0;
00055     
00056     m_response_zstream = (z_streamp)malloc(sizeof(z_stream));
00057     BOOST_ASSERT(m_response_zstream);
00058     
00059     m_response_zstream->zalloc = Z_NULL;
00060     m_response_zstream->zfree = Z_NULL;
00061     m_response_zstream->opaque = Z_NULL;
00062     m_response_zstream->next_in = Z_NULL;
00063     m_response_zstream->next_out = Z_NULL;
00064     m_response_zstream->avail_in = 0;
00065     m_response_zstream->avail_out = 0;
00066     
00067     int retcode = inflateInit2(m_request_zstream, MAX_WBITS);
00068     if (retcode == Z_OK) {
00069         retcode = inflateInit2(m_response_zstream, MAX_WBITS);
00070         if (retcode == Z_OK) {
00071             // Get the dictionary id
00072             m_dictionary_id = adler32(0L, Z_NULL, 0);
00073             
00074             m_dictionary_id = adler32(m_dictionary_id,
00075                                       (const Bytef *)SPDY_ZLIB_DICTIONARY,
00076                                       sizeof(SPDY_ZLIB_DICTIONARY));
00077         }
00078     }
00079 }
00080 
00081 decompressor::~decompressor()
00082 {
00083     inflateEnd(m_request_zstream);
00084     inflateEnd(m_response_zstream);
00085     free(m_request_zstream);
00086     free(m_response_zstream);
00087 }
00088 
00089 char* decompressor::decompress(const char *compressed_data_ptr,
00090                                boost::uint32_t stream_id,
00091                                const spdy_control_frame_info& frame,
00092                                boost::uint32_t header_block_length)
00093 {
00095     z_streamp decomp = NULL;
00096     if (stream_id % 2 == 0) {
00097         // Even streams are server-initiated and should never get a
00098         // client-initiated header block. Use reply decompressor.
00099         decomp = m_response_zstream;
00100     } else if (frame.type == SPDY_HEADERS) {
00101         // Odd streams are client-initiated, but may have HEADERS from either
00102         // side. Currently, no known clients send HEADERS so we assume they are
00103         // all from the server.
00104         decomp = m_response_zstream;
00105     } else if (frame.type == SPDY_SYN_STREAM) {
00106         decomp = m_request_zstream;
00107     } else if (frame.type == SPDY_SYN_REPLY) {
00108         decomp = m_response_zstream;
00109     } else {
00110         // Unhandled case. This should never happen.
00111         BOOST_ASSERT(false);
00112     }
00113     BOOST_ASSERT(decomp);
00114     
00115     // Decompress the data
00116     boost::uint32_t uncomp_length = 0;
00117     
00118     // Catch decompression failures.
00119     if (!spdy_decompress_header(compressed_data_ptr, decomp,
00120                                 header_block_length, uncomp_length))
00121     {
00122         // Error in decompressing
00123         // This error is not catastrophic as many times we might get inconsistent
00124         // spdy header frames and we should just log error and continue.
00125         // No need to call SetError()
00126         return NULL;
00127     }
00128     return reinterpret_cast<char*>(m_uncompressed_header);
00129 }
00130 
00131 bool decompressor::spdy_decompress_header(const char *compressed_data_ptr,
00132                                           z_streamp decomp,
00133                                           boost::uint32_t length,
00134                                           boost::uint32_t& uncomp_length) {
00135     int retcode;
00136     const boost::uint8_t *hptr = (boost::uint8_t *)compressed_data_ptr;
00137     
00138     decomp->next_in = (Bytef *)hptr;
00139     decomp->avail_in = length;
00140     decomp->next_out = m_uncompressed_header;
00141     decomp->avail_out = MAX_UNCOMPRESSED_DATA_BUF_SIZE;
00142     
00143     retcode = inflate(decomp, Z_SYNC_FLUSH);
00144     
00145     if (retcode == Z_NEED_DICT) {
00146         if (decomp->adler != m_dictionary_id) {
00147             // Decompressor wants a different dictionary id
00148         } else {
00149             retcode = inflateSetDictionary(decomp,
00150                                            (const Bytef *)SPDY_ZLIB_DICTIONARY,
00151                                            sizeof(SPDY_ZLIB_DICTIONARY));
00152             if (retcode == Z_OK) {
00153                 retcode = inflate(decomp, Z_SYNC_FLUSH);
00154             }
00155         }
00156     }
00157     
00158     // Handle Errors. 
00159     if (retcode != Z_OK) {
00160         // This error is not catastrophic as many times we might get inconsistent
00161         // spdy header frames and we should just log error and continue.
00162         // No need to call SetError()
00163         return false;
00164     }
00165     
00166     // Handle successful inflation. 
00167     uncomp_length = MAX_UNCOMPRESSED_DATA_BUF_SIZE - decomp->avail_out;
00168     if (decomp->avail_in != 0) {
00169         // Error condition
00170         // This error is not catastrophic as many times we might get inconsistent
00171         // spdy header frames and we should just log error and continue.
00172         // No need to call SetError()
00173         return false;
00174     }
00175     
00176     return true;
00177 }
00178         
00179 }   // end namespace spdy
00180 }   // end namespace pion