예제 #1
0
// Handle takes the next handler as an argument and wraps it in this middleware.
func (m *Middle) Handle(next httpware.Handler) httpware.Handler {
	return httpware.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		q := r.URL.Query()
		s := q.Get(m.startQuery)
		l := q.Get(m.limitQuery)
		page := Page{}
		var err error
		if s == "" {
			page.Start = 0
		} else {
			page.Start, err = strconv.Atoi(s)
			if err != nil {
				return httpware.NewErr("invalid query parameter", http.StatusBadRequest).WithField(m.startQuery, "must be an integer")
			}
			if page.Start < 0 {
				return httpware.NewErr("invalid query parameter", http.StatusBadRequest).WithField(m.startQuery, "must not be negative")
			}
		}
		if l == "" {
			page.Limit = m.limitDefault
		} else {
			page.Limit, err = strconv.Atoi(l)
			if err != nil {
				return httpware.NewErr("invalid query parameter", http.StatusBadRequest).WithField(m.limitQuery, "must not be an integer")
			}
			if page.Limit <= 0 {
				return httpware.NewErr("invalid query parameter", http.StatusBadRequest).WithField(m.limitQuery, "must not be greater than zero")
			}
		}

		return next.ServeHTTPCtx(context.WithValue(ctx, httpware.PageKey, page), w, r)
	})
}
예제 #2
0
// Handle takes the next handler as an argument and wraps it in this middleware.
func (m *Middle) Handle(next httpware.Handler) httpware.Handler {
	return httpware.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		ctx = context.WithValue(ctx, httpware.RequestContentTypeKey, GetContentMatch(r.Header.Get("Content-Type")))
		ct := GetContentMatch(r.Header.Get("Accept"))
		ctx = context.WithValue(ctx, httpware.ResponseContentTypeKey, ct)
		w.Header().Set("Content-Type", ct.Value)

		return next.ServeHTTPCtx(ctx, w, r)
	})
}
예제 #3
0
파일: cors.go 프로젝트: nstogner/httpware
// Handle takes the next handler as an argument and wraps it in this middleware.
func (m *Middle) Handle(next httpware.Handler) httpware.Handler {
	return httpware.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		w.Header().Set("Access-Control-Allow-Origin", m.allowOrigin)
		w.Header().Set("Access-Control-Allow-Credentials", m.allowCredentials)
		if m.shouldExposeHeaders {
			w.Header().Set("Access-Control-Expose-Headers", m.exposeHeaders)
		}
		return next.ServeHTTPCtx(ctx, w, r)
	})
}
예제 #4
0
파일: limit.go 프로젝트: nstogner/httpware
// Handle takes the next handler as an argument and wraps it in this middleware.
func (m *Middle) Handle(next httpware.Handler) httpware.Handler {
	return httpware.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		remote := strings.Split(r.RemoteAddr, ":")
		if len(remote) != 2 {
			return next.ServeHTTPCtx(ctx, w, r)
		}

		if m.increment(remote[0]) {
			defer m.decrement(remote[0])
			return next.ServeHTTPCtx(ctx, w, r)
		}

		// Send a 429 response (Too Many Requests).
		m.retryHeader(w)
		return httpware.NewErr("exceeded request rate limit", 429)
	})
}
예제 #5
0
파일: token.go 프로젝트: nstogner/httpware
// Handle takes the next handler as an argument and wraps it in this middleware.
func (m *Middle) Handle(next httpware.Handler) httpware.Handler {
	return httpware.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		token, err := request.ParseFromRequest(
			r,
			request.AuthorizationHeaderExtractor,
			func(token *jwt.Token) (interface{}, error) {
				return m.conf.Secret, nil
			},
		)

		if err == nil && token.Valid {
			newCtx := context.WithValue(ctx, httpware.TokenKey, token)
			return next.ServeHTTPCtx(newCtx, w, r)
		}

		// No soup for you.
		return httpware.NewErr("invalid token", http.StatusUnauthorized)
	})
}
예제 #6
0
파일: log.go 프로젝트: nstogner/httpware
// Handle takes the next handler as an argument and wraps it in this middleware.
func (m *Middle) Handle(next httpware.Handler) httpware.Handler {
	return httpware.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// Log panics.
		defer func() {
			if rcv := recover(); rcv != nil {
				m.conf.Logger.WithField("error", rcv).Error("handler panic detected")
				// Pass on the panic.
				panic(rcv)
			}
		}()

		// Always log the method and path.
		entry := m.conf.Logger.WithFields(logrus.Fields{
			"method": r.Method,
			"path":   r.URL.Path,
		})

		// Conditional logging...
		if m.conf.Referer {
			entry = entry.WithField("referrer", r.Referer())
		}
		if m.conf.RemoteAddr {
			entry = entry.WithField("remoteAddr", r.RemoteAddr)
		}
		for _, h := range m.conf.Headers {
			entry = entry.WithField(h, r.Header.Get(h))
		}

		if m.conf.Start {
			entry.Info("new request")
		}

		// Call downstream handlers.
		err := next.ServeHTTPCtx(ctx, w, r)

		if m.conf.End {
			// Add any errors to the log entry.
			statusCode := 0
			if err != nil {
				if httpErr, ok := err.(httpware.Err); ok {
					entry = entry.WithFields(logrus.Fields{
						"statusCode": httpErr.StatusCode,
						"message":    httpErr.Message,
					})
					entry = entry.WithFields(httpErr.Fields)
					statusCode = httpErr.StatusCode
				} else {
					entry = entry.WithField("error",
						map[string]interface{}{
							"statusCode": http.StatusInternalServerError,
							"message":    err,
						},
					)
					statusCode = http.StatusInternalServerError
				}
			}

			// Log with the right level and pass on the error.
			if statusCode >= 500 {
				entry.Error("request resulted in server error")
			} else {
				if statusCode >= 400 {
					if !m.conf.Ignore4XX {
						entry.Info("request resulted in client error")
					}
				} else if !m.conf.IgnoreUnder400 {
					entry.Info("request successful")
				}
			}
		}
		return err
	})
}