Catch all other missing request patterns to logs
This commit is contained in:
@@ -48,6 +48,23 @@ func isTLSError(err error) bool {
|
|||||||
return strings.HasPrefix(err.Error(), "tls:")
|
return strings.HasPrefix(err.Error(), "tls:")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// withStatusLogging wraps a handler to log 404 and 405 responses.
|
||||||
|
func withStatusLogging(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
rw := &responseWriterWrapper{ResponseWriter: w, statusCode: http.StatusOK}
|
||||||
|
next.ServeHTTP(rw, r)
|
||||||
|
|
||||||
|
clientIP := getClientIP(r)
|
||||||
|
|
||||||
|
switch rw.statusCode {
|
||||||
|
case http.StatusNotFound:
|
||||||
|
log.Infof("Not Found from %s to %s", clientIP, r.RequestURI)
|
||||||
|
case http.StatusMethodNotAllowed:
|
||||||
|
log.Infof("Method Not Allowed from %s for %s %s", clientIP, r.Method, r.RequestURI)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// withServerHeader wraps a handler to add Server header to all responses.
|
// withServerHeader wraps a handler to add Server header to all responses.
|
||||||
func withServerHeader(next http.Handler) http.Handler {
|
func withServerHeader(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -74,6 +91,13 @@ func NewServer(configPath string) (*ServerWrapper, error) {
|
|||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
backupHandler := func(w http.ResponseWriter, r *http.Request) {
|
backupHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Only GET method is allowed.
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
username, password, ok := r.BasicAuth()
|
username, password, ok := r.BasicAuth()
|
||||||
|
|
||||||
// No auth? Good bye.
|
// No auth? Good bye.
|
||||||
@@ -219,7 +243,7 @@ func NewServer(configPath string) (*ServerWrapper, error) {
|
|||||||
srv := &ServerWrapper{
|
srv := &ServerWrapper{
|
||||||
Server: &http.Server{
|
Server: &http.Server{
|
||||||
Addr: fmt.Sprintf("%s:%d", C.Address, C.Port),
|
Addr: fmt.Sprintf("%s:%d", C.Address, C.Port),
|
||||||
Handler: withServerHeader(mux),
|
Handler: withServerHeader(withStatusLogging(mux)),
|
||||||
ReadHeaderTimeout: readHeaderTimeout,
|
ReadHeaderTimeout: readHeaderTimeout,
|
||||||
WriteTimeout: time.Duration(C.BackupTimeout) * time.Minute,
|
WriteTimeout: time.Duration(C.BackupTimeout) * time.Minute,
|
||||||
IdleTimeout: idleTimeout,
|
IdleTimeout: idleTimeout,
|
||||||
@@ -235,7 +259,7 @@ func NewServer(configPath string) (*ServerWrapper, error) {
|
|||||||
srv := &ServerWrapper{
|
srv := &ServerWrapper{
|
||||||
Server: &http.Server{
|
Server: &http.Server{
|
||||||
Addr: fmt.Sprintf("%s:%d", C.Address, C.Port),
|
Addr: fmt.Sprintf("%s:%d", C.Address, C.Port),
|
||||||
Handler: withServerHeader(mux),
|
Handler: withServerHeader(withStatusLogging(mux)),
|
||||||
ReadHeaderTimeout: readHeaderTimeout,
|
ReadHeaderTimeout: readHeaderTimeout,
|
||||||
WriteTimeout: time.Duration(C.BackupTimeout) * time.Minute,
|
WriteTimeout: time.Duration(C.BackupTimeout) * time.Minute,
|
||||||
IdleTimeout: idleTimeout,
|
IdleTimeout: idleTimeout,
|
||||||
@@ -374,8 +398,20 @@ type (
|
|||||||
ServerWrapper struct {
|
ServerWrapper struct {
|
||||||
*http.Server
|
*http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// responseWriterWrapper wraps http.ResponseWriter to capture status code.
|
||||||
|
responseWriterWrapper struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
statusCode int
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// WriteHeader captures the status code for logging.
|
||||||
|
func (rw *responseWriterWrapper) WriteHeader(code int) {
|
||||||
|
rw.statusCode = code
|
||||||
|
rw.ResponseWriter.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
// Serve starts the server and classifies any errors returned.
|
// Serve starts the server and classifies any errors returned.
|
||||||
// TLS-related errors are logged at debug level, other errors at warn level.
|
// TLS-related errors are logged at debug level, other errors at warn level.
|
||||||
func (sw *ServerWrapper) Serve() error {
|
func (sw *ServerWrapper) Serve() error {
|
||||||
|
|||||||
@@ -523,4 +523,99 @@ func TestCopyWithContextCancellation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestStatusLogging404 tests that 404 responses are logged.
|
||||||
|
func TestStatusLogging404(t *testing.T) {
|
||||||
|
C = Config{} // Reset global config.
|
||||||
|
excludePatterns = nil
|
||||||
|
|
||||||
|
// Create a minimal mux that returns 404 for all paths
|
||||||
|
testMux := http.NewServeMux()
|
||||||
|
testMux.HandleFunc("/archive", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
wrappedMux := withStatusLogging(testMux)
|
||||||
|
|
||||||
|
// Use a ResponseRecorder to capture the response
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
req, _ := http.NewRequest("GET", "/nonexistent", nil)
|
||||||
|
req.RemoteAddr = "127.0.0.1:12345"
|
||||||
|
|
||||||
|
wrappedMux.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
if rr.Code != http.StatusNotFound {
|
||||||
|
t.Errorf("Expected status 404, got %d", rr.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestStatusLogging405 tests that 405 responses are logged.
|
||||||
|
func TestStatusLogging405(t *testing.T) {
|
||||||
|
C = Config{} // Reset global config.
|
||||||
|
excludePatterns = nil
|
||||||
|
|
||||||
|
// Create a minimal mux that returns 405 for wrong method
|
||||||
|
testMux := http.NewServeMux()
|
||||||
|
testMux.HandleFunc("/archive", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
})
|
||||||
|
|
||||||
|
wrappedMux := withStatusLogging(testMux)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
req, _ := http.NewRequest("GET", "/archive", nil)
|
||||||
|
req.RemoteAddr = "127.0.0.1:12345"
|
||||||
|
|
||||||
|
wrappedMux.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
if rr.Code != http.StatusMethodNotAllowed {
|
||||||
|
t.Errorf("Expected status 405, got %d", rr.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMethodNotAllowedHandler tests that non-GET methods return 405.
|
||||||
|
func TestMethodNotAllowedHandler(t *testing.T) {
|
||||||
|
C = Config{
|
||||||
|
Address: "0.0.0.0",
|
||||||
|
Port: 8086,
|
||||||
|
Location: "/archive",
|
||||||
|
User: "testuser",
|
||||||
|
Password: "testpass",
|
||||||
|
Directories: []string{"../../test_data/test1/foo"},
|
||||||
|
}
|
||||||
|
excludePatterns = nil
|
||||||
|
|
||||||
|
// Create test server
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
username, password, ok := r.BasicAuth()
|
||||||
|
if !ok || username != C.User || password != C.Password {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
w.Header().Set("Server", "backer")
|
||||||
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
// Test POST method
|
||||||
|
req, _ := http.NewRequest("POST", server.URL+"/archive", nil)
|
||||||
|
req.SetBasicAuth("testuser", "testpass")
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Request failed: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Errorf("Expected status 405, got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* vim: setlocal ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: setlocal ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
Reference in New Issue
Block a user