`GET /token/{tokenId}/request/{requestId}/raw` crashes with 500 Internal Server Error when request lacks a `Content-Type` header
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 88/100
Research direction
Start in app/Storage/Request.php and inspect isJson(), then review how app/Http/Controllers/RequestController.php uses it in raw(). Add the regression test in tests/RequestControllerTest.php and verify that a captured request without a Content-Type returns 200, preserves its body, and uses text/plain.
Written by the indexing model from the issue text.
Description
📋 Overview
When attempting to view the raw content of an incoming webhook or HTTP request via GET /token/{tokenId}/request/{requestId}/raw (or clicking Raw content in the Web UI), the endpoint crashes with a 500 Internal Server Error (ErrorException: Undefined index: content-type) whenever the captured request did not include an explicit Content-Type header.
🔍 Root Cause Analysis
In app/Http/Controllers/RequestController.php (lines 138–146), the raw() method determines the response Content-Type by calling $request->isJson():
public function raw($tokenId, $requestId)
{
$token = $this->tokens->find($tokenId);
$request = $this->requests->find($token, $requestId);
$contentType = $request->isJson() ? 'application/json' : 'text/plain';
return new Response($request->content, Response::HTTP_OK, ['content-type' => $contentType]);
}
In app/Storage/Request.php (lines 56–59), isJson() is implemented as:
/**
* @return bool
*/
public function isJson()
{
return $this->headers['content-type'][0] === 'application/json';
}
There are two key problems in this implementation:
-
Unchecked Array Access on Missing Headers:
Many standard HTTP requests (e.g.GETrequests, standard webhook pings, plain curl requests, or health checks) do not send aContent-Typeheader. When stored,$this->headerscontains no'content-type'key.
Accessing$this->headers['content-type']directly without checking if the key exists raises:ErrorException: Undefined index: content-type(or
Undefined array key "content-type"in newer PHP runtimes), causing the request to fail with an unhandled500 Internal Server Error. -
Fragile Exact Matching for Valid JSON Types:
The strict comparison=== 'application/json'fails to detect valid JSON requests containing parameters such as charset or vendor types (e.g.application/json; charset=utf-8orapplication/problem+json).
🔁 Steps to Reproduce
-
Create a new token:
TOKEN_ID=$(curl -s -X POST http://localhost:8084/token | grep -o '"uuid":"[^"]*' | cut -d'"' -f4) -
Send an HTTP request without a
Content-Typeheader (e.g. standard GET or plain text POST):curl -X POST http://localhost:8084/${TOKEN_ID} -d "sample body without content type header" -
Retrieve the captured request's UUID:
REQUEST_ID=$(curl -s http://localhost:8084/token/${TOKEN_ID}/requests | grep -o '"uuid":"[^"]*' | head -n 1 | cut -d'"' -f4) -
Attempt to fetch the raw content:
curl -i http://localhost:8084/token/${TOKEN_ID}/request/${REQUEST_ID}/raw -
Observe the result:
- Actual Response:
HTTP/1.1 500 Internal Server Error - Expected Response:
HTTP/1.1 200 OKwith content body andContent-Type: text/plain
- Actual Response:
🛠️ Proposed Fix / Patch
Safely check for the existence of the content-type header and use case-insensitive substring matching in app/Storage/Request.php.
Unified Diff:
--- a/app/Storage/Request.php
+++ b/app/Storage/Request.php
@@ -53,8 +53,12 @@ public static function createFromRequest(HttpRequest $httpRequest)
*/
public function isJson()
{
- return $this->headers['content-type'][0] === 'application/json';
+ if (empty($this->headers['content-type'][0])) {
+ return false;
+ }
+
+ return stripos($this->headers['content-type'][0], 'application/json') !== false;
}
}
🧪 Unit Test
This test can be added to tests/RequestControllerTest.php to verify the fix and prevent regressions:
public function testRawContentWithoutContentTypeHeaderReturns200()
{
$this->withoutMiddleware();
$tokenId = $this->json('POST', 'token')->json()['uuid'];
// Send a request without Content-Type header
$this->call('POST', $tokenId, [], [], [], [], 'Plain text content body');
$requests = $this->json('GET', "token/{$tokenId}/requests")->json()['data'];
$requestId = $requests[0]['uuid'];
// Fetch raw content
$response = $this->call('GET', "token/{$tokenId}/request/{$requestId}/raw");
$response->assertStatus(200);
$this->assertEquals('Plain text content body', $response->getContent());
$this->assertStringStartsWith('text/plain', $response->headers->get('content-type'));
}
- Dominant language
- JavaScript
- Stars
- 6.8k
- Forks
- 530
- PR merge metrics
- No merged PRs in 30d
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from webhooksite/webhook.site
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
webhooksite/webhook.site#196 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 64/100
webhooksite/webhook.site#198 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
webhooksite/webhook.site#193 · 2 comments ·
-
Difficulty 4/5 3-5 days Newbie friendliness 30/100
webhooksite/webhook.site#172 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 35/100
webhooksite/webhook.site#166 · 1 comment ·
All issues in webhooksite/webhook.site
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
HarperFast/skills#96 ·
-
[Block] Latest Posts [Type] Bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
sugarlabs/musicblocks#8847 ·