Skip to content

Commit

Permalink
issues/92: Add support for http.NewResponseController (#368)
Browse files Browse the repository at this point in the history
- Updates: #92
- This still doesn't fix the issue, there is still #213 for that
  • Loading branch information
komuw authored Aug 28, 2023
1 parent d6e111b commit 622b997
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 21 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.77
- ong/middleware: Add support for http.NewResponseController: https://github.com/komuw/ong/pull/368

# v0.0.76
- ong/middleware: Configure what percentage of ratelimited or loadshed responses should be logged: https://github.com/komuw/ong/pull/364

Expand Down
2 changes: 1 addition & 1 deletion example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func main() {
mux.NewRoute(
"staticAssets/:file",
mux.MethodAll,
middleware.BasicAuth(api.handleFileServer(), "user", "some-long-passwd"),
middleware.BasicAuth(api.handleFileServer(), "user", "some-long-p1sswd"),
),
mux.NewRoute(
"check/:age/",
Expand Down
9 changes: 7 additions & 2 deletions internal/key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import (
// IsSecure checks that secretKey has at least some minimum desirable security properties.
func IsSecure(secretKey string) error {
const (
minLen = 6
// Using a password like `4$kplejewjdsnv`(one number, one symbol & length 14)
// would take about 90 million years to crack.
// see:
// - https://www.passwordmonster.com/
// - https://thesecurityfactory.be/password-cracking-speed/
minLen = 14
maxLen = 256
expected = 1
)
Expand All @@ -28,7 +33,7 @@ func IsSecure(secretKey string) error {
if unicode.IsDigit(r) {
hasDigit = hasDigit + 1
}
if unicode.IsPunct(r) {
if unicode.IsPunct(r) || unicode.IsSymbol(r) {
hasSymbol = hasSymbol + 1
}
if unicode.IsLetter(r) {
Expand Down
7 changes: 7 additions & 0 deletions internal/key/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ func TestCheckSecretKey(t *testing.T) {
attest.Ok(t, err)
},
},
{
name: "small secure key is ok",
key: "4$kplejewjdsnv",
check: func(err error) {
attest.Ok(t, err)
},
},
{
name: "bad key",
key: "super-h@rd-password",
Expand Down
9 changes: 4 additions & 5 deletions middleware/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ package middleware

import (
"crypto/subtle"
"fmt"
"net/http"
)

const minPasswdSize = 16
"github.com/komuw/ong/internal/key"
)

// BasicAuth is a middleware that protects wrappedHandler using basic authentication.
func BasicAuth(wrappedHandler http.Handler, user, passwd string) http.HandlerFunc {
const realm = "enter username and password"

if len(passwd) < minPasswdSize {
panic(fmt.Sprintf("passwd cannot be less than %d in size.", minPasswdSize))
if err := key.IsSecure(passwd); err != nil {
panic(err)
}

e := func(w http.ResponseWriter) {
Expand Down
5 changes: 2 additions & 3 deletions middleware/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ func TestBasicAuth(t *testing.T) {
{
// small passwd panics.
attest.Panics(t, func() {
BasicAuth(protectedHandler("hello"), "user", strings.Repeat("a", (minPasswdSize-3)))
BasicAuth(protectedHandler("hello"), "user", strings.Repeat("a", 8))
},
)
}

msg := "hello"
user := "some-user"
passwd := "some-long-passwd"
passwd := "some-long-p1sswd"
wrappedHandler := BasicAuth(protectedHandler(msg), user, passwd)

tests := []struct {
Expand Down Expand Up @@ -81,7 +81,6 @@ func TestBasicAuth(t *testing.T) {
res := rec.Result()
defer res.Body.Close()

attest.True(t, len(passwd) >= minPasswdSize)
attest.Equal(t, res.StatusCode, tt.wantCode)
})
}
Expand Down
12 changes: 9 additions & 3 deletions middleware/gzip.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ var (
_ http.Pusher = &gzipRW{}
_ io.WriteCloser = &gzipRW{}
_ io.ReaderFrom = &gzipRW{}
_ httpRespCtrler = &logRW{}
// _ http.CloseNotifier = &gzipRW{} // `http.CloseNotifier` has been deprecated sinc Go v1.11(year 2018)
)

Expand Down Expand Up @@ -220,10 +221,8 @@ func (grw *gzipRW) Hijack() (net.Conn, *bufio.ReadWriter, error) {
// ReadFrom implements io.ReaderFrom
// It is necessary for the sendfile syscall
// https://github.com/caddyserver/caddy/pull/5022
// https://github.com/caddyserver/caddy/blob/v2.7.4/modules/caddyhttp/responsewriter.go#L45-L49
func (grw *gzipRW) ReadFrom(src io.Reader) (n int64, err error) {
if rf, ok := grw.ResponseWriter.(io.ReaderFrom); ok {
return rf.ReadFrom(src)
}
return io.Copy(grw.ResponseWriter, src)
}

Expand All @@ -235,6 +234,13 @@ func (grw *gzipRW) Push(target string, opts *http.PushOptions) error {
return fmt.Errorf("ong/middleware: http.Pusher interface is not supported")
}

// Unwrap implements http.ResponseController.
// It returns the underlying ResponseWriter,
// which is necessary for http.ResponseController to work correctly.
func (grw *gzipRW) Unwrap() http.ResponseWriter {
return grw.ResponseWriter
}

// shouldGzipReq checks whether the request is eligible to be gzipped.
func shouldGzipReq(r *http.Request) bool {
// Examples of the `acceptEncodingHeader` are:
Expand Down
12 changes: 9 additions & 3 deletions middleware/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ var (
_ http.Hijacker = &logRW{}
_ http.Pusher = &logRW{}
_ io.ReaderFrom = &logRW{}
_ httpRespCtrler = &logRW{}
// _ http.CloseNotifier = &logRW{} // `http.CloseNotifier` has been deprecated sinc Go v1.11(year 2018)
)

Expand Down Expand Up @@ -172,9 +173,14 @@ func (lrw *logRW) Push(target string, opts *http.PushOptions) error {
// ReadFrom implements io.ReaderFrom
// It is necessary for the sendfile syscall
// https://github.com/caddyserver/caddy/pull/5022
// https://github.com/caddyserver/caddy/blob/v2.7.4/modules/caddyhttp/responsewriter.go#L45-L49
func (lrw *logRW) ReadFrom(src io.Reader) (n int64, err error) {
if rf, ok := lrw.ResponseWriter.(io.ReaderFrom); ok {
return rf.ReadFrom(src)
}
return io.Copy(lrw.ResponseWriter, src)
}

// Unwrap implements http.ResponseController.
// It returns the underlying ResponseWriter,
// which is necessary for http.ResponseController to work correctly.
func (lrw *logRW) Unwrap() http.ResponseWriter {
return lrw.ResponseWriter
}
8 changes: 8 additions & 0 deletions middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,3 +507,11 @@ func deleteH(wrappedHandler http.Handler) http.HandlerFunc {
wrappedHandler.ServeHTTP(w, r)
}
}

// httpRespCtrler represents the interface that has to be implemented for a
// responseWriter to satisfy [http.ResponseController]
//
// https://github.com/golang/go/blob/go1.21.0/src/net/http/responsecontroller.go#L42-L44
type httpRespCtrler interface {
Unwrap() http.ResponseWriter
}
12 changes: 9 additions & 3 deletions middleware/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ var (
_ http.Hijacker = &sessRW{}
_ http.Pusher = &sessRW{}
_ io.ReaderFrom = &sessRW{}
_ httpRespCtrler = &logRW{}
// _ http.CloseNotifier = &sessRW{} // `http.CloseNotifier` has been deprecated sinc Go v1.11(year 2018)
)

Expand Down Expand Up @@ -132,9 +133,14 @@ func (srw *sessRW) Push(target string, opts *http.PushOptions) error {
// ReadFrom implements io.ReaderFrom
// It is necessary for the sendfile syscall
// https://github.com/caddyserver/caddy/pull/5022
// https://github.com/caddyserver/caddy/blob/v2.7.4/modules/caddyhttp/responsewriter.go#L45-L49
func (srw *sessRW) ReadFrom(src io.Reader) (n int64, err error) {
if rf, ok := srw.ResponseWriter.(io.ReaderFrom); ok {
return rf.ReadFrom(src)
}
return io.Copy(srw.ResponseWriter, src)
}

// Unwrap implements http.ResponseController.
// It returns the underlying ResponseWriter,
// which is necessary for http.ResponseController to work correctly.
func (srw *sessRW) Unwrap() http.ResponseWriter {
return srw.ResponseWriter
}
2 changes: 1 addition & 1 deletion mux/mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestNewRoute(t *testing.T) {
_ = NewRoute(
"/api",
MethodGet,
middleware.BasicAuth(someMuxHandler("msg"), "some-user", "some-very-very-hard-passwd"),
middleware.BasicAuth(someMuxHandler("msg"), "some-user", "some-very-very-h1rd-passwd"),
)

// fails
Expand Down

0 comments on commit 622b997

Please sign in to comment.