Esempio n. 1
0
    //
    // blocking accept()
    //
    Pj_Sock_Stream accept(Pj_Inet_Addr *remote_addr = NULL)
    {
        pj_sock_t newsock;
        int *addrlen = remote_addr ? &remote_addr->addrlen_ : NULL;
        pj_status_t status;
        
        status = pj_sock_accept(sock_, &newsock, remote_addr, addrlen);
        if (status != PJ_SUCCESS)
            return Pj_Sock_Stream(-1);

        return Pj_Sock_Stream(newsock);
    }
Esempio n. 2
0
static int server_thread(void *p)
{
    struct server_t *srv = (struct server_t*)p;
    char *pkt = (char*)pj_pool_alloc(pool, srv->buf_size);
    pj_sock_t newsock = PJ_INVALID_SOCKET;

    while (!thread_quit) {
	pj_ssize_t pkt_len;
	int rc;
        pj_fd_set_t rset;
	pj_time_val timeout = {0, 500};

	while (!thread_quit) {
	    PJ_FD_ZERO(&rset);
	    PJ_FD_SET(srv->sock, &rset);
	    rc = pj_sock_select((int)srv->sock+1, &rset, NULL, NULL, &timeout);
	    if (rc != 1) {
		continue;
	    }

	    rc = pj_sock_accept(srv->sock, &newsock, NULL, NULL);
	    if (rc == PJ_SUCCESS) {
		break;
	    }
	}

	if (thread_quit)
	    break;

	while (!thread_quit) {
            PJ_FD_ZERO(&rset);
            PJ_FD_SET(newsock, &rset);
            rc = pj_sock_select((int)newsock+1, &rset, NULL, NULL, &timeout);
            if (rc != 1) {
        	PJ_LOG(3,("http test", "client timeout"));
                continue;
            }

            pkt_len = srv->buf_size;
            rc = pj_sock_recv(newsock, pkt, &pkt_len, 0);
            if (rc == PJ_SUCCESS) {
                break;
            }
        }

	if (thread_quit)
	    break;

	/* Simulate network RTT */
	pj_thread_sleep(50);

	if (srv->action == ACTION_IGNORE) {
	    continue;
	} else if (srv->action == ACTION_REPLY) {
            pj_size_t send_size = 0;
	    unsigned ctr = 0;
            pj_ansi_sprintf(pkt, "HTTP/1.0 200 OK\r\n");
            if (srv->send_content_length) {
                pj_ansi_sprintf(pkt + pj_ansi_strlen(pkt), 
                                "Content-Length: %d\r\n",
                                srv->data_size);
            }
            pj_ansi_sprintf(pkt + pj_ansi_strlen(pkt), "\r\n");
            pkt_len = pj_ansi_strlen(pkt);
            rc = pj_sock_send(newsock, pkt, &pkt_len, 0);
            if (rc != PJ_SUCCESS) {
        	pj_sock_close(newsock);
        	continue;
            }
            while (send_size < srv->data_size) {
                pkt_len = srv->data_size - send_size;
                if (pkt_len > (signed)srv->buf_size)
                    pkt_len = srv->buf_size;
                send_size += pkt_len;
                pj_create_random_string(pkt, pkt_len);
                pj_ansi_sprintf(pkt, "\nPacket: %d", ++ctr);
                pkt[pj_ansi_strlen(pkt)] = '\n';
		rc = pj_sock_send(newsock, pkt, &pkt_len, 0);
		if (rc != PJ_SUCCESS)
		    break;
            }
            pj_sock_close(newsock);
	}
    }

    return 0;
}
Esempio n. 3
0
pj_status_t app_socketpair(int family, int type, int protocol,
                           pj_sock_t *serverfd, pj_sock_t *clientfd)
{
    int i;
    static unsigned short port = 11000;
    pj_sockaddr_in addr;
    pj_str_t s;
    pj_status_t rc = 0;
    pj_sock_t sock[2];

    /* Create both sockets. */
    for (i=0; i<2; ++i) {
        rc = pj_sock_socket(family, type, protocol, &sock[i]);
        if (rc != PJ_SUCCESS) {
            if (i==1)
                pj_sock_close(sock[0]);
            return rc;
        }
    }

    /* Retry bind */
    pj_bzero(&addr, sizeof(addr));
    addr.sin_family = pj_AF_INET();
    for (i=0; i<5; ++i) {
        addr.sin_port = pj_htons(port++);
        rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr));
        if (rc == PJ_SUCCESS)
            break;
    }

    if (rc != PJ_SUCCESS)
        goto on_error;

    /* For TCP, listen the socket. */
#if PJ_HAS_TCP
    if (type == pj_SOCK_STREAM()) {
        rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN);
        if (rc != PJ_SUCCESS)
            goto on_error;
    }
#endif

    /* Connect client socket. */
    addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
    rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr));
    if (rc != PJ_SUCCESS)
        goto on_error;

    /* For TCP, must accept(), and get the new socket. */
#if PJ_HAS_TCP
    if (type == pj_SOCK_STREAM()) {
        pj_sock_t newserver;

        rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL);
        if (rc != PJ_SUCCESS)
            goto on_error;

        /* Replace server socket with new socket. */
        pj_sock_close(sock[SERVER]);
        sock[SERVER] = newserver;
    }
#endif

    *serverfd = sock[SERVER];
    *clientfd = sock[CLIENT];

    return rc;

on_error:
    for (i=0; i<2; ++i)
        pj_sock_close(sock[i]);
    return rc;
}
Esempio n. 4
0
/*
 * Initiate overlapped accept() operation.
 */
PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
                                       pj_ioqueue_op_key_t *op_key,
			               pj_sock_t *new_sock,
			               pj_sockaddr_t *local,
			               pj_sockaddr_t *remote,
			               int *addrlen)
{
    struct accept_operation *accept_op;
    pj_status_t status;

    /* check parameters. All must be specified! */
    PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);

    /* Check if key is closing. */
    if (IS_CLOSING(key))
	return PJ_ECANCELLED;

    accept_op = (struct accept_operation*)op_key;
    accept_op->op = PJ_IOQUEUE_OP_NONE;

    /* Fast track:
     *  See if there's new connection available immediately.
     */
    if (pj_list_empty(&key->accept_list)) {
        status = pj_sock_accept(key->fd, new_sock, remote, addrlen);
        if (status == PJ_SUCCESS) {
            /* Yes! New connection is available! */
            if (local && addrlen) {
                status = pj_sock_getsockname(*new_sock, local, addrlen);
                if (status != PJ_SUCCESS) {
                    pj_sock_close(*new_sock);
                    *new_sock = PJ_INVALID_SOCKET;
                    return status;
                }
            }
            return PJ_SUCCESS;
        } else {
            /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
             * the error to caller.
             */
            if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
                return status;
            }
        }
    }

    /*
     * No connection is available immediately.
     * Schedule accept() operation to be completed when there is incoming
     * connection available.
     */
    accept_op->op = PJ_IOQUEUE_OP_ACCEPT;
    accept_op->accept_fd = new_sock;
    accept_op->rmt_addr = remote;
    accept_op->addrlen= addrlen;
    accept_op->local_addr = local;

    pj_mutex_lock(key->mutex);
    pj_list_insert_before(&key->accept_list, accept_op);
    ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT);
    pj_mutex_unlock(key->mutex);

    return PJ_EPENDING;
}
Esempio n. 5
0
void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
{
    pj_status_t rc;

    /* Lock the key. */
    pj_mutex_lock(h->mutex);

    if (IS_CLOSING(h)) {
	pj_mutex_unlock(h->mutex);
	return;
    }

#   if PJ_HAS_TCP
    if (!pj_list_empty(&h->accept_list)) {

        struct accept_operation *accept_op;
	pj_bool_t has_lock;
	
        /* Get one accept operation from the list. */
	accept_op = h->accept_list.next;
        pj_list_erase(accept_op);
        accept_op->op = PJ_IOQUEUE_OP_NONE;

	/* Clear bit in fdset if there is no more pending accept */
        if (pj_list_empty(&h->accept_list))
            ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT);

	rc=pj_sock_accept(h->fd, accept_op->accept_fd, 
                          accept_op->rmt_addr, accept_op->addrlen);
	if (rc==PJ_SUCCESS && accept_op->local_addr) {
	    rc = pj_sock_getsockname(*accept_op->accept_fd, 
                                     accept_op->local_addr,
				     accept_op->addrlen);
	}

	/* Unlock; from this point we don't need to hold key's mutex
	 * (unless concurrency is disabled, which in this case we should
	 * hold the mutex while calling the callback) */
	if (h->allow_concurrent) {
	    /* concurrency may be changed while we're in the callback, so
	     * save it to a flag.
	     */
	    has_lock = PJ_FALSE;
	    pj_mutex_unlock(h->mutex);
	} else {
	    has_lock = PJ_TRUE;
	}

	/* Call callback. */
        if (h->cb.on_accept_complete && !IS_CLOSING(h)) {
	    (*h->cb.on_accept_complete)(h, 
                                        (pj_ioqueue_op_key_t*)accept_op,
                                        *accept_op->accept_fd, rc);
	}

	if (has_lock) {
	    pj_mutex_unlock(h->mutex);
	}
    }
    else
#   endif
    if (key_has_pending_read(h)) {
        struct read_operation *read_op;
        pj_ssize_t bytes_read;
	pj_bool_t has_lock;

        /* Get one pending read operation from the list. */
        read_op = h->read_list.next;
        pj_list_erase(read_op);

        /* Clear fdset if there is no pending read. */
        if (pj_list_empty(&h->read_list))
            ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT);

        bytes_read = read_op->size;

	if ((read_op->op == PJ_IOQUEUE_OP_RECV_FROM)) {
	    read_op->op = PJ_IOQUEUE_OP_NONE;
	    rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, 
				  read_op->flags,
				  read_op->rmt_addr, 
                                  read_op->rmt_addrlen);
	} else if ((read_op->op == PJ_IOQUEUE_OP_RECV)) {
	    read_op->op = PJ_IOQUEUE_OP_NONE;
	    rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 
			      read_op->flags);
        } else {
            pj_assert(read_op->op == PJ_IOQUEUE_OP_READ);
	    read_op->op = PJ_IOQUEUE_OP_NONE;
            /*
             * User has specified pj_ioqueue_read().
             * On Win32, we should do ReadFile(). But because we got
             * here because of select() anyway, user must have put a
             * socket descriptor on h->fd, which in this case we can
             * just call pj_sock_recv() instead of ReadFile().
             * On Unix, user may put a file in h->fd, so we'll have
             * to call read() here.
             * This may not compile on systems which doesn't have 
             * read(). That's why we only specify PJ_LINUX here so
             * that error is easier to catch.
             */
#	    if defined(PJ_WIN32) && PJ_WIN32 != 0 || \
	       defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
                rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 
				  read_op->flags);
                //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size,
                //              &bytes_read, NULL);
#           elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0)
                bytes_read = read(h->fd, read_op->buf, bytes_read);
                rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error();
#	    elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
                bytes_read = sys_read(h->fd, read_op->buf, bytes_read);
                rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read;
#           else
#               error "Implement read() for this platform!"
#           endif
        }
	
	if (rc != PJ_SUCCESS) {
#	    if defined(PJ_WIN32) && PJ_WIN32 != 0
	    /* On Win32, for UDP, WSAECONNRESET on the receive side 
	     * indicates that previous sending has triggered ICMP Port 
	     * Unreachable message.
	     * But we wouldn't know at this point which one of previous 
	     * key that has triggered the error, since UDP socket can
	     * be shared!
	     * So we'll just ignore it!
	     */

	    if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) {
		//PJ_LOG(4,(THIS_FILE, 
                //          "Ignored ICMP port unreach. on key=%p", h));
	    }
#	    endif

            /* In any case we would report this to caller. */
            bytes_read = -rc;
	}

	/* Unlock; from this point we don't need to hold key's mutex
	 * (unless concurrency is disabled, which in this case we should
	 * hold the mutex while calling the callback) */
	if (h->allow_concurrent) {
	    /* concurrency may be changed while we're in the callback, so
	     * save it to a flag.
	     */
	    has_lock = PJ_FALSE;
	    pj_mutex_unlock(h->mutex);
	} else {
	    has_lock = PJ_TRUE;
	}

	/* Call callback. */
        if (h->cb.on_read_complete && !IS_CLOSING(h)) {
	    (*h->cb.on_read_complete)(h, 
                                      (pj_ioqueue_op_key_t*)read_op,
                                      bytes_read);
        }

	if (has_lock) {
	    pj_mutex_unlock(h->mutex);
	}

    } else {
        /*
         * This is normal; execution may fall here when multiple threads
         * are signalled for the same event, but only one thread eventually
         * able to process the event.
         */
        pj_mutex_unlock(h->mutex);
    }
}
Esempio n. 6
0
PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
{
    pj_fdset_t rfdset, wfdset, xfdset;
    int rc;
    pj_ioqueue_key_t *h;
    
    /* Copy ioqueue's fd_set to local variables. */
    pj_mutex_lock(ioque->mutex);

    rfdset = ioque->rfdset;
    wfdset = ioque->wfdset;
#if PJ_HAS_TCP
    xfdset = ioque->xfdset;
#else
    PJ_FD_ZERO(&xfdset);
#endif

    /* Unlock ioqueue before select(). */
    pj_mutex_unlock(ioque->mutex);

    rc = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout);
    
    if (rc <= 0)
	return rc;

    /* Lock ioqueue again before scanning for signalled sockets. */
    pj_mutex_lock(ioque->mutex);

#if PJ_HAS_TCP
    /* Scan for exception socket */
    h = ioque->hlist.next;
    for ( ; h!=&ioque->hlist; h = h->next) {
	if ((h->op & PJ_IOQUEUE_OP_CONNECT) && PJ_FD_ISSET(h->fd, &xfdset))
	    break;
    }
    if (h != &ioque->hlist) {
	/* 'connect()' should be the only operation. */
	pj_assert((h->op == PJ_IOQUEUE_OP_CONNECT));

	/* Clear operation. */
	h->op &= ~(PJ_IOQUEUE_OP_CONNECT);
	PJ_FD_CLR(h->fd, &ioque->wfdset);
	PJ_FD_CLR(h->fd, &ioque->xfdset);

	/* Unlock I/O queue before calling callback. */
	pj_mutex_unlock(ioque->mutex);

	/* Call callback. */
	(*h->cb.on_connect_complete)(h, -1);
	return 1;
    }
#endif	/* PJ_HAS_TCP */

    /* Scan for writable socket  */
    h = ioque->hlist.next;
    for ( ; h!=&ioque->hlist; h = h->next) {
	if ((PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op)) && PJ_FD_ISSET(h->fd, &wfdset))
	    break;
    }
    if (h != &ioque->hlist) {
	pj_assert(PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op));

#if PJ_HAS_TCP
	if ((h->op & PJ_IOQUEUE_OP_CONNECT)) {
	    /* Completion of connect() operation */
	    pj_ssize_t bytes_transfered;

#if defined(PJ_LINUX)
	    /* from connect(2): 
		* On Linux, use getsockopt to read the SO_ERROR option at
		* level SOL_SOCKET to determine whether connect() completed
		* successfully (if SO_ERROR is zero).
		*/
	    int value;
	    socklen_t vallen = sizeof(value);
	    int rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &value, &vallen);
	    if (rc != 0) {
		/* Argh!! What to do now??? 
		    * Just indicate that the socket is connected. The
		    * application will get error as soon as it tries to use
		    * the socket to send/receive.
		    */
		PJ_PERROR(("ioqueue", "Unable to determine connect() status"));
		bytes_transfered = 0;
	    } else {
		bytes_transfered = value;
	    }
#elif defined(PJ_WIN32)
	    bytes_transfered = 0; /* success */
#else
#  error "Got to check this one!"
#endif

	    /* Clear operation. */
	    h->op &= (~PJ_IOQUEUE_OP_CONNECT);
	    PJ_FD_CLR(h->fd, &ioque->wfdset);
	    PJ_FD_CLR(h->fd, &ioque->xfdset);

	    /* Unlock mutex before calling callback. */
	    pj_mutex_unlock(ioque->mutex);

	    /* Call callback. */
	    (*h->cb.on_connect_complete)(h, bytes_transfered);

	    return 1;

	} else 
#endif /* PJ_HAS_TCP */
	{
	    /* Completion of write(), send(), or sendto() operation. */

	    /* Clear operation. */
	    h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND_TO);
	    PJ_FD_CLR(h->fd, &ioque->wfdset);

	    /* Unlock mutex before calling callback. */
	    pj_mutex_unlock(ioque->mutex);

	    /* Call callback. */
	    /* All data must have been sent? */
	    (*h->cb.on_write_complete)(h, h->wr_buflen);

	    return 1;
	}

	/* Unreached. */
    }

    /* Scan for readable socket. */
    h = ioque->hlist.next;
    for ( ; h!=&ioque->hlist; h = h->next) {
	if ((PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op)) && 
	    PJ_FD_ISSET(h->fd, &rfdset))
	    break;
    }
    if (h != &ioque->hlist) {
	pj_assert(PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op));
#	if PJ_HAS_TCP
	if ((h->op & PJ_IOQUEUE_OP_ACCEPT)) {
	    /* accept() must be the only operation specified on server socket */
	    pj_assert(h->op == PJ_IOQUEUE_OP_ACCEPT);

	    *h->accept_fd = pj_sock_accept(h->fd, h->rmt_addr, h->rmt_addrlen);
	    if (*h->accept_fd == PJ_INVALID_SOCKET) {
		rc = -1;
	    } else if (h->local_addr) {
		rc = pj_sock_getsockname(*h->accept_fd, h->local_addr, h->local_addrlen);
	    } else {
		rc = 0;
	    }

	    h->op &= ~(PJ_IOQUEUE_OP_ACCEPT);
	    PJ_FD_CLR(h->fd, &ioque->rfdset);

	    /* Unlock mutex before calling callback. */
	    pj_mutex_unlock(ioque->mutex);

	    /* Call callback. */
	    (*h->cb.on_accept_complete)(h, rc);

	    return 1;

	} else 
#	endif
	if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) {
	    rc = pj_sock_recvfrom(h->fd, h->rd_buf, h->rd_buflen, 0,
				    h->rmt_addr, h->rmt_addrlen);
	} else {
	    rc = pj_sock_recv(h->fd, h->rd_buf, h->rd_buflen, 0);
	}
	
	if (rc < 0) {
	    pj_status_t sock_err = -1;
#	    if defined(_WIN32)
	    /* On Win32, for UDP, WSAECONNRESET on the receive side 
	     * indicates that previous sending has triggered ICMP Port 
	     * Unreachable message.
	     * But we wouldn't know at this point which one of previous 
	     * key that has triggered the error, since UDP socket can
	     * be shared!
	     * So we'll just ignore it!
	     */

	    sock_err = pj_sock_getlasterror();
	    if (sock_err == PJ_ECONNRESET) {
		pj_mutex_unlock(ioque->mutex);
		PJ_LOG(4,(THIS_FILE, "Received ICMP port unreachable on key=%p (ignored)!", h));
		return 0;
	    } 
#	    endif

	    PJ_LOG(4, (THIS_FILE, "socket recv error on key %p, rc=%d, err=%d", h, rc, sock_err));
	}

	h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV_FROM);
	PJ_FD_CLR(h->fd, &ioque->rfdset);

	/* Unlock mutex before callback. */
	pj_mutex_unlock(ioque->mutex);

	/* Call callback. */
	(*h->cb.on_read_complete)(h, rc);
	return 1;
    }

    /* Shouldn't happen. */
    /* For strange reason on WinXP select() can return 1 while there is no
     * fd_set signaled. */
    /* pj_assert(0); */

    rc = 0;

    pj_mutex_unlock(ioque->mutex);
    return rc;
}