[Libvir] Patch to make reading of HTTP response more efficient

Daniel P. Berrange berrange at redhat.com
Thu Mar 30 14:51:06 UTC 2006


When processing the header of HTTP responses from Xend, libvirt currently
reads data a single byte at a time. Since its using lowlevel UNIX read()
function there is no buffering, which results in a large number of system
calls. The attached patch re-factors the code so that it reads data in 
4k chunks whenever possible. Since most rsponses are < 4k in size processing
the response now only takes two read() syscalls, rather than several hundred.

Of course most (90%) of the CPU overhead is still on the Xend/XenStored 
end of the channel, but that's out of our control until the XML-RPC API
comes along.

Regards,
Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
-------------- next part --------------
# HG changeset patch
# User "Daniel P. Berrange <berrange at redhat.com>"
# Node ID a8770bad7f151fe35be5db12e49efcc75b484061
# Parent  04a40926a63da997d35ef7d0de8006c4b81ba138
Switch to more efficient reading of HTTP responses

diff -r 04a40926a63d -r a8770bad7f15 src/xend_internal.c
--- a/src/xend_internal.c	Wed Mar 29 08:53:55 2006 -0500
+++ b/src/xend_internal.c	Thu Mar 30 09:45:37 2006 -0500
@@ -287,42 +287,6 @@ swrites(int fd, const char *string)
     return swrite(fd, string, strlen(string));
 }
 
-/**
- * sreads:
- * @fd:  the file descriptor
- * @buffer: the I/O buffer
- * @n_buffer: the size of the I/O buffer
- *
- * Internal routine to do a synchronous read of a line
- *
- * Returns the number of bytes read, or -1 in case of error
- */
-static ssize_t
-sreads(int fd, char *buffer, size_t n_buffer)
-{
-    size_t offset;
-
-    if (n_buffer < 1)
-        return (-1);
-
-    for (offset = 0; offset < (n_buffer - 1); offset++) {
-        ssize_t ret;
-
-        ret = sread(fd, buffer + offset, 1);
-        if (ret == 0)
-            break;
-        else if (ret == -1)
-            return ret;
-
-        if (buffer[offset] == '\n') {
-            offset++;
-            break;
-        }
-    }
-    buffer[offset] = 0;
-
-    return offset;
-}
 
 static int
 istartswith(const char *haystack, const char *needle)
@@ -344,32 +308,86 @@ xend_req(int fd, char *content, size_t n
 xend_req(int fd, char *content, size_t n_content)
 {
     char buffer[4096];
+    int nbuf = -1;
     int content_length = -1;
     int retcode = 0;
 
-
-    while (sreads(fd, buffer, sizeof(buffer)) > 0) {
-        if (strcmp(buffer, "\r\n") == 0)
-            break;
+    /* Fill buffer with as much as possible to get 
+       process going */
+    nbuf = sread(fd, buffer, sizeof(buffer));
+
+    /* Extract lines from the buffer, until the 
+       end of header is found */
+    while (nbuf > -1) {
+      /* Seach for offset of first \r\n pair */
+      int i, offset = -1;
+      //printf("Got [%s]\n\n\n", buffer);
+      for (i = 0 ; i < (nbuf-1) ; i++) {
+        if (buffer[i] == '\r' &&
+            buffer[i+1] == '\n') {
+          offset = i;
+          break;
+        }
+      }
+      
+      if (offset == -1) { /* No newline found, so try to fill more data */
+
+        if (nbuf == sizeof(buffer)) {
+          /* Already have 4096 bytes of data & no newline, 
+             get the hell out of this game */
+          break;
+        }
+        
+        /* Fill remainder of buffer with more data */
+        int extra = sread(fd, buffer+nbuf, sizeof(buffer)-nbuf);
+        if (extra < 1) {
+          /* Couldn't get more, so quit trying */
+          break;
+        }
+        nbuf += extra;
+      } else if (!offset) { /* Immediate newline, indicates end of header */
+        /* Overwrite the \r\n pair */
+        offset += 2;
+        nbuf -= offset;
+        memmove(buffer, buffer+offset, nbuf);
+        break;
+      } else { /* We have a single line */
+        buffer[offset] = '\0';
 
         if (istartswith(buffer, "Content-Length: "))
             content_length = atoi(buffer + 16);
         else if (istartswith(buffer, "HTTP/1.1 "))
             retcode = atoi(buffer + 9);
-    }
-
-    if (content_length > -1) {
-        ssize_t ret;
-
+        
+        /* Now move buffer, overwriting first line, to make room for
+           more data on next iteration of loop (if needed) */
+        offset += 2;
+        nbuf -= offset;
+        memmove(buffer, buffer+offset, nbuf);
+      }
+    }
+
+    if (content_length > -1) { /* Read the header, now get body */
         if ((unsigned int) content_length > (n_content + 1))
             content_length = n_content - 1;
-
-        ret = sread(fd, content, content_length);
-        if (ret < 0)
+        
+        /* Copy across any data left in buffer after
+           reading header */
+        if (nbuf > content_length) {
+          nbuf = content_length;
+        }
+        memmove(content, buffer, nbuf);
+
+        if (nbuf < content_length) {    /* Still need more data for body */
+          size_t ret = sread(fd, content+nbuf, content_length-nbuf);
+          if (ret < 0)
             return -1;
 
-        content[ret] = 0;
-    } else {
+          content[nbuf+ret+1] = '\0';
+        } else {
+          content[nbuf+1] = '\0';
+        }
+    } else { /* Unable to complete reading header */
         content[0] = 0;
     }
 


More information about the libvir-list mailing list