Example #1
0
static ssize_t net_chanel_queue_read_from_net(net_chanel_t bc, int fd) {
    struct net_chanel_queue * chanel;
    ssize_t recv_size;

    assert(bc);
    assert(bc->m_type == &s_net_chanel_type_queue);

    chanel = (struct net_chanel_queue *)bc;

    recv_size = cpe_recv(fd, chanel->m_buf + chanel->m_size, chanel->m_capacity - chanel->m_size, 0);
    if (recv_size < 0) return -1;

    chanel->m_size += recv_size;
    chanel->m_state = net_chanel_queue_calac_state(chanel);

    return recv_size;
}
Example #2
0
/** Process incoming data, passing a complete message to a specified
 *  consumer.
 *
 * @param fixed_len       Specify the minimum header size containg enough
 *                        information for \p get_msg_size_cb.
 * @param get_msg_size_cb Determine the size of the full msg by looking at the
 *                        header.
 * @param msg_handler_cb  Message consumer.
 *
 * To be generic, perform a two-pass read (this notion is partly inspired by
 * Wireshark's tcp_dissect_pdus parameters):
 * - first pass: read the minimum necessary (\p fixed_len) to figure out the
 *   full packet size
 * - second pass: read the full packet, and pass it to the specified consumer
 *   (\p msg_handler_cb)
 *
 * @remark There is no queue; once a packet is read, it is passed to its
 * consumer. Memory for the iobuf is allocated here, from a new pool, and
 * must be deallocated by the consumer (another more advanced option, useful
 * in case the consumer wants to recycle the iobuf, is having the consumer
 * set iobuf->destroy, plus a cpe_send_enqueue(). Then cpe_sender() will
 * deallocate the buffer on behalf of the consumer.
 *
 * @remark There is a general resync problem with protocols on top of a
 * stream-oriented transport like TCP: if I don't find what I expected, how
 * do I known where is the start of the next PDU ? For example if the version
 * is not what I expected, then I have no guarantee at all that the header
 * layout is the same, as so how can I calculate the length ? So what we do
 * is to drop the connection.
 */
apr_status_t
cpe_receiver(cpe_network_ctx *nctx, apr_pollfd_t *pfd, int iobufsize,
    apr_pool_t *pool, int fixed_len, cpe_get_msg_size_t get_msg_size_cb,
    cpe_handle_msg_t msg_handler_cb)
{
    cpe_io_buf  *iobuf;
    apr_size_t   howmany;
    apr_status_t rv;

    cpe_log(CPE_DEB, "%s", "enter");
    if (get_msg_size_cb == NULL || msg_handler_cb == NULL) {
        cpe_log(CPE_ERR, "%s", "NULL callbacks");
        return APR_EINVAL;
    }

    iobuf = nctx->nc_iobuf;
    if (iobuf == NULL) {
        CHECK(cpe_iobuf_create(&iobuf, iobufsize, pool));
        nctx->nc_iobuf = iobuf;
    }

    /* first-pass read
     */
    if (iobuf->buf_len < fixed_len) {
        howmany = fixed_len - iobuf->buf_len;
        rv = cpe_recv(pfd->desc.s, iobuf, &howmany);
        nctx->nc_total_received += howmany;
        if (rv != APR_SUCCESS) {
            if (APR_STATUS_IS_EOF(rv)) {
                cpe_log(CPE_ERR, "%s", "remote end closed connection");
            } else {
                cpe_log(CPE_ERR, "%s", "cpe_recv() failed in first-pass");
            }
            cpe_iobuf_destroy(&iobuf, nctx);
            cpe_event_remove(pfd->client_data);
            pfd->client_data = NULL;
            cpe_socket_close(pfd->desc.s);
            cpe_resource_destroy_users(pfd->desc.s);
            pfd->desc.s = NULL;
            return rv;
        }
        if (iobuf->buf_len < fixed_len) {
            /* we haven't read enough; retry next time */
            cpe_log(CPE_DEB, "iobuf %p first-pass: buf_len %d, needed %d",
                iobuf, iobuf->buf_len, fixed_len);
            return APR_SUCCESS;
        }
        rv = get_msg_size_cb(iobuf, &nctx->nc_msg_size);
        if (rv != APR_SUCCESS) {
            cpe_log(CPE_ERR, "%s",
                "error in obtaining msg size, dropping connection");

            cpe_iobuf_destroy(&iobuf, nctx);
            cpe_event_remove(pfd->client_data);
            pfd->client_data = NULL;
            cpe_socket_close(pfd->desc.s);
            cpe_resource_destroy_users(pfd->desc.s);
            pfd->desc.s = NULL;
            return rv;
        }
        /* we are ready for the second-pass */
    }

    /* second-pass read
     */
    howmany = nctx->nc_msg_size - iobuf->buf_len;
    if (howmany > 0) {
        rv = cpe_recv(pfd->desc.s, iobuf, &howmany);
        nctx->nc_total_received += howmany;
        /* XXX an error could be a socket close on the other side; should
         * handle better
         */
        if (rv != APR_SUCCESS) {
            if (iobuf->buf_len < nctx->nc_msg_size) {
            cpe_log(CPE_ERR, "iobuf %p cpe_recv() failed in second-pass and "
                "not enough data to call consumer", iobuf);

                cpe_iobuf_destroy(&iobuf, nctx);
                cpe_event_remove(pfd->client_data);
                pfd->client_data = NULL;
                cpe_socket_close(pfd->desc.s);
                cpe_resource_destroy_users(pfd->desc.s);
                pfd->desc.s = NULL;
                return rv;
            } else {
                cpe_log(CPE_ERR, "iobuf %p cpe_recv() failed in second-pass but"
                    "enough data to call consumer", iobuf);
            }
        }
        if (iobuf->buf_len < nctx->nc_msg_size) {
            /* we haven't read enough; retry next time */
            cpe_log(CPE_DEB, "iobuf %p second-pass: buf_len %d, needed %d",
                iobuf, iobuf->buf_len, nctx->nc_msg_size);
            return APR_SUCCESS;
        }
    }
    cpe_log(CPE_DEB, "invoking callback %p on iobuf %p", msg_handler_cb, iobuf);
    return msg_handler_cb(iobuf, nctx);
}