13 changed files with 200 additions and 105 deletions
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
package httpserv |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net/http" |
||||
"os" |
||||
"runtime" |
||||
) |
||||
|
||||
// exit when there's a panic inside the HTTP handler.
|
||||
// https://github.com/golang/go/issues/16542
|
||||
type handlerExitOnPanic struct { |
||||
http.Handler |
||||
} |
||||
|
||||
func (h *handlerExitOnPanic) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
||||
defer func() { |
||||
err := recover() |
||||
if err != nil { |
||||
buf := make([]byte, 1<<20) |
||||
n := runtime.Stack(buf, true) |
||||
fmt.Fprintf(os.Stderr, "panic: %v\n\n%s", err, buf[:n]) |
||||
os.Exit(1) |
||||
} |
||||
}() |
||||
h.Handler.ServeHTTP(w, r) |
||||
} |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
package httpserv |
||||
|
||||
import ( |
||||
"net/http" |
||||
) |
||||
|
||||
// reject requests with empty paths.
|
||||
type handlerFilterRequests struct { |
||||
http.Handler |
||||
} |
||||
|
||||
func (h *handlerFilterRequests) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
||||
if r.URL.Path == "" || r.URL.Path[0] != '/' { |
||||
w.WriteHeader(http.StatusBadRequest) |
||||
return |
||||
} |
||||
h.Handler.ServeHTTP(w, r) |
||||
} |
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
package httpserv |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"net/http" |
||||
"net/http/httputil" |
||||
|
||||
"github.com/bluenviron/mediamtx/internal/logger" |
||||
) |
||||
|
||||
type loggerWriter struct { |
||||
w http.ResponseWriter |
||||
status int |
||||
buf bytes.Buffer |
||||
} |
||||
|
||||
func (w *loggerWriter) Header() http.Header { |
||||
return w.w.Header() |
||||
} |
||||
|
||||
func (w *loggerWriter) Write(b []byte) (int, error) { |
||||
if w.status == 0 { |
||||
w.status = http.StatusOK |
||||
} |
||||
w.buf.Write(b) |
||||
return w.w.Write(b) |
||||
} |
||||
|
||||
func (w *loggerWriter) WriteHeader(statusCode int) { |
||||
w.status = statusCode |
||||
w.w.WriteHeader(statusCode) |
||||
} |
||||
|
||||
func (w *loggerWriter) dump() string { |
||||
var buf bytes.Buffer |
||||
fmt.Fprintf(&buf, "%s %d %s\n", "HTTP/1.1", w.status, http.StatusText(w.status)) |
||||
w.w.Header().Write(&buf) |
||||
buf.Write([]byte("\n")) |
||||
if w.buf.Len() > 0 { |
||||
fmt.Fprintf(&buf, "(body of %d bytes)", w.buf.Len()) |
||||
} |
||||
return buf.String() |
||||
} |
||||
|
||||
// log requests and responses.
|
||||
type handlerLogger struct { |
||||
http.Handler |
||||
log logger.Writer |
||||
} |
||||
|
||||
func (h *handlerLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
||||
h.log.Log(logger.Debug, "[conn %v] %s %s", r.RemoteAddr, r.Method, r.URL.Path) |
||||
|
||||
byts, _ := httputil.DumpRequest(r, true) |
||||
h.log.Log(logger.Debug, "[conn %v] [c->s] %s", r.RemoteAddr, string(byts)) |
||||
|
||||
logw := &loggerWriter{w: w} |
||||
|
||||
h.Handler.ServeHTTP(logw, r) |
||||
|
||||
h.log.Log(logger.Debug, "[conn %v] [s->c] %s", r.RemoteAddr, logw.dump()) |
||||
} |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
package httpserv |
||||
|
||||
import ( |
||||
"net/http" |
||||
) |
||||
|
||||
// set the Server header.
|
||||
type handlerServerHeader struct { |
||||
http.Handler |
||||
} |
||||
|
||||
func (h *handlerServerHeader) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
||||
w.Header().Set("Server", "mediamtx") |
||||
h.Handler.ServeHTTP(w, r) |
||||
} |
@ -1,55 +0,0 @@
@@ -1,55 +0,0 @@
|
||||
package httpserv |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"net/http" |
||||
"net/http/httputil" |
||||
|
||||
"github.com/gin-gonic/gin" |
||||
|
||||
"github.com/bluenviron/mediamtx/internal/logger" |
||||
) |
||||
|
||||
type loggerWriter struct { |
||||
gin.ResponseWriter |
||||
buf bytes.Buffer |
||||
} |
||||
|
||||
func (w *loggerWriter) Write(b []byte) (int, error) { |
||||
w.buf.Write(b) |
||||
return w.ResponseWriter.Write(b) |
||||
} |
||||
|
||||
func (w *loggerWriter) WriteString(s string) (int, error) { |
||||
w.buf.WriteString(s) |
||||
return w.ResponseWriter.WriteString(s) |
||||
} |
||||
|
||||
func (w *loggerWriter) dump() string { |
||||
var buf bytes.Buffer |
||||
fmt.Fprintf(&buf, "%s %d %s\n", "HTTP/1.1", w.ResponseWriter.Status(), http.StatusText(w.ResponseWriter.Status())) |
||||
w.ResponseWriter.Header().Write(&buf) |
||||
buf.Write([]byte("\n")) |
||||
if w.buf.Len() > 0 { |
||||
fmt.Fprintf(&buf, "(body of %d bytes)", w.buf.Len()) |
||||
} |
||||
return buf.String() |
||||
} |
||||
|
||||
// MiddlewareLogger is a middleware that logs requests and responses.
|
||||
func MiddlewareLogger(p logger.Writer) func(*gin.Context) { |
||||
return func(ctx *gin.Context) { |
||||
p.Log(logger.Debug, "[conn %v] %s %s", ctx.Request.RemoteAddr, ctx.Request.Method, ctx.Request.URL.Path) |
||||
|
||||
byts, _ := httputil.DumpRequest(ctx.Request, true) |
||||
p.Log(logger.Debug, "[conn %v] [c->s] %s", ctx.Request.RemoteAddr, string(byts)) |
||||
|
||||
logw := &loggerWriter{ResponseWriter: ctx.Writer} |
||||
ctx.Writer = logw |
||||
|
||||
ctx.Next() |
||||
|
||||
p.Log(logger.Debug, "[conn %v] [s->c] %s", ctx.Request.RemoteAddr, logw.dump()) |
||||
} |
||||
} |
@ -1,11 +0,0 @@
@@ -1,11 +0,0 @@
|
||||
package httpserv |
||||
|
||||
import ( |
||||
"github.com/gin-gonic/gin" |
||||
) |
||||
|
||||
// MiddlewareServerHeader is a middleware that sets the Server header.
|
||||
func MiddlewareServerHeader(ctx *gin.Context) { |
||||
ctx.Writer.Header().Set("Server", "mediamtx") |
||||
ctx.Next() |
||||
} |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
package httpserv |
||||
|
||||
import ( |
||||
"io" |
||||
"net" |
||||
"testing" |
||||
"time" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
|
||||
"github.com/bluenviron/mediamtx/internal/logger" |
||||
) |
||||
|
||||
type testLogger struct{} |
||||
|
||||
func (testLogger) Log(_ logger.Level, _ string, _ ...interface{}) { |
||||
// fmt.Printf(format, args...)
|
||||
} |
||||
|
||||
func TestFilterEmptyPath(t *testing.T) { |
||||
s, err := NewWrappedServer( |
||||
"tcp", |
||||
"localhost:4555", |
||||
10*time.Second, |
||||
"", |
||||
"", |
||||
nil, |
||||
&testLogger{}) |
||||
require.NoError(t, err) |
||||
defer s.Close() |
||||
|
||||
conn, err := net.Dial("tcp", "localhost:4555") |
||||
require.NoError(t, err) |
||||
defer conn.Close() |
||||
|
||||
_, err = conn.Write([]byte("OPTIONS http://localhost HTTP/1.1\n" + |
||||
"Host: localhost:8889\n" + |
||||
"Accept-Encoding: gzip\n" + |
||||
"User-Agent: Go-http-client/1.1\n\n")) |
||||
require.NoError(t, err) |
||||
|
||||
buf := make([]byte, 20) |
||||
_, err = io.ReadFull(conn, buf) |
||||
require.NoError(t, err) |
||||
} |
Loading…
Reference in new issue