[bugfix] ensure timeline limit query is respected (#4141)

# Description

Fixes a bug in the new timeline code in which the limit query parameter wasn't always being fulfilled, in which case some clients like Tusky would then assume it didn't need to add a "load more" placeholder view even when there were more statuses to be loaded. This also fiddles around a bit in the logging middleware handler to add some more code comments, and add logging of full request URIs when it is safe to do so.

## Checklist

- [x] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md).
- [x] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat.
- [x] I/we have not leveraged AI to create the proposed changes.
- [x] I/we have performed a self-review of added code.
- [x] I/we have written code that is legible and maintainable by others.
- [x] I/we have commented the added code, particularly in hard-to-understand areas.
- [ ] I/we have made any necessary changes to documentation.
- [x] I/we have added tests that cover new code.
- [x] I/we have run tests and they pass locally with the changes.
- [x] I/we have run `go fmt ./...` and `golangci-lint run`.

Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4141
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
kim
2025-05-06 13:30:23 +00:00
committed by tobi
parent e464de1322
commit 8264b63337
5 changed files with 172 additions and 65 deletions

View File

@ -21,6 +21,7 @@ import (
"fmt"
"net/http"
"runtime"
"strings"
"time"
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
@ -35,19 +36,21 @@ import (
// Logger returns a gin middleware which provides request logging and panic recovery.
func Logger(logClientIP bool) gin.HandlerFunc {
return func(c *gin.Context) {
// Initialize the logging fields
fields := make(kv.Fields, 5, 7)
// Determine pre-handler time
before := time.Now()
// defer so that we log *after the request has completed*
// defer so that we log *after
// the request has completed*
defer func() {
// Get response status code.
code := c.Writer.Status()
path := c.Request.URL.Path
// Get request context.
ctx := c.Request.Context()
if r := recover(); r != nil {
if c.Writer.Status() == 0 {
if code == 0 {
// No response was written, send a generic Internal Error
c.Writer.WriteHeader(http.StatusInternalServerError)
}
@ -65,37 +68,51 @@ func Logger(logClientIP bool) gin.HandlerFunc {
WithField("stacktrace", callers).Error(err)
}
// NOTE:
// It is very important here that we are ONLY logging
// the request path, and none of the query parameters.
// Query parameters can contain sensitive information
// and could lead to storing plaintext API keys in logs
// Initialize the logging fields
fields := make(kv.Fields, 5, 8)
// Set request logging fields
fields[0] = kv.Field{"latency", time.Since(before)}
fields[1] = kv.Field{"userAgent", c.Request.UserAgent()}
fields[2] = kv.Field{"method", c.Request.Method}
fields[3] = kv.Field{"statusCode", code}
fields[4] = kv.Field{"path", path}
// Set optional request logging fields.
// If the request contains sensitive query
// data only log path, else log entire URI.
if sensitiveQuery(c.Request.URL.RawQuery) {
path := c.Request.URL.Path
fields[4] = kv.Field{"uri", path}
} else {
uri := c.Request.RequestURI
fields[4] = kv.Field{"uri", uri}
}
if logClientIP {
// Append IP only if configured to.
fields = append(fields, kv.Field{
"clientIP", c.ClientIP(),
})
}
ctx := c.Request.Context()
if pubKeyID := gtscontext.HTTPSignaturePubKeyID(ctx); pubKeyID != nil {
// Append public key ID if attached.
fields = append(fields, kv.Field{
"pubKeyID", pubKeyID.String(),
})
}
// Create log entry with fields
l := log.New()
l = l.WithContext(ctx)
l = l.WithFields(fields...)
if len(c.Errors) > 0 {
// Always attach any found errors.
fields = append(fields, kv.Field{
"errors", c.Errors,
})
}
// Create entry
// with fields.
l := log.New().
WithContext(ctx).
WithFields(fields...)
// Default is info
lvl := log.INFO
@ -105,11 +122,6 @@ func Logger(logClientIP bool) gin.HandlerFunc {
lvl = log.ERROR
}
if len(c.Errors) > 0 {
// Always attach any found errors.
l = l.WithField("errors", c.Errors)
}
// Get appropriate text for this code.
statusText := http.StatusText(code)
if statusText == "" {
@ -125,15 +137,22 @@ func Logger(logClientIP bool) gin.HandlerFunc {
// Generate a nicer looking bytecount
size := bytesize.Size(c.Writer.Size()) // #nosec G115 -- Just logging
// Finally, write log entry with status text + body size.
// Write log entry with status text + body size.
l.Logf(lvl, "%s: wrote %s", statusText, size)
}()
// Process request
// Process
// request.
c.Next()
}
}
// sensitiveQuery checks whether given query string
// contains sensitive data that shouldn't be logged.
func sensitiveQuery(query string) bool {
return strings.Contains(query, "token")
}
// gatherFrames gathers runtime frames from a frame iterator.
func gatherFrames(iter *runtime.Frames, n int) []runtime.Frame {
if iter == nil {