From 68f70d0de866598b56135c696c47ffd10e582c6f Mon Sep 17 00:00:00 2001
From: Bruce Toll <btoll@dhsus.com>
Date: Wed, 8 May 2019 08:06:41 -0400
Subject: [PATCH 2/3] Process last payload fragment in ws13 continuation

Fix issue where the last fragment of a ws13 payload gets dropped
if additional data is available in the call to
parseWebSocketMessage. See block comments for details.

Note: There may be a cleaner solution to this issue, but the commit is
designed to localize changes to the failure case, while minimizing
impact on existing code.
---
 src/http/RequestParser.C | 64 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 61 insertions(+), 3 deletions(-)

diff --git a/src/http/RequestParser.C b/src/http/RequestParser.C
index 09fe423a..f20d3077 100644
--- a/src/http/RequestParser.C
+++ b/src/http/RequestParser.C
@@ -710,14 +710,72 @@ RequestParser::parseWebSocketMessage(Request& req, ReplyPtr reply,
 
 	LOG_DEBUG("ws: reading payload, remains = " << remainder_);
 
+        /*
+         * At this point, care must be taken that data bracketed by dataBegin and dataEnd is consumed
+         */
 	if (remainder_ == 0) {
+          /*
+           * If this frame is marked final (the common case), then the state is set to
+           * Request::Complete which causes the loop to exit and the payload fragment
+           * will get consumed by the code which follows the loop.
+           */
 	  if (wsFrameType_ & 0x80)
 	    state = Request::Complete;
+          /*
+           * If this frame is not final, then we expect a continuation frame.
+           * Since we are still in status Request::Partial, the loop will not exit UNLESS
+           * (begin == end), indicating that we have processed all data that was available
+           * at the call to parseWebSocketMessage.
+           *
+           * If more data is available, e.g. the continuation frame is already partially
+           * or fully available, then (begin < end). In that case, this payload fragment
+           * will be lost unless it is consumed here (or code is restructured...)
+           *
+           * Care must be taken to resume the loop correctly after consuming this
+           * payload so that any available data is processed. Thus, unlike in the similar
+           * code that follows the loop, it is not possible to return unless reporting an error.
+           * Likewise, an appendBlock is not added for this case.
+           *
+           * NOTE: We set dataBegin = dataEnd for the case when (begin == end)
+           * so that when we exit the loop, we do not process the same payload fragment
+           * a second time....
+           */
+          else {
+            LOG_DEBUG("ws: (last payload, not complete): " << (dataEnd - dataBegin) << "," << state);
+            if (dataBegin < dataEnd) {
+              char* beg = &*dataBegin;
+              char* end = &*dataEnd;
+#ifdef WTHTTP_WITH_ZLIB
+              if (frameCompressed_) {
+                Reply::ws_opcode opcode = (Reply::ws_opcode)(wsFrameType_ & 0x0F);
+                bool hasMore = false;
+                char buffer[16 * 1024];
+                do {
+                  read_ = 0;
+                  bool ret1 =  inflate(reinterpret_cast<unsigned char*>(&*beg),
+                          end - beg, reinterpret_cast<unsigned char*>(buffer), hasMore);
+
+                  if(!ret1) return Request::Error;
+
+                  reply->consumeWebSocketMessage(opcode, &buffer[0], &buffer[read_], Request::Partial);
+                } while (hasMore);
+                dataBegin = dataEnd;
+              }
+#endif
 
-	  wsState_ = ws13_frame_start;
-	}
+              // handle uncompressed frame
+              if (!frameCompressed_) {
+                Reply::ws_opcode opcode = (Reply::ws_opcode)(wsFrameType_ & 0x0F);
+                reply->consumeWebSocketMessage(opcode, beg, end, state);
+                dataBegin = dataEnd;
+              }
+            }
+          }
 
-	break;
+          wsState_ = ws13_frame_start;
+        }
+
+        break;
       }
     default:
       assert(false);
-- 
2.18.1

