diff --git a/go.mod b/go.mod
index e48bae3..1ad4c34 100644
--- a/go.mod
+++ b/go.mod
@@ -9,12 +9,10 @@ require (
github.com/go-sql-driver/mysql v1.7.0
github.com/gorilla/csrf v1.7.1
github.com/gorilla/sessions v1.2.1
- github.com/gorilla/websocket v1.5.0
github.com/julienschmidt/httprouter v1.3.0
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
github.com/rs/zerolog v1.29.0
- github.com/tdewolff/minify v2.3.6+incompatible
github.com/tkw1536/goprogram v0.3.0
github.com/tkw1536/pkglib v0.0.0-20230225192547-93a1aa42a292
github.com/yuin/goldmark v1.5.4
@@ -34,12 +32,14 @@ require (
github.com/boombuler/barcode v1.0.1 // indirect
github.com/feiin/sqlstring v0.3.0 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
+ github.com/gorilla/websocket v1.5.0 // indirect
github.com/gosuri/uilive v0.0.4 // indirect
github.com/jessevdk/go-flags v1.5.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
+ github.com/tdewolff/minify v2.3.6+incompatible // indirect
github.com/tdewolff/parse v2.3.4+incompatible // indirect
golang.org/x/sys v0.5.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
diff --git a/internal/dis/component/auth/next/next.go b/internal/dis/component/auth/next/next.go
index 7415c26..6e985eb 100644
--- a/internal/dis/component/auth/next/next.go
+++ b/internal/dis/component/auth/next/next.go
@@ -11,7 +11,7 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/users"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
+ "github.com/tkw1536/pkglib/httpx"
)
type Next struct {
diff --git a/internal/dis/component/auth/panel/panel.go b/internal/dis/component/auth/panel/panel.go
index 2e2e9c2..d90995d 100644
--- a/internal/dis/component/auth/panel/panel.go
+++ b/internal/dis/component/auth/panel/panel.go
@@ -12,8 +12,8 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/ssh2/sshkeys"
"github.com/FAU-CDI/wisski-distillery/internal/models"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
"github.com/julienschmidt/httprouter"
+ "github.com/tkw1536/pkglib/httpx"
)
type UserPanel struct {
diff --git a/internal/dis/component/auth/panel/password.go b/internal/dis/component/auth/panel/password.go
index 1ae39f2..f199575 100644
--- a/internal/dis/component/auth/panel/password.go
+++ b/internal/dis/component/auth/panel/password.go
@@ -9,8 +9,8 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
+ "github.com/tkw1536/pkglib/httpx"
+ "github.com/tkw1536/pkglib/httpx/field"
)
//go:embed "templates/password.html"
diff --git a/internal/dis/component/auth/panel/ssh.go b/internal/dis/component/auth/panel/ssh.go
index 846daa7..d7c27fc 100644
--- a/internal/dis/component/auth/panel/ssh.go
+++ b/internal/dis/component/auth/panel/ssh.go
@@ -9,10 +9,10 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
"github.com/FAU-CDI/wisski-distillery/internal/models"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
"github.com/gliderlabs/ssh"
"github.com/rs/zerolog"
+ "github.com/tkw1536/pkglib/httpx"
+ "github.com/tkw1536/pkglib/httpx/field"
gossh "golang.org/x/crypto/ssh"
diff --git a/internal/dis/component/auth/panel/totp.go b/internal/dis/component/auth/panel/totp.go
index 7a74588..c8b14fd 100644
--- a/internal/dis/component/auth/panel/totp.go
+++ b/internal/dis/component/auth/panel/totp.go
@@ -8,8 +8,8 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
+ "github.com/tkw1536/pkglib/httpx"
+ "github.com/tkw1536/pkglib/httpx/field"
_ "embed"
)
diff --git a/internal/dis/component/auth/protect.go b/internal/dis/component/auth/protect.go
index 24ca1fa..8f64c3b 100644
--- a/internal/dis/component/auth/protect.go
+++ b/internal/dis/component/auth/protect.go
@@ -5,7 +5,7 @@ import (
"net/http"
"net/url"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
+ "github.com/tkw1536/pkglib/httpx"
)
// Protect returns a new handler which requires a user to be logged in and pass the perm function.
diff --git a/internal/dis/component/auth/session.go b/internal/dis/component/auth/session.go
index e8eada1..3c56308 100644
--- a/internal/dis/component/auth/session.go
+++ b/internal/dis/component/auth/session.go
@@ -10,8 +10,9 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
+ "github.com/tkw1536/pkglib/httpx"
+ "github.com/tkw1536/pkglib/httpx/field"
+
"github.com/gorilla/sessions"
_ "embed"
diff --git a/internal/dis/component/resolver/resolver.go b/internal/dis/component/resolver/resolver.go
index 15a74e6..4b8d2b7 100644
--- a/internal/dis/component/resolver/resolver.go
+++ b/internal/dis/component/resolver/resolver.go
@@ -14,8 +14,8 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
"github.com/rs/zerolog"
+ "github.com/tkw1536/pkglib/httpx"
"github.com/tkw1536/pkglib/lazy"
_ "embed"
diff --git a/internal/dis/component/server/admin/admin.go b/internal/dis/component/server/admin/admin.go
index 79f34c3..a9e8aac 100644
--- a/internal/dis/component/server/admin/admin.go
+++ b/internal/dis/component/server/admin/admin.go
@@ -14,7 +14,7 @@ import (
"github.com/tkw1536/pkglib/lifetime"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
+ "github.com/tkw1536/pkglib/httpx"
)
type Admin struct {
diff --git a/internal/dis/component/server/admin/components.go b/internal/dis/component/server/admin/components.go
index 985ad2e..36b0d3c 100644
--- a/internal/dis/component/server/admin/components.go
+++ b/internal/dis/component/server/admin/components.go
@@ -5,15 +5,15 @@ import (
"html/template"
"net/http"
- _ "embed"
-
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
"github.com/julienschmidt/httprouter"
+ "github.com/tkw1536/pkglib/httpx"
"github.com/tkw1536/pkglib/lifetime"
+
+ _ "embed"
)
//go:embed "html/anal.html"
diff --git a/internal/dis/component/server/admin/grants.go b/internal/dis/component/server/admin/grants.go
index df104a0..0e29fb3 100644
--- a/internal/dis/component/server/admin/grants.go
+++ b/internal/dis/component/server/admin/grants.go
@@ -13,8 +13,9 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
"github.com/FAU-CDI/wisski-distillery/internal/models"
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
+ "github.com/tkw1536/pkglib/httpx"
+ "github.com/tkw1536/pkglib/httpx/field"
+
"github.com/julienschmidt/httprouter"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
diff --git a/internal/dis/component/server/admin/instance.go b/internal/dis/component/server/admin/instance.go
index 298b1eb..7cd8f4a 100644
--- a/internal/dis/component/server/admin/instance.go
+++ b/internal/dis/component/server/admin/instance.go
@@ -2,7 +2,6 @@ package admin
import (
"context"
- _ "embed"
"html/template"
"net/http"
@@ -12,8 +11,10 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
"github.com/FAU-CDI/wisski-distillery/internal/models"
"github.com/FAU-CDI/wisski-distillery/internal/status"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
"github.com/julienschmidt/httprouter"
+ "github.com/tkw1536/pkglib/httpx"
+
+ _ "embed"
)
//go:embed "html/instance.html"
diff --git a/internal/dis/component/server/admin/socket/socket.go b/internal/dis/component/server/admin/socket/socket.go
index 4ef0fb2..7dda845 100644
--- a/internal/dis/component/server/admin/socket/socket.go
+++ b/internal/dis/component/server/admin/socket/socket.go
@@ -11,8 +11,8 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances/purger"
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
"github.com/tkw1536/goprogram/status"
+ "github.com/tkw1536/pkglib/httpx"
)
type Sockets struct {
diff --git a/internal/dis/component/server/admin/users.go b/internal/dis/component/server/admin/users.go
index 3bf960b..016662f 100644
--- a/internal/dis/component/server/admin/users.go
+++ b/internal/dis/component/server/admin/users.go
@@ -6,14 +6,14 @@ import (
"net/http"
"net/url"
- _ "embed"
-
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
"github.com/rs/zerolog"
+ "github.com/tkw1536/pkglib/httpx"
+ "github.com/tkw1536/pkglib/httpx/field"
+
+ _ "embed"
)
//go:embed "html/users.html"
diff --git a/internal/dis/component/server/home/public.go b/internal/dis/component/server/home/public.go
index f61a1fb..8fef9fc 100644
--- a/internal/dis/component/server/home/public.go
+++ b/internal/dis/component/server/home/public.go
@@ -10,7 +10,7 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
"github.com/FAU-CDI/wisski-distillery/internal/status"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
+ "github.com/tkw1536/pkglib/httpx"
"github.com/tkw1536/pkglib/pools"
)
diff --git a/internal/dis/component/server/logo/logo.go b/internal/dis/component/server/logo/logo.go
index dd1e173..ab92802 100644
--- a/internal/dis/component/server/logo/logo.go
+++ b/internal/dis/component/server/logo/logo.go
@@ -5,7 +5,7 @@ import (
"net/http"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
+ "github.com/tkw1536/pkglib/httpx"
_ "embed"
)
@@ -41,8 +41,8 @@ var faviconRoute = httpx.Response{
var logoSVGRoute = httpx.Response{
ContentType: "image/svg+xml",
- Body: httpx.MinifySVG(logoSVG),
-}
+ Body: logoSVG,
+}.Minify()
func (*Logo) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
diff --git a/internal/dis/component/server/server.go b/internal/dis/component/server/server.go
index db2577e..e27d8d8 100644
--- a/internal/dis/component/server/server.go
+++ b/internal/dis/component/server/server.go
@@ -9,9 +9,9 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
"github.com/tkw1536/pkglib/contextx"
+ "github.com/tkw1536/pkglib/httpx"
"github.com/tkw1536/pkglib/mux"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
"github.com/gorilla/csrf"
"github.com/rs/zerolog"
)
diff --git a/internal/dis/component/server/templating/base.go b/internal/dis/component/server/templating/base.go
index 382931a..1d21fcf 100644
--- a/internal/dis/component/server/templating/base.go
+++ b/internal/dis/component/server/templating/base.go
@@ -10,9 +10,9 @@ import (
"reflect"
"time"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
"github.com/gorilla/csrf"
"github.com/rs/zerolog"
+ "github.com/tkw1536/pkglib/httpx"
"github.com/tkw1536/pkglib/pools"
"github.com/tkw1536/pkglib/timex"
)
diff --git a/internal/dis/component/ssh2/api.go b/internal/dis/component/ssh2/api.go
index 783a7e9..6d65699 100644
--- a/internal/dis/component/ssh2/api.go
+++ b/internal/dis/component/ssh2/api.go
@@ -5,7 +5,7 @@ import (
"net/http"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx"
+ "github.com/tkw1536/pkglib/httpx"
gossh "golang.org/x/crypto/ssh"
)
diff --git a/pkg/httpx/errors.go b/pkg/httpx/errors.go
deleted file mode 100644
index 6300142..0000000
--- a/pkg/httpx/errors.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package httpx
-
-import (
- "encoding/json"
- "errors"
- "net/http"
-)
-
-// ErrInterceptor intercepts errors and directly returns specific responses for them
-type ErrInterceptor struct {
- Errors map[error]Response
- Fallback Response
-}
-
-// Intercept attempts to intercept the given error.
-// When err is nil, does nothing.
-//
-// When err is not nil, first attempts to find a static response in errors and respond with that.
-// Otherwise it returns the Fallback response.
-// intercepted indicates if some response was sent.
-func (ei ErrInterceptor) Intercept(w http.ResponseWriter, r *http.Request, err error) (intercepted bool) {
- if err == nil {
- return false
- }
-
- res, ok := ei.Errors[err]
- if !ok {
- res = ei.Fallback
- }
-
- res.ServeHTTP(w, r)
- return true
-}
-
-// StatusInterceptor creates a new ErrInterceptor handling default responses.
-// If body returns err != nil, StatusInterceptor calls panic().
-func StatusInterceptor(contentType string, body func(code int, text string) ([]byte, error)) ErrInterceptor {
- makeResponse := func(code int) (res Response) {
- var err error
- res.Body, err = body(code, http.StatusText(code))
- if err != nil {
- panic("StatusInterceptor: err != nil")
- }
-
- res.ContentType = contentType
- res.StatusCode = code
- return
- }
-
- return ErrInterceptor{
- Errors: map[error]Response{
- ErrInternalServerError: makeResponse(http.StatusInternalServerError),
- ErrBadRequest: makeResponse(http.StatusBadRequest),
- ErrNotFound: makeResponse(http.StatusNotFound),
- ErrForbidden: makeResponse(http.StatusForbidden),
- ErrMethodNotAllowed: makeResponse(http.StatusMethodNotAllowed),
- },
- Fallback: makeResponse(http.StatusInternalServerError),
- }
-}
-
-// Common errors accepted by all httpx handlers
-var (
- ErrInternalServerError = errors.New("httpx: Internal Server Error")
- ErrBadRequest = errors.New("httpx: Bad Request")
- ErrNotFound = errors.New("httpx: Not Found")
- ErrForbidden = errors.New("httpx: Forbidden")
- ErrMethodNotAllowed = errors.New("httpx: Method Not Allowed")
-)
-
-var (
- TextInterceptor = StatusInterceptor("text/plain", func(code int, text string) ([]byte, error) {
- return []byte(text), nil
- })
- JSONInterceptor = StatusInterceptor("application/json", func(code int, text string) ([]byte, error) {
- return json.Marshal(map[string]any{"status": text, "code": code})
- })
- HTMLInterceptor = StatusInterceptor("text/html", func(code int, text string) ([]byte, error) {
- return MinifyHTML([]byte(`
` + text + `` + text)), nil
- })
-)
diff --git a/pkg/httpx/field/autocomplete.go b/pkg/httpx/field/autocomplete.go
deleted file mode 100644
index fcb05d7..0000000
--- a/pkg/httpx/field/autocomplete.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package field
-
-// Autocomplete represents different autocomplete options
-type Autocomplete string
-
-const (
- Off Autocomplete = "off"
- On Autocomplete = "on"
- Name Autocomplete = "name"
- HonorificPrefix Autocomplete = "honorific-prefix"
- GivenName Autocomplete = "given-name"
- AdditionalName Autocomplete = "additional-name"
- FamilyName Autocomplete = "family-name"
- HonorificSuffix Autocomplete = "honorific-suffix"
- Nickname Autocomplete = "nickname"
- Email_ Autocomplete = "email"
- Username Autocomplete = "username"
- NewPassword Autocomplete = "new-password"
- CurrentPassword Autocomplete = "current-password"
- OneTimeCode Autocomplete = "one-time-code"
- OrganizationTitle Autocomplete = "organization-title"
- Organization Autocomplete = "organization"
- AddressLine1 Autocomplete = "address-line1"
- AddressLine2 Autocomplete = "address-line2"
- AddressLine3 Autocomplete = "address-line3"
- StreetAddress Autocomplete = "street-address"
- AddressLevel4 Autocomplete = "address-level4"
- AddressLevel3 Autocomplete = "address-level3"
- AddressLevel2 Autocomplete = "address-level2"
- AddressLevel1 Autocomplete = "address-level1"
- Country Autocomplete = "country"
- CountryName Autocomplete = "country-name"
- PostalCode Autocomplete = "postal-code"
- CcName Autocomplete = "cc-name"
- CcGivenName Autocomplete = "cc-given-name"
- CcAdditionalName Autocomplete = "cc-additional-name"
- CcFamilyName Autocomplete = "cc-family-name"
- CcNumber Autocomplete = "cc-number"
- CcExp Autocomplete = "cc-exp"
- CcExpMonth Autocomplete = "cc-exp-month"
- CcExpYear Autocomplete = "cc-exp-year"
- CcCsc Autocomplete = "cc-csc"
- CcType Autocomplete = "cc-type"
- TransactionCurrency Autocomplete = "transaction-currency"
- TransactionAmount Autocomplete = "transaction-amount"
- Language Autocomplete = "language"
- Bday Autocomplete = "bday"
- BdayDay Autocomplete = "bday-day"
- BdayMonth Autocomplete = "bday-month"
- BdayYear Autocomplete = "bday-year"
- Sex Autocomplete = "sex"
- Tel_ Autocomplete = "tel"
- TelCountryCode Autocomplete = "tel-country-code"
- TelNational Autocomplete = "tel-national"
- TelAreaCode Autocomplete = "tel-area-code"
- TelLocal Autocomplete = "tel-local"
- TelLocalPrefix Autocomplete = "tel-local-prefix"
- TelLocalSuffix Autocomplete = "tel-local-suffix"
- TelExtension Autocomplete = "tel-extension"
- Impp Autocomplete = "impp"
- Url_ Autocomplete = "url"
- Photo Autocomplete = "photo"
-)
diff --git a/pkg/httpx/field/field.go b/pkg/httpx/field/field.go
deleted file mode 100644
index 50615bf..0000000
--- a/pkg/httpx/field/field.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package field
-
-import (
- "html/template"
- "io"
-)
-
-// DefaultFieldTemplate is the default template to render fields.
-var DefaultFieldTemplate = template.Must(template.New("").Parse(`
-{{ if (eq .Type "textarea" )}}
-
-{{ else }}
-
-
-{{ if (eq .Type "textarea" )}}
-
-{{ else }}
-
-{{ end }}
-`))
-
-// Field represents a field inside a form.
-type Field struct {
- Name string // Name is the name of the field
- Type InputType // Type is the type of the field. It corresponds to the "name" attribute in html.
-
- Placeholder string // Value for the "placeholder" attribute
- Label string // (External) Label for the field. Not used by the default template.
-
- Autocomplete Autocomplete
-
- EmptyOnError bool // indicates if the field should be reset on error
-}
-
-// fieldContext is passed to the template context
-type fieldContext struct {
- Field
- Value string
-}
-
-func (field Field) WriteTo(w io.Writer, template *template.Template, value string) {
- if template == nil {
- template = DefaultFieldTemplate
- }
- template.Execute(w, fieldContext{Field: field, Value: value})
-}
-
-// CheckboxChecked is the default value of a checked checkbox
-const CheckboxChecked = "on"
diff --git a/pkg/httpx/field/type.go b/pkg/httpx/field/type.go
deleted file mode 100644
index c056854..0000000
--- a/pkg/httpx/field/type.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package field
-
-// InputType represents the type of input
-type InputType string
-
-const (
- Button InputType = "button"
- Checkbox InputType = "checkbox"
- Color InputType = "color"
- Date InputType = "date"
- DatetimeLocal InputType = "datetime-local"
- Email InputType = "email"
- File InputType = "file"
- Hidden InputType = "hidden"
- Image InputType = "image"
- Month InputType = "month"
- Number InputType = "number"
- Password InputType = "password"
- Radio InputType = "radio"
- Range InputType = "range"
- Reset InputType = "reset"
- Search InputType = "search"
- Submit InputType = "submit"
- Tel InputType = "tel"
- Text InputType = "text"
- Time InputType = "time"
- Url InputType = "url"
- Week InputType = "week"
- Datetime InputType = "datetime"
-
- Textarea InputType = "textarea" // special
-)
diff --git a/pkg/httpx/form.go b/pkg/httpx/form.go
deleted file mode 100644
index bbe982a..0000000
--- a/pkg/httpx/form.go
+++ /dev/null
@@ -1,170 +0,0 @@
-package httpx
-
-import (
- "html/template"
- "net/http"
-
- _ "embed"
-
- "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
- "github.com/gorilla/csrf"
- "github.com/tkw1536/pkglib/pools"
-)
-
-// Form provides a form that a user can submit via a http POST method call.
-// It implements [http.Handler].
-type Form[D any] struct {
- // Fields are the fields this form consists of.
- Fields []field.Field
-
- // FieldTemplate is an optional template to be executed for each field.
- // FieldTemplate may be nil; in which case [DefaultFieldTemplate] is used.
- FieldTemplate *template.Template
-
- // SkipCSRF if CSRF should be explicitly omitted
- SkipCSRF bool
-
- // SkipForm, if non-nil, is called on every get request to determine if form parsing should be skipped entirely.
- // If skip is true, RenderSuccess is directly called with the given values map.
- SkipForm func(r *http.Request) (data D, skip bool)
-
- // RenderTemplate represents the template to render for GET requests.
- // It is passed the return value of [RenderTemplateContext], or a [FormContext] instance if this does not exist.
- RenderTemplate *template.Template
-
- // RenderTemplateContext is the context to be used for RenderTemplate.
- // When nil, assumed to be the identify function
- RenderTemplateContext func(ctx FormContext, r *http.Request) any
-
- // Validate, if non-nil, validates the given submitted values.
- // There is no guarantee that the values are set.
- Validate func(r *http.Request, values map[string]string) (D, error)
-
- // RenderSuccess handles rendering a success result into a response.
- RenderSuccess func(data D, values map[string]string, w http.ResponseWriter, r *http.Request) error
-}
-
-// Form renders the gives values into a template html string to be inserted into a template.
-func (form *Form[D]) Form(values map[string]string, isError bool) template.HTML {
- builder := pools.GetBuilder()
- defer pools.ReleaseBuilder(builder)
-
- for _, field := range form.Fields {
- value := values[field.Name]
- if isError && field.EmptyOnError {
- value = ""
- }
-
- field.WriteTo(builder, form.FieldTemplate, value)
- }
-
- return template.HTML(builder.String())
-}
-
-// Values returns (validated) form values contained in the given request
-func (form *Form[D]) Values(r *http.Request) (v map[string]string, d D, err error) {
- // parse the form
- if err := r.ParseForm(); err != nil {
- return nil, d, err
- }
-
- // pick each of the values
- values := make(map[string]string, len(form.Fields))
- for _, field := range form.Fields {
- values[field.Name] = r.PostForm.Get(field.Name)
- }
-
- // validate the form
- if form.Validate != nil {
- d, err = form.Validate(r, values)
- if err != nil {
- return nil, d, err
- }
- }
-
- // and return them
- return values, d, nil
-}
-
-// ServeHTTP implements [http.Handler] and serves the form
-func (form *Form[D]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- switch {
- default:
- TextInterceptor.Intercept(w, r, ErrMethodNotAllowed)
- return
- case r.Method == http.MethodPost:
- values, data, err := form.Values(r)
- if err != nil {
- form.renderForm(err, values, w, r)
- } else {
- form.renderSuccess(data, values, w, r)
- }
- case r.Method == http.MethodGet && form.SkipForm != nil:
- if data, skip := form.SkipForm(r); skip {
- form.renderSuccess(data, nil, w, r)
- return
- }
- fallthrough
- case r.Method == http.MethodGet:
- form.renderForm(nil, nil, w, r)
- }
-}
-
-// renderForm renders the form onto the request
-func (form *Form[D]) renderForm(err error, values map[string]string, w http.ResponseWriter, r *http.Request) {
- template := form.Form(values, err != nil)
- if !form.SkipCSRF {
- template += csrf.TemplateField(r)
- }
-
- ctx := FormContext{Err: err, Form: template}
-
- // must have a form or a RenderForm
- if form.RenderTemplate == nil {
- panic("form.RenderTemplate is nil")
- }
-
- // get the template context
- var tplctx any
- if form.RenderTemplateContext == nil {
- tplctx = ctx
- } else {
- tplctx = form.RenderTemplateContext(ctx, r)
- }
-
- // render the form
- WriteHTML(tplctx, nil, form.RenderTemplate, "", w, r)
-}
-
-// FormContext is passed to Form.Form when used
-type FormContext struct {
- // Error is the underlying error (if any)
- Err error
-
- // Template is the underlying template rendered as html
- Form template.HTML
-}
-
-// Error returns the underlying error string
-func (fc FormContext) Error() string {
- if fc.Err == nil {
- return ""
- }
- return fc.Err.Error()
-}
-
-// renderSuccess renders a successfull pass of the form
-// if an error occurs during rendering, renderForm is called instead
-func (form *Form[D]) renderSuccess(data D, values map[string]string, w http.ResponseWriter, r *http.Request) {
- err := form.RenderSuccess(data, values, w, r)
- if err == nil {
- return
- }
- form.renderForm(err, values, w, r)
-}
-
-//go:embed "form.html"
-var formBytes []byte
-
-// FormTeplate is a template to embed a form
-var FormTemplate = template.Must(template.New("form.html").Parse(string(formBytes)))
diff --git a/pkg/httpx/form.html b/pkg/httpx/form.html
deleted file mode 100644
index 455776c..0000000
--- a/pkg/httpx/form.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
- {{ block "form/extra" . }}{{ end }}
-
-
-
diff --git a/pkg/httpx/html.go b/pkg/httpx/html.go
deleted file mode 100644
index b87fba5..0000000
--- a/pkg/httpx/html.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package httpx
-
-import (
- "html/template"
- "net/http"
- "time"
-
- "github.com/rs/zerolog"
- "github.com/tkw1536/pkglib/timex"
-)
-
-const HTMLFlushInterval = time.Second / 10
-
-// WriteHTML writes a html response of type T to w.
-// If an error occured, writes an error response instead.
-func WriteHTML[T any](result T, err error, template *template.Template, templateName string, w http.ResponseWriter, r *http.Request) (e error) {
- // log any error that occurs;
- defer func() {
- if e != nil {
- zerolog.Ctx(r.Context()).Err(e).Str("path", r.URL.String()).Msg("error rendering template")
- }
- }()
-
- // create a synced respone writer
- sw := &SyncedResponseWriter{ResponseWriter: w}
-
- done := make(chan struct{})
- defer close(done)
-
- // and regularly flush it until the end of the function
- go func() {
- timer := timex.NewTimer()
- defer timex.ReleaseTimer(timer)
-
- for {
- timer.Reset(HTMLFlushInterval)
- select {
- case <-timer.C:
- sw.Flush()
- case <-done:
- return
- }
- }
- }()
-
- // intercept any errors
- if HTMLInterceptor.Intercept(sw, r, err) {
- return nil
- }
-
- // write out the response as html
- sw.Header().Set("Content-Type", "text/html")
- sw.WriteHeader(http.StatusOK)
-
- // minify html!
- minifier := MinifyHTMLWriter(sw)
- defer minifier.Close()
-
- // and return the template
- if templateName != "" {
- return template.ExecuteTemplate(minifier, templateName, result)
- } else {
- return template.Execute(minifier, result)
- }
-}
-
-type HTMLHandler[T any] struct {
- Handler func(r *http.Request) (T, error)
-
- Template *template.Template // called with T
- TemplateName string // name of template to render, defaults to root
-}
-
-// ServeHTTP calls j(r) and returns json
-func (h HTMLHandler[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // call the function
- result, err := h.Handler(r)
- WriteHTML(result, err, h.Template, h.TemplateName, w, r)
-}
diff --git a/pkg/httpx/html_minify.go b/pkg/httpx/html_minify.go
deleted file mode 100644
index 6bf7617..0000000
--- a/pkg/httpx/html_minify.go
+++ /dev/null
@@ -1,54 +0,0 @@
-//go:build !nominify
-
-package httpx
-
-import (
- "io"
- "regexp"
-
- "github.com/tdewolff/minify"
- "github.com/tdewolff/minify/css"
- "github.com/tdewolff/minify/html"
- "github.com/tdewolff/minify/js"
- "github.com/tdewolff/minify/svg"
-)
-
-// minifier holds the minfier used for all html minification
-//
-// NOTE(twiesing): We can't use an init function for this, because otherwise initialization order is incorrect.
-var minifier = (func() *minify.M {
- m := minify.New()
- m.AddFunc("text/html", html.Minify)
- m.AddFunc("text/css", css.Minify)
- m.AddFunc("image/svg+xml", svg.Minify)
- m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify)
- return m
-})()
-
-// MinifyHTMLWriter wraps the given io.Writer to minify the given html instead.
-// The writer must be closed explicitly.
-//
-// Specific environments may chose to disable http minification, in which case MinifyHTMLWriter becomes the identity function.
-func MinifyHTMLWriter(dest io.Writer) io.WriteCloser {
- return minifier.Writer("text/html", dest)
-}
-
-// MinifyHTML minifies the html source.
-// If an error occurs, returns the unmodified source instead.
-func MinifyHTML(source []byte) []byte {
- result, err := minifier.Bytes("text/html", source)
- if err != nil {
- return source
- }
- return result
-}
-
-// MinifySVG minifies the svg source.
-// If an error occurs, returns the minified source instead.
-func MinifySVG(source []byte) []byte {
- result, err := minifier.Bytes("image/svg+xml", source)
- if err != nil {
- return source
- }
- return result
-}
diff --git a/pkg/httpx/html_minify_off.go b/pkg/httpx/html_minify_off.go
deleted file mode 100644
index b42b14b..0000000
--- a/pkg/httpx/html_minify_off.go
+++ /dev/null
@@ -1,31 +0,0 @@
-//go:build nominify
-
-package httpx
-
-import "io"
-
-// MinifyHTMLWriter wraps the given io.Writer to minify the given html instead.
-// The writer must be closed explicitly.
-//
-// Specific environments may chose to disable http minification, in which case MinifyHTMLWriter becomes the identity function.
-func MinifyHTMLWriter(dest io.Writer) io.WriteCloser {
- return noop{Writer: dest}
-}
-
-type noop struct {
- io.Writer
-}
-
-func (noop) Close() error {
- return nil
-}
-
-// MinifyHTML minifies the html source.
-// If an error occurs, returns the unmodified source instead.
-func MinifyHTML(source []byte) []byte {
- return source
-}
-
-func MinifySVG(source []byte) []byte {
- return source
-}
diff --git a/pkg/httpx/httpx.go b/pkg/httpx/httpx.go
deleted file mode 100644
index b9b23b9..0000000
--- a/pkg/httpx/httpx.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package httpx
-
-import (
- "net/http"
- "strconv"
-)
-
-// Response represents a response to an http request.
-type Response struct {
- ContentType string // defaults to text/plain
- Body []byte
- StatusCode int // defaults to [http.StatusOK]
-}
-
-func (response Response) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if response.ContentType == "" {
- response.ContentType = "text/plain"
- }
- w.Header().Set("Content-Type", response.ContentType)
- w.Header().Set("Content-Length", strconv.Itoa(len(response.Body)))
-
- if response.StatusCode <= 0 {
- response.StatusCode = http.StatusOK
- }
- w.WriteHeader(response.StatusCode)
- w.Write(response.Body)
-}
diff --git a/pkg/httpx/json.go b/pkg/httpx/json.go
deleted file mode 100644
index 468aa26..0000000
--- a/pkg/httpx/json.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package httpx
-
-import (
- "encoding/json"
- "net/http"
-)
-
-// JSON creates a new JSONHandler
-func JSON[T any](f func(r *http.Request) (T, error)) JSONHandler[T] {
- return JSONHandler[T](f)
-}
-
-// WriteJSON writes a JSON response of type T to w.
-// If an error occured, writes an error response instead.
-func WriteJSON[T any](result T, err error, w http.ResponseWriter, r *http.Request) {
- // handle any errors
- if JSONInterceptor.Intercept(w, r, err) {
- return
- }
-
- // write out the response as json
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(http.StatusOK)
- json.NewEncoder(w).Encode(result)
-}
-
-// JSONHandler implements [http.Handler] by returning values as json to the caller.
-// In case of an error, a generic "internal server error" message is returned.
-type JSONHandler[T any] func(r *http.Request) (T, error)
-
-// ServeHTTP calls j(r) and returns json
-func (j JSONHandler[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- result, err := j(r)
- WriteJSON(result, err, w, r)
-}
diff --git a/pkg/httpx/redirect.go b/pkg/httpx/redirect.go
deleted file mode 100644
index a82b414..0000000
--- a/pkg/httpx/redirect.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package httpx
-
-import (
- "net/http"
-)
-
-// RedirectHandler represents a handler that redirects the user to the address returned
-type RedirectHandler func(r *http.Request) (string, int, error)
-
-// ServeHTTP calls r(r) and returns json
-func (rh RedirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // call the function
- url, code, err := rh(r)
-
- // intercept the errors
- if TextInterceptor.Intercept(w, r, err) {
- return
- }
-
- // do the redirect
- http.Redirect(w, r, url, code)
-}
diff --git a/pkg/httpx/socket.go b/pkg/httpx/socket.go
deleted file mode 100644
index 3ebea2b..0000000
--- a/pkg/httpx/socket.go
+++ /dev/null
@@ -1,316 +0,0 @@
-package httpx
-
-import (
- "context"
- "net/http"
- "sync"
- "time"
-
- "github.com/gorilla/websocket"
- "github.com/tkw1536/pkglib/lazy"
-)
-
-// WebSocket implements serving a WebSocket
-type WebSocket struct {
- Context context.Context // context which closes all connections
- Limits WebSocketLimits // limits for websocket operations
-
- Handler func(ws WebSocketConnection)
- Fallback http.Handler
-
- pool lazy.Lazy[*sync.Pool] // pool holds *WebSocketConn objects
- upgrader websocket.Upgrader // upgrades upgrades connections
-}
-
-type WebSocketLimits struct {
- WriteWait time.Duration // maximum time to wait for writing
- PongWait time.Duration // time to wait for pong responses
- PingInterval time.Duration // interval to send pings to the client
- MaxMessageSize int64 // maximal message size in bytes
-}
-
-func (limits *WebSocketLimits) SetDefaults() {
- if limits.WriteWait == 0 {
- limits.WriteWait = 10 * time.Second
- }
- if limits.PongWait == 0 {
- limits.PongWait = time.Minute
- }
- if limits.PingInterval <= 0 {
- limits.PingInterval = (limits.PongWait * 9) / 10
- }
- if limits.MaxMessageSize <= 0 {
- limits.MaxMessageSize = 2048
- }
-}
-
-// makePoolSocket creates a new socket and makes sure that the pool is initialized
-func (h *WebSocket) makePoolSocket() *webSocketConn {
- return h.pool.Get(func() *sync.Pool {
- return &sync.Pool{
- New: func() any { return new(webSocketConn) },
- }
- }).Get().(*webSocketConn)
-}
-
-func (h *WebSocket) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- // if the user did not request a websocket, go to the fallbacjk handler
- if !websocket.IsWebSocketUpgrade(r) {
- h.serveFallback(w, r)
- return
- }
-
- // else deal with the websocket!
- h.serveWebsocket(w, r)
-}
-
-func (h *WebSocket) serveFallback(w http.ResponseWriter, r *http.Request) {
- if h.Fallback == nil {
- http.NotFound(w, r)
- return
- }
-
- h.Fallback.ServeHTTP(w, r)
-}
-
-func (h *WebSocket) serveWebsocket(w http.ResponseWriter, r *http.Request) {
- // upgrade the connection or bail out!
- conn, err := h.upgrader.Upgrade(w, r, nil)
- if err != nil {
- return
- }
-
- // get a new socket from the pool
- socket := h.makePoolSocket()
- socket.Serve(h.Context, h.Limits, conn, h.Handler)
-
- // return a reset socket to the pool
- socket.reset()
- h.pool.Get(nil).Put(socket)
-}
-
-// WebSocketConnection represents a connected WebSocket.
-type WebSocketConnection interface {
- // Context returns a context that is closed once the connection is terminated.
- Context() context.Context
-
- // Read returns a channel that receives message.
- // The channel is closed if no more messags are available (for instance because the server closed).
- Read() <-chan WebSocketMessage
-
- // Write queues the provided message for sending.
- // The returned channel is closed once the message has been sent.
- Write(WebSocketMessage) <-chan struct{}
-
- // WriteText is a convenience method to send a TextMessage.
- // The returned channel is closed once the message has been sent.
- WriteText(text string) <-chan struct{}
-
- // Close closes the underlying connection
- Close()
-}
-
-// WebSocketMessage represents a connected Websocket
-type WebSocketMessage struct {
- Type int
- Bytes []byte
-}
-
-type outWebSocketMessage struct {
- WebSocketMessage
- done chan<- struct{} // done should be closed when finished
-}
-
-// webSocketConn implements [WebSocketConnection]
-type webSocketConn struct {
- conn *websocket.Conn // underlying connection
- limits WebSocketLimits
-
- context context.Context // context to cancel the connection
- cancel context.CancelFunc
-
- wg sync.WaitGroup // blocks all the ongoing tasks
-
- // incoming and outgoing tasks
- incoming chan WebSocketMessage
- outgoing chan outWebSocketMessage
-}
-
-// Serve serves the provided connection
-func (h *webSocketConn) Serve(ctx context.Context, limits WebSocketLimits, conn *websocket.Conn, handler func(ws WebSocketConnection)) {
- // use the connection!
- h.conn = conn
-
- // setup limits
- h.limits = limits
- h.limits.SetDefaults()
-
- // create a context for the connection
- if ctx == nil {
- ctx = context.Background()
- }
- h.context, h.cancel = context.WithCancel(ctx)
-
- // start receiving and sending messages
- h.wg.Add(2)
- h.sendMessages()
- h.recvMessages()
-
- // wait for the context to be cancelled, then close the connection
- h.wg.Add(1)
- go func() {
- defer h.wg.Done()
- <-h.context.Done()
- h.conn.Close()
- }()
-
- // start the application logic
- h.wg.Add(1)
- go h.handle(handler)
-
- // wait for closing operations
- h.wg.Wait()
-}
-
-func (h *webSocketConn) handle(handler func(ws WebSocketConnection)) {
- defer func() {
- h.wg.Done()
- h.cancel()
- }()
-
- handler(h)
-}
-
-func (h *webSocketConn) sendMessages() {
- h.outgoing = make(chan outWebSocketMessage)
-
- go func() {
- // close connection when done!
- defer func() {
- h.wg.Done()
- h.cancel()
- }()
-
- // setup a timer for pings!
- ticker := time.NewTicker(h.limits.PingInterval)
- defer ticker.Stop()
-
- for {
- select {
- // everything is done!
- case <-h.context.Done():
- return
-
- // send outgoing messages
- case message := <-h.outgoing:
- (func() {
- defer close(message.done)
-
- err := h.writeRaw(message.Type, message.Bytes)
- if err != nil {
- return
- }
- message.done <- struct{}{}
- })()
- // send a ping message
- case <-ticker.C:
- if err := h.writeRaw(websocket.PingMessage, []byte{}); err != nil {
- return
- }
- }
- }
- }()
-
-}
-
-// writeRaw writes to the underlying socket
-func (h *webSocketConn) writeRaw(messageType int, data []byte) error {
- h.conn.SetWriteDeadline(time.Now().Add(h.limits.WriteWait))
- return h.conn.WriteMessage(messageType, data)
-}
-
-// Write writes a message to the websocket connection.
-func (sh *webSocketConn) Write(message WebSocketMessage) <-chan struct{} {
- callback := make(chan struct{}, 1)
- go func() {
- select {
- // write an outgoing message
- case sh.outgoing <- outWebSocketMessage{
- WebSocketMessage: message,
- done: callback,
- }:
- // context
- case <-sh.context.Done():
- close(callback)
- }
- }()
- return callback
-}
-
-func (sh *webSocketConn) WriteText(text string) <-chan struct{} {
- return sh.Write(WebSocketMessage{
- Type: websocket.TextMessage,
- Bytes: []byte(text),
- })
-}
-
-func (h *webSocketConn) recvMessages() {
- h.incoming = make(chan WebSocketMessage)
-
- // set a read handler
- h.conn.SetReadLimit(h.limits.MaxMessageSize)
-
- // configure a pong handler
- h.conn.SetReadDeadline(time.Now().Add(h.limits.PongWait))
- h.conn.SetPongHandler(func(string) error { h.conn.SetReadDeadline(time.Now().Add(h.limits.PongWait)); return nil })
-
- // handle incoming messages
- go func() {
- // close connection when done!
- defer func() {
- h.wg.Done()
- h.cancel()
- }()
-
- for {
- messageType, messageBytes, err := h.conn.ReadMessage()
- if err != nil {
- return
- }
-
- // try to send a message to the incoming message channel
- select {
- case h.incoming <- WebSocketMessage{
- Type: messageType,
- Bytes: messageBytes,
- }:
- case <-h.context.Done():
- return
- }
- }
- }()
-}
-
-// Read returns a channel that receives incoming messages.
-// The channel is close once no more messages are available, or the context is canceled.
-func (h *webSocketConn) Read() <-chan WebSocketMessage {
- return h.incoming
-}
-
-// Context returns a context that is closed once this connection is closed.
-func (h *webSocketConn) Context() context.Context {
- return h.context
-}
-
-func (h *webSocketConn) Close() {
- h.cancel()
-}
-
-// reset resets this websocket
-func (h *webSocketConn) reset() {
- h.limits = WebSocketLimits{}
- h.conn = nil
- h.incoming = nil
- h.outgoing = nil
- h.context, h.cancel = nil, nil
-}
diff --git a/pkg/httpx/sync.go b/pkg/httpx/sync.go
deleted file mode 100644
index 2d7aa72..0000000
--- a/pkg/httpx/sync.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package httpx
-
-import (
- "net/http"
- "sync"
-)
-
-// SyncedResponseWriter wraps a http ResponseWriter to syncronize all actions
-type SyncedResponseWriter struct {
- m sync.Mutex
- http.ResponseWriter
-}
-
-func (rw *SyncedResponseWriter) Header() http.Header {
- rw.m.Lock()
- defer rw.m.Unlock()
-
- return rw.ResponseWriter.Header()
-}
-
-func (rw *SyncedResponseWriter) Write(data []byte) (int, error) {
- rw.m.Lock()
- defer rw.m.Unlock()
-
- return rw.ResponseWriter.Write(data)
-}
-
-func (rw *SyncedResponseWriter) WriteHeader(statusCode int) {
- rw.m.Lock()
- defer rw.m.Unlock()
-
- rw.ResponseWriter.WriteHeader(statusCode)
-}
-
-// Flush flushes any partial output to the underlying ResponseWriter.
-// If the wrapped ResponseWriter does not implement flush, the function performs no operation.
-func (rw *SyncedResponseWriter) Flush() {
- f, ok := rw.ResponseWriter.(http.Flusher)
- if !ok {
- return
- }
-
- rw.m.Lock()
- defer rw.m.Unlock()
-
- f.Flush()
-}
diff --git a/pkg/httpx/wrap.go b/pkg/httpx/wrap.go
deleted file mode 100644
index 0c082d4..0000000
--- a/pkg/httpx/wrap.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package httpx
-
-import (
- "context"
- "net/http"
-)
-
-// WithContextWrapper generates a new handler that wraps the context of each request with the wrapper function.
-func WithContextWrapper(handler http.Handler, wrapper func(context.Context) context.Context) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- r = r.WithContext(wrapper(r.Context()))
- handler.ServeHTTP(w, r)
- })
-}