Compare commits

...

4 Commits

Author SHA1 Message Date
Kévin Dunglas
34262949d7 ci: fix push or Docker dev images 2024-02-02 11:27:54 +01:00
Kévin Dunglas
e53b1ce891 ci: use master Buildx version 2024-02-01 23:51:08 +01:00
Kévin Dunglas
6dee113a01 perf: add $_SERVER creation benchmark 2024-02-01 11:40:36 +01:00
Kévin Dunglas
fe7e9e7c79 fix: crash when using apache_request_headers() (#536)
* fix: potential crash when using apache_request_headers()

* refactor: better apache_request_headers
2024-02-01 10:00:11 +01:00
5 changed files with 102 additions and 18 deletions

View File

@@ -162,7 +162,7 @@ jobs:
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-${{ matrix.platform }}
*.cache-from=type=gha,scope=refs/heads/main-${{ matrix.platform }}
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-${{ matrix.platform }},ignore-error=true
${{ fromJson(needs.prepare.outputs.push) && '*.output=type=image,name=dunglas/frankenphp,push-by-digest=true,name-canonical=true,push=true' || '' }}
${{ fromJson(needs.prepare.outputs.push) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
env:
SHA: ${{ github.sha }}
VERSION: ${{ github.ref_type == 'tag' && github.ref_name || needs.prepare.outputs.ref || github.sha }}
@@ -232,7 +232,7 @@ jobs:
uses: docker/setup-buildx-action@v3
with:
# Temporary fix for https://github.com/docker/buildx/issues/2229
version: "https://github.com/jedevc/buildx.git#imagetools-resolver-copy-dupe"
version: "https://github.com/docker/buildx.git#master"
-
name: Login to DockerHub
uses: docker/login-action@v3
@@ -246,7 +246,7 @@ jobs:
set -x
# shellcheck disable=SC2046,SC2086
docker buildx imagetools create $(jq -cr '.target."${{ matrix.target }}-${{ matrix.variant }}".tags | map("-t " + .) | join(" ")' <<< ${METADATA}) \
$(printf 'dunglas/frankenphp@sha256:%s ' *)
$(printf "${IMAGE_NAME}@sha256:%s " *)
env:
METADATA: ${{ needs.prepare.outputs.metadata }}
-

View File

@@ -114,7 +114,7 @@ jobs:
*.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder
*.cache-from=type=gha,scope=refs/heads/main-static-builder
*.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder,ignore-error=true
${{ fromJson(needs.prepare.outputs.push) && '*.output=type=image,name=dunglas/frankenphp,push-by-digest=true,name-canonical=true,push=true' || '' }}
${{ fromJson(needs.prepare.outputs.push) && format('*.output=type=image,name={0},push-by-digest=true,name-canonical=true,push=true', env.IMAGE_NAME) || '' }}
env:
SHA: ${{ github.sha }}
VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || needs.prepare.outputs.ref || github.sha}}
@@ -189,7 +189,7 @@ jobs:
run: |
# shellcheck disable=SC2046,SC2086
docker buildx imagetools create $(jq -cr '.target."static-builder".tags | map("-t " + .) | join(" ")' <<< "${METADATA}") \
$(printf 'dunglas/frankenphp@sha256:%s ' *)
$(printf "${IMAGE_NAME}@sha256:%s " *)
env:
METADATA: ${{ needs.prepare.outputs.metadata }}
-

View File

@@ -261,7 +261,7 @@ PHP_FUNCTION(frankenphp_request_headers) {
add_assoc_stringl_ex(return_value, key.data, key.len, val.data, val.len);
}
free(headers.r0);
go_apache_request_cleanup(headers.r2);
}
/* }}} */

View File

@@ -582,24 +582,41 @@ func go_register_variables(rh C.uintptr_t, trackVarsArray *C.zval) {
}
//export go_apache_request_headers
func go_apache_request_headers(rh C.uintptr_t) (*C.go_string, C.size_t) {
func go_apache_request_headers(rh C.uintptr_t) (*C.go_string, C.size_t, C.uintptr_t) {
r := cgo.Handle(rh).Value().(*http.Request)
rl := len(r.Header)
scs := unsafe.Sizeof(C.go_string{})
pinner := &runtime.Pinner{}
pinnerHandle := C.uintptr_t(cgo.NewHandle(pinner))
headers := make([]C.go_string, 0, len(r.Header)*2)
headers := (*C.go_string)(unsafe.Pointer(C.malloc(C.size_t(rl*2) * (C.size_t)(scs))))
header := headers
for field, val := range r.Header {
*header = C.go_string{C.size_t(len(field)), (*C.char)(unsafe.Pointer(unsafe.StringData(field)))}
header = (*C.go_string)(unsafe.Add(unsafe.Pointer(header), scs))
fd := unsafe.StringData(field)
pinner.Pin(fd)
cv := strings.Join(val, ", ")
*header = C.go_string{C.size_t(len(cv)), (*C.char)(unsafe.Pointer(unsafe.StringData(cv)))}
header = (*C.go_string)(unsafe.Add(unsafe.Pointer(header), scs))
vd := unsafe.StringData(cv)
pinner.Pin(vd)
headers = append(
headers,
C.go_string{C.size_t(len(field)), (*C.char)(unsafe.Pointer(fd))},
C.go_string{C.size_t(len(cv)), (*C.char)(unsafe.Pointer(vd))},
)
}
return headers, C.size_t(rl)
sd := unsafe.SliceData(headers)
pinner.Pin(sd)
return sd, C.size_t(len(r.Header)), pinnerHandle
}
//export go_apache_request_cleanup
func go_apache_request_cleanup(rh C.uintptr_t) {
h := cgo.Handle(rh)
p := h.Value().(*runtime.Pinner)
p.Unpin()
h.Delete()
}
func addHeader(fc *FrankenPHPContext, cString *C.char, length C.int) {

View File

@@ -581,8 +581,8 @@ func TestRequestHeaders_worker(t *testing.T) {
func testRequestHeaders(t *testing.T, opts *testOptions) {
runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
req := httptest.NewRequest("GET", fmt.Sprintf("http://example.com/request-headers.php?i=%d", i), nil)
req.Header.Add("Content-Type", "text/plain")
req.Header.Add("Frankenphp-I", strconv.Itoa(i))
req.Header.Add(strings.Clone("Content-Type"), strings.Clone("text/plain"))
req.Header.Add(strings.Clone("Frankenphp-I"), strings.Clone(strconv.Itoa(i)))
w := httptest.NewRecorder()
handler(w, req)
@@ -735,3 +735,70 @@ func BenchmarkEcho(b *testing.B) {
handler(w, req)
}
}
func BenchmarkServerSuperGlobal(b *testing.B) {
if err := frankenphp.Init(frankenphp.WithLogger(zap.NewNop())); err != nil {
panic(err)
}
defer frankenphp.Shutdown()
cwd, _ := os.Getwd()
testDataDir := cwd + "/testdata/"
// Mimicks headers of a request sent by Firefox to GitHub
headers := http.Header{}
headers.Add(strings.Clone("Accept"), strings.Clone("text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"))
headers.Add(strings.Clone("Accept-Encoding"), strings.Clone("gzip, deflate, br"))
headers.Add(strings.Clone("Accept-Language"), strings.Clone("fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3"))
headers.Add(strings.Clone("Cache-Control"), strings.Clone("no-cache"))
headers.Add(strings.Clone("Connection"), strings.Clone("keep-alive"))
headers.Add(strings.Clone("Cookie"), strings.Clone("user_session=myrandomuuid; __Host-user_session_same_site=myotherrandomuuid; dotcom_user=dunglas; logged_in=yes; _foo=barbarbarbarbarbar; _device_id=anotherrandomuuid; color_mode=foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar; preferred_color_mode=light; tz=Europe%2FParis; has_recent_activity=1"))
headers.Add(strings.Clone("DNT"), strings.Clone("1"))
headers.Add(strings.Clone("Host"), strings.Clone("example.com"))
headers.Add(strings.Clone("Pragma"), strings.Clone("no-cache"))
headers.Add(strings.Clone("Sec-Fetch-Dest"), strings.Clone("document"))
headers.Add(strings.Clone("Sec-Fetch-Mode"), strings.Clone("navigate"))
headers.Add(strings.Clone("Sec-Fetch-Site"), strings.Clone("cross-site"))
headers.Add(strings.Clone("Sec-GPC"), strings.Clone("1"))
headers.Add(strings.Clone("Upgrade-Insecure-Requests"), strings.Clone("1"))
headers.Add(strings.Clone("User-Agent"), strings.Clone("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:122.0) Gecko/20100101 Firefox/122.0"))
// Env vars available in a typical Docker container
env := map[string]string{
"HOSTNAME": "a88e81aa22e4",
"PHP_INI_DIR": "/usr/local/etc/php",
"HOME": "/root",
"GODEBUG": "cgocheck=0",
"PHP_LDFLAGS": "-Wl,-O1 -pie",
"PHP_CFLAGS": "-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64",
"PHP_VERSION": "8.3.2",
"GPG_KEYS": "1198C0117593497A5EC5C199286AF1F9897469DC C28D937575603EB4ABB725861C0779DC5C0A9DE4 AFD8691FDAEDF03BDF6E460563F15A9B715376CA",
"PHP_CPPFLAGS": "-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64",
"PHP_ASC_URL": "https://www.php.net/distributions/php-8.3.2.tar.xz.asc",
"PHP_URL": "https://www.php.net/distributions/php-8.3.2.tar.xz",
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"XDG_CONFIG_HOME": "/config",
"XDG_DATA_HOME": "/data",
"PHPIZE_DEPS": "autoconf dpkg-dev file g++ gcc libc-dev make pkg-config re2c",
"PWD": "/app",
"PHP_SHA256": "4ffa3e44afc9c590e28dc0d2d31fc61f0139f8b335f11880a121b9f9b9f0634e",
}
handler := func(w http.ResponseWriter, r *http.Request) {
req, err := frankenphp.NewRequestWithContext(r, frankenphp.WithRequestDocumentRoot(testDataDir, false), frankenphp.WithRequestEnv(env))
if err != nil {
panic(err)
}
r.Header = headers
if err := frankenphp.ServeHTTP(w, req); err != nil {
panic(err)
}
}
req := httptest.NewRequest("GET", "http://example.com/server-variable.php", nil)
w := httptest.NewRecorder()
for i := 0; i < b.N; i++ {
handler(w, req)
}
}