Commit 411412f90296ed596340fe3928d6b9a46934f39f

Authored by Wiebe Cazemier
1 parent 67df67f0

Fix length bug in sending websocket frames

One part is a fix, where the frame was advertised to be bigger than it
was.

The other change is making it possible to send chunks larger than the
initial buffer size, by resizing the buffer (and resetting it later).
client.cpp
@@ -315,6 +315,7 @@ std::string Client::getKeepAliveInfoString() const @@ -315,6 +315,7 @@ std::string Client::getKeepAliveInfoString() const
315 void Client::resetBuffersIfEligible() 315 void Client::resetBuffersIfEligible()
316 { 316 {
317 readbuf.resetSizeIfEligable(initialBufferSize); 317 readbuf.resetSizeIfEligable(initialBufferSize);
  318 + ioWrapper.resetBuffersIfEligible();
318 319
319 // Write buffers are written to from other threads, and this resetting takes place from the Client's own thread, so we need to lock. 320 // Write buffers are written to from other threads, and this resetting takes place from the Client's own thread, so we need to lock.
320 std::lock_guard<std::mutex> locker(writeBufMutex); 321 std::lock_guard<std::mutex> locker(writeBufMutex);
iowrapper.cpp
@@ -579,27 +579,28 @@ ssize_t IoWrapper::websocketBytesToReadBuffer(void *buf, const size_t nbytes, Io @@ -579,27 +579,28 @@ ssize_t IoWrapper::websocketBytesToReadBuffer(void *buf, const size_t nbytes, Io
579 * @param nbytes. The amount of bytes. Can be 0, for just an empty websocket frame. 579 * @param nbytes. The amount of bytes. Can be 0, for just an empty websocket frame.
580 * @return 580 * @return
581 */ 581 */
582 -ssize_t IoWrapper::writeAsMuchOfBufAsWebsocketFrame(const void *buf, size_t nbytes, WebsocketOpcode opcode) 582 +ssize_t IoWrapper::writeAsMuchOfBufAsWebsocketFrame(const void *buf, const size_t nbytes, WebsocketOpcode opcode)
583 { 583 {
584 // We do allow pong frames to generate a zero payload packet, but for binary, that's not necessary. 584 // We do allow pong frames to generate a zero payload packet, but for binary, that's not necessary.
585 if (nbytes == 0 && opcode == WebsocketOpcode::Binary) 585 if (nbytes == 0 && opcode == WebsocketOpcode::Binary)
586 return 0; 586 return 0;
587 587
588 - ssize_t nBytesReal = 0; 588 + websocketWriteRemainder.ensureFreeSpace(nbytes + WEBSOCKET_MAX_SENDING_HEADER_SIZE, 131072);
  589 + const ssize_t nBytesReal = std::min<size_t>(nbytes, websocketWriteRemainder.freeSpace() - WEBSOCKET_MAX_SENDING_HEADER_SIZE);
589 590
590 // We normally wrap each write in a frame, but if a previous one didn't fit in the system's write buffers, we're still working on it. 591 // We normally wrap each write in a frame, but if a previous one didn't fit in the system's write buffers, we're still working on it.
591 if (websocketWriteRemainder.freeSpace() > WEBSOCKET_MAX_SENDING_HEADER_SIZE) 592 if (websocketWriteRemainder.freeSpace() > WEBSOCKET_MAX_SENDING_HEADER_SIZE)
592 { 593 {
593 uint8_t extended_payload_length_num_bytes = 0; 594 uint8_t extended_payload_length_num_bytes = 0;
594 uint8_t payload_length = 0; 595 uint8_t payload_length = 0;
595 - if (nbytes < 126)  
596 - payload_length = nbytes;  
597 - else if (nbytes >= 126 && nbytes <= 0xFFFF) 596 + if (nBytesReal < 126)
  597 + payload_length = nBytesReal;
  598 + else if (nBytesReal >= 126 && nBytesReal <= 0xFFFF)
598 { 599 {
599 payload_length = 126; 600 payload_length = 126;
600 extended_payload_length_num_bytes = 2; 601 extended_payload_length_num_bytes = 2;
601 } 602 }
602 - else if (nbytes > 0xFFFF) 603 + else if (nBytesReal > 0xFFFF)
603 { 604 {
604 payload_length = 127; 605 payload_length = 127;
605 extended_payload_length_num_bytes = 8; 606 extended_payload_length_num_bytes = 8;
@@ -613,7 +614,6 @@ ssize_t IoWrapper::writeAsMuchOfBufAsWebsocketFrame(const void *buf, size_t nbyt @@ -613,7 +614,6 @@ ssize_t IoWrapper::writeAsMuchOfBufAsWebsocketFrame(const void *buf, size_t nbyt
613 const int header_length = x + extended_payload_length_num_bytes; 614 const int header_length = x + extended_payload_length_num_bytes;
614 615
615 // This block writes the extended payload length. 616 // This block writes the extended payload length.
616 - nBytesReal = std::min<size_t>(nbytes, websocketWriteRemainder.freeSpace() - header_length);  
617 const uint64_t nbytes64 = nBytesReal; 617 const uint64_t nbytes64 = nBytesReal;
618 for (int z = extended_payload_length_num_bytes - 1; z >= 0; z--) 618 for (int z = extended_payload_length_num_bytes - 1; z >= 0; z--)
619 { 619 {
@@ -667,3 +667,10 @@ ssize_t IoWrapper::writeWebsocketAndOrSsl(int fd, const void *buf, size_t nbytes @@ -667,3 +667,10 @@ ssize_t IoWrapper::writeWebsocketAndOrSsl(int fd, const void *buf, size_t nbytes
667 return n; 667 return n;
668 } 668 }
669 } 669 }
  670 +
  671 +void IoWrapper::resetBuffersIfEligible()
  672 +{
  673 + const size_t sz = websocket ? initialBufferSize : 0;
  674 + websocketPendingBytes.resetSizeIfEligable(sz),
  675 + websocketWriteRemainder.resetSizeIfEligable(sz);
  676 +}
iowrapper.h
@@ -118,7 +118,7 @@ class IoWrapper @@ -118,7 +118,7 @@ class IoWrapper
118 ssize_t websocketBytesToReadBuffer(void *buf, const size_t nbytes, IoWrapResult *error); 118 ssize_t websocketBytesToReadBuffer(void *buf, const size_t nbytes, IoWrapResult *error);
119 ssize_t readOrSslRead(int fd, void *buf, size_t nbytes, IoWrapResult *error); 119 ssize_t readOrSslRead(int fd, void *buf, size_t nbytes, IoWrapResult *error);
120 ssize_t writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWrapResult *error); 120 ssize_t writeOrSslWrite(int fd, const void *buf, size_t nbytes, IoWrapResult *error);
121 - ssize_t writeAsMuchOfBufAsWebsocketFrame(const void *buf, size_t nbytes, WebsocketOpcode opcode = WebsocketOpcode::Binary); 121 + ssize_t writeAsMuchOfBufAsWebsocketFrame(const void *buf, const size_t nbytes, WebsocketOpcode opcode = WebsocketOpcode::Binary);
122 public: 122 public:
123 IoWrapper(SSL *ssl, bool websocket, const size_t initialBufferSize, Client *parent); 123 IoWrapper(SSL *ssl, bool websocket, const size_t initialBufferSize, Client *parent);
124 ~IoWrapper(); 124 ~IoWrapper();
@@ -138,6 +138,8 @@ public: @@ -138,6 +138,8 @@ public:
138 138
139 ssize_t readWebsocketAndOrSsl(int fd, void *buf, size_t nbytes, IoWrapResult *error); 139 ssize_t readWebsocketAndOrSsl(int fd, void *buf, size_t nbytes, IoWrapResult *error);
140 ssize_t writeWebsocketAndOrSsl(int fd, const void *buf, size_t nbytes, IoWrapResult *error); 140 ssize_t writeWebsocketAndOrSsl(int fd, const void *buf, size_t nbytes, IoWrapResult *error);
  141 +
  142 + void resetBuffersIfEligible();
141 }; 143 };
142 144
143 #endif // IOWRAPPER_H 145 #endif // IOWRAPPER_H