pkg/nginx/nginx.go (148 lines of code) (raw):
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package nginx contains nginx buildpack library code.
package nginx
import (
"fmt"
"os"
"path/filepath"
"text/template"
)
// PHPFpmTemplate is a template that produces a snippet of php-fpm config that sets up the PHP with Nginx.
var PHPFpmTemplate = template.Must(template.New("phpfpm").Parse(`
; Send errors to stderr.
error_log = /proc/self/fd/2
log_level = warning
pid = {{.PidPath}}
; Pool configuration
[app]
; Unix user/group of processes
user = {{.Username}}
group = {{.Username}}
; The address on which to accept FastCGI requests
listen = {{.ListenAddress}}
{{if .DynamicWorkers}}
; Create child processes with a dynamic policy.
pm = dynamic
; The number of child processes to be created
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = {{.NumWorkers}}
pm.max_children = {{.NumWorkers}}
{{else}}
; Create child processes with a static policy.
pm = static
; The number of child processes to be created
pm.max_children = {{.NumWorkers}}
{{end}}
; Keep the environment variables of the parent.
clear_env = no
catch_workers_output = yes
{{if .AddNoDecorateWorkers}}
decorate_workers_output = no
{{end}}
{{- if .ConfOverride}}
include = {{.ConfOverride}}
{{- end}}
`))
// NginxTemplate is a template that produces a snippet of nginx config that sets up the
// upstream and server for PHP. It is included in the http{} section of the config by
// the pid1 program.
var NginxTemplate = template.Must(template.New("nginx").Parse(`
fastcgi_read_timeout 24h;
# proxy_* are not set for PHP because fastcgi is used.
upstream fast_cgi_app {
server {{.AppListenAddress}} fail_timeout=0;
}
server {
listen {{.Port}} default_server;
listen [::]:{{.Port}} default_server;
server_name "";
root {{.Root}};
{{if .ServesStaticFiles}}
location / {
try_files $uri /{{.FrontControllerScript}}$uri$is_args$args;
}
{{else}}
rewrite ^/(.*)$ /{{.FrontControllerScript}}$uri;
{{end}}
location ~ ^/{{.FrontControllerScript}} {
error_log stderr;
fastcgi_pass fast_cgi_app;
fastcgi_buffering off;
fastcgi_request_buffering off;
fastcgi_cache off;
fastcgi_store off;
fastcgi_intercept_errors off;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $document_root/{{.FrontControllerScript}};
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
if ($http_x_forwarded_proto = 'https') {
set $https_setting 'on';
}
fastcgi_param HTTPS $https_setting if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param REMOTE_HOST $remote_addr;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
fastcgi_param X_FORWARDED_FOR $proxy_add_x_forwarded_for;
fastcgi_param X_FORWARDED_HOST $http_x_forwarded_host;
fastcgi_param X_FORWARDED_PROTO $http_x_forwarded_proto;
fastcgi_param FORWARDED $http_forwarded;
}
{{- if .NginxConfInclude}}
include {{.NginxConfInclude}};
{{- end}}
}
`))
// FPMConfig represents the content values of a php-fpm config file.
type FPMConfig struct {
PidPath string
ListenAddress string
DynamicWorkers bool
NumWorkers int
Username string
AddNoDecorateWorkers bool
ConfOverride string
}
// Config represents the content values of a nginx config file.
type Config struct {
Port int
Root string
AppListenAddress string
FrontControllerScript string
NginxConfInclude string
ServesStaticFiles bool
}
const (
// nginx
nginxServerConf = "nginxserver.conf"
// php-fpm
phpFpmConf = "php-fpm.conf"
)
// WriteNginxConfigToPath writes the configuration for the nginx server to the given path.
func WriteNginxConfigToPath(path string, conf Config) (*os.File, error) {
nginxConfFilePath := filepath.Join(path, nginxServerConf)
nginxConfFile, err := os.Create(nginxConfFilePath)
if err != nil {
return nil, err
}
if err := NginxTemplate.Execute(nginxConfFile, conf); err != nil {
return nil, fmt.Errorf("writing nginx config file: %w", err)
}
return nginxConfFile, nil
}
// WriteFpmConfigToPath writes the fpm configuration file to the given path.
func WriteFpmConfigToPath(path string, conf FPMConfig) (*os.File, error) {
fpmConfFilePath := filepath.Join(path, phpFpmConf)
fpmConfFile, err := os.Create(fpmConfFilePath)
if err != nil {
return nil, err
}
if err := PHPFpmTemplate.Execute(fpmConfFile, conf); err != nil {
return nil, fmt.Errorf("writing php-fpm config file: %w", err)
}
return fpmConfFile, nil
}