mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
fix(http): properly handle pipelined data in CONNECT requests (#25938)
Fixes #25862 ### What does this PR do? When a client sends pipelined data immediately after CONNECT request headers in the same TCP segment, Bun now properly delivers this data to the `head` parameter of the 'connect' event handler, matching Node.js behavior. This enables compatibility with Cap'n Proto's KJ HTTP library used by Cloudflare's workerd runtime, which pipelines RPC data after CONNECT. ### How did you verify your code works? <img width="694" height="612" alt="CleanShot 2026-01-09 at 15 30 22@2x" src="https://github.com/user-attachments/assets/3ffe840e-1792-429c-8303-d98ac3e6912a" /> Tests --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <map>
|
||||
#include "MoveOnlyFunction.h"
|
||||
#include "ChunkedEncoding.h"
|
||||
@@ -160,6 +161,13 @@ namespace uWS
|
||||
std::map<std::string, unsigned short, std::less<>> *currentParameterOffsets = nullptr;
|
||||
|
||||
public:
|
||||
/* Any data pipelined after the HTTP headers (before response).
|
||||
* Used for Node.js compatibility: 'connect' and 'upgrade' events
|
||||
* pass this as the 'head' Buffer parameter.
|
||||
* WARNING: This points to data in the receive buffer and may be stack-allocated.
|
||||
* Must be cloned before the request handler returns. */
|
||||
std::span<const char> head;
|
||||
|
||||
bool isAncient()
|
||||
{
|
||||
return ancientHttp;
|
||||
@@ -883,6 +891,8 @@ namespace uWS
|
||||
/* If returned socket is not what we put in we need
|
||||
* to break here as we either have upgraded to
|
||||
* WebSockets or otherwise closed the socket. */
|
||||
/* Store any remaining data as head for Node.js compat (connect/upgrade events) */
|
||||
req->head = std::span<const char>(data, length);
|
||||
void *returnedUser = requestHandler(user, req);
|
||||
if (returnedUser != user) {
|
||||
/* We are upgraded to WebSocket or otherwise broken */
|
||||
@@ -928,9 +938,13 @@ namespace uWS
|
||||
consumedTotal += emittable;
|
||||
}
|
||||
} else if(isConnectRequest) {
|
||||
// This only server to mark that the connect request read all headers
|
||||
// and can starting emitting data
|
||||
// This only serves to mark that the connect request read all headers
|
||||
// and can start emitting data. Don't try to parse remaining data as HTTP -
|
||||
// it's pipelined data that we've already captured in req->head.
|
||||
remainingStreamingBytes = STATE_IS_CHUNKED;
|
||||
// Mark remaining data as consumed and break - it's not HTTP
|
||||
consumedTotal += length;
|
||||
break;
|
||||
} else {
|
||||
/* If we came here without a body; emit an empty data chunk to signal no data */
|
||||
dataHandler(user, {}, true);
|
||||
|
||||
Reference in New Issue
Block a user