Skip to content

Commit f50248a

Browse files
refactor: removes context on the C side (#1404)
1 parent 09b8219 commit f50248a

File tree

13 files changed

+402
-403
lines changed

13 files changed

+402
-403
lines changed

cgi.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import "C"
1414
import (
1515
"crypto/tls"
1616
"net"
17-
"net/http"
1817
"path/filepath"
1918
"strings"
2019
"unsafe"
@@ -56,7 +55,8 @@ var knownServerKeys = []string{
5655
//
5756
// TODO: handle this case https://github.com/caddyserver/caddy/issues/3718
5857
// Inspired by https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
59-
func addKnownVariablesToServer(thread *phpThread, request *http.Request, fc *FrankenPHPContext, trackVarsArray *C.zval) {
58+
func addKnownVariablesToServer(thread *phpThread, fc *frankenPHPContext, trackVarsArray *C.zval) {
59+
request := fc.request
6060
keys := mainThread.knownServerKeys
6161
// Separate remote IP and port; more lenient than net.SplitHostPort
6262
var ip, port string
@@ -168,8 +168,8 @@ func packCgiVariable(key *C.zend_string, value string) C.ht_key_value_pair {
168168
return C.ht_key_value_pair{key, toUnsafeChar(value), C.size_t(len(value))}
169169
}
170170

171-
func addHeadersToServer(request *http.Request, thread *phpThread, fc *FrankenPHPContext, trackVarsArray *C.zval) {
172-
for field, val := range request.Header {
171+
func addHeadersToServer(thread *phpThread, fc *frankenPHPContext, trackVarsArray *C.zval) {
172+
for field, val := range fc.request.Header {
173173
if k := mainThread.commonHeaders[field]; k != nil {
174174
v := strings.Join(val, ", ")
175175
C.frankenphp_register_single(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray)
@@ -184,7 +184,7 @@ func addHeadersToServer(request *http.Request, thread *phpThread, fc *FrankenPHP
184184
}
185185
}
186186

187-
func addPreparedEnvToServer(fc *FrankenPHPContext, trackVarsArray *C.zval) {
187+
func addPreparedEnvToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
188188
for k, v := range fc.env {
189189
C.frankenphp_register_variable_safe(toUnsafeChar(k), toUnsafeChar(v), C.size_t(len(v)), trackVarsArray)
190190
}
@@ -194,11 +194,10 @@ func addPreparedEnvToServer(fc *FrankenPHPContext, trackVarsArray *C.zval) {
194194
//export go_register_variables
195195
func go_register_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) {
196196
thread := phpThreads[threadIndex]
197-
r := thread.getActiveRequest()
198-
fc := r.Context().Value(contextKey).(*FrankenPHPContext)
197+
fc := thread.getRequestContext()
199198

200-
addKnownVariablesToServer(thread, r, fc, trackVarsArray)
201-
addHeadersToServer(r, thread, fc, trackVarsArray)
199+
addKnownVariablesToServer(thread, fc, trackVarsArray)
200+
addHeadersToServer(thread, fc, trackVarsArray)
202201

203202
// The Prepared Environment is registered last and can overwrite any previous values
204203
addPreparedEnvToServer(fc, trackVarsArray)
@@ -209,7 +208,7 @@ func go_register_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) {
209208
//
210209
// Adapted from https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go
211210
// Copyright 2015 Matthew Holt and The Caddy Authors
212-
func splitPos(fc *FrankenPHPContext, path string) int {
211+
func splitPos(fc *frankenPHPContext, path string) int {
213212
if len(fc.splitPath) == 0 {
214213
return 0
215214
}

context.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package frankenphp
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"os"
7+
"strings"
8+
"time"
9+
10+
"go.uber.org/zap"
11+
)
12+
13+
// frankenPHPContext provides contextual information about the Request to handle.
14+
type frankenPHPContext struct {
15+
documentRoot string
16+
splitPath []string
17+
env PreparedEnv
18+
logger *zap.Logger
19+
request *http.Request
20+
originalRequest *http.Request
21+
22+
docURI string
23+
pathInfo string
24+
scriptName string
25+
scriptFilename string
26+
27+
// Whether the request is already closed by us
28+
isDone bool
29+
30+
responseWriter http.ResponseWriter
31+
32+
done chan interface{}
33+
startedAt time.Time
34+
}
35+
36+
// fromContext extracts the frankenPHPContext from a context.
37+
func fromContext(ctx context.Context) (fctx *frankenPHPContext, ok bool) {
38+
fctx, ok = ctx.Value(contextKey).(*frankenPHPContext)
39+
return
40+
}
41+
42+
// NewRequestWithContext creates a new FrankenPHP request context.
43+
func NewRequestWithContext(r *http.Request, opts ...RequestOption) (*http.Request, error) {
44+
fc := &frankenPHPContext{
45+
done: make(chan interface{}),
46+
startedAt: time.Now(),
47+
request: r,
48+
}
49+
for _, o := range opts {
50+
if err := o(fc); err != nil {
51+
return nil, err
52+
}
53+
}
54+
55+
if fc.logger == nil {
56+
fc.logger = logger
57+
}
58+
59+
if fc.documentRoot == "" {
60+
if EmbeddedAppPath != "" {
61+
fc.documentRoot = EmbeddedAppPath
62+
} else {
63+
var err error
64+
if fc.documentRoot, err = os.Getwd(); err != nil {
65+
return nil, err
66+
}
67+
}
68+
}
69+
70+
if fc.splitPath == nil {
71+
fc.splitPath = []string{".php"}
72+
}
73+
74+
if fc.env == nil {
75+
fc.env = make(map[string]string)
76+
}
77+
78+
if splitPos := splitPos(fc, r.URL.Path); splitPos > -1 {
79+
fc.docURI = r.URL.Path[:splitPos]
80+
fc.pathInfo = r.URL.Path[splitPos:]
81+
82+
// Strip PATH_INFO from SCRIPT_NAME
83+
fc.scriptName = strings.TrimSuffix(r.URL.Path, fc.pathInfo)
84+
85+
// Ensure the SCRIPT_NAME has a leading slash for compliance with RFC3875
86+
// Info: https://tools.ietf.org/html/rfc3875#section-4.1.13
87+
if fc.scriptName != "" && !strings.HasPrefix(fc.scriptName, "/") {
88+
fc.scriptName = "/" + fc.scriptName
89+
}
90+
}
91+
92+
// SCRIPT_FILENAME is the absolute path of SCRIPT_NAME
93+
fc.scriptFilename = sanitizedPathJoin(fc.documentRoot, fc.scriptName)
94+
95+
c := context.WithValue(r.Context(), contextKey, fc)
96+
97+
return r.WithContext(c), nil
98+
}
99+
100+
func newDummyContext(requestPath string, opts ...RequestOption) (*frankenPHPContext, error) {
101+
r, err := http.NewRequest(http.MethodGet, requestPath, nil)
102+
if err != nil {
103+
return nil, err
104+
}
105+
106+
fr, err := NewRequestWithContext(r, opts...)
107+
if err != nil {
108+
return nil, err
109+
}
110+
111+
fc, _ := fromContext(fr.Context())
112+
113+
return fc, nil
114+
}
115+
116+
// closeContext sends the response to the client
117+
func (fc *frankenPHPContext) closeContext() {
118+
if fc.isDone {
119+
return
120+
}
121+
122+
close(fc.done)
123+
fc.isDone = true
124+
}
125+
126+
// validate checks if the request should be outright rejected
127+
func (fc *frankenPHPContext) validate() bool {
128+
if !strings.Contains(fc.request.URL.Path, "\x00") {
129+
return true
130+
}
131+
132+
fc.rejectBadRequest("Invalid request path")
133+
134+
return false
135+
}
136+
137+
func (fc *frankenPHPContext) clientHasClosed() bool {
138+
select {
139+
case <-fc.request.Context().Done():
140+
return true
141+
default:
142+
return false
143+
}
144+
}
145+
146+
// reject sends a response with the given status code and message
147+
func (fc *frankenPHPContext) reject(statusCode int, message string) {
148+
if fc.isDone {
149+
return
150+
}
151+
152+
rw := fc.responseWriter
153+
if rw != nil {
154+
rw.WriteHeader(statusCode)
155+
_, _ = rw.Write([]byte(message))
156+
rw.(http.Flusher).Flush()
157+
}
158+
159+
fc.closeContext()
160+
}
161+
162+
func (fc *frankenPHPContext) rejectBadRequest(message string) {
163+
fc.reject(http.StatusBadRequest, message)
164+
}

0 commit comments

Comments
 (0)