1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-28 15:27:03 +00:00

(#1554) Add support for all common compressions (write and read)

- Remove the context.Context interface and export the *context, the iris.Context now points to the pointer\nSupport compression and rate limiting in the FileServer\nBit of code organisation


Former-commit-id: ad1c61bf968059510c6be9e7f2cceec7da70ba17
This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-07-10 23:21:09 +03:00
parent 645da2b2ef
commit 0f113dfcda
112 changed files with 2119 additions and 3390 deletions

View File

@@ -97,7 +97,7 @@ func (b *basicAuthMiddleware) findAuth(headerValue string) (*encodedUser, bool)
return nil, false
}
func (b *basicAuthMiddleware) askForCredentials(ctx context.Context) {
func (b *basicAuthMiddleware) askForCredentials(ctx *context.Context) {
ctx.Header("WWW-Authenticate", b.realmHeaderValue)
ctx.StatusCode(iris.StatusUnauthorized)
if b.askHandlerEnabled {
@@ -106,7 +106,7 @@ func (b *basicAuthMiddleware) askForCredentials(ctx context.Context) {
}
// Serve the actual middleware
func (b *basicAuthMiddleware) Serve(ctx context.Context) {
func (b *basicAuthMiddleware) Serve(ctx *context.Context) {
auth, found := b.findAuth(ctx.GetHeader("Authorization"))
if !found {
b.askForCredentials(ctx)

View File

@@ -50,6 +50,6 @@ func DefaultConfig() Config {
}
// User returns the user from context key same as ctx.Request().BasicAuth().
func (c Config) User(ctx context.Context) (string, string, bool) {
func (c Config) User(ctx *context.Context) (string, string, bool) {
return ctx.Request().BasicAuth()
}

View File

@@ -19,7 +19,7 @@ var (
ResponseContextKey string = "iris.hcaptcha"
// DefaultFailureHandler is the default HTTP handler that is fired on hcaptcha failures.
// See `Client.FailureHandler`.
DefaultFailureHandler = func(ctx context.Context) {
DefaultFailureHandler = func(ctx *context.Context) {
ctx.StopWithStatus(http.StatusTooManyRequests)
}
)
@@ -73,7 +73,7 @@ func New(secret string, options ...Option) context.Handler {
// otherwise it calls the Client's `FailureHandler`.
// The hcaptcha's `Response` (which contains any `ErrorCodes`)
// is saved on the Request's Context (see `GetResponseFromContext`).
func (c *Client) Handler(ctx context.Context) {
func (c *Client) Handler(ctx *context.Context) {
v := SiteVerify(ctx, c.secret)
ctx.Values().Set(ResponseContextKey, v)
if v.Success {
@@ -93,7 +93,7 @@ const apiURL = "https://hcaptcha.com/siteverify"
// It returns the hcaptcha's `Response`.
// The `response.Success` reports whether the validation passed.
// Any errors are passed through the `response.ErrorCodes` field.
func SiteVerify(ctx context.Context, secret string) (response Response) {
func SiteVerify(ctx *context.Context, secret string) (response Response) {
generatedResponseID := ctx.FormValue("h-captcha-response")
if generatedResponseID == "" {
@@ -130,7 +130,7 @@ func SiteVerify(ctx context.Context, secret string) (response Response) {
}
// Get returns the hcaptcha `Response` of the current request and reports whether was found or not.
func Get(ctx context.Context) (Response, bool) {
func Get(ctx *context.Context) (Response, bool) {
v := ctx.Values().Get(ResponseContextKey)
if v != nil {
if response, ok := v.(Response); ok {
@@ -161,6 +161,6 @@ func ParseForm(dataSiteKey, postActionRelativePath string) string {
// RenderForm writes the `HTMLForm` to "w" response writer.
// See `_examples/auth/hcaptcha/templates/register_form.html` example for a custom form instead.
func RenderForm(ctx context.Context, dataSiteKey, postActionRelativePath string) (int, error) {
func RenderForm(ctx *context.Context, dataSiteKey, postActionRelativePath string) (int, error) {
return ctx.HTML(ParseForm(dataSiteKey, postActionRelativePath))
}

View File

@@ -21,12 +21,12 @@ func init() {
// TokenExtractor is a function that takes a context as input and returns
// a token. An empty string should be returned if no token found
// without additional information.
type TokenExtractor func(context.Context) string
type TokenExtractor func(*context.Context) string
// FromHeader is a token extractor.
// It reads the token from the Authorization request header of form:
// Authorization: "Bearer {token}".
func FromHeader(ctx context.Context) string {
func FromHeader(ctx *context.Context) string {
authHeader := ctx.GetHeader("Authorization")
if authHeader == "" {
return ""
@@ -43,7 +43,7 @@ func FromHeader(ctx context.Context) string {
// FromQuery is a token extractor.
// It reads the token from the "token" url query parameter.
func FromQuery(ctx context.Context) string {
func FromQuery(ctx *context.Context) string {
return ctx.URLParam("token")
}
@@ -52,7 +52,7 @@ func FromQuery(ctx context.Context) string {
// The request content-type should contain the: application/json header value, otherwise
// this method will not try to read and consume the body.
func FromJSON(jsonKey string) TokenExtractor {
return func(ctx context.Context) string {
return func(ctx *context.Context) string {
if ctx.GetContentTypeRequested() != context.ContentJSONHeaderValue {
return ""
}
@@ -388,7 +388,7 @@ func validateMapClaims(m map[string]interface{}, e jwt.Expected, leeway time.Dur
// a new token to the client in plain text format.
//
// Use the `Token` method to get a new generated token raw string value.
func (j *JWT) WriteToken(ctx context.Context, claims interface{}) error {
func (j *JWT) WriteToken(ctx *context.Context, claims interface{}) error {
token, err := j.Token(claims)
if err != nil {
ctx.StatusCode(500)
@@ -418,18 +418,18 @@ type (
Validate() error
}
claimsContextValidator interface {
Validate(ctx context.Context) error
Validate(ctx *context.Context) error
}
)
// IsValidated reports whether a token is already validated through
// `VerifyToken`. It returns true when the claims are compatible
// validators: a `Claims` value or a value that implements the `Validate() error` method.
func IsValidated(ctx context.Context) bool { // see the `ReadClaims`.
func IsValidated(ctx *context.Context) bool { // see the `ReadClaims`.
return ctx.Values().Get(needsValidationContextKey) == nil
}
func validateClaims(ctx context.Context, claims interface{}) (err error) {
func validateClaims(ctx *context.Context, claims interface{}) (err error) {
switch c := claims.(type) {
case claimsValidator:
err = c.ValidateWithLeeway(jwt.Expected{Time: time.Now()}, 0)
@@ -467,7 +467,7 @@ func validateClaims(ctx context.Context, claims interface{}) (err error) {
// VerifyToken verifies (and decrypts) the request token,
// it also validates and binds the parsed token's claims to the "claimsPtr" (destination).
// It does return a nil error on success.
func (j *JWT) VerifyToken(ctx context.Context, claimsPtr interface{}) error {
func (j *JWT) VerifyToken(ctx *context.Context, claimsPtr interface{}) error {
var token string
for _, extract := range j.Extractors {
@@ -520,7 +520,7 @@ const (
//
// A call of `ReadClaims` is required to validate and acquire the jwt claims
// on the next request.
func (j *JWT) Verify(ctx context.Context) {
func (j *JWT) Verify(ctx *context.Context) {
var raw json.RawMessage
if err := j.VerifyToken(ctx, &raw); err != nil {
ctx.StopWithStatus(401)
@@ -534,7 +534,7 @@ func (j *JWT) Verify(ctx context.Context) {
// ReadClaims binds the "claimsPtr" (destination)
// to the verified (and decrypted) claims.
// The `Verify` method should be called first (registered as middleware).
func ReadClaims(ctx context.Context, claimsPtr interface{}) error {
func ReadClaims(ctx *context.Context, claimsPtr interface{}) error {
v := ctx.Values().Get(ClaimsContextKey)
if v == nil {
return ErrMissing
@@ -589,7 +589,7 @@ func ReadClaims(ctx context.Context, claimsPtr interface{}) error {
// }
// [use claims...]
// })
func Get(ctx context.Context) (interface{}, error) {
func Get(ctx *context.Context) (interface{}, error) {
claims := ctx.Values().Get(ClaimsContextKey)
if claims == nil {
return nil, ErrMissing

View File

@@ -8,7 +8,7 @@ import (
// The SkipperFunc signature, used to serve the main request without logs.
// See `Configuration` too.
type SkipperFunc func(ctx context.Context) bool
type SkipperFunc func(ctx *context.Context) bool
// Config contains the options for the logger middleware
// can be optionally be passed to the `New`.
@@ -71,7 +71,7 @@ type Config struct {
LogFunc func(endTime time.Time, latency time.Duration, status, ip, method, path string, message interface{}, headerMessage interface{})
// LogFuncCtx can be used instead of `LogFunc` if handlers need to customize the output based on
// custom request-time information that the LogFunc isn't aware of.
LogFuncCtx func(ctx context.Context, latency time.Duration)
LogFuncCtx func(ctx *context.Context, latency time.Duration)
// Skippers used to skip the logging i.e by `ctx.Path()` and serve
// the next/main handler immediately.
Skippers []SkipperFunc
@@ -110,7 +110,7 @@ func (c *Config) buildSkipper() {
return
}
skippersLocked := c.Skippers[0:]
c.skip = func(ctx context.Context) bool {
c.skip = func(ctx *context.Context) bool {
for _, s := range skippersLocked {
if s(ctx) {
return true

View File

@@ -36,7 +36,7 @@ func New(cfg ...Config) context.Handler {
}
// Serve serves the middleware
func (l *requestLoggerMiddleware) ServeHTTP(ctx context.Context) {
func (l *requestLoggerMiddleware) ServeHTTP(ctx *context.Context) {
// skip logs and serve the main request immediately
if l.config.skip != nil {
if l.config.skip(ctx) {

View File

@@ -26,7 +26,7 @@ func New() context.Handler {
threadcreateHandler := handlerconv.FromStd(pprof.Handler("threadcreate"))
debugBlockHandler := handlerconv.FromStd(pprof.Handler("block"))
return func(ctx context.Context) {
return func(ctx *context.Context) {
ctx.ContentType("text/html")
action := ctx.Params().Get("action")
if action != "" {

View File

@@ -35,7 +35,7 @@ func ExceedHandler(handler context.Handler) Option {
// ClientData is an `Option` that can be passed at the `Limit` package-level function.
// It accepts a function which provides the Iris Context and should return custom data
// that will be stored to the Client and be retrieved as `Get(ctx).Client.Data` later on.
func ClientData(clientDataFunc func(ctx context.Context) interface{}) Option {
func ClientData(clientDataFunc func(ctx *context.Context) interface{}) Option {
return func(l *Limiter) {
l.clientDataFunc = clientDataFunc
}
@@ -79,8 +79,8 @@ type (
// old clients from the memory. Limiter is not exposed by a function,
// callers should use it inside an `Option` for the `Limit` package-level function.
Limiter struct {
clientDataFunc func(ctx context.Context) interface{} // fill the Client's Data field.
exceedHandler context.Handler // when too many requests.
clientDataFunc func(ctx *context.Context) interface{} // fill the Client's Data field.
exceedHandler context.Handler // when too many requests.
limit rate.Limit
burstSize int
@@ -116,7 +116,7 @@ func Limit(limit float64, burst int, options ...Option) context.Handler {
clients: make(map[string]*Client),
limit: rate.Limit(limit),
burstSize: burst,
exceedHandler: func(ctx context.Context) {
exceedHandler: func(ctx *context.Context) {
ctx.StopWithStatus(429) // Too Many Requests.
},
}
@@ -139,7 +139,7 @@ func (l *Limiter) Purge(condition func(*Client) bool) {
l.mu.Unlock()
}
func (l *Limiter) serveHTTP(ctx context.Context) {
func (l *Limiter) serveHTTP(ctx *context.Context) {
id := getIdentifier(ctx)
l.mu.RLock()
client, ok := l.clients[id]
@@ -182,11 +182,11 @@ const identifierContextKey = "iris.ratelimit.identifier"
// SetIdentifier can be called manually from a handler or a middleare
// to change the identifier per client. The default key for a client is its Remote IP.
func SetIdentifier(ctx context.Context, key string) {
func SetIdentifier(ctx *context.Context, key string) {
ctx.Values().Set(identifierContextKey, key)
}
func getIdentifier(ctx context.Context) string {
func getIdentifier(ctx *context.Context) string {
if entry, ok := ctx.Values().GetEntry(identifierContextKey); ok {
return entry.ValueRaw.(string)
}
@@ -202,7 +202,7 @@ const clientContextKey = "iris.ratelimit.client"
// You can read more about X-RateLimit response headers at:
// https://tools.ietf.org/id/draft-polli-ratelimit-headers-00.html.
// A good example of that is the GitHub API itself: https://developer.github.com/v3/#rate-limiting
func Get(ctx context.Context) *Client {
func Get(ctx *context.Context) *Client {
if v := ctx.Values().Get(clientContextKey); v != nil {
if c, ok := v.(*Client); ok {
return c

View File

@@ -42,7 +42,7 @@ var Client = netutil.Client(time.Duration(20 * time.Second))
//
// Use `SiteVerify` to verify a request inside another handler if needed.
func New(secret string) context.Handler {
return func(ctx context.Context) {
return func(ctx *context.Context) {
if SiteVerify(ctx, secret).Success {
ctx.Next()
}
@@ -54,7 +54,7 @@ func New(secret string) context.Handler {
// then validation passed.
//
// Use `New` for middleware use instead.
func SiteVerify(ctx context.Context, secret string) (response Response) {
func SiteVerify(ctx *context.Context, secret string) (response Response) {
generatedResponseID := ctx.FormValue("g-recaptcha-response")
if generatedResponseID == "" {
response.ErrorCodes = append(response.ErrorCodes,
@@ -113,7 +113,7 @@ var recaptchaForm = `<form action="%s" method="POST">
// Example Code:
//
// Method: "POST" | Path: "/contact"
// func postContact(ctx context.Context) {
// func postContact(ctx *context.Context) {
// // [...]
// response := recaptcha.SiteVerify(ctx, recaptchaSecret)
//
@@ -125,7 +125,7 @@ var recaptchaForm = `<form action="%s" method="POST">
// }
//
// Method: "GET" | Path: "/contact"
// func getContact(ctx context.Context) {
// func getContact(ctx *context.Context) {
// // render the recaptcha form
// ctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, "/contact"))
// }

View File

@@ -13,7 +13,7 @@ func init() {
context.SetHandlerName("iris/middleware/recover.*", "iris.recover")
}
func getRequestLogs(ctx context.Context) string {
func getRequestLogs(ctx *context.Context) string {
var status, ip, method, path string
status = strconv.Itoa(ctx.GetStatusCode())
path = ctx.Path()
@@ -27,7 +27,7 @@ func getRequestLogs(ctx context.Context) string {
// it recovers from panics and logs
// the panic message to the application's logger "Warn" level.
func New() context.Handler {
return func(ctx context.Context) {
return func(ctx *context.Context) {
defer func() {
if err := recover(); err != nil {
if ctx.IsStopped() {

View File

@@ -14,7 +14,7 @@ const xRequestIDHeaderValue = "X-Request-Id"
// Generator defines the function which should extract or generate
// a Request ID. See `DefaultGenerator` and `New` package-level functions.
type Generator func(ctx context.Context) string
type Generator func(ctx *context.Context) string
// DefaultGenerator is the default `Generator` that is used
// when nil is passed on `New` package-level function.
@@ -22,7 +22,7 @@ type Generator func(ctx context.Context) string
// or, if missing, it generates a new UUID(v4) and sets the header and context value.
//
// See `Get` package-level function too.
var DefaultGenerator Generator = func(ctx context.Context) string {
var DefaultGenerator Generator = func(ctx *context.Context) string {
id := ctx.GetHeader(xRequestIDHeaderValue)
if id == "" {
@@ -50,7 +50,7 @@ func New(generator ...Generator) context.Handler {
gen = generator[0]
}
return func(ctx context.Context) {
return func(ctx *context.Context) {
if Get(ctx) != "" {
ctx.Next()
return
@@ -71,7 +71,7 @@ func New(generator ...Generator) context.Handler {
// Get returns the Request ID or empty string.
//
// A shortcut of `context.GetID().(string)`.
func Get(ctx context.Context) string {
func Get(ctx *context.Context) string {
v := ctx.GetID()
if v != nil {
if id, ok := v.(string); ok {

View File

@@ -24,7 +24,7 @@ func TestRequestID(t *testing.T) {
const expectedCustomID = "my_id"
custom := app.Party("/custom")
{
customGen := func(ctx context.Context) string {
customGen := func(ctx *context.Context) string {
return expectedCustomID
}
@@ -35,7 +35,7 @@ func TestRequestID(t *testing.T) {
const expectedErrMsg = "no id"
customWithErr := app.Party("/custom_err")
{
customGen := func(ctx context.Context) string {
customGen := func(ctx *context.Context) string {
ctx.StopWithText(iris.StatusUnauthorized, expectedErrMsg)
return ""
}