@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 02:15 UTC

master
go 298 lines 7.52 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // NOTE: These tests were adapted from the original
16 // repository from which this package was forked.
17 // The tests are slow (~10s) and in dire need of rewriting.
18 // As such, the tests have been disabled to speed up
19 // automated builds until they can be properly written.
20
21 package fastcgi
22
23 import (
24 "bytes"
25 "crypto/md5"
26 "encoding/binary"
27 "fmt"
28 "io"
29 "log"
30 "math/rand/v2"
31 "net"
32 "net/http"
33 "net/http/fcgi"
34 "net/url"
35 "os"
36 "path/filepath"
37 "strconv"
38 "strings"
39 "testing"
40 "time"
41 )
42
43 // test fcgi protocol includes:
44 // Get, Post, Post in multipart/form-data, and Post with files
45 // each key should be the md5 of the value or the file uploaded
46 // specify remote fcgi responder ip:port to test with php
47 // test failed if the remote fcgi(script) failed md5 verification
48 // and output "FAILED" in response
49 const (
50 scriptFile = "/tank/www/fcgic_test.php"
51 // ipPort = "remote-php-serv:59000"
52 ipPort = "127.0.0.1:59000"
53 )
54
55 var globalt *testing.T
56
57 type FastCGIServer struct{}
58
59 func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
60 if err := req.ParseMultipartForm(100000000); err != nil {
61 log.Printf("[ERROR] failed to parse: %v", err)
62 }
63
64 stat := "PASSED"
65 fmt.Fprintln(resp, "-")
66 fileNum := 0
67 {
68 length := 0
69 for k0, v0 := range req.Form {
70 h := md5.New()
71 _, _ = io.WriteString(h, v0[0])
72 _md5 := fmt.Sprintf("%x", h.Sum(nil))
73
74 length += len(k0)
75 length += len(v0[0])
76
77 // echo error when key != _md5(val)
78 if _md5 != k0 {
79 fmt.Fprintln(resp, "server:err ", _md5, k0)
80 stat = "FAILED"
81 }
82 }
83 if req.MultipartForm != nil {
84 fileNum = len(req.MultipartForm.File)
85 for kn, fns := range req.MultipartForm.File {
86 // fmt.Fprintln(resp, "server:filekey ", kn )
87 length += len(kn)
88 for _, f := range fns {
89 fd, err := f.Open()
90 if err != nil {
91 log.Println("server:", err)
92 return
93 }
94 h := md5.New()
95 l0, err := io.Copy(h, fd)
96 if err != nil {
97 log.Println(err)
98 return
99 }
100 length += int(l0)
101 defer fd.Close()
102 md5 := fmt.Sprintf("%x", h.Sum(nil))
103 // fmt.Fprintln(resp, "server:filemd5 ", md5 )
104
105 if kn != md5 {
106 fmt.Fprintln(resp, "server:err ", md5, kn)
107 stat = "FAILED"
108 }
109 // fmt.Fprintln(resp, "server:filename ", f.Filename )
110 }
111 }
112 }
113
114 fmt.Fprintln(resp, "server:got data length", length)
115 }
116 fmt.Fprintln(resp, "-"+stat+"-POST(", len(req.Form), ")-FILE(", fileNum, ")--")
117 }
118
119 func sendFcgi(reqType int, fcgiParams map[string]string, data []byte, posts map[string]string, files map[string]string) (content []byte) {
120 conn, err := net.Dial("tcp", ipPort)
121 if err != nil {
122 log.Println("err:", err)
123 return content
124 }
125
126 fcgi := client{rwc: conn, reqID: 1}
127
128 length := 0
129
130 var resp *http.Response
131 switch reqType {
132 case 0:
133 if len(data) > 0 {
134 length = len(data)
135 rd := bytes.NewReader(data)
136 resp, err = fcgi.Post(fcgiParams, "", "", rd, int64(rd.Len()))
137 } else if len(posts) > 0 {
138 values := url.Values{}
139 for k, v := range posts {
140 values.Set(k, v)
141 length += len(k) + 2 + len(v)
142 }
143 resp, err = fcgi.PostForm(fcgiParams, values)
144 } else {
145 rd := bytes.NewReader(data)
146 resp, err = fcgi.Get(fcgiParams, rd, int64(rd.Len()))
147 }
148
149 default:
150 values := url.Values{}
151 for k, v := range posts {
152 values.Set(k, v)
153 length += len(k) + 2 + len(v)
154 }
155
156 for k, v := range files {
157 fi, _ := os.Lstat(v)
158 length += len(k) + int(fi.Size())
159 }
160 resp, err = fcgi.PostFile(fcgiParams, values, files)
161 }
162
163 if err != nil {
164 log.Println("err:", err)
165 return content
166 }
167
168 defer resp.Body.Close()
169 content, _ = io.ReadAll(resp.Body)
170
171 log.Println("c: send data length ≈", length, string(content))
172 conn.Close()
173 time.Sleep(250 * time.Millisecond)
174
175 if bytes.Contains(content, []byte("FAILED")) {
176 globalt.Error("Server return failed message")
177 }
178
179 return content
180 }
181
182 func generateRandFile(size int) (p string, m string) {
183 p = filepath.Join(os.TempDir(), "fcgict"+strconv.Itoa(rand.Int()))
184
185 // open output file
186 fo, err := os.Create(p)
187 if err != nil {
188 panic(err)
189 }
190 // close fo on exit and check for its returned error
191 defer func() {
192 if err := fo.Close(); err != nil {
193 panic(err)
194 }
195 }()
196
197 h := md5.New()
198 for i := 0; i < size/16; i++ {
199 buf := make([]byte, 16)
200 binary.PutVarint(buf, rand.Int64())
201 if _, err := fo.Write(buf); err != nil {
202 log.Printf("[ERROR] failed to write buffer: %v\n", err)
203 }
204 if _, err := h.Write(buf); err != nil {
205 log.Printf("[ERROR] failed to write buffer: %v\n", err)
206 }
207 }
208 m = fmt.Sprintf("%x", h.Sum(nil))
209 return p, m
210 }
211
212 func DisabledTest(t *testing.T) {
213 // TODO: test chunked reader
214 globalt = t
215
216 // server
217 go func() {
218 listener, err := net.Listen("tcp", ipPort)
219 if err != nil {
220 log.Println("listener creation failed: ", err)
221 }
222
223 srv := new(FastCGIServer)
224 if err := fcgi.Serve(listener, srv); err != nil {
225 log.Print("[ERROR] failed to start server: ", err)
226 }
227 }()
228
229 time.Sleep(250 * time.Millisecond)
230
231 // init
232 fcgiParams := make(map[string]string)
233 fcgiParams["REQUEST_METHOD"] = "GET"
234 fcgiParams["SERVER_PROTOCOL"] = "HTTP/1.1"
235 // fcgi_params["GATEWAY_INTERFACE"] = "CGI/1.1"
236 fcgiParams["SCRIPT_FILENAME"] = scriptFile
237
238 // simple GET
239 log.Println("test:", "get")
240 sendFcgi(0, fcgiParams, nil, nil, nil)
241
242 // simple post data
243 log.Println("test:", "post")
244 sendFcgi(0, fcgiParams, []byte("c4ca4238a0b923820dcc509a6f75849b=1&7b8b965ad4bca0e41ab51de7b31363a1=n"), nil, nil)
245
246 log.Println("test:", "post data (more than 60KB)")
247 data := ""
248 for i := 0x00; i < 0xff; i++ {
249 v0 := strings.Repeat(fmt.Sprint(i), 256)
250 h := md5.New()
251 _, _ = io.WriteString(h, v0)
252 k0 := fmt.Sprintf("%x", h.Sum(nil))
253 data += k0 + "=" + url.QueryEscape(v0) + "&"
254 }
255 sendFcgi(0, fcgiParams, []byte(data), nil, nil)
256
257 log.Println("test:", "post form (use url.Values)")
258 p0 := make(map[string]string, 1)
259 p0["c4ca4238a0b923820dcc509a6f75849b"] = "1"
260 p0["7b8b965ad4bca0e41ab51de7b31363a1"] = "n"
261 sendFcgi(1, fcgiParams, nil, p0, nil)
262
263 log.Println("test:", "post forms (256 keys, more than 1MB)")
264 p1 := make(map[string]string, 1)
265 for i := 0x00; i < 0xff; i++ {
266 v0 := strings.Repeat(fmt.Sprint(i), 4096)
267 h := md5.New()
268 _, _ = io.WriteString(h, v0)
269 k0 := fmt.Sprintf("%x", h.Sum(nil))
270 p1[k0] = v0
271 }
272 sendFcgi(1, fcgiParams, nil, p1, nil)
273
274 log.Println("test:", "post file (1 file, 500KB)) ")
275 f0 := make(map[string]string, 1)
276 path0, m0 := generateRandFile(500000)
277 f0[m0] = path0
278 sendFcgi(1, fcgiParams, nil, p1, f0)
279
280 log.Println("test:", "post multiple files (2 files, 5M each) and forms (256 keys, more than 1MB data")
281 path1, m1 := generateRandFile(5000000)
282 f0[m1] = path1
283 sendFcgi(1, fcgiParams, nil, p1, f0)
284
285 log.Println("test:", "post only files (2 files, 5M each)")
286 sendFcgi(1, fcgiParams, nil, nil, f0)
287
288 log.Println("test:", "post only 1 file")
289 delete(f0, "m0")
290 sendFcgi(1, fcgiParams, nil, nil, f0)
291
292 if err := os.Remove(path0); err != nil {
293 log.Println("[ERROR] failed to remove path: ", err)
294 }
295 if err := os.Remove(path1); err != nil {
296 log.Println("[ERROR] failed to remove path: ", err)
297 }
298 }