What's the Issue
The HttpRoutingFilter had two related problems:
Issue 1: POST/PUT/PATCH Handlers Cannot Access Request Body
Route handlers were executed immediately in onHeaders() before the request body was received. This meant POST/PUT/PATCH handlers always received an empty body field, making it impossible to process request payloads.
// Handler registered for POST endpoint
filter->registerHandler("POST", "/api/data", [](const RequestContext& req) {
// req.body is ALWAYS empty - body hasn't arrived yet!
auto data = parseJson(req.body); // Fails
return Response{200, "OK"};
});
Issue 2: Query Strings Break Route Matching
Routes were matched against the full URL path including query strings. A request to /search?q=test would not match a handler registered for /search.
filter->registerHandler("GET", "/search", handler);
// Request: GET /search?q=test&page=1
// Expected: Routes to /search handler
// Actual: No handler found (404) because "/search?q=test&page=1" != "/search"
How to Reproduce
Reproducing Issue 1 (Missing POST Body):
-
Register a POST handler:
filter->registerHandler("POST", "/api/users", [](const RequestContext& req) {
std::cout << "Body: " << req.body << std::endl; // Empty!
return Response{201, "Created"};
});
-
Send a POST request with JSON body:
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name": "John", "email": "john@example.com"}'
-
Expected: Handler receives the JSON body
-
Actual: Handler receives empty string for req.body
Reproducing Issue 2 (Query String Routing):
-
Register a GET handler:
filter->registerHandler("GET", "/search", [](const RequestContext& req) {
return Response{200, "Search results"};
});
-
Send a request with query parameters:
curl "http://localhost:8080/search?q=hello&limit=10"
-
Expected: Routes to /search handler, handler sees full URL in req.path
-
Actual: Returns 404 because no handler matches /search?q=hello&limit=10
How to Fix
Fix 1: Defer POST/PUT/PATCH Handler Execution
Add state to accumulate the body and defer handler execution until onMessageComplete():
In http_routing_filter.h:
class HttpRoutingFilter {
private:
// State for POST requests that need body
bool pending_post_request_ = false;
RequestContext pending_context_;
HandlerFunc pending_handler_;
std::string accumulated_body_;
};
In onHeaders() - defer POST/PUT/PATCH:
if (method == "POST" || method == "PUT" || method == "PATCH") {
pending_post_request_ = true;
pending_context_ = ctx;
pending_handler_ = handler_it->second;
accumulated_body_.clear();
return; // Wait for body
}
In onBody() - accumulate body:
if (pending_post_request_) {
accumulated_body_ += data;
return; // Don't forward - we'll handle in onMessageComplete
}
In onMessageComplete() - execute handler with full body:
if (pending_post_request_) {
pending_context_.body = accumulated_body_;
Response resp = pending_handler_(pending_context_);
if (resp.status_code != 0) {
sendResponse(resp);
}
pending_post_request_ = false;
accumulated_body_.clear();
return;
}
Fix 2: Strip Query String for Routing, Preserve for Handler
In extractPath() - strip query string:
std::string HttpRoutingFilter::extractPath(const Headers& headers) {
std::string full_path = /* get from headers */;
// Strip query string for routing purposes
size_t query_pos = full_path.find('?');
if (query_pos != std::string::npos) {
return full_path.substr(0, query_pos);
}
return full_path;
}
In onHeaders() - provide full URL to handler:
RequestContext ctx;
ctx.method = method;
ctx.path = full_url; // Full URL with query string for handler access
ctx.headers = headers;
Files changed:
include/mcp/filter/http_routing_filter.h - Add state variables
src/filter/http_routing_filter.cc - Implement body accumulation and query string handling
Tests added:
tests/filter/test_http_routing_filter_simple.cc
PostBodyHandling - Verifies POST handlers wait for body
PostBodyAccumulation - Verifies chunked body accumulation
QueryStringRouting - Verifies /search?q=test routes to /search
PutBodyHandling - Verifies PUT handlers receive body
GetRequestImmediateExecution - Verifies GET still executes immediately
What's the Issue
The
HttpRoutingFilterhad two related problems:Issue 1: POST/PUT/PATCH Handlers Cannot Access Request Body
Route handlers were executed immediately in
onHeaders()before the request body was received. This meant POST/PUT/PATCH handlers always received an emptybodyfield, making it impossible to process request payloads.Issue 2: Query Strings Break Route Matching
Routes were matched against the full URL path including query strings. A request to
/search?q=testwould not match a handler registered for/search.How to Reproduce
Reproducing Issue 1 (Missing POST Body):
Register a POST handler:
Send a POST request with JSON body:
curl -X POST http://localhost:8080/api/users \ -H "Content-Type: application/json" \ -d '{"name": "John", "email": "john@example.com"}'Expected: Handler receives the JSON body
Actual: Handler receives empty string for
req.bodyReproducing Issue 2 (Query String Routing):
Register a GET handler:
Send a request with query parameters:
curl "http://localhost:8080/search?q=hello&limit=10"Expected: Routes to
/searchhandler, handler sees full URL inreq.pathActual: Returns 404 because no handler matches
/search?q=hello&limit=10How to Fix
Fix 1: Defer POST/PUT/PATCH Handler Execution
Add state to accumulate the body and defer handler execution until
onMessageComplete():In
http_routing_filter.h:In
onHeaders()- defer POST/PUT/PATCH:In
onBody()- accumulate body:In
onMessageComplete()- execute handler with full body:Fix 2: Strip Query String for Routing, Preserve for Handler
In
extractPath()- strip query string:In
onHeaders()- provide full URL to handler:RequestContext ctx; ctx.method = method; ctx.path = full_url; // Full URL with query string for handler access ctx.headers = headers;Files changed:
include/mcp/filter/http_routing_filter.h- Add state variablessrc/filter/http_routing_filter.cc- Implement body accumulation and query string handlingTests added:
tests/filter/test_http_routing_filter_simple.ccPostBodyHandling- Verifies POST handlers wait for bodyPostBodyAccumulation- Verifies chunked body accumulationQueryStringRouting- Verifies/search?q=testroutes to/searchPutBodyHandling- Verifies PUT handlers receive bodyGetRequestImmediateExecution- Verifies GET still executes immediately