Commit 411412f90296ed596340fe3928d6b9a46934f39f
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).
Showing
3 changed files
with
18 additions
and
8 deletions
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 |