router

package
v1.11.5 Latest Latest
Warning

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

Go to latest
Published: Mar 28, 2024 License: MIT Imports: 17 Imported by: 0

README

router

Package router provides a simple & efficient parametrized HTTP router.

A HTTP router allows you to map a HTTP request method and path to a specific function. A parameterized HTTP router allows you to designate specific portions of the request path as a parameter, which can later be fetched during the request itself.

This package allows you modify the routing table ad-hoc, even while the server is running.

Install

This package is provided by 'github.com/ecnepsnai/web', so add that to your go.mod file:

go get github.com/ecnepsnai/web

Documentation

Overview

Package router provides a simple & efficient parametrized HTTP router.

A HTTP router allows you to map a HTTP request method and path to a specific function. A parameterized HTTP router allows you to designate specific portions of the request path as a parameter, which can later be fetched during the request itself.

This package allows you modify the routing table ad-hoc, even while the server is running.

Index

Examples

Constants

This section is empty.

Variables

View Source
var CacheMaxAge time.Duration = 24 * time.Hour

CacheMaxAge the amount of time browsers may consider static content to be fresh. Set this to 0 to not include a "Cache-Control" header for static requests.

View Source
var GenerateDirectoryListing = true

GenerateDirectoryListing if the router should generate a directory listing for static directories that do not have an index file (see also IndexFileName)

View Source
var IndexFileName = "index.html"

IndexFileName is the name used when searching a directory for an index

Functions

func ServeHTTPRange added in v1.9.0

func ServeHTTPRange(options ServeHTTPRangeOptions) error

ServeHTTPRange serve a HTTP range

Types

type ByteRange added in v1.9.0

type ByteRange struct {
	Start int64
	End   int64
}

ByteRange describes a range of offsets for reading from a byte slice.

There are thee possabilities for byte ranges: (Start=100, End=200) Read data from offset 100 until offset 200. (Start=100, End=-1) Read all remaining data from offset 100 until the end of the reader. (Start=-1, End=100) Read the last 100 bytes of the reader.

func ParseRangeHeader added in v1.9.0

func ParseRangeHeader(value string) []ByteRange

ParseRangeHeader will parse the value from the HTTP ranges header and return a slice of byte ranges, or nil if the headers value is malformed.

func (ByteRange) ContentRangeValue added in v1.9.0

func (br ByteRange) ContentRangeValue(total uint64) string

ContentRangeValue will return a value sutible for the content-range header

func (ByteRange) Length added in v1.9.1

func (br ByteRange) Length(total uint64) uint64

Length return the length of data represented by this byte range

type Handle

type Handle func(http.ResponseWriter, Request)

Handle describes the signature for a handle of a path

type IMime

type IMime interface {
	// GetMime is called with the path to a file when the router needs a value for the "Content-Type" header.
	// This method must be thread-safe and should not panic. If an error occurs you must return
	// "application/octet-stream".
	//
	// filePath is guranteed to exist and be readable by the running process.
	GetMime(filePath string) string
}

IMime describes an interface for a MIME detection system

var MimeGetter IMime = &extensionMimeGetterType{}

MimeGetter the implementation used to determine the value of the "Content-Type" header for static files. By default this uses a simple implentation that guesses the MIME type based on the extension in the files name.

type Request

type Request struct {
	// The underlaying HTTP request
	HTTP *http.Request
	// A map of any parameters from the router path mapped to their values from the request path
	Parameters map[string]string
}

Request describes a HTTP request

type ServeHTTPRangeOptions added in v1.9.0

type ServeHTTPRangeOptions struct {
	// Any additional headers to append to the request.
	// Do not specify a content-type here, instead use the MIMEType property.
	Headers map[string]string
	// Cookies to set on the response
	Cookies []http.Cookie
	// Byte ranges from the HTTP request
	Ranges []ByteRange
	// The incoming reader, must support seeking
	Reader io.ReadSeeker
	// The total length of the data
	TotalLength uint64
	// The content type of the data
	MIMEType string
	// The outgoing HTTP response writer
	Writer http.ResponseWriter
}

ServeHTTPRangeOptions options for serving a HTTP range request

type Server

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

Server describes a server. Do not initialize a new copy of a Server{}, but instead use router.New()

func New

func New() *Server

New will initialize a new Server instance and return it. This does not start the server.

func (*Server) Handle

func (s *Server) Handle(method, path string, handler Handle)

Handle registers a handler for an HTTP request of method to path.

Method must be a valid HTTP method, in all caps. Path must always begin with a forward slash /. Will panic on invalid vales. Will panic if registering a duplicate method & path.

Handle may be called even while the server is listening and is threadsafe.

Any segment that begins with a colon (:) will be parameterized. The value of all parameters for the path will be populated into the Parameters map included in the Request object in the handler. For example:

handle path  = "/widgets/:widget_id/cost/:currency"
request path = "/widgets/1234/cost/cad"
parameters   = { "widget_id": "1234", "currency": "cad" }

Any segment that begins with an astreisk (*) will be parameterized as well, however unlike colon parameters, these will include the entire remaining path as the value of the parameter, whereas colon parameters will only include that segment as the value. Multiple methods can be registered for the same wildcard path, provided they use the same parameter name. Any segments included after the parameter name are ignored. For example

handle path  = "/proxy/*url"
request path = "/proxy/some/multi/segmented/value"
parameters   = { "url": "some/multi/segmented/value" }

Parameter segments are exclusive, meaning you can not have a static segment at the same position as a parameterized element. For example, these both will panic:

// This panics because /all occupies the same segment as the parameter :username
server.Handle("GET", "/users/:username", ...)
server.Handle("GET", "/users/all", ...)

// This panics because /user/id occupied the same segment as the wildcard parameter *param
server.Handle("GET", "/users/*param", ...)
server.Handle("GET", "/users/user/id", ...)

Paths that end with a slash are unique to those that don't. For example, these would be considred unique by the router:

server.Handle("GET", "/users/all/", ...)
server.Handle("GET", "/users/all", ...)
Example
package main

import (
	"net/http"

	"github.com/ecnepsnai/web/router"
)

func main() {
	server := router.New()
	server.Handle("GET", "/hello/:greeting", func(rw http.ResponseWriter, r router.Request) {
		rw.Write([]byte("Hello, " + r.Parameters["greeting"]))
	})
}
Output:

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(addr string) error

ListenAndServe will listen for HTTP requests on the specified socket address. Valid addresses are typically in the form of: <IP Address>:<Port Number>. For IPv6 addresses, wrap the address in brackets.

An error will only be returned if there was an error listening or the listener was closed.

Example
package main

import (
	"github.com/ecnepsnai/web/router"
)

func main() {
	server := router.New()
	server.ListenAndServe("127.0.0.1:8080")
}
Output:

func (*Server) RemoveHandle

func (s *Server) RemoveHandle(method, path string)

RemoveHandle will remove any handler for the given method and path. If no handle exists, it does nothing. If both method and path are * it removes everything from the routing table.

Note that parameter names are not considered when removing a path. For example, you may register a path with `/:username` and remove it with `/:something_else`.

This may be called even while the server is listening and is threadsafe.

Example
package main

import (
	"net/http"

	"github.com/ecnepsnai/web/router"
)

func main() {
	server := router.New()
	server.Handle("GET", "/hello/:greeting", func(rw http.ResponseWriter, r router.Request) {
		rw.Write([]byte("Hello, " + r.Parameters["greeting"]))
	})
	server.RemoveHandle("GET", "/hello/:greeting")
	server.RemoveHandle("*", "*") // Will remove everything from the routing table!
}
Output:

func (*Server) Serve

func (s *Server) Serve(listener net.Listener) error

Serve will listen for HTTP requests on the given listener.

An error will only be returned if there was an error listening or the listener was abruptly closed.

Example
package main

import (
	"net"

	"github.com/ecnepsnai/web/router"
)

func main() {
	server := router.New()
	l, _ := net.Listen("tcp", "[::1]:8080")
	server.Serve(l)
}
Output:

func (*Server) ServeFiles

func (s *Server) ServeFiles(localRoot string, urlRoot string)

ServeFiles registers a handler for all requests under urlRoot to serve any files matching the same path in a local filesystem directory localRoot.

For example:

localRoot = /usr/share/www/
urlRoot   = /static/

Request for '/static/image.jpg' would read file '/usr/share/www/image.jpg'

Will panic if any handle is registered under urlRoot. Attempting to register a new handle under urlRoot after calling ServeFiles will panic.

Caching will be enabled by default for all files served by this router. The mtime of the file will be used for the Last-Modified date.

By default, the server will use the file extension (if any) to determine the MIME type for the response. You may use your own MIME detection by implementing the IMime interface and setting MimeGetter.

The server will also instruct clients to cache files served for up-to 1 day. You can control this with the CacheMaxAge variable.

When a directory is requested, the router will look for an index file with the name from the IndexFileName variable. If no file is found, a directory listing will automatically be generated. You can control this with the GenerateDirectoryListing variable.

Example
package main

import (
	"github.com/ecnepsnai/web/router"
)

func main() {
	server := router.New()

	localDirectory := "/usr/share/http" // Directory to serve files from
	urlRoot := "/assets/"               // Top-level path for all requests to be directed to the local directory

	server.ServeFiles(localDirectory, urlRoot)
	// Now any HTTP GET or HEAD requests to /assets/ will read files from /usr/share/http.
	// For example:
	// HTTP GET "/assets/index.html" will read file "/usr/share/http/index.html"
}
Output:

func (*Server) SetMethodNotAllowedHandle

func (s *Server) SetMethodNotAllowedHandle(handle func(w http.ResponseWriter, r *http.Request))

SetMethodNotAllowedHandle will set the handle called when a request comes in for a known path but not the correct method.

A default handle is set when the server is created.

func (*Server) SetNotFoundHandle

func (s *Server) SetNotFoundHandle(handle func(w http.ResponseWriter, r *http.Request))

SetNotFoundHandle will set the handle called when a request that did not match any registered path comes in.

A default handle is set when the server is created.

func (*Server) Stop

func (s *Server) Stop()

Stop will stop the server. Server.ListenAndServe or Server.Serve will return net.ErrClosed. Does nothing if the was not listening or was already stopped.

Jump to

Keyboard shortcuts

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