Documentation
¶
Overview ¶
Package router provides a simple & efficient parameterized HTTP router.
An HTTP router allows you to map an 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 ¶
- Variables
- func ServeHTTPRange(options ServeHTTPRangeOptions) error
- type ByteRange
- type Handle
- type IMime
- type Request
- type RequestResult
- type ServeHTTPRangeOptions
- type Server
- func (s *Server) Handle(method, path string, handler Handle)
- func (s *Server) ListenAndServe(addr string) error
- func (s *Server) RemoveHandle(method, path string)
- func (s *Server) Serve(listener net.Listener) error
- func (s *Server) ServeFS(filesystem fs.FS, fsRoot, urlRoot string, options *StaticOptions)
- func (s *Server) ServeFiles(localRoot string, urlRoot string, options *StaticOptions)
- func (s *Server) SetMethodNotAllowedHandle(handle func(w http.ResponseWriter, r *http.Request))
- func (s *Server) SetNotFoundHandle(handle func(w http.ResponseWriter, r *http.Request))
- func (s *Server) SetPostRequestHandle(handle func(r *RequestResult))
- func (s *Server) Stop()
- type StaticOptions
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ReadTimeout time.Duration ReadHeaderTimeout time.Duration WriteTimeout time.Duration IdleTimeout time.Duration )
var DefaultStaticOptions = StaticOptions{ GenerateDirectoryListing: true, IndexFileNames: []string{"index.htm", "index.html"}, CacheMaxAge: 24 * time.Hour, GenerateETag: true, IncludeLastModifiedDate: true, }
Default options for static requests
Functions ¶
func ServeHTTPRange ¶
func ServeHTTPRange(options ServeHTTPRangeOptions) error
ServeHTTPRange serve an HTTP range
Types ¶
type ByteRange ¶
ByteRange describes a range of offsets for reading from a byte slice.
There are thee possibilities 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 ¶
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 ¶
ContentRangeValue will return a value sutible for the content-range header
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 guaranteed to exist and be readable by the running process.
GetMime(filePath string) string
}
IMime describes an interface for an 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 implantation that guesses the MIME type based on the extension in the files name.
type Request ¶
type Request struct {
*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 an HTTP request
type RequestResult ¶ added in v1.1.1
type RequestResult struct {
// The HTTP method from the request
Method string
// The URL from the request
URL *url.URL
// The host from the request
Host string
// The remote address from the request
RemoteAddr string
// The headers written to the response
Header http.Header
// The status code of the response
StatusCode int
// The elapsed time that this request took
Elapsed time.Duration
// The panic reason if a panic occured during the handle of the request
Panic any
}
RequestResult describes the result of a HTTP request
type ServeHTTPRangeOptions ¶
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 an HTTP range request
type Server ¶
type Server struct {
// Optional channel to recieve a signal when this server is ready, meaning that HTTP requests sent to it will be
// processed.
Ready chan bool
// contains filtered or unexported fields
}
Server describes a server. Do not initialize a new copy of a Server{}, but instead use router.New(logtic.Log.Connect("HTTP"))
func (*Server) 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 thread-safe.
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. 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 considered unique by the router:
server.Handle("GET", "/users/all/", ...)
server.Handle("GET", "/users/all", ...)
Example ¶
package main
import (
"net/http"
"git.ecn.io/ian/w3/router"
"github.com/ecnepsnai/logtic"
)
func main() {
server := router.New(logtic.Log.Connect("HTTP"))
server.Handle("GET", "/hello/:greeting", func(rw http.ResponseWriter, r *router.Request) {
rw.Write([]byte("Hello, " + r.Parameters["greeting"]))
})
}
Output:
func (*Server) ListenAndServe ¶
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 (
"git.ecn.io/ian/w3/router"
"github.com/ecnepsnai/logtic"
)
func main() {
server := router.New(logtic.Log.Connect("HTTP"))
server.ListenAndServe("127.0.0.1:8080")
}
Output:
func (*Server) RemoveHandle ¶
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 can be called even while the server is listening and is thread-safe.
Example ¶
package main
import (
"net/http"
"git.ecn.io/ian/w3/router"
"github.com/ecnepsnai/logtic"
)
func main() {
server := router.New(logtic.Log.Connect("HTTP"))
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 ¶
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"
"git.ecn.io/ian/w3/router"
"github.com/ecnepsnai/logtic"
)
func main() {
server := router.New(logtic.Log.Connect("HTTP"))
l, _ := net.Listen("tcp", "[::1]:8080")
server.Serve(l)
}
Output:
func (*Server) ServeFS ¶
func (s *Server) ServeFS(filesystem fs.FS, fsRoot, urlRoot string, options *StaticOptions)
ServeFS registers a handler for all requests under urlRoot to serve any files matching the same path the provided filesystem. If you're trying to serve files found on-disk, it's recommended that you use ServeFiles instead.
Files will be served from the root of the filesystem. Use fsRoot to define a transparent root for all requests to be prepended with.
Follows all the sames rules and notes about ServeFiles, however one limitation of ServeFS is HTTP Range requests are not supported.
func (*Server) ServeFiles ¶
func (s *Server) ServeFiles(localRoot string, urlRoot string, options *StaticOptions)
ServeFiles registers a GET and HEAD 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 (
"git.ecn.io/ian/w3/router"
"github.com/ecnepsnai/logtic"
)
func main() {
server := router.New(logtic.Log.Connect("HTTP"))
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, nil)
// 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) SetPostRequestHandle ¶ added in v1.1.1
func (s *Server) SetPostRequestHandle(handle func(r *RequestResult))
SetPostRequestHandle will set the handle called after every request has finished. The parameter to the handle function includes information about the request and its result.
type StaticOptions ¶ added in v1.0.1
type StaticOptions struct {
// If the router should generate a directory listing for static directories that do not have
// an index file (see also IndexFileName)
GenerateDirectoryListing bool
// The names used when searching a directory for an index file
IndexFileNames []string
// 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.
CacheMaxAge time.Duration
// If etags should be generated automatically for the files. etags are generated whenever a file
// is requested and cached. If the files modified date changes, the cache is refreshed.
GenerateETag bool
// If the last modified date header should be included in the response.
IncludeLastModifiedDate bool
// Prefer to use this date as the value of the Last Modified header rather than refer to the
// file's metadata. When using an embedded FS this is required as otherwise the date will be
// go's zero date.
OverrideLastModifiedDate *time.Time
}
Additional options for static handles. Recommended to take a copy of DefaultStaticOptions.