/*
 * The contents of this file are subject to the AOLserver Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://aolserver.com/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is AOLserver Code and related documentation
 * distributed by AOL.
 * 
 * The Initial Developer of the Original Code is America Online,
 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
 * Inc. All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU General Public License (the "GPL"), in which case the
 * provisions of GPL are applicable instead of those above.  If you wish
 * to allow use of your version of this file only under the terms of the
 * GPL and not to allow others to use your version of this file under the
 * License, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the GPL.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under either the License or the GPL.
 */

/* 
 * nssock.c --
 *
 *	Call internal Ns_DriverInit.
 *
 */

#include "ns.h"

static Ns_DriverProc SockProc;
static int SockRecv(SOCKET sock, struct iovec *bufs, int nbufs);
static int SockSend(SOCKET sock, struct iovec *bufs, int nbufs);


/*
 *----------------------------------------------------------------------
 *
 * NsSock_ModInit --
 *
 *	Sock module init routine.
 *
 * Results:
 *	See Ns_DriverInit.
 *
 * Side effects:
 *	See Ns_DriverInit.
 *
 *----------------------------------------------------------------------
 */

int
NsSock_ModInit(char *server, char *module)
{
    Ns_DriverInitData init;
    char *path;
    int async;

    path = Ns_ConfigGetPath(server, module, NULL);
    if (!Ns_ConfigGetBool(path, "async", &async)) {
	async = 1;
    }

    /*
     * Initialize the driver with the async option so that the driver thread
     * will perform event-driven read-ahead of the request before
     * passing to the connection for processing.
     */

    init.version = NS_DRIVER_VERSION_1;
    init.name = "nssock";
    init.proc = SockProc;
    init.opts = (async ? NS_DRIVER_ASYNC : 0);
    init.arg = NULL;
    init.path = NULL;

    return Ns_DriverInit(server, module, &init);
}


/*
 *----------------------------------------------------------------------
 *
 * SockProc --
 *
 *	Socket driver callback proc.  This driver attempts efficient
 *	scatter/gatter I/O if requested and only blocks for the
 *	driver configured time once if no bytes are available.
 *
 * Results:
 *	For close and keep, always 0.  For send and recv, # of bytes
 *	processed or -1 on error or timeout.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
SockProc(Ns_DriverCmd cmd, Ns_Sock *sock, struct iovec *bufs, int nbufs)
{
    int n;

    switch (cmd) {
    case DriverRecv:
	n = SockRecv(sock->sock, bufs, nbufs);
	if (n < 0
	    && ns_sockerrno == EWOULDBLOCK
	    && Ns_SockWait(sock->sock, NS_SOCK_READ, sock->driver->recvwait) == NS_OK) {
	    n = SockRecv(sock->sock, bufs, nbufs);
	}
	break;

    case DriverSend:
	n = SockSend(sock->sock, bufs, nbufs);
	if (n < 0
	    && ns_sockerrno == EWOULDBLOCK
	    && Ns_SockWait(sock->sock, NS_SOCK_WRITE, sock->driver->sendwait) == NS_OK) {
	    n = SockSend(sock->sock, bufs, nbufs);
	}
	break;

    case DriverKeep:
    case DriverClose:
	/* NB: Nothing to do. */
	n = 0;
	break;

    default:
	/* Unsupported command. */
	n = -1;
	break;
    }
    return n;
}


static int
SockRecv(SOCKET sock, struct iovec *bufs, int nbufs)
{
#ifdef _WIN32
    int n, flags;

    flags = 0;
    if (WSARecv(sock, (LPWSABUF)bufs, nbufs, &n, &flags, NULL, NULL) != 0) {
	n = -1;
    }
    return n;
#else
    struct msghdr msg;

    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = bufs;
    msg.msg_iovlen = nbufs;
    return recvmsg(sock, &msg, 0);
#endif
}


static int
SockSend(SOCKET sock, struct iovec *bufs, int nbufs)
{
#ifdef _WIN32
    int n;

    if (WSASend(sock, (LPWSABUF)bufs, nbufs, &n, 0, NULL, NULL) != 0) {
	n = -1;
    }
    return n;
#else
    struct msghdr msg;

    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = bufs;
    msg.msg_iovlen = nbufs;
    return sendmsg(sock, &msg, 0);
#endif
}
