]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow for multiple packets in one read
authorAlan T. DeKok <aland@freeradius.org>
Tue, 11 Jul 2017 18:08:01 +0000 (14:08 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 11 Jul 2017 18:08:32 +0000 (14:08 -0400)
src/lib/io/io.h
src/lib/io/network.c

index b584602d22b2130d6acf8ca270df8570c4121943..924778e9e32b224ee51e4dd20879c89935401b52 100644 (file)
@@ -151,28 +151,18 @@ typedef size_t (*fr_io_nak_t)(void const *instance, uint8_t *const packet, size_
 
 /** 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.
index ec961ba0c13814049832e3f7de1afcd42c997ed4..4e21871fcbd2456a2e8bd7b9e4f8f1ebfb745803 100644 (file)
@@ -312,7 +312,7 @@ static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int f
        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);
@@ -343,6 +343,7 @@ static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int f
         *      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) {
@@ -387,12 +388,33 @@ static void fr_network_read(UNUSED fr_event_list_t *el, int sockfd, UNUSED int f
        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