-
Notifications
You must be signed in to change notification settings - Fork 439
Expand file tree
/
Copy pathrequestoptions.go
More file actions
166 lines (135 loc) · 4.21 KB
/
requestoptions.go
File metadata and controls
166 lines (135 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package frankenphp
import (
"errors"
"log/slog"
"net/http"
"path/filepath"
"strings"
"sync"
"sync/atomic"
"unicode/utf8"
"github.com/dunglas/frankenphp/internal/fastabs"
)
// RequestOption instances allow to configure a FrankenPHP Request.
type RequestOption func(h *frankenPHPContext) error
var (
ErrInvalidSplitPath = errors.New("split path contains non-ASCII characters")
documentRootCache sync.Map
documentRootCacheLen atomic.Uint32
)
// WithRequestDocumentRoot sets the root directory of the PHP application.
// if resolveSymlink is true, oath declared as root directory will be resolved
// to its absolute value after the evaluation of any symbolic links.
// Due to the nature of PHP opcache, root directory path is cached: when
// using a symlinked directory as root this could generate errors when
// symlink is changed without PHP being restarted; enabling this
// directive will set $_SERVER['DOCUMENT_ROOT'] to the real directory path.
func WithRequestDocumentRoot(documentRoot string, resolveSymlink bool) RequestOption {
return func(o *frankenPHPContext) (err error) {
v, ok := documentRootCache.Load(documentRoot)
if !ok {
// make sure file root is absolute
v, err = fastabs.FastAbs(documentRoot)
if err != nil {
return err
}
// prevent the cache to grow forever, this is a totally arbitrary value
if documentRootCacheLen.Load() < 1024 {
documentRootCache.LoadOrStore(documentRoot, v)
documentRootCacheLen.Add(1)
}
}
if resolveSymlink {
if v, err = filepath.EvalSymlinks(v.(string)); err != nil {
return err
}
}
o.documentRoot = v.(string)
return nil
}
}
// WithRequestResolvedDocumentRoot is similar to WithRequestDocumentRoot
// but doesn't do any checks or resolving on the path to improve performance.
func WithRequestResolvedDocumentRoot(documentRoot string) RequestOption {
return func(o *frankenPHPContext) error {
o.documentRoot = documentRoot
return nil
}
}
// WithRequestSplitPath contains a list of split path strings.
//
// The path in the URL will be split into two, with the first piece ending
// with the value of splitPath. The first piece will be assumed as the
// actual resource (CGI script) name, and the second piece will be set to
// PATH_INFO for the CGI script to use.
//
// Split paths can only contain ASCII characters.
// Comparison is case-insensitive.
//
// Future enhancements should be careful to avoid CVE-2019-11043,
// which can be mitigated with use of a try_files-like behavior
// that 404s if the FastCGI path info is not found.
func WithRequestSplitPath(splitPath []string) (RequestOption, error) {
var b strings.Builder
for i, split := range splitPath {
b.Grow(len(split))
for j := 0; j < len(split); j++ {
c := split[j]
if c >= utf8.RuneSelf {
return nil, ErrInvalidSplitPath
}
if 'A' <= c && c <= 'Z' {
b.WriteByte(c + 'a' - 'A')
} else {
b.WriteByte(c)
}
}
splitPath[i] = b.String()
b.Reset()
}
return func(o *frankenPHPContext) error {
o.splitPath = splitPath
return nil
}, nil
}
type PreparedEnv = map[string]string
func PrepareEnv(env map[string]string) PreparedEnv {
preparedEnv := make(PreparedEnv, len(env))
for k, v := range env {
preparedEnv[k+"\x00"] = v
}
return preparedEnv
}
// WithRequestEnv set CGI-like environment variables that will be available in $_SERVER.
// Values set with WithEnv always have priority over automatically populated values.
func WithRequestEnv(env map[string]string) RequestOption {
return WithRequestPreparedEnv(PrepareEnv(env))
}
func WithRequestPreparedEnv(env PreparedEnv) RequestOption {
return func(o *frankenPHPContext) error {
o.env = env
return nil
}
}
func WithOriginalRequest(r *http.Request) RequestOption {
return func(o *frankenPHPContext) error {
o.originalRequest = r
return nil
}
}
// WithRequestLogger sets the logger associated with the current request
func WithRequestLogger(logger *slog.Logger) RequestOption {
return func(o *frankenPHPContext) error {
o.logger = logger
return nil
}
}
// WithWorkerName sets the worker that should handle the request
func WithWorkerName(name string) RequestOption {
return func(o *frankenPHPContext) error {
if name != "" {
o.worker = workersByName[name]
}
return nil
}
}