httpf

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 22, 2023 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Example
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"sync"

	"github.com/Prastiwar/Go-flow/httpf"
)

const hostPrefix = "localhost:"

var (
	port     = 8080
	serverMu = sync.Mutex{}
)

// runServer creates new server and listens in new goroutine on address that is returned from this function.
func runServer(router httpf.Router) string {
	serverMu.Lock()
	defer serverMu.Unlock()

	port++
	address := hostPrefix + strconv.Itoa(port)
	go func() {
		_ = httpf.NewServer(address, router).ListenAndServe()
	}()
	return "http://" + address
}

func main() {
	mux := httpf.NewServeMuxBuilder()

	mux.WithErrorHandler(httpf.ErrorHandlerFunc(func(w http.ResponseWriter, r *http.Request, err error) {
		// define standard structure for error response
		type httpError struct {
			Error string `json:"error"`
			Code  int    `json:"code"`
		}

		// map infrastructure errors to http error response
		var resultError httpError
		if errors.Is(err, context.DeadlineExceeded) {
			resultError = httpError{
				Error: http.StatusText(http.StatusRequestTimeout),
				Code:  http.StatusRequestTimeout,
			}
		} else {
			resultError = httpError{
				Error: http.StatusText(http.StatusInternalServerError),
				Code:  http.StatusInternalServerError,
			}
		}

		// marshal error and write to Response
		result, err := json.Marshal(resultError)
		if err != nil {
			log.Fatal(err)
		}

		_, err = w.Write(result)
		if err != nil {
			log.Fatal(err)
		}
	}))

	mux.Post("/api/test/", httpf.HandlerFunc(func(w httpf.ResponseWriter, r *http.Request) error {
		result := struct {
			Id string `json:"id"`
		}{
			Id: "1234",
		}

		return w.Response(http.StatusCreated, result)
	}))

	serverAddress := runServer(mux.Build())

	resp, err := http.Post(serverAddress+"/api/test/", httpf.ApplicationJsonType, bytes.NewBufferString("{}"))
	if err != nil {
		fmt.Println(err)
		return
	}
	defer resp.Body.Close()

	fmt.Println(resp.StatusCode)
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))

}
Output:
201
{"id":"1234"}

Index

Examples

Constants

View Source
const (
	AcceptHeader                  = "Accept"
	AcceptCharsetHeader           = "Accept-Charset"
	AcceptEncodingHeader          = "Accept-Encoding"
	AcceptLanguageHeader          = "Accept-Language"
	AcceptRangesHeader            = "Accept-Ranges"
	CacheControlHeader            = "Cache-Control"
	CcHeader                      = "Cc"
	ConnectionHeader              = "Connection"
	ContentIdHeader               = "Content-Id"
	ContentLanguageHeader         = "Content-Language"
	ContentLengthHeader           = "Content-Length"
	ContentTransferEncodingHeader = "Content-Transfer-Encoding"
	ContentTypeHeader             = "Content-Type"
	CookieHeader                  = "Cookie"
	DateHeader                    = "Date"
	ExpiresHeader                 = "Expires"
	FromHeader                    = "From"
	HostHeader                    = "Host"
	LocationHeader                = "Location"
	ServerHeader                  = "Server"
	SetCookieHeader               = "Set-Cookie"
	UserAgentHeader               = "User-Agent"
	XForwardedForHeader           = "X-Forwarded-For"

	ApplicationJsonType        = "application/json"
	ApplicationFormEncodedType = "application/x-www-form-urlencoded"
)
View Source
const (
	RateLimitLimitHeader     = "X-Rate-Limit-Limit"
	RateLimitRemainingHeader = "X-Rate-Limit-Remaining"
	RateLimitResetHeader     = "X-Rate-Limit-Reset"
)

Variables

This section is empty.

Functions

func HasParam

func HasParam(r *http.Request, key string) bool

HasParam returns true if key exists in path params map.

func Json

func Json(w http.ResponseWriter, status int, data interface{}) error

Json marshals the data and writes it to http.ResponseWriter with given status code. "Content-Type" header is set to "application/json".

func NewClient

func NewClient(opts ...ClientOption) *client

NewClient returns a new instace of Client which is adapter for http.Client. Provided options can be set optionally to pass the values in http.Client construction.

func NewServeMuxBuilder

func NewServeMuxBuilder() *serveMuxBuilder

NewServeMuxBuilder returns RouterBuilder which build results in adapting http.ServeMux implementation to handle errors, decorate http.ResponseWriter or use ParamsParser. Note http.ServeMux does not support defining parameters in pattern. For default behaviour of corresponding With.. option can be found in option func comment.

func NewServer

func NewServer(addr string, router Router) *http.Server

NewServer returns a new instance of Server.

func Param

func Param(r *http.Request, key string) string

Params returns raw value for path param by key. If no key was set it returns "". To distinguish between empty value and value was not set use HasParam or Params directly.

func Params

func Params(r *http.Request) map[string]string

Params retrieves path param values from request context or empty map if it was not set. Router is responsible to decorate http request with WithParams using ParamsParser.

func WithParams

func WithParams(r *http.Request, params map[string]string) *http.Request

WithParams returns a shallow copy of r with its context changed to contain params as context value.

Types

type Client

type Client interface {
	Send(ctx context.Context, req *http.Request) (*http.Response, error)

	Get(ctx context.Context, url string) (*http.Response, error)
	Post(ctx context.Context, url string, body io.Reader) (*http.Response, error)
	PostForm(ctx context.Context, url string, form url.Values) (*http.Response, error)
	Put(ctx context.Context, url string, body io.Reader) (*http.Response, error)
	Delete(ctx context.Context, url string) (*http.Response, error)

	Close()
}

A Client is an HTTP client containing convenient API to send request with common HTTP methods. Send function is the fundamental implementation for the Client which provides a way to send request over HTTP and receive response.

type ClientOption

type ClientOption func(*ClientOptions)

ClientOption defines single function to mutate options.

func WithCookies

func WithCookies(cookieJar http.CookieJar) ClientOption

WithCookies sets option which specifies cookie jar used to insert relevant cookies into every outbound Request and is updated with the cookie values of every inbound Response.

func WithRedirectHandler

func WithRedirectHandler(handler func(req *http.Request, via []*http.Request) error) ClientOption

WithRedirectHandler sets option which specifies the policy for handling redirects.

func WithTimeout

func WithTimeout(timeout time.Duration) ClientOption

WithTimeout sets option which specifies a time limit for requests made by Client.

func WithTransport

func WithTransport(transport http.RoundTripper) ClientOption

WithTransport sets option which specifies the mechanism by which individual HTTP requests are made.

type ClientOptions

type ClientOptions struct {
	Transport     http.RoundTripper
	CheckRedirect func(req *http.Request, via []*http.Request) error
	Jar           http.CookieJar
	Timeout       time.Duration
}

ClientOptions defines http.Client constructor parameters which can be set on NewClient.

func NewClientOptions

func NewClientOptions(opts ...ClientOption) ClientOptions

NewClientOptions returns a new instance of ClientOptions with is result of merged ClientOption slice.

type ErrorHandler

type ErrorHandler interface {
	Handle(w http.ResponseWriter, r *http.Request, err error)
}

A ErrorHandler handles error returned from Handler

Handle should write response to the ResponseWriter in common used format with proper mapped error.

type ErrorHandlerFunc

type ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)

The ErrorHandlerFunc type is an adapter to allow the use of ordinary functions as Error handlers. If h is a function with the appropriate signature, ErrorHandlerFunc(h) is a ErrorHandler that calls h.

func (ErrorHandlerFunc) Handle

func (h ErrorHandlerFunc) Handle(w http.ResponseWriter, r *http.Request, err error)

Handle calls h(w, r, err)

type Handler

type Handler interface {
	ServeHTTP(w ResponseWriter, r *http.Request) error
}

A Handler responds to an HTTP request

ServeHTTP should write reply headers and data to the ResponseWriter and then return any occurring error. The error should be handler by Router which should finish request process.

func RateLimitMiddleware added in v0.7.0

func RateLimitMiddleware(h Handler, store rate.LimiterStore, keyFactory RateHttpKeyFactory) Handler

RateLimitMiddleware returns httpf.Handler which used rate-limiting feature to decide if h Handler can be requested. If rate limit exceeds maximum value, the error is returned and should be handled by ErrorHandler to actually return 429 status code with appropiate body. This middleware writes all of "X-Rate-Limit-Limit", "X-Rate-Limit-Remaining" and "X-Rate-Limit-Reset" headers with correct values.

Example
package main

import (
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/Prastiwar/Go-flow/httpf"
	"github.com/Prastiwar/Go-flow/rate"
	"github.com/Prastiwar/Go-flow/tests/mocks"
)

const hostPrefix = "localhost:"

var (
	port     = 8080
	serverMu = sync.Mutex{}
)

// runServer creates new server and listens in new goroutine on address that is returned from this function.
func runServer(router httpf.Router) string {
	serverMu.Lock()
	defer serverMu.Unlock()

	port++
	address := hostPrefix + strconv.Itoa(port)
	go func() {
		_ = httpf.NewServer(address, router).ListenAndServe()
	}()
	return "http://" + address
}

func main() {
	mux := httpf.NewServeMuxBuilder()

	mux.WithErrorHandler(httpf.ErrorHandlerFunc(func(w http.ResponseWriter, r *http.Request, err error) {
		if errors.Is(err, rate.ErrRateLimitExceeded) {
			w.WriteHeader(http.StatusTooManyRequests)
			return
		}
		panic(err)
	}))

	var (
		resetPeriod time.Duration = time.Second
		maxTokens   uint64        = 2
		tokens      uint64        = maxTokens
	)
	storeLimiter := mocks.LimiterStoreMock{
		OnLimit: func(key string) rate.Limiter {
			return mocks.LimiterMock{
				OnLimit:  func() uint64 { return maxTokens },
				OnTokens: func() uint64 { return tokens },
				OnTake: func() rate.Token {
					return mocks.TokenMock{
						OnUse: func() error {
							if tokens <= 0 {
								return rate.ErrRateLimitExceeded
							}
							tokens--
							go func() {
								time.Sleep(resetPeriod)
								tokens++
							}()
							return nil
						},
						OnResetsAt: func() time.Time { return time.Now().Add(resetPeriod) },
					}
				},
			}
		},
	}

	mux.Get("/api/test/", httpf.RateLimitMiddleware(httpf.HandlerFunc(func(w httpf.ResponseWriter, r *http.Request) error {
		return w.Response(http.StatusOK, nil)
	}), storeLimiter, httpf.PathRateKey()))

	serverAddress := runServer(mux.Build())

	callGet := func() {
		resp, err := http.Get(serverAddress + "/api/test/")
		if err != nil {
			panic(err)
		}
		defer resp.Body.Close()

		fmt.Println(resp.StatusCode)
	}

	for i := uint64(0); i <= maxTokens; i++ {
		callGet()
	}

	time.Sleep(resetPeriod)
	callGet()

}
Output:
200
200
429
200

type HandlerFunc

type HandlerFunc func(w ResponseWriter, r *http.Request) error

The HandlerFunc type is an adapter to allow the use of ordinary functions as HTTP handlers. If h is a function with the appropriate signature, HandlerFunc(h) is a Handler that calls h.

func (HandlerFunc) ServeHTTP

func (h HandlerFunc) ServeHTTP(w ResponseWriter, r *http.Request) error

ServeHTTP calls h(w, r).

type ParamsParser

type ParamsParser interface {
	ParseParams(r *http.Request) map[string]string
}

ParamsParser is parser for request to retrieve path parameters. Implementation should decide how to retrieve params based on values in specified http request.

type ParamsParserFunc

type ParamsParserFunc func(r *http.Request) map[string]string

The ParamsParserFunc type is an adapter to allow the use of ordinary functions as ParamsParser. If p is a function with the appropriate signature, ParamsParserFunc(p) is a ParamsParser that will return p.

func (ParamsParserFunc) ParseParams

func (p ParamsParserFunc) ParseParams(r *http.Request) map[string]string

ParseParams returns p(r)

type RateHttpKeyFactory added in v0.7.0

type RateHttpKeyFactory func(r *http.Request) string

RateHttpKeyFactory is factory func to create a string key based on http.Request.

func ComposeRateKeyFactories added in v0.7.0

func ComposeRateKeyFactories(factories ...RateHttpKeyFactory) RateHttpKeyFactory

ComposeRateKeyFactories aggregates many RateHttpKeyFactory into single which will invoke all factories and merge the keys into single string key using whitespace as separator.

func IPRateKey added in v0.7.0

func IPRateKey(headerNames ...string) RateHttpKeyFactory

IPRateKey returns RateHttpKeyFactory that extracts value from http.Request that's either remote IP or header value sent with request and are specified in 'headerNames'. If more than one header would match, only the first value will be returned.

func PathRateKey added in v0.7.0

func PathRateKey() RateHttpKeyFactory

PathRateKey returns RateKeyFactory that extracts url path from http.Request. Note this extracts whole URL path without query and not the registered route pattern therefore should not be used together with endpoints which use path parameters. To distinguish between same pattern using different methods it appends http.Request Method as prefix with ':' separator.

type ResponseWriter

type ResponseWriter interface {
	http.ResponseWriter

	Response(code int, data interface{}) error
}

A ResponseWriter interface is used by an HTTP handler to construct an HTTP response. It extends http.ResponseWriter with Response function which should be used to share common response format.

type RouteBuilder

type RouteBuilder interface {
	Get(pattern string, handler Handler) RouteBuilder
	Post(pattern string, handler Handler) RouteBuilder
	Put(pattern string, handler Handler) RouteBuilder
	Delete(pattern string, handler Handler) RouteBuilder
	Patch(pattern string, handler Handler) RouteBuilder
	Options(pattern string, handler Handler) RouteBuilder

	WithErrorHandler(handler ErrorHandler) RouteBuilder
	WithWriterDecorator(decorator func(http.ResponseWriter) ResponseWriter) RouteBuilder
	WithParamsParser(parser ParamsParser) RouteBuilder

	Build() Router
}

A RouteBuilder is convenient builder for routing registration. It defines function for each HTTP Method. Pattern should be able to be registered with any method. It's also responsible to use ErrorHandler and WriterDecorator in mapping from Handler to http.Handler so errors can be handled gracefully and http.ResponseWriter would be decorated with Response function.

type Router

type Router interface {
	http.Handler

	Handle(pattern string, handler http.Handler)
}

A Router is an HTTP request multiplexer. It should match the URL of each incoming request against a list of registered patterns and call the handler for the pattern that most closely matches the URL. Router also should take care of sanitizing the URL request path and the Host header, stripping the port number and redirecting any request containing . or .. elements or repeated slashes to an equivalent, cleaner URL.

type Server

type Server interface {
	Close() error
	Shutdown(ctx context.Context) error
	RegisterOnShutdown(f func())

	ListenAndServe() error
	Serve(l net.Listener) error

	ListenAndServeTLS(certFile, keyFile string) error
	ServeTLS(l net.Listener, certFile, keyFile string) error
}

A Server defines functionality for running an HTTP server.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL