int evbuffer_add_vprintf (struct evbuffer *buf, const char *fmt, va_list ap) { char *buffer; size_t space; size_t oldoff = buf->off; int sz; va_list aq; /* make sure that at least some space is available */ evbuffer_expand (buf, 64); for (;;) { size_t used = buf->misalign + buf->off; buffer = (char *) buf->buffer + buf->off; assert (buf->totallen >= used); space = buf->totallen - used; #ifndef va_copy #define va_copy(dst, src) memcpy(&(dst), &(src), sizeof(va_list)) #endif va_copy (aq, ap); #ifdef WIN32 sz = vsnprintf (buffer, space - 1, fmt, aq); buffer[space - 1] = '\0'; #else sz = vsnprintf (buffer, space, fmt, aq); #endif va_end (aq); if (sz < 0) return (-1); if ( (unsigned int) sz < space) { buf->off += sz; if (buf->cb != NULL) (*buf->cb) (buf, oldoff, buf->off, buf->cbarg); return (sz); } if (evbuffer_expand (buf, sz + 1) == -1) return (-1); } /* NOTREACHED */ }
const void *LibEventTransport::getMorePostData(int &size) { #ifdef EVHTTP_READ_LIMITING if (m_request->ntoread == 0) { size = 0; return NULL; } evbuffer *buf = m_request->input_buffer; ASSERT(buf); evbuffer_drain(buf, EVBUFFER_LENGTH(buf)); if (evhttp_get_more_post_data(m_request, &m_epollfd, &m_epollevent)) { buf = m_request->input_buffer; ASSERT(buf); size = EVBUFFER_LENGTH(buf); evbuffer_expand(buf, size + 1); // allowing NULL termination // EVBUFFER_DATA(buf) might change after evbuffer_expand ((char*)EVBUFFER_DATA(buf))[size] = '\0'; if (m_request->ntoread == 0) { evhttp_get_post_data_done(m_request); } return EVBUFFER_DATA(buf); } if (m_epollfd != -1) { close(m_epollfd); m_epollfd = -1; } evhttp_get_post_data_done(m_request); size = 0; return NULL; #else size = 0; return NULL; #endif }
LibEventTransport::LibEventTransport(LibEventServer *server, evhttp_request *request, int workerId) : m_server(server), m_request(request), m_eventBasePostData(nullptr), m_workerId(workerId), m_sendStarted(false), m_sendEnded(false) { // HttpProtocol::PrepareSystemVariables needs this evbuffer *buf = m_request->input_buffer; assert(buf); m_requestSize = EVBUFFER_LENGTH(buf); if (m_requestSize) { evbuffer_expand(buf, m_requestSize + 1); // allowing NULL termination // EVBUFFER_DATA(buf) might change after evbuffer_expand ((char*)EVBUFFER_DATA(buf))[m_requestSize] = '\0'; } m_remote_host = m_request->remote_host; m_remote_port = m_request->remote_port; { char buf[6]; snprintf(buf, 6, "%d.%d", m_request->major, m_request->minor); m_http_version = buf; } switch (m_request->type) { case EVHTTP_REQ_GET: m_method = Transport::GET; m_requestSize += 3; break; case EVHTTP_REQ_POST: m_method = Transport::POST; m_requestSize += 4; break; case EVHTTP_REQ_HEAD: m_method = Transport::HEAD; m_requestSize += 4; break; default: assert(false); m_method = Transport::UnknownMethod; break; } m_extended_method = m_request->ext_method; assert(m_request->input_headers); for (evkeyval *p = ((m_evkeyvalq*)m_request->input_headers)->tqh_first; p; p = p->next.tqe_next) { if (p->key && p->value) { m_requestHeaders[p->key].push_back(p->value); //key, value, ": " and CR/LF m_requestSize += strlen(p->key) + strlen(p->value) + 4; } } m_url = m_request->uri; m_requestSize += m_url.size(); m_requestSize += m_http_version.size(); //version number in "HTTP/x.y" m_requestSize += 11; // HTTP/=5, 2 spaces for url, and CR/LF x2 (first+last) }
LibEventTransport::LibEventTransport(LibEventServer *server, evhttp_request *request, int workerId) : m_server(server), m_request(request), m_eventBasePostData(NULL), m_workerId(workerId), m_sendStarted(false), m_sendEnded(false) { // HttpProtocol::PrepareSystemVariables needs this evbuffer *buf = m_request->input_buffer; ASSERT(buf); int size = EVBUFFER_LENGTH(buf); if (size) { evbuffer_expand(buf, size + 1); // allowing NULL termination // EVBUFFER_DATA(buf) might change after evbuffer_expand ((char*)EVBUFFER_DATA(buf))[size] = '\0'; } m_remote_host = m_request->remote_host; m_remote_port = m_request->remote_port; { char buf[6]; snprintf(buf, 6, "%d.%d", m_request->major, m_request->minor); m_http_version = buf; } switch (m_request->type) { case EVHTTP_REQ_GET: m_method = Transport::GET; break; case EVHTTP_REQ_POST: m_method = Transport::POST; break; case EVHTTP_REQ_HEAD: m_method = Transport::HEAD; break; default: ASSERT(false); m_method = Transport::UnknownMethod; break; } m_extended_method = m_request->ext_method; ASSERT(m_request->input_headers); for (evkeyval *p = ((m_evkeyvalq*)m_request->input_headers)->tqh_first; p; p = p->next.tqe_next) { if (p->key && p->value) { m_requestHeaders[p->key].push_back(p->value); } } m_url = m_request->uri; }
void tr_peerIoWrite( tr_peerIo * io, const void * bytes, size_t byteCount, tr_bool isPieceData ) { /* FIXME(libevent2): this implementation snould be moved to tr_peerIoWriteBuf. This function should be implemented as evbuffer_new() + evbuffer_add_reference() + a call to tr_peerIoWriteBuf() + evbuffer_free() */ struct tr_datatype * datatype; assert( tr_amInEventThread( io->session ) ); dbgmsg( io, "adding %zu bytes into io->output", byteCount ); datatype = tr_new( struct tr_datatype, 1 ); datatype->isPieceData = isPieceData != 0; datatype->length = byteCount; tr_list_append( &io->outbuf_datatypes, datatype ); switch( io->encryptionMode ) { case PEER_ENCRYPTION_RC4: { /* FIXME(libevent2): use evbuffer_reserve_space() and evbuffer_commit_space() instead of tmp */ void * tmp = tr_sessionGetBuffer( io->session ); const size_t tmplen = SESSION_BUFFER_SIZE; const uint8_t * walk = bytes; evbuffer_expand( io->outbuf, byteCount ); while( byteCount > 0 ) { const size_t thisPass = MIN( byteCount, tmplen ); tr_cryptoEncrypt( io->crypto, thisPass, walk, tmp ); evbuffer_add( io->outbuf, tmp, thisPass ); walk += thisPass; byteCount -= thisPass; } tr_sessionReleaseBuffer( io->session ); break; } case PEER_ENCRYPTION_NONE: evbuffer_add( io->outbuf, bytes, byteCount ); break; default: assert( 0 ); break; } }
const void *LibEventTransport::getMorePostData(int &size) { #ifdef EVHTTP_PORTABLE_READ_LIMITING if (m_request->ntoread == 0) { if (m_eventBasePostData != nullptr) { event_base_free(m_eventBasePostData); m_eventBasePostData = nullptr; } size = 0; return nullptr; } evbuffer *buf = m_request->input_buffer; assert(buf); evbuffer_drain(buf, EVBUFFER_LENGTH(buf)); if (evhttp_get_more_post_data(m_request, &m_eventBasePostData, &m_moreDataRead)) { buf = m_request->input_buffer; assert(buf); size = EVBUFFER_LENGTH(buf); evbuffer_expand(buf, size + 1); // allowing NULL termination // EVBUFFER_DATA(buf) might change after evbuffer_expand ((char*)EVBUFFER_DATA(buf))[size] = '\0'; if (m_request->ntoread == 0) { if (m_eventBasePostData != nullptr) { event_base_free(m_eventBasePostData); m_eventBasePostData = nullptr; } evhttp_get_post_data_done(m_request); } return EVBUFFER_DATA(buf); } if (m_eventBasePostData != nullptr) { event_base_free(m_eventBasePostData); m_eventBasePostData = nullptr; } evhttp_get_post_data_done(m_request); size = 0; return nullptr; #else size = 0; return nullptr; #endif }
int evtls_read(struct evbuffer *buf, int fd, int howmuch, struct tls *ctx) { u_char *p; size_t oldoff = buf->off; int n = EVBUFFER_MAX_READ; if (ioctl(fd, FIONREAD, &n) == -1 || n <= 0) { n = EVBUFFER_MAX_READ; } else if (n > EVBUFFER_MAX_READ && n > howmuch) { /* * It's possible that a lot of data is available for * reading. We do not want to exhaust resources * before the reader has a chance to do something * about it. If the reader does not tell us how much * data we should read, we artifically limit it. */ if ((size_t)n > buf->totallen << 2) n = buf->totallen << 2; if (n < EVBUFFER_MAX_READ) n = EVBUFFER_MAX_READ; } if (howmuch < 0 || howmuch > n) howmuch = n; /* If we don't have FIONREAD, we might waste some space here */ if (evbuffer_expand(buf, howmuch) == -1) return (-1); /* We can append new data at this point */ p = buf->buffer + buf->off; n = tls_read(ctx, p, howmuch); if (n <= 0) return (n); buf->off += n; /* Tell someone about changes in this buffer */ if (buf->off != oldoff && buf->cb != NULL) (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); return (n); }
int evbuffer_add(struct evbuffer *buf, const void *data, size_t datlen) { size_t need = buf->misalign + buf->off + datlen; size_t oldoff = buf->off; if (buf->totallen < need) { if (evbuffer_expand(buf, datlen) == -1) return (-1); } memcpy(buf->buffer + buf->off, data, datlen); buf->off += datlen; if (datlen && buf->cb != NULL) (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); return (0); }
static int artwork_read(char *filename, struct evbuffer *evbuf) { uint8_t buf[4096]; struct stat sb; int fd; int ret; fd = open(filename, O_RDONLY); if (fd < 0) { DPRINTF(E_WARN, L_ART, "Could not open artwork file '%s': %s\n", filename, strerror(errno)); return -1; } ret = fstat(fd, &sb); if (ret < 0) { DPRINTF(E_WARN, L_ART, "Could not stat() artwork file '%s': %s\n", filename, strerror(errno)); goto out_fail; } ret = evbuffer_expand(evbuf, sb.st_size); if (ret < 0) { DPRINTF(E_LOG, L_ART, "Out of memory for artwork\n"); goto out_fail; } while ((ret = read(fd, buf, sizeof(buf))) > 0) evbuffer_add(evbuf, buf, ret); close(fd); return 0; out_fail: close(fd); return -1; }
static char* announce_url_new (const tr_session * session, const tr_announce_request * req) { const char * str; const unsigned char * ipv6; struct evbuffer * buf = evbuffer_new (); char escaped_info_hash[SHA_DIGEST_LENGTH*3 + 1]; tr_http_escape_sha1 (escaped_info_hash, req->info_hash); evbuffer_expand (buf, 1024); evbuffer_add_printf (buf, "%s" "%c" "info_hash=%s" "&peer_id=%*.*s" "&port=%d" "&uploaded=%" PRIu64 "&downloaded=%" PRIu64 "&left=%" PRIu64 "&numwant=%d" "&key=%x" "&compact=1" "&supportcrypto=1", req->url, strchr (req->url, '?') ? '&' : '?', escaped_info_hash, PEER_ID_LEN, PEER_ID_LEN, req->peer_id, req->port, req->up, req->down, req->leftUntilComplete, req->numwant, req->key); if (session->encryptionMode == TR_ENCRYPTION_REQUIRED) evbuffer_add_printf (buf, "&requirecrypto=1"); if (req->corrupt) evbuffer_add_printf (buf, "&corrupt=%" PRIu64, req->corrupt); str = get_event_string (req); if (str && *str) evbuffer_add_printf (buf, "&event=%s", str); str = req->tracker_id_str; if (str && *str) evbuffer_add_printf (buf, "&trackerid=%s", str); /* There are two incompatible techniques for announcing an IPv6 address. BEP-7 suggests adding an "ipv6=" parameter to the announce URL, while OpenTracker requires that peers announce twice, once over IPv4 and once over IPv6. To be safe, we should do both: add the "ipv6=" parameter and announce twice. At any rate, we're already computing our IPv6 address (for the LTEP handshake), so this comes for free. */ ipv6 = tr_globalIPv6 (); if (ipv6) { char ipv6_readable[INET6_ADDRSTRLEN]; evutil_inet_ntop (AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN); evbuffer_add_printf (buf, "&ipv6="); tr_http_escape (buf, ipv6_readable, -1, true); } return evbuffer_free_to_str (buf); }
/* Thread: httpd */ void httpd_stream_file(struct evhttp_request *req, int id) { struct media_file_info *mfi; struct stream_ctx *st; void (*stream_cb)(int fd, short event, void *arg); struct stat sb; struct timeval tv; struct evhttp_connection *evcon; struct evkeyvalq *input_headers; struct evkeyvalq *output_headers; const char *param; const char *param_end; const char *ua; const char *client_codecs; char buf[64]; int64_t offset; int64_t end_offset; off_t pos; int transcode; int ret; offset = 0; end_offset = 0; input_headers = evhttp_request_get_input_headers(req); param = evhttp_find_header(input_headers, "Range"); if (param) { DPRINTF(E_DBG, L_HTTPD, "Found Range header: %s\n", param); /* Start offset */ ret = safe_atoi64(param + strlen("bytes="), &offset); if (ret < 0) { DPRINTF(E_LOG, L_HTTPD, "Invalid start offset, will stream whole file (%s)\n", param); offset = 0; } /* End offset, if any */ else { param_end = strchr(param, '-'); if (param_end && (strlen(param_end) > 1)) { ret = safe_atoi64(param_end + 1, &end_offset); if (ret < 0) { DPRINTF(E_LOG, L_HTTPD, "Invalid end offset, will stream to end of file (%s)\n", param); end_offset = 0; } if (end_offset < offset) { DPRINTF(E_LOG, L_HTTPD, "End offset < start offset, will stream to end of file (%" PRIi64 " < %" PRIi64 ")\n", end_offset, offset); end_offset = 0; } } } } mfi = db_file_fetch_byid(id); if (!mfi) { DPRINTF(E_LOG, L_HTTPD, "Item %d not found\n", id); evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); return; } if (mfi->data_kind != DATA_KIND_FILE) { evhttp_send_error(req, 500, "Cannot stream radio station"); goto out_free_mfi; } st = (struct stream_ctx *)malloc(sizeof(struct stream_ctx)); if (!st) { DPRINTF(E_LOG, L_HTTPD, "Out of memory for struct stream_ctx\n"); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_free_mfi; } memset(st, 0, sizeof(struct stream_ctx)); st->fd = -1; ua = evhttp_find_header(input_headers, "User-Agent"); client_codecs = evhttp_find_header(input_headers, "Accept-Codecs"); transcode = transcode_needed(ua, client_codecs, mfi->codectype); output_headers = evhttp_request_get_output_headers(req); if (transcode) { DPRINTF(E_INFO, L_HTTPD, "Preparing to transcode %s\n", mfi->path); stream_cb = stream_chunk_xcode_cb; st->xcode = transcode_setup(mfi, XCODE_PCM16_HEADER, &st->size); if (!st->xcode) { DPRINTF(E_WARN, L_HTTPD, "Transcoding setup failed, aborting streaming\n"); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_free_st; } if (!evhttp_find_header(output_headers, "Content-Type")) evhttp_add_header(output_headers, "Content-Type", "audio/wav"); } else { /* Stream the raw file */ DPRINTF(E_INFO, L_HTTPD, "Preparing to stream %s\n", mfi->path); st->buf = (uint8_t *)malloc(STREAM_CHUNK_SIZE); if (!st->buf) { DPRINTF(E_LOG, L_HTTPD, "Out of memory for raw streaming buffer\n"); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_free_st; } stream_cb = stream_chunk_raw_cb; st->fd = open(mfi->path, O_RDONLY); if (st->fd < 0) { DPRINTF(E_LOG, L_HTTPD, "Could not open %s: %s\n", mfi->path, strerror(errno)); evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); goto out_cleanup; } ret = stat(mfi->path, &sb); if (ret < 0) { DPRINTF(E_LOG, L_HTTPD, "Could not stat() %s: %s\n", mfi->path, strerror(errno)); evhttp_send_error(req, HTTP_NOTFOUND, "Not Found"); goto out_cleanup; } st->size = sb.st_size; pos = lseek(st->fd, offset, SEEK_SET); if (pos == (off_t) -1) { DPRINTF(E_LOG, L_HTTPD, "Could not seek into %s: %s\n", mfi->path, strerror(errno)); evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request"); goto out_cleanup; } st->offset = offset; st->end_offset = end_offset; /* Content-Type for video files is different than for audio files * and overrides whatever may have been set previously, like * application/x-dmap-tagged when we're speaking DAAP. */ if (mfi->has_video) { /* Front Row and others expect video/<type> */ ret = snprintf(buf, sizeof(buf), "video/%s", mfi->type); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n"); else { evhttp_remove_header(output_headers, "Content-Type"); evhttp_add_header(output_headers, "Content-Type", buf); } } /* If no Content-Type has been set and we're streaming audio, add a proper * Content-Type for the file we're streaming. Remember DAAP streams audio * with application/x-dmap-tagged as the Content-Type (ugh!). */ else if (!evhttp_find_header(output_headers, "Content-Type") && mfi->type) { ret = snprintf(buf, sizeof(buf), "audio/%s", mfi->type); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Type too large for buffer, dropping\n"); else evhttp_add_header(output_headers, "Content-Type", buf); } } st->evbuf = evbuffer_new(); if (!st->evbuf) { DPRINTF(E_LOG, L_HTTPD, "Could not allocate an evbuffer for streaming\n"); evhttp_clear_headers(output_headers); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_cleanup; } ret = evbuffer_expand(st->evbuf, STREAM_CHUNK_SIZE); if (ret != 0) { DPRINTF(E_LOG, L_HTTPD, "Could not expand evbuffer for streaming\n"); evhttp_clear_headers(output_headers); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_cleanup; } st->ev = event_new(evbase_httpd, -1, EV_TIMEOUT, stream_cb, st); evutil_timerclear(&tv); if (!st->ev || (event_add(st->ev, &tv) < 0)) { DPRINTF(E_LOG, L_HTTPD, "Could not add one-shot event for streaming\n"); evhttp_clear_headers(output_headers); evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error"); goto out_cleanup; } st->id = mfi->id; st->start_offset = offset; st->stream_size = st->size; st->req = req; if ((offset == 0) && (end_offset == 0)) { /* If we are not decoding, send the Content-Length. We don't do * that if we are decoding because we can only guesstimate the * size in this case and the error margin is unknown and variable. */ if (!transcode) { ret = snprintf(buf, sizeof(buf), "%" PRIi64, (int64_t)st->size); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n"); else evhttp_add_header(output_headers, "Content-Length", buf); } evhttp_send_reply_start(req, HTTP_OK, "OK"); } else { if (offset > 0) st->stream_size -= offset; if (end_offset > 0) st->stream_size -= (st->size - end_offset); DPRINTF(E_DBG, L_HTTPD, "Stream request with range %" PRIi64 "-%" PRIi64 "\n", offset, end_offset); ret = snprintf(buf, sizeof(buf), "bytes %" PRIi64 "-%" PRIi64 "/%" PRIi64, offset, (end_offset) ? end_offset : (int64_t)st->size, (int64_t)st->size); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Range too large for buffer, dropping\n"); else evhttp_add_header(output_headers, "Content-Range", buf); ret = snprintf(buf, sizeof(buf), "%" PRIi64, ((end_offset) ? end_offset + 1 : (int64_t)st->size) - offset); if ((ret < 0) || (ret >= sizeof(buf))) DPRINTF(E_LOG, L_HTTPD, "Content-Length too large for buffer, dropping\n"); else evhttp_add_header(output_headers, "Content-Length", buf); evhttp_send_reply_start(req, 206, "Partial Content"); } #ifdef HAVE_POSIX_FADVISE if (!transcode) { /* Hint the OS */ posix_fadvise(st->fd, st->start_offset, st->stream_size, POSIX_FADV_WILLNEED); posix_fadvise(st->fd, st->start_offset, st->stream_size, POSIX_FADV_SEQUENTIAL); posix_fadvise(st->fd, st->start_offset, st->stream_size, POSIX_FADV_NOREUSE); } #endif evcon = evhttp_request_get_connection(req); evhttp_connection_set_closecb(evcon, stream_fail_cb, st); DPRINTF(E_INFO, L_HTTPD, "Kicking off streaming for %s\n", mfi->path); free_mfi(mfi, 0); return; out_cleanup: if (st->evbuf) evbuffer_free(st->evbuf); if (st->xcode) transcode_cleanup(st->xcode); if (st->buf) free(st->buf); if (st->fd > 0) close(st->fd); out_free_st: free(st); out_free_mfi: free_mfi(mfi, 0); }
int evbuffer_read (struct evbuffer *buf, int fd, int howmuch) { u_char *p; size_t oldoff = buf->off; int n = EVBUFFER_MAX_READ; #if defined(FIONREAD) #ifdef WIN32 unsigned long lng = n; if (ioctlsocket (fd, FIONREAD, &lng) == -1 || (n = lng) == 0) { #else if (ioctl (fd, FIONREAD, &n) == -1 || n == 0) { #endif n = EVBUFFER_MAX_READ; } else if (n > EVBUFFER_MAX_READ && n > howmuch) { /* * It's possible that a lot of data is available for * reading. We do not want to exhaust resources * before the reader has a chance to do something * about it. If the reader does not tell us how much * data we should read, we artifically limit it. */ if ( (unsigned int) n > buf->totallen << 2) n = buf->totallen << 2; if (n < EVBUFFER_MAX_READ) n = EVBUFFER_MAX_READ; } #endif if (howmuch < 0 || howmuch > n) howmuch = n; /* If we don't have FIONREAD, we might waste some space here */ if (evbuffer_expand (buf, howmuch) == -1) return (-1); /* We can append new data at this point */ p = buf->buffer + buf->off; #ifndef WIN32 n = read (fd, p, howmuch); #else n = recv (fd, (char *) p, howmuch, 0); #endif if (n == -1) return (-1); if (n == 0) return (0); buf->off += n; /* Tell someone about changes in this buffer */ if (buf->off != oldoff && buf->cb != NULL) (*buf->cb) (buf, oldoff, buf->off, buf->cbarg); return (n); } int evbuffer_write (struct evbuffer *buffer, int fd) { int n; #ifndef WIN32 n = write (fd, buffer->buffer, buffer->off); #else n = send (fd, (void *) buffer->buffer, buffer->off, 0); #endif if (n == -1) return (-1); if (n == 0) return (0); evbuffer_drain (buffer, n); return (n); }
static void add_response( struct evhttp_request * req, struct tr_rpc_server * server, struct evbuffer * out, const void * content, size_t content_len ) { #ifndef HAVE_ZLIB evbuffer_add( out, content, content_len ); #else const char * key = "Accept-Encoding"; const char * encoding = evhttp_find_header( req->input_headers, key ); const int do_deflate = encoding && strstr( encoding, "deflate" ); if( !do_deflate ) { evbuffer_add( out, content, content_len ); } else { int state; server->stream.next_in = (Bytef*) content; server->stream.avail_in = content_len; /* allocate space for the raw data and call deflate() just once -- * we won't use the deflated data if it's longer than the raw data, * so it's okay to let deflate() run out of output buffer space */ evbuffer_expand( out, content_len ); server->stream.next_out = EVBUFFER_DATA( out ); server->stream.avail_out = content_len; state = deflate( &server->stream, Z_FINISH ); if( state == Z_STREAM_END ) { EVBUFFER_LENGTH( out ) = content_len - server->stream.avail_out; /* http://carsten.codimi.de/gzip.yaws/ It turns out that some browsers expect deflated data without the first two bytes (a kind of header) and and the last four bytes (an ADLER32 checksum). This format can of course be produced by simply stripping these off. */ if( EVBUFFER_LENGTH( out ) >= 6 ) { EVBUFFER_LENGTH( out ) -= 4; evbuffer_drain( out, 2 ); } #if 0 tr_ninf( MY_NAME, _( "Deflated response from %zu bytes to %zu" ), content_len, EVBUFFER_LENGTH( out ) ); #endif evhttp_add_header( req->output_headers, "Content-Encoding", "deflate" ); } else { evbuffer_drain( out, EVBUFFER_LENGTH( out ) ); evbuffer_add( out, content, content_len ); } deflateReset( &server->stream ); } #endif }