Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
51b35036ab | |||
7881a55436 | |||
4eb5d68fbf | |||
d7ed5b1eae | |||
506834c881 | |||
4b8a62ba12 |
10 changed files with 97 additions and 7 deletions
|
@ -1,5 +1,8 @@
|
|||
# GoUtils
|
||||
|
||||
 [](https://git.mstar.dev/mstar/goutils/src/branch/main/LICENSE)
|
||||

|
||||
[](https://git.mstar.dev/mstar/goutils/src/branch/main/LICENSE)
|
||||
[](https://godocs.io/git.mstar.dev/mstar/goutils)
|
||||
[](https://pkg.go.dev/git.mstar.dev/mstar/goutils)
|
||||
|
||||
A collection of useful functions and structs that I use for my projects.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package containers contains implementations of a generic queue and stack
|
||||
package containers
|
||||
|
||||
type chainElem[T any] struct {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Package embedFsWrapper contains a wrapper around [io/fs.FS] for working
|
||||
// around a limitation of the [embed] package
|
||||
package embedFsWrapper
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Package webutils contains various functions for easier interaction and common tasks
|
||||
// when working with [net/http.Handler] based webservers
|
||||
package webutils
|
||||
|
||||
import (
|
||||
|
|
37
http/sse.go
Normal file
37
http/sse.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package webutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrWriterNotFlushable = errors.New("response writer not flushable")
|
||||
)
|
||||
|
||||
// SseWriter provides a simple implementation for sending data via Server Side Events
|
||||
// to the client. The function runs until the dataStream channel is closed.
|
||||
// The ResponseWriter must not be used after calling this function
|
||||
//
|
||||
// Inspired by and partially copied from https://medium.com/@rian.eka.cahya/server-sent-event-sse-with-go-10592d9c2aa1
|
||||
func SseWriter(w http.ResponseWriter, dataStream chan []byte) error {
|
||||
w.Header().Set("Access-Control-Expose-Headers", "Content-Type")
|
||||
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
return ErrWriterNotFlushable
|
||||
}
|
||||
for data := range dataStream {
|
||||
_, err := fmt.Fprintf(w, "%s\n\n", string(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flusher.Flush()
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -5,19 +5,36 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/hlog"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func BuildLoggingMiddleware(extras map[string]string) HandlerBuilder {
|
||||
func BuildLoggingMiddleware(
|
||||
status500IsError bool,
|
||||
ignorePaths []string,
|
||||
extras map[string]string,
|
||||
) HandlerBuilder {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return ChainMiddlewares(h,
|
||||
hlog.NewHandler(log.Logger),
|
||||
hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
|
||||
if strings.HasPrefix(r.URL.Path, "/assets") {
|
||||
return
|
||||
for _, p := range ignorePaths {
|
||||
if strings.HasPrefix(r.URL.Path, p) {
|
||||
return
|
||||
}
|
||||
}
|
||||
logger := hlog.FromRequest(r).Info().
|
||||
var logger *zerolog.Event
|
||||
if status >= 500 {
|
||||
if status500IsError {
|
||||
logger = hlog.FromRequest(r).Error()
|
||||
} else {
|
||||
logger = hlog.FromRequest(r).Warn()
|
||||
}
|
||||
} else {
|
||||
logger = hlog.FromRequest(r).Info()
|
||||
}
|
||||
logger = logger.
|
||||
Str("method", r.Method).
|
||||
Stringer("url", r.URL).
|
||||
Int("status", status).
|
||||
|
@ -28,7 +45,7 @@ func BuildLoggingMiddleware(extras map[string]string) HandlerBuilder {
|
|||
}
|
||||
logger.Send()
|
||||
}),
|
||||
hlog.RemoteAddrHandler("ip"),
|
||||
RealIpAppenderMiddleware("ip"),
|
||||
hlog.UserAgentHandler("user_agent"),
|
||||
hlog.RefererHandler("referer"),
|
||||
hlog.RequestIDHandler("req_id", "Request-Id"),
|
||||
|
@ -51,9 +68,33 @@ func LoggingMiddleware(handler http.Handler) http.Handler {
|
|||
Dur("duration", duration).
|
||||
Send()
|
||||
}),
|
||||
hlog.RemoteAddrHandler("ip"),
|
||||
RealIpAppenderMiddleware("ip"),
|
||||
hlog.UserAgentHandler("user_agent"),
|
||||
hlog.RefererHandler("referer"),
|
||||
hlog.RequestIDHandler("req_id", "Request-Id"),
|
||||
)
|
||||
}
|
||||
|
||||
// hlog.RemoteAddrHandler except fixed to check the X-Real-Ip and X-Forwarded-For
|
||||
// headers first for the IP instead of relying on RemoteAddr
|
||||
// (which would only return the last proxy's address instead of the caller's)
|
||||
func RealIpAppenderMiddleware(fieldKey string) func(handler http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
IPAddress := r.Header.Get("X-Real-Ip")
|
||||
if IPAddress == "" {
|
||||
IPAddress = r.Header.Get("X-Forwarded-For")
|
||||
}
|
||||
if IPAddress == "" {
|
||||
IPAddress = r.RemoteAddr
|
||||
}
|
||||
if IPAddress != "" {
|
||||
log := zerolog.Ctx(r.Context())
|
||||
log.UpdateContext(func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str(fieldKey, IPAddress)
|
||||
})
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package maputils contains various generic functions for applying an operation on an entire map
|
||||
package maputils
|
||||
|
||||
import "git.mstar.dev/mstar/goutils/sliceutils"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package mathutils contains helper functions for performing common mathematical operations
|
||||
package mathutils
|
||||
|
||||
type SignedNumber interface {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package other contains various funtions that didn't fit into any of the other packages
|
||||
package other
|
||||
|
||||
import "fmt"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package sliceutils contains various generic functions for applying an operation across an entire slice
|
||||
package sliceutils
|
||||
|
||||
// MapS applies a given function to every element of a slice.
|
||||
|
|
Loading…
Reference in a new issue