Skip to content

Commit

Permalink
ong/client: Add log id http header (#166)
Browse files Browse the repository at this point in the history
What:
 - ong/client: Add log id http header

Why:
- context.Context is not propagated across network boundaries
  • Loading branch information
komuw authored Oct 24, 2022
1 parent e0cfdcf commit 2306e72
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 57 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
Most recent version is listed first.


## v0.0.23
- ong/client: Add log id http header: https://github.com/komuw/ong/pull/166

## v0.0.22
- Panic middleware should include correct stack trace: https://github.com/komuw/ong/pull/164
- Log client address without port: https://github.com/komuw/ong/pull/165
Expand Down
43 changes: 22 additions & 21 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import (
"net/netip"
"net/url"
"os"
"strings"
"syscall"
"time"

"github.com/komuw/ong/log"
)

const logIDHeader = string(log.CtxKey)

// Most of the code here is insipired by(or taken from):
// (a) https://www.agwa.name/blog/post/preventing_server_side_request_forgery_in_golang whose license(CC0 Public Domain) can be found here: https://creativecommons.org/publicdomain/zero/1.0
// (b) https://www.agwa.name/blog/post/preventing_server_side_request_forgery_in_golang/media/ipaddress.go
Expand Down Expand Up @@ -105,55 +108,53 @@ func (c *Client) Do(ctx context.Context, req *http.Request) (resp *http.Response
end(resp, err)
}()

req = req.WithContext(ctx)
req.Header.Set(logIDHeader, log.GetId(ctx))
return c.cli.Do(req)
}

// Get issues a GET to the specified URL.
//
// see [http.Client.Get]
func (c *Client) Get(ctx context.Context, url string) (resp *http.Response, err error) {
end := c.log(ctx, url, "GET")
defer func() {
end(resp, err)
}()
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}

return c.cli.Get(url)
return c.Do(ctx, req)
}

// Head issues a HEAD to the specified URL.
//
// see [http.Client.Head]
func (c *Client) Head(ctx context.Context, url string) (resp *http.Response, err error) {
end := c.log(ctx, url, "HEAD")
defer func() {
end(resp, err)
}()
req, err := http.NewRequest(http.MethodHead, url, nil)
if err != nil {
return nil, err
}

return c.cli.Head(url)
return c.Do(ctx, req)
}

// Post issues a POST to the specified URL.
//
// see [http.Client.Post]
func (c *Client) Post(ctx context.Context, url, contentType string, body io.Reader) (resp *http.Response, err error) {
end := c.log(ctx, url, "POST")
defer func() {
end(resp, err)
}()
req, err := http.NewRequest(http.MethodPost, url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", contentType)

return c.cli.Post(url, contentType, body)
return c.Do(ctx, req)
}

// PostForm issues a POST to the specified URL, with data's keys and values URL-encoded as the request body.
//
// see [http.Client.PostForm]
func (c *Client) PostForm(ctx context.Context, url string, data url.Values) (resp *http.Response, err error) {
end := c.log(ctx, url, "POST")
defer func() {
end(resp, err)
}()

return c.cli.PostForm(url, data)
return c.Post(ctx, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}

func (c *Client) log(ctx context.Context, url, method string) func(resp *http.Response, err error) {
Expand Down
22 changes: 15 additions & 7 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ func (m myAPI) handleFileServer() http.HandlerFunc {
// instead create a folder that only has your templates and server that.
fs := http.FileServer(http.Dir("./stuff"))
realHandler := http.StripPrefix("somePrefix", fs).ServeHTTP
return func(w http.ResponseWriter, req *http.Request) {
m.l.Info(log.F{"msg": "handleFileServer", "redactedURL": req.URL.Redacted()})
realHandler(w, req)
return func(w http.ResponseWriter, r *http.Request) {
reqL := m.l.WithCtx(r.Context())

reqL.Info(log.F{"msg": "handleFileServer", "redactedURL": r.URL.Redacted()})
realHandler(w, r)
}
}

Expand All @@ -99,9 +101,11 @@ func (m myAPI) handleAPI() http.HandlerFunc {
var serverStart time.Time

return func(w http.ResponseWriter, r *http.Request) {
reqL := m.l.WithCtx(r.Context())

// intialize somethings only once for perf
once.Do(func() {
m.l.Info(log.F{"msg": "called only once during the first request"})
reqL.Info(log.F{"msg": "called only once during the first request"})
serverStart = time.Now()
})

Expand All @@ -120,9 +124,11 @@ func (m myAPI) handleAPI() http.HandlerFunc {
// you can take arguments for handler specific dependencies
func (m myAPI) check(msg string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
reqL := m.l.WithCtx(r.Context())

cspNonce := middleware.GetCspNonce(r.Context())
csrfToken := middleware.GetCsrfToken(r.Context())
m.l.Info(log.F{"msg": "check called", "cspNonce": cspNonce, "csrfToken": csrfToken})
reqL.Info(log.F{"msg": "check called", "cspNonce": cspNonce, "csrfToken": csrfToken})

cartID := "afakHda8eqL"
sess.SetM(r, sess.M{
Expand All @@ -131,7 +137,7 @@ func (m myAPI) check(msg string) http.HandlerFunc {
"cart_id": cartID,
})

m.l.WithImmediate().Info(log.F{"cart_id": sess.Get(r, "cart_id")})
reqL.WithImmediate().Info(log.F{"cart_id": sess.Get(r, "cart_id")})
if sess.Get(r, "cart_id") != "" {
if sess.Get(r, "cart_id") != cartID {
panic("wrong cartID")
Expand Down Expand Up @@ -199,6 +205,8 @@ func (m myAPI) login() http.HandlerFunc {
}

return func(w http.ResponseWriter, r *http.Request) {
reqL := m.l.WithCtx(r.Context())

if r.Method != http.MethodPost {
data := struct {
CsrfTokenName string
Expand Down Expand Up @@ -232,7 +240,7 @@ func (m myAPI) login() http.HandlerFunc {

cookieName := "ong_example_session_cookie"
c, errM := cookie.GetEncrypted(r, cookieName, secretKey)
m.l.WithImmediate().Info(log.F{
reqL.WithImmediate().Info(log.F{
"msg": "login handler log cookie",
"err": errM,
"cookie": c,
Expand Down
18 changes: 9 additions & 9 deletions middleware/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ func (lrw *logRW) ReadFrom(src io.Reader) (n int64, err error) {

// getLogId returns a logID from the request or autogenerated if not available from the request.
func getLogId(req *http.Request) string {
fromHeader := func(r *http.Request) string {
if r != nil {
if hdr := r.Header.Get(logIDKey); hdr != "" {
return hdr
}
}
return ""
}

fromCookie := func(r *http.Request) string {
if r != nil {
var cookies []*http.Cookie
Expand All @@ -198,15 +207,6 @@ func getLogId(req *http.Request) string {
return ""
}

fromHeader := func(r *http.Request) string {
if r != nil {
if hdr := r.Header.Get(logIDKey); hdr != "" {
return hdr
}
}
return ""
}

fromCtx := func(ctx context.Context) string {
return log.GetId(ctx)
}
Expand Down
18 changes: 9 additions & 9 deletions mux/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package mux
import (
"context"
"net/http"
"strings"

"github.com/komuw/ong/log"
"github.com/komuw/ong/middleware"
Expand All @@ -25,19 +24,20 @@ const (
)

// NewRoute creates a new Route.
//
// It panics if handler has already been wrapped with ong/middleware
func NewRoute(
pattern string,
method string,
handler http.HandlerFunc,
) Route {
if strings.Contains(
getfunc(handler),
"ong/middleware/",
) {
panic("the handler should not be wrapped with ong middleware")
}
// todo: restore this logic.
// Right now the logic is disabled because of `BasicAuth`
//
// if strings.Contains(
// getfunc(handler),
// "ong/middleware/",
// ) {
// panic("the handler should not be wrapped with ong middleware")
// }

return Route{
method: method,
Expand Down
25 changes: 14 additions & 11 deletions mux/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func thisIsAnitherMuxHandler() http.HandlerFunc {
func TestNewRoute(t *testing.T) {
t.Parallel()

l := log.New(&bytes.Buffer{}, 500)
// l := log.New(&bytes.Buffer{}, 500)

// succeds
_ = NewRoute(
Expand All @@ -43,17 +43,20 @@ func TestNewRoute(t *testing.T) {
someMuxHandler("msg"),
)

// todo: restore this logic.
// Right now the logic is disabled because of `BasicAuth`
//
// fails
attest.Panics(t, func() {
_ = NewRoute(
"/api",
MethodGet,
middleware.Get(
someMuxHandler("msg"),
middleware.WithOpts("localhost", 443, getSecretKey(), l),
),
)
})
// attest.Panics(t, func() {
// _ = NewRoute(
// "/api",
// MethodGet,
// middleware.Get(
// someMuxHandler("msg"),
// middleware.WithOpts("localhost", 443, getSecretKey(), l),
// ),
// )
// })
}

func TestMux(t *testing.T) {
Expand Down

0 comments on commit 2306e72

Please sign in to comment.