fix: allow headers without whitespace after colon (#1741)

* Allows headers without whitespace after colon.

* Makes headers faster.

* Optimizes header splitting.

* Formatting.
This commit is contained in:
Alexander Stecher
2025-07-16 08:57:37 +02:00
committed by GitHub
parent 23073b6626
commit a161af26ae
3 changed files with 36 additions and 5 deletions

View File

@@ -481,14 +481,43 @@ func go_apache_request_headers(threadIndex C.uintptr_t) (*C.go_string, C.size_t)
}
func addHeader(fc *frankenPHPContext, cString *C.char, length C.int) {
parts := strings.SplitN(C.GoStringN(cString, length), ": ", 2)
if len(parts) != 2 {
fc.logger.LogAttrs(context.Background(), slog.LevelDebug, "invalid header", slog.String("header", parts[0]))
key, val := splitRawHeader(cString, int(length))
if key == "" {
fc.logger.LogAttrs(context.Background(), slog.LevelDebug, "invalid header", slog.String("header", C.GoStringN(cString, length)))
return
}
fc.responseWriter.Header().Add(key, val)
}
fc.responseWriter.Header().Add(parts[0], parts[1])
// split the raw header coming from C with minimal allocations
func splitRawHeader(rawHeader *C.char, length int) (string, string) {
buf := unsafe.Slice((*byte)(unsafe.Pointer(rawHeader)), length)
// Search for the colon in 'Header-Key: value'
var i int
for i = 0; i < length; i++ {
if buf[i] == ':' {
break
}
}
if i == length {
return "", "" // No colon found, invalid header
}
headerKey := C.GoStringN(rawHeader, C.int(i))
// skip whitespaces after the colon
j := i + 1
for j < length && buf[j] == ' ' {
j++
}
// anything left is the header value
valuePtr := (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(rawHeader)) + uintptr(j)))
headerValue := C.GoStringN(valuePtr, C.int(length-j))
return headerKey, headerValue
}
//export go_write_headers

View File

@@ -242,6 +242,7 @@ func testHeaders(t *testing.T, opts *testOptions) {
assert.Equal(t, 201, resp.StatusCode)
assert.Equal(t, "bar", resp.Header.Get("Foo"))
assert.Equal(t, "bar2", resp.Header.Get("Foo2"))
assert.Equal(t, "bar3", resp.Header.Get("Foo3"), "header without whitespace after colon")
assert.Empty(t, resp.Header.Get("Invalid"))
assert.Equal(t, fmt.Sprintf("%d", i), resp.Header.Get("I"))
}, opts)

View File

@@ -5,6 +5,7 @@ require_once __DIR__.'/_executor.php';
return function () {
header('Foo: bar');
header('Foo2: bar2');
header('Foo3:bar3'); // no space after colon (also valid, not recommended)
header('Invalid');
header('I: ' . ($_GET['i'] ?? 'i not set'));
http_response_code(201);