http

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jan 25, 2024 License: MIT Imports: 16 Imported by: 0

README

http

Test license

yet another http routing library

goal

  1. provide minimum surface area for functioning a http library
  2. understanding the underlying mechanisms of http routing
  3. evaluate alternative approaches to various existing solutions
  4. provide model for custom http routines

usage

routing
import (
  h "github.com/aakash-rajur/http"
  "github.com/aakash-rajur/http/params"
  "net/http"
)

router := h.NewRouter()

router.GetFunc(
  "/health",
  func (w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    
    _, _ = w.Write([]byte("OK"))
  },
)

router.GetFunc(
  "/api/v2/books/:id",
  func(w http.ResponseWriter, r *http.Request) {
    p, ok := params.FromContext(r.Context())
    
    if !ok {
      http.Error(w, "unable to parse param", http.StatusInternalServerError)
      
      return
    }
    
    idString := p.Get("id", "")
    
    id, err := strconv.Atoi(idString)
    
    if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      
      return
    }
    
    book := books[id-1]
    
    buffer, err := json.Marshal(book)
    
    if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      
      return
    }
    
    w.Header().Set("Content-Type", "application/json")
    
    w.WriteHeader(http.StatusOK)
    
    _, _ = w.Write(buffer)
  },
)

server := &http.Server{
  Addr:    address,
  Handler: router,
}
middleware
import (
  h "github.com/aakash-rajur/http"
)

router := h.NewRouter()

router.Use(h.Logger(h.LoggerConfig{}))

router.Use(
  func(w http.ResponseWriter, r *http.Request, next h.Next) {
    next(r)
  },
)
logging
import (
  h "github.com/aakash-rajur/http"
)

router := h.NewRouter()

router.Use(h.Logger(h.LoggerConfig{}))

router.GetFunc(
  "/api/v2/books",
  func (w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    
    _, _ = w.Write([]byte("OK"))
  },
)
2024-01-07 14:21:33 | HTTP/2 |  200 |  237.083µs |                     text/plain |               application/json |       127.0.0.1 |     GET /api/v2/books 
2024-01-07 14:21:34 | HTTP/2 |  404 |   71.666µs |                     text/plain |      text/plain; charset=utf-8 |       127.0.0.1 |     GET /favicon.ico 
2024-01-07 14:21:36 | HTTP/2 |  200 |   46.125µs |                     text/plain |               application/json |       127.0.0.1 |     GET /api/v2/books 
2024-01-07 14:21:37 | HTTP/2 |  200 |  156.541µs |                     text/plain |               application/json |       127.0.0.1 |     GET /api/v2/books 
2024-01-07 14:21:37 | HTTP/2 |  200 |  105.125µs |                     text/plain |               application/json |       127.0.0.1 |     GET /api/v2/books 
2024-01-07 14:21:37 | HTTP/2 |  200 |   80.167µs |                     text/plain |               application/json |       127.0.0.1 |     GET /api/v2/books 
2024-01-07 14:21:38 | HTTP/2 |  200 |   78.458µs |                     text/plain |               application/json |       127.0.0.1 |     GET /api/v2/books 
2024-01-07 14:21:38 | HTTP/2 |  200 |   85.083µs |                     text/plain |               application/json |       127.0.0.1 |     GET /api/v2/books 
custom logger
import (
  h "github.com/aakash-rajur/http"
)

router := h.NewRouter()

router.Use(
  h.Logger(
    h.LoggerConfig{
      LogFormatter: func(formatterParams h.LogFormatterParams) string {
        jsonPayload, err := json.Marshal(formatterParams)

        if err != nil {
          return ""
        }

        return fmt.Sprintf("%s\n", jsonPayload)
      },
    },
  ),
)

router.GetFunc(
  "/api/v2/books",
  func (w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    
    _, _ = w.Write([]byte("OK"))
  },
)
{"timestamp":"2024-01-07T14:34:19.03845+05:30","status_code":200,"latency":384750,"client_ip":"127.0.0.1","method":"GET","path":"/api/v2/books","query":{},"request_content_type":"text/plain","request_content_encoding":"identity","response_content_type":"application/json","response_content_encoding":"identity","protocol_version":2}
{"timestamp":"2024-01-07T14:34:20.315136+05:30","status_code":200,"latency":86334,"client_ip":"127.0.0.1","method":"GET","path":"/api/v2/books","query":{},"request_content_type":"text/plain","request_content_encoding":"identity","response_content_type":"application/json","response_content_encoding":"identity","protocol_version":2}
{"timestamp":"2024-01-07T14:34:20.871688+05:30","status_code":200,"latency":146875,"client_ip":"127.0.0.1","method":"GET","path":"/api/v2/books","query":{},"request_content_type":"text/plain","request_content_encoding":"identity","response_content_type":"application/json","response_content_encoding":"identity","protocol_version":2}
{"timestamp":"2024-01-07T14:34:23.677465+05:30","status_code":200,"latency":29875,"client_ip":"127.0.0.1","method":"GET","path":"/api/v2/books","query":{},"request_content_type":"text/plain","request_content_encoding":"identity","response_content_type":"application/json","response_content_encoding":"identity","protocol_version":2}

mechanism

search for incoming path in a pre-sorted list of registered routes using binary search costing O(log n) time where n is the number of registered routes

registration
  1. while registering we've always receive a path pattern and a method (along with handler).
  2. create a new pattern of form /method/pattern and run sanity through the same.
  3. split the pattern into segments (array of strings)
  4. append segments to a list of segments
  5. sort this list of segments with static segments taking precedence over path params.
matching
  1. prepend the method to the incoming path and run sanity through the same.
  2. split the path into segments (array of strings)
  3. search for the segments in the list of segments using binary search.
  4. path params are matched to true for all values in that corresponding segment position.

alternatives

trie
  1. a tree like data structure that is used to store strings.
  2. more difficult to implement than a simple list of segments.
  3. similar runtime complexity as a list of segments O(log n) where n is the number of registered routes.
  1. search for incoming path in a list of registered routes.
  2. runtime complexity of O(n) where n is the number of registered routes.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Body

type Body map[string]any

func (Body) Map

func (b Body) Map() map[string]any

func (Body) Reader

func (b Body) Reader() io.Reader

type LogFormatterParams

type LogFormatterParams struct {
	Timestamp               time.Time           `json:"timestamp" yaml:"timestamp"`
	StatusCode              int                 `json:"status_code" yaml:"status_code"`
	Latency                 time.Duration       `json:"latency" yaml:"latency"`
	ClientIP                string              `json:"client_ip" yaml:"client_ip"`
	Method                  string              `json:"method" yaml:"method"`
	Path                    string              `json:"path" yaml:"path"`
	Query                   map[string][]string `json:"query" yaml:"query"`
	RequestContentType      string              `json:"request_content_type" yaml:"request_content_type"`
	RequestContentEncoding  string              `json:"request_content_encoding" yaml:"request_content_encoding"`
	ResponseContentType     string              `json:"response_content_type" yaml:"response_content_type"`
	ResponseContentEncoding string              `json:"response_content_encoding" yaml:"response_content_encoding"`
	ProtocolVersion         int                 `json:"protocol_version" yaml:"protocol_version"`
}

func (LogFormatterParams) String

func (l LogFormatterParams) String() string

type LoggerConfig

type LoggerConfig struct {
	Output       io.Writer
	LogFormatter func(LogFormatterParams) string
}

type Middleware

type Middleware func(http.ResponseWriter, *http.Request, Next)

func Logger

func Logger(config LoggerConfig) Middleware

type Middlewares

type Middlewares []Middleware

func (Middlewares) Append

func (m Middlewares) Append(middleware Middleware) Middlewares

func (Middlewares) Chain

func (m Middlewares) Chain(final http.HandlerFunc) http.HandlerFunc

type MockHandler

type MockHandler struct {
	mock.Mock
	// contains filtered or unexported fields
}

func NewMockHandler

func NewMockHandler(handler http.Handler) *MockHandler

func (*MockHandler) Handler

func (mh *MockHandler) Handler() http.Handler

func (*MockHandler) ServeHTTP

func (mh *MockHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request)

type MockMiddleware

type MockMiddleware struct {
	mock.Mock
}

func NewMockMiddleware

func NewMockMiddleware() *MockMiddleware

func (*MockMiddleware) Middleware

func (mm *MockMiddleware) Middleware() Middleware

func (*MockMiddleware) Run

func (mm *MockMiddleware) Run(rw http.ResponseWriter, r *http.Request, next Next)

type Next

type Next func(*http.Request)

type ResponseWriter

type ResponseWriter struct {
	http.ResponseWriter

	StatusCode int
}

func (*ResponseWriter) Flush

func (rw *ResponseWriter) Flush()

func (*ResponseWriter) Flusher

func (rw *ResponseWriter) Flusher() (http.Flusher, bool)

func (*ResponseWriter) Hijack

func (rw *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)

func (*ResponseWriter) Hijacker

func (rw *ResponseWriter) Hijacker() (http.Hijacker, bool)

func (*ResponseWriter) WriteHeader

func (rw *ResponseWriter) WriteHeader(statusCode int)

type Router

type Router struct {
	// contains filtered or unexported fields
}

func NewRouter

func NewRouter() *Router

func (*Router) Connect

func (router *Router) Connect(pattern string, handler http.Handler)

func (*Router) ConnectFunc

func (router *Router) ConnectFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) Delete

func (router *Router) Delete(pattern string, handler http.Handler)

func (*Router) DeleteFunc

func (router *Router) DeleteFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) Get

func (router *Router) Get(pattern string, handler http.Handler)

func (*Router) GetFunc

func (router *Router) GetFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) Handle

func (router *Router) Handle(pattern string, handler http.Handler)

func (*Router) HandleFunc

func (router *Router) HandleFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) HandleMethod

func (router *Router) HandleMethod(method, pattern string, handler http.Handler)

func (*Router) HandleMethodFunc

func (router *Router) HandleMethodFunc(method, pattern string, handlerFunc http.HandlerFunc)

func (*Router) Head

func (router *Router) Head(pattern string, handler http.Handler)

func (*Router) HeadFunc

func (router *Router) HeadFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) NotFound

func (router *Router) NotFound(handler http.Handler)

func (*Router) Options

func (router *Router) Options(pattern string, handler http.Handler)

func (*Router) OptionsFunc

func (router *Router) OptionsFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) Patch

func (router *Router) Patch(pattern string, handler http.Handler)

func (*Router) PatchFunc

func (router *Router) PatchFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) Post

func (router *Router) Post(pattern string, handler http.Handler)

func (*Router) PostFunc

func (router *Router) PostFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) Put

func (router *Router) Put(pattern string, handler http.Handler)

func (*Router) PutFunc

func (router *Router) PutFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) ServeHTTP

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Router) Trace

func (router *Router) Trace(pattern string, handler http.Handler)

func (*Router) TraceFunc

func (router *Router) TraceFunc(pattern string, handlerFunc http.HandlerFunc)

func (*Router) Use

func (router *Router) Use(middleware Middleware)

type TestRoute

type TestRoute struct {
	Name    string `json:"Name" yaml:"Name"`
	Method  string `json:"method" yaml:"method"`
	Pattern string `json:"Pattern" yaml:"Pattern"`
}

type TestRouteParams

type TestRouteParams struct {
	Method  string
	Pattern string
	Path    string
	Body    Body
	Params  map[string]any
}

Directories

Path Synopsis
cmd
app

Jump to

Keyboard shortcuts

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