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