mirror of
https://github.com/php/frankenphp.git
synced 2026-03-24 00:52:11 +01:00
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:
committed by
GitHub
parent
23073b6626
commit
a161af26ae
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
1
testdata/headers.php
vendored
1
testdata/headers.php
vendored
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user