/** Read from a socket.
*
- * If the socket is a datagram socket, then the function can read or
- * write directly into the buffer. Stream sockets are a bit more complicated.
- *
- * A stream reader can read data into the buffer, and be guaranteed
- * that the data will not change in between subsequent calls to the
- * read routine.
- *
- * A stream writer MUST be prepared for the caller to delete the data
- * immediately after calling the write routine. This means that if
- * the socket is not ready, the writer MUST copy the data to an
- * internal buffer, usually in instance. It MUST then have a
- * write callback on the socket, which is called when the socket is
- * ready for writing. That callback can then write the internal
- * buffer to the socket.
- *
- * i.e. this write() function is a way for the network thread to
- * write packets to the transport context. The data may or may not
- * go out to the network right away.
- *
- * If the writer returns LESS THAN buffer_len, that's a special case
- * saying "I took saved the data, but the socket wasn't ready, so you
- * need to call me again at a later point".
+ * The network side guarantees that the read routine can leave partial
+ * data in the buffer. That data will be there on the next call to
+ * read. However, the data MAY have moved, so please do not keep a
+ * pointer to 'buffer' around.
+ *
+ * datagram sockets should always set '*leftover = 0'.
+ *
+ * stream sockets can read one packet, and set '*leftover' to how many
+ * bytes are left in the buffer. The read routine will be called
+ * again, with a (possibly new) buffer, but with 'leftover' bytes left
+ * in the buffer. The value in 'leftover'' will be the same as from
+ * the previous call, so the reader does not need to track it.
*
* @param[in] instance the context for this function
* @param[out] packet_ctx Where to write a newly allocated packet_ctx struct containing request specific data.
fr_network_socket_t *s = ctx;
fr_network_t *nr = talloc_parent(s);
ssize_t data_size;
- fr_channel_data_t *cd;
+ fr_channel_data_t *cd, *next;
fr_time_t *recv_time;
rad_assert(s->listen->app_io->fd(s->listen->app_io_instance) == sockfd);
* network side knows that it needs to close the
* connection.
*/
+next_message:
data_size = s->listen->app_io->read(s->listen->app_io_instance, &cd->packet_ctx, &recv_time,
cd->m.data, cd->m.rb_size, &s->leftover);
if (data_size == 0) {
cd->listen = s->listen;
cd->request.recv_time = recv_time;
- (void) fr_message_alloc(s->ms, &cd->m, data_size);
+ if (!s->leftover) {
+ (void) fr_message_alloc(s->ms, &cd->m, data_size);
+ next = NULL;
+ } else {
+ /*
+ * There are leftover bytes in the buffer, feed
+ * them to the next incantation of the module.
+ */
+ next = (fr_channel_data_t *) fr_message_alloc_reserve(s->ms, &cd->m, data_size, s->listen->default_message_size);
+ if (!next) {
+ fr_log(nr->log, L_ERR, "Failed reserving partial packet.");
+ // @todo - probably close the socket...
+ }
+ }
if (!fr_network_send_request(nr, cd)) {
fr_log(nr->log, L_ERR, "Failed sending packet to worker");
fr_message_done(&cd->m);
}
+
+ /*
+ * If there is a next message, go read it from the buffer.
+ */
+ if (next) {
+ cd = next;
+ goto next_message;
+ }
}
#if 0