$ git clone https://ion.nu/git/webchat
commit 2eb39c0f8d5d4d116fee2e873c91739dfdcbcb9c
Author: Alicia <...>
Date:   Wed Jan 6 20:49:27 2016 +0100

    Don't try to read whole packets at a time (especially for big packets like video data)

diff --git a/libwebsocket/websock.c b/libwebsocket/websock.c
index 28055f1..4222756 100644
--- a/libwebsocket/websock.c
+++ b/libwebsocket/websock.c
@@ -187,6 +187,7 @@ void websock_write(void* fd, void* buf, unsigned int len, unsigned char opcode,
 
 char websock_readhead(void* fd, struct websock_head* head_info, char tls)
 {
+  head_info->received=0;
   unsigned char head[2];
   if(aread(fd, head, sizeof(unsigned char)*2)!=sizeof(unsigned char)*2){return 0;}
   if(!(head[0]&0x80))
@@ -221,14 +222,17 @@ printf("Non-FIN length: %llu\n", head_info->length);
   return 1;
 }
 
-void websock_readcontent(void* fd, void* buf_, struct websock_head* head, char tls)
+char websock_readcontent(void* fd, void* buf_, struct websock_head* head, char tls)
 {
+  if(head->length==0){return 1;} // Nothing to read, success by default
   unsigned char* buf=buf_;
-  unsigned int pos=0;
-  int r;
-  while(pos<head->length && (r=aread(fd, buf+pos, head->length-pos))>0)
+  int r=aread(fd, buf+head->received, head->length-head->received);
+  if(r>0)
   {
-    pos+=r;
+    head->received+=r;
+    if(head->received<head->length){return 0;} // Not done yet
+  }else{
+    return 0;
   }
   if(head->masked)
   {
@@ -238,4 +242,5 @@ void websock_readcontent(void* fd, void* buf_, struct websock_head* head, char t
       buf[i]^=head->mask[i%4];
     }
   }
+  return 1; // Got whole packet
 }
diff --git a/libwebsocket/websock.h b/libwebsocket/websock.h
index eac98e0..37f0749 100644
--- a/libwebsocket/websock.h
+++ b/libwebsocket/websock.h
@@ -14,6 +14,8 @@
     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
+#ifndef WEBSOCK_H
+#define WEBSOCK_H
 #define WEBSOCK_CONT   0x0
 #define WEBSOCK_TEXT   0x1
 #define WEBSOCK_BINARY 0x2
@@ -27,6 +29,7 @@ struct websock_head
   char masked;
   unsigned char mask[4];
   unsigned long long int length;
+  unsigned long long int received;
 };
 
 // NOTE: The callback decides whether or not to accept the request, and if so with which protocol, return 0/NULL to reject, nonsockcb is an option to not treat the session as a websocket session
@@ -34,4 +37,5 @@ extern char websock_handshake_server(void* fd, const char*(*cb)(const char* path
 // TODO: implement websock_handshake_client()
 extern void websock_write(void* fd, void* buf, unsigned int len, unsigned char opcode, char tls);
 extern char websock_readhead(void* fd, struct websock_head* head_info, char tls);
-extern void websock_readcontent(void* fd, void* buf_, struct websock_head* head, char tls);
+extern char websock_readcontent(void* fd, void* buf_, struct websock_head* head, char tls);
+#endif
diff --git a/src/chat.c b/src/chat.c
index 8a0ee39..667621b 100644
--- a/src/chat.c
+++ b/src/chat.c
@@ -105,7 +105,7 @@ int main()
   fds[0].revents=0;
   unsigned int i;
   unsigned int j;
-  struct websock_head head;
+  unsigned char* data=0;
   while(1)
   {
     poll(fds, usercount+1, -1);
@@ -221,25 +221,33 @@ printf("New connection\n");
         }
         continue;
       }
-      char status;
-      if(!(status=websock_readhead(users[i]->socket, &head, use_tls)) || head.opcode==WEBSOCK_CLOSE) // Disconnect
+      if(!users[i]->packet.data)
       {
-        if(status){websock_write(users[i]->socket, 0, 0, WEBSOCK_CLOSE, use_tls);}
-printf("User %u disconnected\n", i);
-        if(users[i]->channel)
+        char status;
+        if(!(status=websock_readhead(users[i]->socket, &users[i]->packet.head, use_tls)) || users[i]->packet.head.opcode==WEBSOCK_CLOSE) // Disconnect
         {
-          char quitmsg[strlen("quit:0")+strlen(users[i]->nickname)];
-          sprintf(quitmsg, "quit:%s", users[i]->nickname);
-          channel_write(users[i]->channel, quitmsg, strlen(quitmsg), WEBSOCK_TEXT, users[i]);
+          if(status){websock_write(users[i]->socket, 0, 0, WEBSOCK_CLOSE, use_tls);}
+printf("User %u disconnected\n", i);
+          if(users[i]->channel)
+          {
+            char quitmsg[strlen("quit:0")+strlen(users[i]->nickname)];
+            sprintf(quitmsg, "quit:%s", users[i]->nickname);
+            channel_write(users[i]->channel, quitmsg, strlen(quitmsg), WEBSOCK_TEXT, users[i]);
+          }
+          freeuser(users[i]); // also takes care of removing user from channel
+          --usercount;
+          memmove(&users[i], &users[i+1], sizeof(struct user*)*(usercount-i));
+          memmove(&fds[i+1], &fds[i+2], sizeof(struct pollfd)*(usercount-i));
+          break;
         }
-        freeuser(users[i]); // also takes care of removing user from channel
-        --usercount;
-        memmove(&users[i], &users[i+1], sizeof(struct user*)*(usercount-i));
-        memmove(&fds[i+1], &fds[i+2], sizeof(struct pollfd)*(usercount-i));
-        break;
+        users[i]->packet.data=malloc(users[i]->packet.head.length+1);
+        continue;
       }
-      unsigned char data[head.length+1];
-      websock_readcontent(users[i]->socket, data, &head, use_tls);
+      if(!websock_readcontent(users[i]->socket, users[i]->packet.data, &users[i]->packet.head, use_tls)){continue;} // Handle other sessions while waiting for the rest of the packet
+      #define head users[i]->packet.head
+      free(data);
+      data=users[i]->packet.data;
+      users[i]->packet.data=0;
       data[head.length]=0;
       // Handle commands
       if(head.opcode==WEBSOCK_TEXT)
@@ -659,6 +667,7 @@ printf("Got video packet\n");
       {
         websock_write(users[i]->socket, data, head.length, WEBSOCK_PONG, use_tls);
       }
+      else{printf("Got unknown data from user %u (length: %llu, opcode %u)\n", i, head.length, head.opcode);}
     }
   }
   return 0;
diff --git a/src/users.c b/src/users.c
index 499a935..c730321 100644
--- a/src/users.c
+++ b/src/users.c
@@ -71,6 +71,7 @@ struct user* adduser(int socket, struct sockaddr* addr, socklen_t addrlen)
   users[usercount-1]->httpcam=0;
   users[usercount-1]->account=0;
   users[usercount-1]->modprivileges=0;
+  users[usercount-1]->packet.data=0;
   unsigned int i=0;
   unsigned int j=0;
   while(j<usercount-1) // Find unique startnick
@@ -124,6 +125,7 @@ void freeuser(struct user* user)
 #endif
   close(user->rawsocket);
   free(user->account);
+  free(user->packet.data);
   free(user);
 }
 
diff --git a/src/users.h b/src/users.h
index d6f8446..17b6a0c 100644
--- a/src/users.h
+++ b/src/users.h
@@ -16,6 +16,7 @@
 */
 #include <sys/socket.h>
 #include <gnutls/gnutls.h>
+#include "../libwebsocket/websock.h"
 #define use_tls 1
 struct channel;
 enum bincmd_type
@@ -55,6 +56,11 @@ struct user
   char* account;
   int modprivileges;
   struct sockaddr sockaddr;
+  struct
+  {
+    struct websock_head head;
+    char* data;
+  } packet;
 };
 extern unsigned int usercount;
 extern struct user** users;