]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow for partial writes to sockets.
authorAlan T. DeKok <aland@freeradius.org>
Mon, 4 Jun 2018 01:21:50 +0000 (21:21 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 4 Jun 2018 01:21:50 +0000 (21:21 -0400)
When that happens, save how much we've written, and remember
to keep doing the writes later.

src/lib/io/network.c

index b955749b38742fb088667a0a725be519523f6f47..2abd0971f5b9290e277e83a50513a2128f39a3e9 100644 (file)
@@ -80,6 +80,7 @@ typedef struct fr_network_socket_t {
        fr_message_set_t        *ms;                    //!< message buffers for this socket.
        fr_channel_data_t       *cd;                    //!< cached in case of allocation & read error
        size_t                  leftover;               //!< leftover data from a previous read
+       size_t                  written;                //!< however much we did in a partial write
 
        fr_channel_data_t       *pending;               //!< the currently pending partial packet
        fr_heap_t               *waiting;               //!< packets waiting to be written
@@ -588,6 +589,12 @@ static void fr_network_write(UNUSED fr_event_list_t *el, UNUSED int sockfd, UNUS
 
        rad_assert(s->pending != NULL);
 
+       /*
+        *      @todo - this code is much the same as in
+        *      fr_network_post_event().  Fix it so we only have one
+        *      copy!
+        */
+
        /*
         *      Start with the currently pending message, and then
         *      work through the priority heap.
@@ -609,6 +616,23 @@ static void fr_network_write(UNUSED fr_event_list_t *el, UNUSED int sockfd, UNUS
                         *      pending message to the current one.
                         */
                        if (errno == EWOULDBLOCK) {
+                               s->pending = 0;
+
+                       save_pending:
+                               /*
+                                *      s->pending MUST be localized.
+                                *      Other messages in the list
+                                *      MUST NOT have been localized
+                                *      yet.
+                                */
+                               if (cd->m.status != FR_MESSAGE_LOCALIZED) {
+                                       fr_message_t *lm;
+
+                                       MEM(lm = fr_message_localize(s, &cd->m, sizeof(*cd)));
+                                       fr_message_done(&cd->m);
+
+                                       cd = (fr_channel_data_t *) lm;
+                               }
                                s->pending = cd;
                                return;
                        }
@@ -631,13 +655,15 @@ static void fr_network_write(UNUSED fr_event_list_t *el, UNUSED int sockfd, UNUS
                }
 
                /*
-                *
-                We MUST have written all of the data.  It is
-                *      up to the app_io->write() function to track
-                *      any partially written data.
+                *      If we've done a partial write, localize the message and continue.
                 */
-               rad_assert((size_t) rcode == cd->m.data_size);
+               if ((rcode > 0) && ((size_t) rcode < cd->m.data_size)) {
+                       s->written = rcode;
+                       goto save_pending;
+               }
+
                s->pending = NULL;
+               s->written = 0;
 
                /*
                 *      Reset for the next message.
@@ -1202,6 +1228,9 @@ static void fr_network_post_event(UNUSED fr_event_list_t *el, UNUSED struct time
                                              cd->reply.request_time,
                                              cd->m.data, cd->m.data_size, 0);
                if (rcode < 0) {
+                       s->pending = 0;
+
+               save_pending:
                        if (errno == EWOULDBLOCK) {
                                if (fr_event_fd_insert(nr, nr->el, s->fd,
                                                       fr_network_read,
@@ -1245,14 +1274,18 @@ static void fr_network_post_event(UNUSED fr_event_list_t *el, UNUSED struct time
                }
 
                /*
-                *      We MUST have written all of the data.  It is
-                *      up to the app_io->write() function to track
-                *      any partially written data.
+                *      If there's a partial write, save the write
+                *      callback for later.
                 */
-               rad_assert(!rcode || (size_t) rcode == cd->m.data_size);
+               if ((rcode > 0) && ((size_t) rcode < cd->m.data_size)) {
+                       s->written = rcode;
+                       goto save_pending;
+               }
 
                DEBUG3("Sending reply to socket %d", s->fd);
                fr_message_done(&cd->m);
+               s->pending = NULL;
+               s->written = 0;
 
                /*
                 *      As a special case, allow write() to return