Пример #1
0
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 */
}
Пример #2
0
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
}
Пример #3
0
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)
}
Пример #4
0
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;
}
Пример #5
0
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;
    }
}
Пример #6
0
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
}
Пример #7
0
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);
}
Пример #8
0
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);
}
Пример #9
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;
}
Пример #10
0
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);
}
Пример #11
0
/* 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);
}
Пример #12
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);
}
Пример #13
0
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
}