Compare commits

...

6 commits

Author SHA1 Message Date
51b35036ab
Merge branch 'main' of git.mstar.dev:mstar/goutils 2025-06-06 17:08:03 +02:00
7881a55436
Add Server Side Events helper 2025-06-06 17:07:31 +02:00
4eb5d68fbf
Fix for the fix
I didn't actually write the found ip to the logging stack (or whatever
zerolog uses). This is now fixed
2025-05-12 15:35:45 +02:00
d7ed5b1eae
fix(webutils): Bad Ip logging
Fix the logging middleware only logging the last proxy's ip instead of
the ip of the actual request
2025-05-12 15:08:53 +02:00
506834c881
Extend logging middleware with more config options 2025-05-04 19:28:03 +02:00
4b8a62ba12
Add some more documentation and links to godoc# 2025-04-28 08:49:13 +02:00
10 changed files with 97 additions and 7 deletions

View file

@ -1,5 +1,8 @@
# GoUtils
![Go Coverage](https://git.mstar.dev/mstar/goutils/raw/branch/main/coverage_badge.png) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://git.mstar.dev/mstar/goutils/src/branch/main/LICENSE)
![Go Coverage](https://git.mstar.dev/mstar/goutils/raw/branch/main/coverage_badge.png)
[![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://git.mstar.dev/mstar/goutils/src/branch/main/LICENSE)
[![Go Documentation](https://godocs.io/git.mstar.dev/mstar/goutils?status.svg)](https://godocs.io/git.mstar.dev/mstar/goutils)
[![GoDoc](https://pkg.go.dev/badge/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.

View file

@ -1,3 +1,4 @@
// Package containers contains implementations of a generic queue and stack
package containers
type chainElem[T any] struct {

View file

@ -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 (

View file

@ -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
View 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
}

View file

@ -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)
})
}
}

View file

@ -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"

View file

@ -1,3 +1,4 @@
// Package mathutils contains helper functions for performing common mathematical operations
package mathutils
type SignedNumber interface {

View file

@ -1,3 +1,4 @@
// Package other contains various funtions that didn't fit into any of the other packages
package other
import "fmt"

View file

@ -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.