#include "stdafx.h" #include#include "socket.h" #include "http.h" #include #include "zlib.h" static char gz_magic[2] = { '\x1f', '\x8b'}; #define BUFSIZE (4095) /* gzip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ class GzipFilter : public IReceiver { public: IReceiver *old; z_stream z; int m_inflate_total; char *outbuf; enum gz_mode { CHECK_HEADER, INFLATE, ERROR_INFLATE, } mode; GzipFilter(IReceiver *a_old) :old( a_old ) ,mode( CHECK_HEADER ) { /* すべてのメモリ管理をライブラリに任せる */ z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = Z_NULL; /* 初期化 */ z.next_in = Z_NULL; z.avail_in = 0; m_inflate_total=0; if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { return; } outbuf = new char [ BUFSIZE+1 ]; } ~GzipFilter() { delete []outbuf; inflateEnd(&z); } int check(const char *buf, int len) { if( buf[0] != gz_magic[0] || buf[1] != gz_magic[1] ) { mode = ERROR_INFLATE; return -1; } int i=2; int method = buf[i++]; int flags = buf[i++]; if (method != Z_DEFLATED || (flags & RESERVED) != 0) { mode = ERROR_INFLATE; return -1; } i+=6; if (flags & EXTRA_FIELD) { /* skip the extra field */ int len = 0; len = (uInt)buf[i++]; len += ((uInt)buf[i++])<<8; /* len is garbage if EOF but the loop below will quit anyway */ i+=len; } if (flags & ORIG_NAME) { /* skip the original file name */ while (buf[i++] !=0) ; } if (flags & COMMENT) { /* skip the .gz file comment */ while (buf[i++] != 0) ; } if (flags & HEAD_CRC) { /* skip the header crc */ i+=2; } mode = INFLATE; return i; } virtual void recv(const char *buf, int len ) { if( mode == CHECK_HEADER ){ int ret = check(buf,len); if( ret > 0 ) { buf += ret; len -= ret; z.avail_out = BUFSIZE; z.next_out = (unsigned char*)outbuf; } } if( mode == INFLATE ) { z.next_in = (unsigned char*)buf; z.avail_in = len; int status = Z_OK; while (status != Z_STREAM_END) { if (z.avail_in == 0) { /* 入力残量がゼロになれば */ break; } status = inflate(&z, Z_NO_FLUSH); /* 展開 */ if (status == Z_STREAM_END) { int used = BUFSIZE - z.avail_out; outbuf[used] = '\0'; m_inflate_total += used; old->recv( outbuf, used); // receiver->notify( IReceiver::CONTENT_LENGTH, (void*)(m_total_out) );\ // NOTIFY( _ContentLength ); break; } if (status != Z_OK) { /* エラー */ mode = ERROR_INFLATE; break; } if (z.avail_out == 0) { outbuf[BUFSIZE] = '\0'; m_inflate_total += BUFSIZE; old->recv( outbuf, BUFSIZE ); z.avail_out = BUFSIZE; z.next_out = (unsigned char*)outbuf; } } } if( mode == ERROR_INFLATE ){ old->recv(buf,len); } } virtual void notify(int code, void*data) { if( code == IReceiver::FINISH) { old->notify( IReceiver::CONTENT_LENGTH, (void*)m_inflate_total ); old->notify( code, data ); delete this; return; } old->notify( code, data ); if( code == IReceiver::TERMINATE_TRANSMIT){ delete this; } }; virtual void status(const char*status) { old->status( status ); } virtual void error( int reason ) { old->error( reason ); } }; IReceiver *Insert_GzipFilter( IReceiver *old ) { return new GzipFilter( old ); }