* Support HTTP connection keep-alive.
authorUrban Wallasch <urban.wallasch@freenet.de>
Sat, 26 Oct 2019 00:59:31 +0000 (02:59 +0200)
committerUrban Wallasch <urban.wallasch@freenet.de>
Sat, 26 Oct 2019 00:59:31 +0000 (02:59 +0200)
* Fixed some minor issues.

telehttpd.c

index 845d91a4b7b27c5f08d43446bb118c4c07d00edf..95363c40133b2662a237208b6fe4a33533b18fa2 100644 (file)
@@ -14,6 +14,7 @@
 #include <pthread.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/stat.h>
 
 #include "net.h"
 #include "log.h"
@@ -24,7 +25,6 @@
 
 struct thread_data_t {
     int sock;
-    struct telemetry_state_t tele;
 };
 
 struct {
@@ -122,7 +122,7 @@ static size_t copy_field_val( char *buf, size_t bufsz, const char *needle, const
 
 static inline ssize_t sockwrite( int fd, const void *buf, size_t len ) {
     ssize_t n;
-    n = write_tm( fd, buf, len, 1000 );
+    n = write_tm( fd, buf, len, 50 );
     if ( 0 > n ) {
         EPRINT( "write_tm: %s\n", strerror(errno) );
     }
@@ -132,9 +132,9 @@ static inline ssize_t sockwrite( int fd, const void *buf, size_t len ) {
     return n;
 }
 
-static void respond( struct thread_data_t *td, const char *req ) {
-    int n;
-    char buf[4096]; // Must be large enough to satisfy tele2json()!
+static int respond( int sock, const char *req ) {
+    int n, ret = -1;
+    char hdr[512];
     char origin[256];
     char host[256];
     enum respond_code {
@@ -153,45 +153,62 @@ static void respond( struct thread_data_t *td, const char *req ) {
     copy_field_val( host, sizeof host, "Host: ", req, cfg.host );
 
     switch (code) {
-    case r_json:
+    case r_json: {
+        struct telemetry_state_t tele;
+        char jbuf[4096]; // Must be large enough to satisfy tele2json()!
+        size_t jsz;
+        if ( 0 != tele_cpy( &tele, telemetry ) ) {
+            DPRINT( "No telemetry, serving dummy data\n" );
+        }
+        jsz = tele2json( jbuf, sizeof jbuf, &tele );
+        if ( 0 == jsz )
+            goto SEND_500;
         copy_field_val( origin, sizeof origin, "Origin: ", req, cfg.origin );
-        n = snprintf( buf, sizeof buf,
+        n = snprintf( hdr, sizeof hdr,
                 "HTTP/1.1 200 OK\r\n"
                 "Host: %s\r\n"
                 "Access-Control-Allow-Origin: %s\r\n"
                 "Content-type: text/json\r\n"
-                "Connection: close\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: %lu\r\n"
                 "\r\n",
                 host,
-                origin
+                origin,
+                (unsigned long)jsz
             );
-        if ( 0 < n && (size_t)n < sizeof buf && n == sockwrite( td->sock, buf, n ) )
-            sockwrite( td->sock, buf, tele2json( buf, sizeof buf, &td->tele ) );
+        if ( 0 < n && (size_t)n < sizeof hdr && n == sockwrite( sock, hdr, n ) )
+            ret = sockwrite( sock, jbuf, jsz );
         break;
+    }
     case r_index: {
         int fi;
+        struct stat st;
         fi = open( cfg.idxfile, O_RDONLY );
-        if ( 0 <= fi ){
-            n = snprintf( buf, sizeof buf,
-                    "HTTP/1.1 200 OK\r\n"
-                    "Host: %s\r\n"
-                    "Content-type: text/html\r\n"
-                    "Connection: close\r\n"
-                    "\r\n",
-                    host
-                );
-            if ( 0 < n && (size_t)n < sizeof buf && n == sockwrite( td->sock, buf, n ) ) {
-                sendfile_tm( td->sock, fi, -1, 5000 );
-            }
+        if ( 0 > fi )
+            goto SEND_404;
+        if ( 0 > fstat(fi, &st) ) {
             close( fi );
-        }
-        else
             goto SEND_404;
+        }
+        n = snprintf( hdr, sizeof hdr,
+                "HTTP/1.1 200 OK\r\n"
+                "Host: %s\r\n"
+                "Content-type: text/html\r\n"
+                "Connection: keep-alive\r\n"
+                "Content-Length: %ld\r\n"
+                "\r\n",
+                host,
+                (long)st.st_size
+            );
+        if ( 0 < n && (size_t)n < sizeof hdr && n == sockwrite( sock, hdr, n ) ) {
+            ret = sendfile_tm( sock, fi, st.st_size, 1000 );
+        }
+        close( fi );
         break;
     }
     case r_none:
     SEND_404:
-        n = snprintf( buf, sizeof buf,
+        n = snprintf( hdr, sizeof hdr,
                 "HTTP/1.1 404 Not Found\r\n"
                 "Host: %s\r\n"
                 "Content-type: text/plain\r\n"
@@ -200,11 +217,12 @@ static void respond( struct thread_data_t *td, const char *req ) {
                 "404 Not Found",
                 host
             );
-        if ( 0 < n && (size_t)n < sizeof buf )
-            sockwrite( td->sock, buf, n );
+        if ( 0 < n && (size_t)n < sizeof hdr )
+            sockwrite( sock, hdr, n );
         break;
     default:
-        n = snprintf( buf, sizeof buf,
+    SEND_500:
+        n = snprintf( hdr, sizeof hdr,
                 "HTTP/1.1 500 Internal Server Error\r\n"
                 "Host: %s\r\n"
                 "Content-type: text/plain\r\n"
@@ -213,10 +231,11 @@ static void respond( struct thread_data_t *td, const char *req ) {
                 "500 Internal Server Error",
                 host
             );
-        if ( 0 < n && (size_t)n < sizeof buf )
-            sockwrite( td->sock, buf, n );
+        if ( 0 < n && (size_t)n < sizeof hdr )
+            sockwrite( sock, hdr, n );
         break;
     }
+    return ret;
 }
 
 static int rcv_request(int sock, char *buf, size_t size, int timeout) {
@@ -230,7 +249,8 @@ static int rcv_request(int sock, char *buf, size_t size, int timeout) {
         if ( 0 > bread ) {
             if ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
                 continue;
-            EPRINT( "read: %s\n", strerror(errno) );
+            if ( errno != ETIME )
+                EPRINT( "read: %s\n", strerror(errno) );
             break;
         }
         else if ( 0 == bread ) {
@@ -238,27 +258,28 @@ static int rcv_request(int sock, char *buf, size_t size, int timeout) {
             break;
         }
         total += bread;
-        buf[total] = 0;
-        if ( strstr( buf, "\r\n\r\n" ) ) {
-            res = 0;
-            break;
-        }
-        timeout = 200;
+        timeout = 5;
     }
+    buf[total] = 0;
+    if ( strstr( buf, "\r\n\r\n" ) )
+        res = 0;
     return res;
 }
 
 static void *handle_conn( void *p ) {
     struct thread_data_t *td = p;
-    char buf[2048];
+    char req[2048];
 
+    DPRINT("-- new thread --\n");
     DPRINT( "sock: %d\n", td->sock );
-    if ( 0 == rcv_request(td->sock, buf, sizeof buf, 1000) ) {
-        DPRINT( "request:\n%s", buf);
-        respond( td, buf );
+    while ( 0 == rcv_request(td->sock, req, sizeof req, 5000) ) {
+        DPRINT( "request:\n%s", req);
+        if ( 0 > respond( td->sock, req ) )
+            break;
     }
     net_close( td->sock );
     free( td );
+    DPRINT("-- thread terminated --\n");
     return NULL;
 }
 
@@ -286,9 +307,6 @@ static int serve_http(void) {
             if ( NULL != (td = malloc( sizeof *td )) ) {
                 int e;
                 td->sock = as;
-                if ( 0 != tele_cpy( &td->tele, telemetry ) ) {
-                    DPRINT( "No telemetry, serving dummy data\n" );
-                }
                 pthread_attr_init( &attr );
                 pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
                 if ( 0 != ( e = pthread_create( &tid, &attr, handle_conn, td ) ) ) {