Inflate response body if content-encoding: gzip is used.

Erase Request from stream2req when stream is closed.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-04-24 23:48:05 +09:00
parent c91a4ec091
commit fa0ab174e1
1 changed files with 126 additions and 3 deletions

View File

@ -51,6 +51,7 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <spdylay/spdylay.h>
#include <zlib.h>
#include "spdylay_ssl.h"
#include "uri.h"
@ -71,7 +72,61 @@ struct Config {
struct Request {
uri::UriStruct us;
Request(const uri::UriStruct& us):us(us) {}
z_stream *inflater;
Request(const uri::UriStruct& us):us(us), inflater(0) {}
~Request()
{
if(inflater) {
inflateEnd(inflater);
delete inflater;
}
}
void init_inflater()
{
inflater = new z_stream();
inflater->next_in = Z_NULL;
inflater->zalloc = Z_NULL;
inflater->zfree = Z_NULL;
inflater->opaque = Z_NULL;
int rv = inflateInit2(inflater, 47);
assert(rv == Z_OK);
}
// Inflates data in |in| with the length |*inlen_ptr| and stores the
// inflated data to |out| which has allocated size at least
// |*outlen_ptr|. On return, |*outlen_ptr| is updated to represent
// the number of data written in |out|. Similarly, |*inlen_ptr| is
// updated to represent the number of input bytes processed.
//
// This function returns 0 if it succeeds, or -1.
int inflate_data(uint8_t *out, size_t *outlen_ptr,
const uint8_t *in, size_t *inlen_ptr)
{
assert(inflater);
inflater->avail_in = *inlen_ptr;
inflater->next_in = const_cast<unsigned char*>(in);
inflater->avail_out = *outlen_ptr;
inflater->next_out = out;
int rv = inflate(inflater, Z_NO_FLUSH);
*inlen_ptr -= inflater->avail_in;
*outlen_ptr -= inflater->avail_out;
switch(rv) {
case Z_OK:
case Z_STREAM_END:
case Z_BUF_ERROR:
return 0;
case Z_DATA_ERROR:
case Z_STREAM_ERROR:
case Z_NEED_DICT:
case Z_MEM_ERROR:
return -1;
default:
assert(0);
}
}
};
std::map<int32_t, Request*> stream2req;
@ -86,7 +141,25 @@ void on_data_chunk_recv_callback
{
std::map<int32_t, Request*>::iterator itr = stream2req.find(stream_id);
if(itr != stream2req.end()) {
std::cout.write(reinterpret_cast<const char*>(data), len);
Request *req = (*itr).second;
if(req->inflater) {
while(len > 0) {
const size_t MAX_OUTLEN = 4096;
uint8_t out[MAX_OUTLEN];
size_t outlen = MAX_OUTLEN;
size_t tlen = len;
int rv = req->inflate_data(out, &outlen, data, &tlen);
if(rv == -1) {
spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR);
break;
}
std::cout.write(reinterpret_cast<const char*>(out), outlen);
data += tlen;
len -= tlen;
}
} else {
std::cout.write(reinterpret_cast<const char*>(data), len);
}
}
}
@ -118,6 +191,54 @@ void on_ctrl_send_callback3
on_ctrl_send_callback(session, type, frame, user_data);
}
void check_gzip
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data)
{
char **nv;
int32_t stream_id;
if(type == SPDYLAY_SYN_REPLY) {
nv = frame->syn_reply.nv;
stream_id = frame->syn_reply.stream_id;
} else if(type == SPDYLAY_HEADERS) {
nv = frame->headers.nv;
stream_id = frame->headers.stream_id;
} else {
return;
}
bool gzip = false;
for(size_t i = 0; nv[i]; i += 2) {
if(strcmp("content-encoding", nv[i]) == 0) {
gzip = strcmp("gzip", nv[i+1]) == 0;
break;
}
}
if(gzip) {
Request *req = (Request*)spdylay_session_get_stream_user_data(session,
stream_id);
assert(req);
if(req->inflater) {
return;
}
req->init_inflater();
}
}
void on_ctrl_recv_callback2
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data)
{
check_gzip(session, type, frame, user_data);
}
void on_ctrl_recv_callback3
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data)
{
check_gzip(session, type, frame, user_data);
on_ctrl_recv_callback(session, type, frame, user_data);
}
void on_stream_close_callback
(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code,
void *user_data)
@ -128,6 +249,7 @@ void on_stream_close_callback
if(complete == numreq) {
spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
}
stream2req.erase(itr);
}
}
@ -258,10 +380,11 @@ int run(char **uris, int n)
callbacks.recv_callback = recv_callback;
callbacks.on_stream_close_callback = on_stream_close_callback;
if(config.verbose) {
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback;
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback3;
callbacks.on_data_recv_callback = on_data_recv_callback;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback3;
} else {
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback2;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;
}
if(!config.null_out) {