github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/handlers/logging_test.go (about) 1 // Copyright 2013 The Gorilla Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package handlers 6 7 import ( 8 "bytes" 9 "encoding/base64" 10 "fmt" 11 "io/ioutil" 12 "math/rand" 13 "mime/multipart" 14 "net/url" 15 "os" 16 "path/filepath" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/hxx258456/ccgo/gmhttp/httptest" 22 23 http "github.com/hxx258456/ccgo/gmhttp" 24 ) 25 26 func TestMakeLogger(t *testing.T) { 27 rec := httptest.NewRecorder() 28 logger, w := makeLogger(rec) 29 // initial status 30 if logger.Status() != http.StatusOK { 31 t.Fatalf("wrong status, got %d want %d", logger.Status(), http.StatusOK) 32 } 33 // WriteHeader 34 w.WriteHeader(http.StatusInternalServerError) 35 if logger.Status() != http.StatusInternalServerError { 36 t.Fatalf("wrong status, got %d want %d", logger.Status(), http.StatusInternalServerError) 37 } 38 // Write 39 w.Write([]byte(ok)) 40 if logger.Size() != len(ok) { 41 t.Fatalf("wrong size, got %d want %d", logger.Size(), len(ok)) 42 } 43 // Header 44 w.Header().Set("key", "value") 45 if val := w.Header().Get("key"); val != "value" { 46 t.Fatalf("wrong header, got %s want %s", val, "value") 47 } 48 } 49 50 func TestLoggerCleanup(t *testing.T) { 51 rand.Seed(time.Now().UnixNano()) 52 53 rbuf := make([]byte, 128) 54 if _, err := rand.Read(rbuf); err != nil { 55 t.Fatalf("Failed to generate random content: %v", err) 56 } 57 contents := base64.StdEncoding.EncodeToString(rbuf) 58 59 var body bytes.Buffer 60 body.WriteString(fmt.Sprintf(` 61 --boundary 62 Content-Disposition: form-data; name="buzz"; filename="example.txt" 63 64 %s 65 --boundary-- 66 `, contents)) 67 r := multipart.NewReader(&body, "boundary") 68 form, err := r.ReadForm(0) // small max memory to force flush to disk 69 if err != nil { 70 t.Fatalf("Failed to read multipart form: %v", err) 71 } 72 73 tmpFiles, err := ioutil.ReadDir(os.TempDir()) 74 if err != nil { 75 t.Fatalf("Failed to list %s: %v", os.TempDir(), err) 76 } 77 78 var tmpFile string 79 for _, f := range tmpFiles { 80 if !strings.HasPrefix(f.Name(), "multipart-") { 81 continue 82 } 83 84 path := filepath.Join(os.TempDir(), f.Name()) 85 switch b, err := ioutil.ReadFile(path); { 86 case err != nil: 87 t.Fatalf("Failed to read %s: %v", path, err) 88 case string(b) != contents: 89 continue 90 default: 91 tmpFile = path 92 break 93 } 94 } 95 96 if tmpFile == "" { 97 t.Fatal("Could not find multipart form tmp file") 98 } 99 100 req := newRequest("GET", "/subdir/asdf") 101 req.MultipartForm = form 102 103 var buf bytes.Buffer 104 handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 105 req.URL.Path = "/" // simulate http.StripPrefix and friends 106 w.WriteHeader(200) 107 }) 108 logger := LoggingHandler(&buf, handler) 109 logger.ServeHTTP(httptest.NewRecorder(), req) 110 111 if _, err := os.Stat(tmpFile); err == nil || !os.IsNotExist(err) { 112 t.Fatalf("Expected %s to not exist, got %v", tmpFile, err) 113 } 114 } 115 116 func TestLogPathRewrites(t *testing.T) { 117 var buf bytes.Buffer 118 119 handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 120 req.URL.Path = "/" // simulate http.StripPrefix and friends 121 w.WriteHeader(200) 122 }) 123 logger := LoggingHandler(&buf, handler) 124 125 logger.ServeHTTP(httptest.NewRecorder(), newRequest("GET", "/subdir/asdf")) 126 127 if !strings.Contains(buf.String(), "GET /subdir/asdf HTTP") { 128 t.Fatalf("Got log %#v, wanted substring %#v", buf.String(), "GET /subdir/asdf HTTP") 129 } 130 } 131 132 func BenchmarkWriteLog(b *testing.B) { 133 loc, err := time.LoadLocation("Europe/Warsaw") 134 if err != nil { 135 b.Fatalf(err.Error()) 136 } 137 ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) 138 139 req := newRequest("GET", "http://example.com") 140 req.RemoteAddr = "192.168.100.5" 141 142 b.ResetTimer() 143 144 params := LogFormatterParams{ 145 Request: req, 146 URL: *req.URL, 147 TimeStamp: ts, 148 StatusCode: http.StatusUnauthorized, 149 Size: 500, 150 } 151 152 buf := &bytes.Buffer{} 153 154 for i := 0; i < b.N; i++ { 155 buf.Reset() 156 writeLog(buf, params) 157 } 158 } 159 160 func TestLogFormatterWriteLog_Scenario1(t *testing.T) { 161 formatter := writeLog 162 expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100\n" 163 LoggingScenario1(t, formatter, expected) 164 } 165 166 func TestLogFormatterCombinedLog_Scenario1(t *testing.T) { 167 formatter := writeCombinedLog 168 expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " + 169 "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + 170 "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" 171 LoggingScenario1(t, formatter, expected) 172 } 173 174 func TestLogFormatterWriteLog_Scenario2(t *testing.T) { 175 formatter := writeLog 176 expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"CONNECT www.example.com:443 HTTP/2.0\" 200 100\n" 177 LoggingScenario2(t, formatter, expected) 178 } 179 180 func TestLogFormatterCombinedLog_Scenario2(t *testing.T) { 181 formatter := writeCombinedLog 182 expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"CONNECT www.example.com:443 HTTP/2.0\" 200 100 \"http://example.com\" " + 183 "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + 184 "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" 185 LoggingScenario2(t, formatter, expected) 186 } 187 188 func TestLogFormatterWriteLog_Scenario3(t *testing.T) { 189 formatter := writeLog 190 expected := "192.168.100.5 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 401 500\n" 191 LoggingScenario3(t, formatter, expected) 192 } 193 194 func TestLogFormatterCombinedLog_Scenario3(t *testing.T) { 195 formatter := writeCombinedLog 196 expected := "192.168.100.5 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 401 500 \"http://example.com\" " + 197 "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + 198 "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" 199 LoggingScenario3(t, formatter, expected) 200 } 201 202 func TestLogFormatterWriteLog_Scenario4(t *testing.T) { 203 formatter := writeLog 204 expected := "192.168.100.5 - - [26/May/1983:03:30:45 +0200] \"GET /test?abc=hello%20world&a=b%3F HTTP/1.1\" 200 100\n" 205 LoggingScenario4(t, formatter, expected) 206 } 207 208 func TestLogFormatterCombinedLog_Scenario5(t *testing.T) { 209 formatter := writeCombinedLog 210 expected := "::1 - kamil [26/May/1983:03:30:45 +0200] \"GET / HTTP/1.1\" 200 100 \"http://example.com\" " + 211 "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) " + 212 "AppleWebKit/537.33 (KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33\"\n" 213 LoggingScenario5(t, formatter, expected) 214 } 215 216 func LoggingScenario1(t *testing.T, formatter LogFormatter, expected string) { 217 loc, err := time.LoadLocation("Europe/Warsaw") 218 if err != nil { 219 panic(err) 220 } 221 ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) 222 223 // A typical request with an OK response 224 req := constructTypicalRequestOk() 225 226 buf := new(bytes.Buffer) 227 params := LogFormatterParams{ 228 Request: req, 229 URL: *req.URL, 230 TimeStamp: ts, 231 StatusCode: http.StatusOK, 232 Size: 100, 233 } 234 235 formatter(buf, params) 236 log := buf.String() 237 238 if log != expected { 239 t.Fatalf("wrong log, got %q want %q", log, expected) 240 } 241 } 242 243 func LoggingScenario2(t *testing.T, formatter LogFormatter, expected string) { 244 loc, err := time.LoadLocation("Europe/Warsaw") 245 if err != nil { 246 panic(err) 247 } 248 ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) 249 250 // CONNECT request over http/2.0 251 req := constructConnectRequest() 252 253 buf := new(bytes.Buffer) 254 params := LogFormatterParams{ 255 Request: req, 256 URL: *req.URL, 257 TimeStamp: ts, 258 StatusCode: http.StatusOK, 259 Size: 100, 260 } 261 formatter(buf, params) 262 log := buf.String() 263 264 if log != expected { 265 t.Fatalf("wrong log, got %q want %q", log, expected) 266 } 267 } 268 269 func LoggingScenario3(t *testing.T, formatter LogFormatter, expected string) { 270 loc, err := time.LoadLocation("Europe/Warsaw") 271 if err != nil { 272 panic(err) 273 } 274 ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) 275 276 // Request with an unauthorized user 277 req := constructTypicalRequestOk() 278 req.URL.User = url.User("kamil") 279 280 buf := new(bytes.Buffer) 281 params := LogFormatterParams{ 282 Request: req, 283 URL: *req.URL, 284 TimeStamp: ts, 285 StatusCode: http.StatusUnauthorized, 286 Size: 500, 287 } 288 formatter(buf, params) 289 log := buf.String() 290 291 if log != expected { 292 t.Fatalf("wrong log, got %q want %q", log, expected) 293 } 294 } 295 296 func LoggingScenario4(t *testing.T, formatter LogFormatter, expected string) { 297 loc, err := time.LoadLocation("Europe/Warsaw") 298 if err != nil { 299 panic(err) 300 } 301 ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) 302 303 // Request with url encoded parameters 304 req := constructEncodedRequest() 305 306 buf := new(bytes.Buffer) 307 params := LogFormatterParams{ 308 Request: req, 309 URL: *req.URL, 310 TimeStamp: ts, 311 StatusCode: http.StatusOK, 312 Size: 100, 313 } 314 formatter(buf, params) 315 log := buf.String() 316 317 if log != expected { 318 t.Fatalf("wrong log, got %q want %q", log, expected) 319 } 320 } 321 322 func LoggingScenario5(t *testing.T, formatter LogFormatter, expected string) { 323 loc, err := time.LoadLocation("Europe/Warsaw") 324 if err != nil { 325 panic(err) 326 } 327 ts := time.Date(1983, 05, 26, 3, 30, 45, 0, loc) 328 329 req := constructTypicalRequestOk() 330 req.URL.User = url.User("kamil") 331 req.RemoteAddr = "::1" 332 333 buf := new(bytes.Buffer) 334 params := LogFormatterParams{ 335 Request: req, 336 URL: *req.URL, 337 TimeStamp: ts, 338 StatusCode: http.StatusOK, 339 Size: 100, 340 } 341 formatter(buf, params) 342 log := buf.String() 343 344 if log != expected { 345 t.Fatalf("wrong log, got %q want %q", log, expected) 346 } 347 } 348 349 // A typical request with an OK response 350 func constructTypicalRequestOk() *http.Request { 351 req := newRequest("GET", "http://example.com") 352 req.RemoteAddr = "192.168.100.5" 353 req.Header.Set("Referer", "http://example.com") 354 req.Header.Set( 355 "User-Agent", 356 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.33 "+ 357 "(KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33", 358 ) 359 return req 360 } 361 362 // CONNECT request over http/2.0 363 func constructConnectRequest() *http.Request { 364 req := &http.Request{ 365 Method: "CONNECT", 366 Host: "www.example.com:443", 367 Proto: "HTTP/2.0", 368 ProtoMajor: 2, 369 ProtoMinor: 0, 370 RemoteAddr: "192.168.100.5", 371 Header: http.Header{}, 372 URL: &url.URL{Host: "www.example.com:443"}, 373 } 374 req.Header.Set("Referer", "http://example.com") 375 req.Header.Set( 376 "User-Agent", 377 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.33 "+ 378 "(KHTML, like Gecko) Chrome/27.0.1430.0 Safari/537.33", 379 ) 380 return req 381 } 382 383 func constructEncodedRequest() *http.Request { 384 req := constructTypicalRequestOk() 385 req.URL, _ = url.Parse("http://example.com/test?abc=hello%20world&a=b%3F") 386 return req 387 }