/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* .
*/
#include "networkio.h"
#include "apr_errno.h"
#include "apr_general.h"
#include "apr_network_io.h"
#include "apr_lib.h"
#include "fileio.h"
#include
/* MAX_SEGMENT_SIZE is the maximum amount of data that will be sent to a client
* in one call of TransmitFile. This number must be small enough to give the
* slowest client time to receive the data before the socket timeout triggers.
* The same problem can exist with apr_send(). In that case, we rely on the
* application to adjust socket timeouts and max send segment sizes appropriately.
* For example, Apache will in most cases call apr_send() with less than 8193
* bytes
* of data.
*/
#define MAX_SEGMENT_SIZE 65536
apr_status_t apr_send(apr_socket_t *sock, const char *buf, apr_ssize_t *len)
{
apr_ssize_t rv;
WSABUF wsaData;
int lasterror;
DWORD dwBytes = 0;
wsaData.len = *len;
wsaData.buf = (char*) buf;
rv = WSASend(sock->sock, &wsaData, 1, &dwBytes, 0, NULL, NULL);
if (rv == SOCKET_ERROR) {
lasterror = apr_get_netos_error();
return lasterror;
}
*len = dwBytes;
return APR_SUCCESS;
}
apr_status_t apr_recv(apr_socket_t *sock, char *buf, apr_ssize_t *len)
{
apr_ssize_t rv;
WSABUF wsaData;
int lasterror;
DWORD dwBytes = 0;
DWORD flags = 0;
wsaData.len = *len;
wsaData.buf = (char*) buf;
rv = WSARecv(sock->sock, &wsaData, 1, &dwBytes, &flags, NULL, NULL);
if (rv == SOCKET_ERROR) {
lasterror = apr_get_netos_error();
*len = 0;
return lasterror;
}
*len = dwBytes;
return APR_SUCCESS;
}
apr_status_t apr_sendv(apr_socket_t *sock, const struct iovec *vec,
apr_int32_t nvec, apr_int32_t *nbytes)
{
apr_ssize_t rv;
int i;
int lasterror;
DWORD dwBytes = 0;
LPWSABUF pWsaData = (LPWSABUF) malloc(sizeof(WSABUF) * nvec);
if (!pWsaData)
return APR_ENOMEM;
for (i = 0; i < nvec; i++) {
pWsaData[i].buf = vec[i].iov_base;
pWsaData[i].len = vec[i].iov_len;
}
rv = WSASend(sock->sock, pWsaData, nvec, &dwBytes, 0, NULL, NULL);
if (rv == SOCKET_ERROR) {
lasterror = apr_get_netos_error();
free(pWsaData);
return lasterror;
}
free(pWsaData);
*nbytes = dwBytes;
return APR_SUCCESS;
}
static void collapse_iovec(char **buf, int *len, struct iovec *iovec, int numvec, apr_pool_t *p)
{
int ptr = 0;
if (numvec == 1) {
*buf = iovec[0].iov_base;
*len = iovec[0].iov_len;
}
else {
int i;
for (i = 0; i < numvec; i++) {
*len += iovec[i].iov_len;
}
*buf = apr_palloc(p, *len); /* Should this be a malloc? */
for (i = 0; i < numvec; i++) {
memcpy((char*)*buf + ptr, iovec[i].iov_base, iovec[i].iov_len);
ptr += iovec[i].iov_len;
}
}
}
#if APR_HAS_SENDFILE
/*
*#define WAIT_FOR_EVENT
* Note: Waiting for the socket directly is much faster than creating a seperate
* wait event. There are a couple of dangerous aspects to waiting directly
* for the socket. First, we should not wait on the socket if concurrent threads
* can wait-on/signal the same socket. This shouldn't be happening with Apache since
* a socket is uniquely tied to a thread. This will change when we begin using
* async I/O with completion ports on the socket.
*/
/*
* apr_status_t apr_sendfile(apr_socket_t *, apr_file_t *, apr_hdtr_t *,
* apr_off_t *, apr_size_t *, apr_int32_t flags)
* Send a file from an open file descriptor to a socket, along with
* optional headers and trailers
* arg 1) The socket to which we're writing
* arg 2) The open file from which to read
* arg 3) A structure containing the headers and trailers to send
* arg 4) Offset into the file where we should begin writing
* arg 5) Number of bytes to send out of the file
* arg 6) APR flags that are mapped to OS specific flags
*/
apr_status_t apr_sendfile(apr_socket_t * sock, apr_file_t * file,
apr_hdtr_t * hdtr, apr_off_t * offset, apr_size_t * len,
apr_int32_t flags)
{
apr_status_t status = APR_SUCCESS;
apr_ssize_t rv;
DWORD dwFlags = 0;
DWORD nbytes;
OVERLAPPED overlapped;
TRANSMIT_FILE_BUFFERS tfb, *ptfb = NULL;
int bytes_to_send;
int ptr = 0;
/* Must pass in a valid length */
if (len == 0) {
return APR_EINVAL;
}
bytes_to_send = *len;
*len = 0;
/* Initialize the overlapped structure */
memset(&overlapped,'\0', sizeof(overlapped));
if (offset && *offset) {
overlapped.Offset = *offset;
}
#ifdef WAIT_FOR_EVENT
overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
/* TransmitFile can only send one header and one footer */
memset(&tfb, '\0', sizeof (tfb));
if (hdtr && hdtr->numheaders) {
ptfb = &tfb;
collapse_iovec((char **)&ptfb->Head, &ptfb->HeadLength, hdtr->headers, hdtr->numheaders, sock->cntxt);
}
/* If we have more than MAX_SEGMENT_SIZE headers to send, send them
* in segments.
*/
if (ptfb && ptfb->HeadLength) {
while (ptfb->HeadLength >= MAX_SEGMENT_SIZE) {
nbytes = MAX_SEGMENT_SIZE;
rv = apr_send(sock, ptfb->Head, &nbytes);
if (rv != APR_SUCCESS)
return rv;
(char*) ptfb->Head += nbytes;
ptfb->HeadLength -= nbytes;
*len += nbytes;
}
}
while (bytes_to_send) {
if (bytes_to_send > MAX_SEGMENT_SIZE) {
nbytes = MAX_SEGMENT_SIZE;
}
else {
/* Last call to TransmitFile() */
nbytes = bytes_to_send;
/* Send trailers on the last packet, even if the total size
* exceeds MAX_SEGMENT_SIZE...
*/
if (hdtr && hdtr->numtrailers) {
ptfb = &tfb;
collapse_iovec((char**) &ptfb->Tail, &ptfb->TailLength,
hdtr->trailers, hdtr->numtrailers, sock->cntxt);
}
/* Disconnect the socket after last send */
if (flags & APR_SENDFILE_DISCONNECT_SOCKET) {
dwFlags |= TF_REUSE_SOCKET;
dwFlags |= TF_DISCONNECT;
}
}
rv = TransmitFile(sock->sock, /* socket */
file->filehand, /* open file descriptor of the file to be sent */
nbytes, /* number of bytes to send. 0=send all */
0, /* Number of bytes per send. 0=use default */
&overlapped, /* OVERLAPPED structure */
ptfb, /* header and trailer buffers */
dwFlags); /* flags to control various aspects of TransmitFile */
if (!rv) {
status = apr_get_netos_error();
if (status == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
#ifdef WAIT_FOR_EVENT
rv = WaitForSingleObject(overlapped.hEvent,
sock->timeout >= 0 ? sock->timeout : INFINITE);
#else
rv = WaitForSingleObject((HANDLE) sock->sock,
sock->timeout >= 0 ? sock->timeout : INFINITE);
#endif
if (rv == WAIT_OBJECT_0)
status = APR_SUCCESS;
else if (rv == WAIT_TIMEOUT)
status = WAIT_TIMEOUT;
else if (rv == WAIT_ABANDONED)
status = WAIT_ABANDONED;
else
status = apr_get_os_error();
}
}
if (status != APR_SUCCESS)
break;
/* Assume the headers have been sent */
ptfb->HeadLength = 0;
ptfb->Head = NULL;
bytes_to_send -= nbytes;
*len += nbytes;
overlapped.Offset += nbytes;
}
if (status == APR_SUCCESS) {
if (ptfb && ptfb->TailLength)
*len += ptfb->TailLength;
/* Mark the socket as disconnected, but do not close it.
* Note: The application must have stored the socket prior to making
* the call to apr_sendfile in order to either reuse it or close it.
*/
if (flags & APR_SENDFILE_DISCONNECT_SOCKET) {
sock->disconnected = 1;
sock->sock = INVALID_SOCKET;
}
}
#ifdef WAIT_FOR_EVENT
CloseHandle(overlapped.hEvent);
#endif
return status;
}
#endif