Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ong/client: Add log id http header #166

Merged
merged 5 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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