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; }
/** 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); }