mirror of
https://github.com/dunglas/frankenphp.git
synced 2025-12-24 13:38:11 +08:00
Compare commits
15 Commits
refactor/b
...
chore/benc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b320dcd38c | ||
|
|
36b752d0a6 | ||
|
|
feaa950d89 | ||
|
|
f90e4614b6 | ||
|
|
f152a5fdaf | ||
|
|
b60fc5c374 | ||
|
|
5da805d9ee | ||
|
|
a996c2ee28 | ||
|
|
4cb77b552d | ||
|
|
241ca55d7a | ||
|
|
ae958516ea | ||
|
|
b61900eae1 | ||
|
|
e53b1ce891 | ||
|
|
6dee113a01 | ||
|
|
fe7e9e7c79 |
6
.github/workflows/docker.yaml
vendored
6
.github/workflows/docker.yaml
vendored
@@ -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 }}
|
||||
-
|
||||
|
||||
18
.github/workflows/static.yaml
vendored
18
.github/workflows/static.yaml
vendored
@@ -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 }}
|
||||
-
|
||||
@@ -214,8 +214,12 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
build-mac:
|
||||
name: Build macOS x86_64 binaries
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: ['arm64', 'x86_64']
|
||||
name: Build macOS ${{ matrix.platform }} binaries
|
||||
runs-on: ${{ matrix.platform == 'arm64' && 'macos-14' || 'macos-13' }}
|
||||
needs: [ prepare ]
|
||||
env:
|
||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||
@@ -227,7 +231,7 @@ jobs:
|
||||
-
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
caddy/go.sum
|
||||
@@ -254,5 +258,5 @@ jobs:
|
||||
if: github.ref_type == 'branch'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: frankenphp-mac-x86_64
|
||||
path: dist/frankenphp-mac-x86_64
|
||||
name: frankenphp-mac-${{ matrix.platform }}
|
||||
path: dist/frankenphp-mac-${{ matrix.platform }}
|
||||
|
||||
2
.github/workflows/tests.yaml
vendored
2
.github/workflows/tests.yaml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
-
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.22'
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
caddy/go.sum
|
||||
|
||||
@@ -59,6 +59,7 @@ You can also run command-line scripts with:
|
||||
* [Create **standalone**, self-executable PHP apps](https://frankenphp.dev/docs/embed/)
|
||||
* [Create static binaries](https://frankenphp.dev/docs/static/)
|
||||
* [Compile from sources](https://frankenphp.dev/docs/compile/)
|
||||
* [Laravel integration](https://frankenphp.dev/docs/laravel/)
|
||||
* [Known issues](https://frankenphp.dev/docs/known-issues/)
|
||||
* [Demo app (Symfony) and benchmarks](https://github.com/dunglas/frankenphp-demo)
|
||||
* [Go library documentation](https://pkg.go.dev/github.com/dunglas/frankenphp)
|
||||
|
||||
@@ -9,17 +9,14 @@ fi
|
||||
|
||||
arch="$(uname -m)"
|
||||
os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
md5binary="md5sum"
|
||||
if [ "${os}" = "darwin" ]; then
|
||||
os="mac"
|
||||
md5binary="md5 -q"
|
||||
fi
|
||||
|
||||
if [ -z "${PHP_EXTENSIONS}" ]; then
|
||||
if [ "${os}" = "mac" ] && [ "${arch}" = "x86_64" ]; then
|
||||
# Temporary fix for https://github.com/crazywhalecc/static-php-cli/issues/280 (remove ldap)
|
||||
export PHP_EXTENSIONS="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,gd,iconv,igbinary,intl,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sodium,sqlite3,sysvsem,tokenizer,xml,xmlreader,xmlwriter,zip,zlib"
|
||||
else
|
||||
export PHP_EXTENSIONS="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,gd,iconv,igbinary,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sodium,sqlite3,sysvsem,tokenizer,xml,xmlreader,xmlwriter,zip,zlib"
|
||||
fi
|
||||
export PHP_EXTENSIONS="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,gd,iconv,igbinary,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sodium,sqlite3,sysvsem,tokenizer,xml,xmlreader,xmlwriter,zip,zlib"
|
||||
fi
|
||||
|
||||
if [ -z "${PHP_EXTENSION_LIBS}" ]; then
|
||||
@@ -57,7 +54,7 @@ if [ -n "${CLEAN}" ]; then
|
||||
go clean -cache
|
||||
fi
|
||||
|
||||
# Build libphp if ncessary
|
||||
# Build libphp if necessary
|
||||
if [ -f "dist/static-php-cli/buildroot/lib/libphp.a" ]; then
|
||||
cd dist/static-php-cli
|
||||
else
|
||||
@@ -73,16 +70,20 @@ else
|
||||
fi
|
||||
|
||||
if type "brew" > /dev/null; then
|
||||
packages="composer"
|
||||
if ! type "composer" > /dev/null; then
|
||||
packages="composer"
|
||||
fi
|
||||
if ! type "go" > /dev/null; then
|
||||
packages="${packages} go"
|
||||
fi
|
||||
if [ -n "${RELEASE}" ]; then
|
||||
if [ -n "${RELEASE}" ] && ! type "gh" > /dev/null; then
|
||||
packages="${packages} gh"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
brew install --formula --quiet ${packages}
|
||||
if [ -n "${packages}" ]; then
|
||||
# shellcheck disable=SC2086
|
||||
brew install --formula --quiet ${packages}
|
||||
fi
|
||||
fi
|
||||
|
||||
composer install --no-dev -a
|
||||
@@ -123,7 +124,7 @@ cd ../..
|
||||
# Embed PHP app, if any
|
||||
if [ -n "${EMBED}" ] && [ -d "${EMBED}" ]; then
|
||||
tar -cf app.tar -C "${EMBED}" .
|
||||
md5 -q app.tar > app_checksum.txt
|
||||
${md5binary} app.tar > app_checksum.txt
|
||||
fi
|
||||
|
||||
if [ "${os}" = "linux" ]; then
|
||||
@@ -144,6 +145,10 @@ if [ -d "${EMBED}" ]; then
|
||||
truncate -s 0 app_checksum.txt
|
||||
fi
|
||||
|
||||
if type "upx" > /dev/null; then
|
||||
upx --best "dist/${bin}"
|
||||
fi
|
||||
|
||||
"dist/${bin}" version
|
||||
|
||||
if [ -n "${RELEASE}" ]; then
|
||||
|
||||
@@ -203,7 +203,7 @@ type FrankenPHPModule struct {
|
||||
// SplitPath sets the substrings for splitting the URI into two parts. The first matching substring will be used to split the "path info" from the path. The first piece is suffixed with the matching substring and will be assumed as the actual resource (CGI script) name. The second piece will be set to PATH_INFO for the CGI script to use. Default: `.php`.
|
||||
SplitPath []string `json:"split_path,omitempty"`
|
||||
// ResolveRootSymlink enables resolving the `root` directory to its actual value by evaluating a symbolic link, if one exists.
|
||||
ResolveRootSymlink bool `json:"resolve_root_symlink,omitempty"`
|
||||
ResolveRootSymlink *bool `json:"resolve_root_symlink,omitempty"`
|
||||
// Env sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
logger *zap.Logger
|
||||
@@ -225,8 +225,9 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
|
||||
if frankenphp.EmbeddedAppPath == "" {
|
||||
f.Root = "{http.vars.root}"
|
||||
} else {
|
||||
rrs := false
|
||||
f.Root = filepath.Join(frankenphp.EmbeddedAppPath, defaultDocumentRoot)
|
||||
f.ResolveRootSymlink = false
|
||||
f.ResolveRootSymlink = &rrs
|
||||
}
|
||||
} else {
|
||||
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(f.Root) {
|
||||
@@ -238,6 +239,11 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
|
||||
f.SplitPath = []string{".php"}
|
||||
}
|
||||
|
||||
if f.ResolveRootSymlink == nil {
|
||||
rrs := true
|
||||
f.ResolveRootSymlink = &rrs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -257,7 +263,7 @@ func (f FrankenPHPModule) ServeHTTP(w http.ResponseWriter, r *http.Request, _ ca
|
||||
|
||||
fr, err := frankenphp.NewRequestWithContext(
|
||||
r,
|
||||
frankenphp.WithRequestDocumentRoot(documentRoot, f.ResolveRootSymlink),
|
||||
frankenphp.WithRequestDocumentRoot(documentRoot, *f.ResolveRootSymlink),
|
||||
frankenphp.WithRequestSplitPath(f.SplitPath),
|
||||
frankenphp.WithRequestEnv(env),
|
||||
)
|
||||
@@ -298,9 +304,18 @@ func (f *FrankenPHPModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||
|
||||
case "resolve_root_symlink":
|
||||
if d.NextArg() {
|
||||
if v, err := strconv.ParseBool(d.Val()); err == nil {
|
||||
f.ResolveRootSymlink = &v
|
||||
|
||||
if d.NextArg() {
|
||||
return d.ArgErr()
|
||||
}
|
||||
}
|
||||
|
||||
return d.ArgErr()
|
||||
}
|
||||
f.ResolveRootSymlink = true
|
||||
rrs := true
|
||||
f.ResolveRootSymlink = &rrs
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -444,7 +459,8 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
|
||||
if phpsrv.Root == "" {
|
||||
phpsrv.Root = filepath.Join(frankenphp.EmbeddedAppPath, defaultDocumentRoot)
|
||||
fsrv.Root = phpsrv.Root
|
||||
phpsrv.ResolveRootSymlink = false
|
||||
rrs := false
|
||||
phpsrv.ResolveRootSymlink = &rrs
|
||||
} else if filepath.IsLocal(fsrv.Root) {
|
||||
phpsrv.Root = filepath.Join(frankenphp.EmbeddedAppPath, phpsrv.Root)
|
||||
fsrv.Root = phpsrv.Root
|
||||
|
||||
@@ -2,6 +2,8 @@ module github.com/dunglas/frankenphp/caddy
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
replace github.com/dunglas/frankenphp => ../
|
||||
|
||||
retract v1.0.0-rc.1 // Human error
|
||||
@@ -10,7 +12,7 @@ require (
|
||||
github.com/caddyserver/caddy/v2 v2.7.6
|
||||
github.com/caddyserver/certmagic v0.20.0
|
||||
github.com/dunglas/caddy-cbrotli v1.0.0
|
||||
github.com/dunglas/frankenphp v1.0.3
|
||||
github.com/dunglas/frankenphp v1.1.0
|
||||
github.com/dunglas/mercure/caddy v0.15.9
|
||||
github.com/dunglas/vulcain/caddy v1.0.1
|
||||
github.com/spf13/cobra v1.8.0
|
||||
|
||||
@@ -125,9 +125,11 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
|
||||
extensions := []string{"php"}
|
||||
tryFiles := []string{"{http.request.uri.path}", "{http.request.uri.path}/" + indexFile, indexFile}
|
||||
|
||||
rrs := true
|
||||
phpHandler := FrankenPHPModule{
|
||||
Root: root,
|
||||
SplitPath: extensions,
|
||||
Root: root,
|
||||
SplitPath: extensions,
|
||||
ResolveRootSymlink: &rrs,
|
||||
}
|
||||
|
||||
// route to redirect to canonical path if index PHP file
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM golang:1.21-alpine
|
||||
FROM golang:1.22-alpine
|
||||
|
||||
ENV CFLAGS="-ggdb3"
|
||||
ENV PHPIZE_DEPS \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM golang:1.21
|
||||
FROM golang:1.22
|
||||
|
||||
ENV CFLAGS="-ggdb3"
|
||||
ENV PHPIZE_DEPS \
|
||||
|
||||
@@ -11,7 +11,7 @@ variable "PHP_VERSION" {
|
||||
}
|
||||
|
||||
variable "GO_VERSION" {
|
||||
default = "1.21"
|
||||
default = "1.22"
|
||||
}
|
||||
|
||||
variable "SHA" {}
|
||||
|
||||
@@ -6,15 +6,16 @@ In the Docker image, the `Caddyfile` is located at `/etc/caddy/Caddyfile`.
|
||||
|
||||
You can also configure PHP using `php.ini` as usual.
|
||||
|
||||
In the Docker image, the `php.ini` file is not present, you can create it or `COPY` manually.
|
||||
|
||||
If you copy `php.ini` from `$PHP_INI_DIR/php.ini-production` or `$PHP_INI_DIR/php.ini-development`, you also must set the variable `variables_order = "EGPCS"`, because the default value for `variables_order` is `"EGPCS"`, but in `php.ini-production` and `php.ini-development` we have `"GPCS"`. And in this case, `worker` does not work properly.
|
||||
In the Docker image, the `php.ini` file is not present, you can create it or `COPY` manually:
|
||||
|
||||
```dockerfile
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini; \
|
||||
sed -i 's/variables_order = "GPCS"/variables_order = "EGPCS"/' $PHP_INI_DIR/php.ini;
|
||||
# Developement:
|
||||
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
|
||||
|
||||
# Or production:
|
||||
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
|
||||
```
|
||||
|
||||
## Caddyfile Config
|
||||
@@ -122,7 +123,7 @@ The `php_server` and the `php` directives have the following options:
|
||||
php_server [<matcher>] {
|
||||
root <directory> # Sets the root folder to the site. Default: `root` directive.
|
||||
split_path <delim...> # Sets the substrings for splitting the URI into two parts. The first matching substring will be used to split the "path info" from the path. The first piece is suffixed with the matching substring and will be assumed as the actual resource (CGI script) name. The second piece will be set to PATH_INFO for the CGI script to use. Default: `.php`
|
||||
resolve_root_symlink # Enables resolving the `root` directory to its actual value by evaluating a symbolic link, if one exists.
|
||||
resolve_root_symlink false # Disables resolving the `root` directory to its actual value by evaluating a symbolic link, if one exists (enabled by default).
|
||||
env <key> <value> # Sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
|
||||
}
|
||||
```
|
||||
|
||||
@@ -136,13 +136,16 @@ Here is a sample `Dockerfile` doing this:
|
||||
FROM dunglas/frankenphp
|
||||
|
||||
ARG USER=www-data
|
||||
USER ${USER}
|
||||
|
||||
RUN adduser -D ${USER} \
|
||||
# Caddy requires an additional capability to bind to port 80 and 443
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp
|
||||
# Caddy requires write access to /data/caddy and /config/caddy
|
||||
RUN chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
|
||||
RUN \
|
||||
# Use "adduser -D ${USER}" for alpine based distros
|
||||
useradd -D ${USER}; \
|
||||
# Add additional capability to bind to port 80 and 443
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
|
||||
# Give write access to /data/caddy and /config/caddy
|
||||
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;
|
||||
|
||||
USER ${USER}
|
||||
```
|
||||
|
||||
## Updates
|
||||
|
||||
@@ -32,10 +32,7 @@ Alternatively, you can run your Laravel projects with FrankenPHP from your local
|
||||
# Enable compression (optional)
|
||||
encode zstd br gzip
|
||||
# Execute PHP files in the current directory and serve assets
|
||||
php_server {
|
||||
# Required for the public/storage/ dir
|
||||
resolve_root_symlink
|
||||
}
|
||||
php_server
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -675,6 +675,11 @@ static void frankenphp_register_variables(zval *track_vars_array) {
|
||||
/* https://www.php.net/manual/en/reserved.variables.server.php */
|
||||
frankenphp_server_context *ctx = SG(server_context);
|
||||
|
||||
/* In CGI mode, we consider the environment to be a part of the server
|
||||
* variables
|
||||
*/
|
||||
php_import_environment_variables(track_vars_array);
|
||||
|
||||
go_register_variables(ctx->current_request ? ctx->current_request
|
||||
: ctx->main_request,
|
||||
track_vars_array);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
@@ -665,6 +665,7 @@ func BenchmarkHelloWorld(b *testing.B) {
|
||||
req := httptest.NewRequest("GET", "http://example.com/index.php", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
handler(w, req)
|
||||
}
|
||||
@@ -730,8 +731,77 @@ func BenchmarkEcho(b *testing.B) {
|
||||
req := httptest.NewRequest("POST", "http://example.com/echo.php", r)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.Reset(body)
|
||||
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()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
handler(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -2,6 +2,8 @@ module github.com/dunglas/frankenphp
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
retract v1.0.0-rc.1 // Human error
|
||||
|
||||
require (
|
||||
|
||||
@@ -47,8 +47,3 @@ tags=$(git tag --list --sort=-version:refname 'v*')
|
||||
previous_tag=$(awk 'NR==2 {print;exit}' <<< "${tags}")
|
||||
|
||||
gh release create --draft --generate-notes --latest --notes-start-tag "${previous_tag}" --verify-tag "v$1"
|
||||
|
||||
if [[ "$(uname -s)" = "Darwin" ]]; then
|
||||
rm -Rf dist/*
|
||||
FRANKENPHP_VERSION=$1 RELEASE=1 ./build-static.sh
|
||||
fi
|
||||
|
||||
@@ -33,6 +33,7 @@ RUN apk update; \
|
||||
bison \
|
||||
build-base \
|
||||
cmake \
|
||||
composer \
|
||||
curl \
|
||||
file \
|
||||
flex \
|
||||
@@ -47,11 +48,6 @@ RUN apk update; \
|
||||
m4 \
|
||||
make \
|
||||
pkgconfig \
|
||||
wget \
|
||||
xz ; \
|
||||
apk add --no-cache \
|
||||
--repository=https://dl-cdn.alpinelinux.org/alpine/edge/main \
|
||||
--repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \
|
||||
php83 \
|
||||
php83-common \
|
||||
php83-ctype \
|
||||
@@ -66,14 +62,14 @@ RUN apk update; \
|
||||
php83-sodium \
|
||||
php83-tokenizer \
|
||||
php83-xml \
|
||||
php83-xmlwriter; \
|
||||
php83-xmlwriter \
|
||||
upx \
|
||||
wget \
|
||||
xz ; \
|
||||
ln -sf /usr/bin/php83 /usr/bin/php
|
||||
|
||||
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
|
||||
ENV COMPOSER_ALLOW_SUPERUSER=1
|
||||
ENV PATH="${PATH}:/root/.composer/vendor/bin"
|
||||
|
||||
COPY --from=composer/composer:2-bin --link /composer /usr/bin/composer
|
||||
|
||||
WORKDIR /go/src/app
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
25
testdata/load-test.js
vendored
25
testdata/load-test.js
vendored
@@ -58,7 +58,30 @@ const payload = 'foo\n'.repeat(1000)
|
||||
// about authoring k6 scripts.
|
||||
//
|
||||
export default function () {
|
||||
const res = http.post('http://localhost/echo.php', payload)
|
||||
const params = {
|
||||
headers: {
|
||||
Accept:
|
||||
'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
|
||||
// 'Accept-Encoding': 'br',
|
||||
'Accept-Language': 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
|
||||
'Cache-Control': 'no-cache',
|
||||
Connection: 'keep-alive',
|
||||
Cookie:
|
||||
'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',
|
||||
DNT: '1',
|
||||
Host: 'example.com',
|
||||
Pragma: 'no-cache',
|
||||
'Sec-Fetch-Dest': 'document',
|
||||
'Sec-Fetch-Mode': 'navigate',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
'Sec-GPC': '1',
|
||||
'Upgrade-Insecure-Requests': '1',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:122.0) Gecko/20100101 Firefox/122.0'
|
||||
}
|
||||
}
|
||||
|
||||
const res = http.post('http://localhost/echo.php', payload, params)
|
||||
check(res, {
|
||||
'is status 200': (r) => r.status === 200,
|
||||
'is echoed': (r) => r.body === payload
|
||||
|
||||
Reference in New Issue
Block a user