Documentation
¶
Overview ¶
Package server provides an HTTP/S server that can be easily configured for serving static files in few lines of code, but also scaled to have a server that can handle multiple domains and subdomains, with their logic for every incoming request, backgound tasks and event logging.
Package structure ¶
The package mainly relies on the Server object, which listens on a specific port and forwards every connection to the inner handler.
Basic Implementation:
For a basic `Server` configuration that serves every connection with the content of the `public` folder located in the working directory, here is how to configure it:
package main import ( "os" "os/signal" "github.com/nixpare/server/v2" ) func main() { // Create a new server on port 8080, not secure // (that means using http and not https) and empty // path (so the path is the working directory) srv, err := server.NewServer(8080, false, "") if err != nil { panic(err) } // Register a default route that serves every files inside // the /public folder srv.RegisterDefaultRoute("Default", server.SubdomainConfig{}) // The server starts listening on the port srv.Start() // Listens for a Control-C exitC := make(chan os.Signal) signal.Notify(exitC, os.Interrupt) <- exitC // Stops the server after the Control-C srv.Stop() }
Advanced Implementation:
For a more anvanced implementation we need a `Router` that will manage every server listening on different ports and domains, along with a `TaskManager` that can be used for creating panic-safe goroutines running in the background.
So we first need a router
// new router with path set to the working directory and log output // equal to the stdout router, _ := server.NewRouter("", nil)
that will be used to create the servers (we will only implement one)
srv, _ := router.NewServer(443, true, server.Certificate{ CertPemPath: "path/to/fullchain_key.pem", KeyPemPath: "path/to/private_key.pem", }, // other certificates concatenated ...)
then on each server we can register multiple domains and subdmains
mainDomain := srv.RegisterDomain("My Domain", "mydomain.com") localDomain := srv.RegisterDomain("Localhost Connections", "localhost") // This registers the subdomain sub.mydomain.com // serveFunction will be explained below mainDomain.RegisterSubdomain("sub", server.SubdomainConfig{ ServeF: serveFunction, Website: server.Website{ Name: "My Website", Dir: "Custom Dir holding files", PageHeaders: map[string][][2]string{ "/": { {"header_name_1", "value for index page"}, {"header_name_2", "other value for index page"} }, "/page": {{"header_name", "value for page page"}}, } // The PageHeaders field can be seen as an object that maps // a string to a series of string couples, each rapresenting // the key-value pair for an http header } }) localDomain.RegisterSubdomain(...)
The serving function - Route ¶
To manage every connection with your own logic, the only structures you need are the `Route` and `Website` ones, and in order to let the server know which function to call you have to provide one in the `ServeF` field of the `SubdomainConfig`. The function must have a signature as descrived by the type `server.ServeFunction -> func(route *server.Route)`, that is a simple function taking in input just the Route. This structure holds everything you need for a web request:
- functions to serve content (`ServeFile`, `ServeData`, `Error`)
- functions to manage cookies (`SetCookie`, `DeleteCookie`, `DecodeCookie`)
- other functions
- reference to the underlying `http.ResponseWriter` and `*http.Request` for using any standard http function from the stardard library (or even third party ones that need those type of structures)
The TaskManager ¶
It can used to manage background function running in background, with panic protection to avoid the crash of the entire program and the possibility to listen for the router shutdown in order to interrupt any sensitive procedure. This functions so have a lifecycle composed of:
- a startup function that is ran when the task is started for the first time (unless it's later stopped)
- a cleaup function that is run when the task is stopped
- an exec function that can be ran manually or by the task timer
The task timer determines if and how ofter the task exec function should be called and can be changed at any time after creation
Other utility functions ¶
Inside the `utility.go` file of the package there are some useful functions, but mostly I highlight these two of them:
- `RandStr`, which generates a random string with the given length populated with the chosen sets of characters (see `CharSet` constants)
- `PanicToErr`, which can be used to call any function returning an error and automatically wrapping them in a panic-safe environment that converts the panic into a simple error with a helpful stack trace for the panic (or just returns the simple error of the function)
Here is a usage example:
func myFunction(arg string) error { if arg == "" { panic("Empty arg") } return errors.New(arg) } myArg = "argument" err := PanicToErr(func() error { return myFunction(myArg) }) // err.Error() is not nil both when the function returned an error // or a panic has occurred, but: // - in the first case, only the err.Err field will be set // - in the second case, both the err.PanicErr and err.Stack will be set if err.Error() != nil { // Handle the error }
Index ¶
- Constants
- Variables
- func DecodeCookie[T any](route *Route, name string) (value T, found bool, err error)
- func DecodeCookiePerm[T any](route *Route, name string) (value T, found bool, err error)
- func DisableFileCache()
- func EnableFileCache()
- func GenerateHashString(data []byte) string
- func GenerateTSLConfig(certs []Certificate) (*tls.Config, error)
- func RandStr(length int, randType CharSet) string
- func ReadJSON[T any](route *Route) (value T, err error)
- func SetFileCacheTTL(ttl time.Duration)
- func TCPPipe(conn1, conn2 net.Conn)
- type BeforeServeFunction
- type Certificate
- type CharSet
- type ConnHandlerFunc
- type Domain
- func (d *Domain) DefaultSubdomain() *Subdomain
- func (d *Domain) DisableSubdomain(name string) error
- func (d *Domain) EnableSubdomain(name string) error
- func (d *Domain) Headers() http.Header
- func (d *Domain) RegisterDefaultSubdomain(c SubdomainConfig) (*Subdomain, error)
- func (d *Domain) RegisterSubdomain(subdomain string, c SubdomainConfig) (*Subdomain, error)
- func (d *Domain) RemoveHeader(name string)
- func (d *Domain) SetErrorTemplate(content string) error
- func (d *Domain) SetHeader(name, value string)
- func (d *Domain) SetHeaders(headers [][2]string)
- func (d *Domain) Subdomain(name string) *Subdomain
- type HTTPServer
- func (srv *HTTPServer) DefaultDomain() *Domain
- func (srv *HTTPServer) Domain(domain string) *Domain
- func (srv *HTTPServer) Header() http.Header
- func (srv *HTTPServer) IsRunning() bool
- func (srv *HTTPServer) Port() int
- func (srv *HTTPServer) RegisterDefaultDomain(displayName string) (*Domain, error)
- func (srv *HTTPServer) RegisterDefaultRoute(displayName string, c SubdomainConfig) (*Domain, *Subdomain, error)
- func (srv *HTTPServer) RegisterDomain(displayName, domain string) (*Domain, error)
- func (srv *HTTPServer) RemoveHeader(name string) *HTTPServer
- func (srv *HTTPServer) SetErrorTemplate(content string) error
- func (srv *HTTPServer) SetHeader(name, value string) *HTTPServer
- func (srv *HTTPServer) SetHeaders(headers [][2]string) *HTTPServer
- func (srv *HTTPServer) Start()
- func (srv *HTTPServer) Stop()
- type InitCloseFunction
- type LifeCycle
- type LifeCycleState
- type PHPProcessor
- type ResponseWriter
- type Route
- func (route *Route) CompressedServe(serveF http.HandlerFunc, compressLevel int)
- func (route *Route) DecodeCookie(name string, value any) (found bool, err error)
- func (route *Route) DecodeCookiePerm(name string, value any) (found bool, err error)
- func (route *Route) DeleteCookie(name string)
- func (route *Route) Error(statusCode int, message any, a ...any)
- func (route *Route) Errorf(statusCode int, message string, format string, a ...any)
- func (route *Route) IsInternalConn() bool
- func (route *Route) IsLocalhost() bool
- func (route *Route) NewReverseProxy(dest string) (*httputil.ReverseProxy, error)
- func (route *Route) RespBody() ([]byte, error)
- func (route *Route) RespBodyString() (string, error)
- func (route *Route) ReverseProxy(dest string) error
- func (route *Route) ServeCompressedContent(name string, modtime time.Time, content io.ReadSeeker, compressLevel int)
- func (route *Route) ServeContent(name string, modtime time.Time, content io.ReadSeeker)
- func (route *Route) ServeCustomFile(fileName string, data []byte)
- func (route *Route) ServeCustomFileWithTime(fileName string, data []byte, t time.Time)
- func (route *Route) ServeData(data []byte)
- func (route *Route) ServeFile(filePath string)
- func (route *Route) ServePHP(php *PHPProcessor)
- func (route *Route) ServeText(text string)
- func (route *Route) ServeWS(wsu websocket.Upgrader, h func(route *Route, conn *websocket.Conn))
- func (route *Route) SetCookie(name string, value any, maxAge int) error
- func (route *Route) SetCookiePerm(name string, value any, maxAge int) error
- func (route *Route) SetErrorCapture(enable bool)
- func (route *Route) StaticServe(serveHTML bool)
- type Router
- func (router *Router) HTTPServer(port int) *HTTPServer
- func (router *Router) IsRunning() bool
- func (router *Router) NewHTTPServer(address string, port int, secure bool, path string, certs ...Certificate) (*HTTPServer, error)
- func (router *Router) NewTCPServer(address string, port int, secure bool, certs ...Certificate) (*TCPServer, error)
- func (router *Router) Start()
- func (router *Router) StartTime() time.Time
- func (router *Router) Stop()
- func (router *Router) TCPServer(port int) *TCPServer
- type ServeFunction
- type Subdomain
- func (sd *Subdomain) Disable()
- func (sd *Subdomain) Enable() error
- func (sd *Subdomain) Header() http.Header
- func (sd *Subdomain) RemoveHeader(name string)
- func (sd *Subdomain) SetErrorTemplate(content string) error
- func (sd *Subdomain) SetHeader(name, value string)
- func (sd *Subdomain) SetHeaders(headers [][2]string)
- type SubdomainConfig
- type TCPServer
- type Task
- type TaskFunc
- type TaskInitFunc
- type TaskManager
- func (tm *TaskManager) ExecTask(name string) error
- func (tm *TaskManager) FindProcess(name string) (*process.Process, error)
- func (tm *TaskManager) GetProcess(name string) *process.Process
- func (tm *TaskManager) GetProcessesNames() []string
- func (tm *TaskManager) GetTask(name string) *Task
- func (tm *TaskManager) GetTasksNames() []string
- func (tm *TaskManager) KillProcess(name string) error
- func (tm *TaskManager) KillTask(name string) error
- func (tm *TaskManager) NewProcess(name, dir string, execName string, args ...string) error
- func (tm *TaskManager) NewTask(name string, f TaskInitFunc, timer TaskTimer) error
- func (tm *TaskManager) ProcessIsRunning(name string) (bool, error)
- func (tm *TaskManager) RemoveTask(name string) error
- func (tm *TaskManager) RestartProcess(name string) error
- func (tm *TaskManager) StartProcess(name string) error
- func (tm *TaskManager) StopProcess(name string) error
- func (tm *TaskManager) WaitProcess(name string) (process.ExitStatus, error)
- type TaskTimer
- type VirtualFile
- type Website
Constants ¶
const ( // TASK_TIMER_10_SECONDS determines a Task execution interval of 10 seconds TASK_TIMER_10_SECONDS = TaskTimer(time.Second / 1000000000 * 10) // TASK_TIMER_1_MINUTE determines a Task execution interval of 1 minute TASK_TIMER_1_MINUTE = TaskTimer(time.Minute / 1000000000 * 1) // TASK_TIMER_10_MINUTES determines a Task execution interval of 10 minutes TASK_TIMER_10_MINUTES = TaskTimer(time.Minute / 1000000000 * 10) // TASK_TIMER_30_MINUTES determines a Task execution interval of 30 minutes TASK_TIMER_30_MINUTES = TaskTimer(time.Minute / 1000000000 * 30) // TASK_TIMER_1_HOUR determines a Task execution interval of 1 hour TASK_TIMER_1_HOUR = TaskTimer(time.Hour / 1000000000) // TASK_TIMER_INACTIVE deactivates the Task automatic execution TASK_TIMER_INACTIVE = -1 )
Variables ¶
var ( HashKeyString = "NixPare Server" BlockKeyString = "github.com/nixpare/server" )
var ( ErrNotFound = errors.New("not found") ErrAlreadyRegistered = errors.New("already registered") )
var (
CachedExtensions = []string{"", "txt", "html", "css", "js", "json"}
)
var (
TimeFormat = "2006-01-02 15:04:05.00" // TimeFormat defines which timestamp to use with the logs. It can be modified.
)
var WebsocketUpgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
Functions ¶
func DecodeCookie ¶ added in v2.2.0
DecodeCookie decodes a previously set cookie with the given name using the method route.SetCookie and returns the saved value. This is a duplicate function of the method route.DecodeCookie as a type parameter function.
More informations provided in that method
func DecodeCookiePerm ¶ added in v2.2.0
DecodeCookiePerm decodes a previously set cookie with the given name using the method route.SetCookiePerm and returns the saved value. This is a duplicate function of the method route.DecodeCookiePerm as a type parameter function
More informations provided in that method
func DisableFileCache ¶ added in v2.8.3
func DisableFileCache()
func EnableFileCache ¶ added in v2.8.3
func EnableFileCache()
func GenerateHashString ¶
GenerateHashString generate a hash with sha256 from data
func GenerateTSLConfig ¶ added in v2.5.0
func GenerateTSLConfig(certs []Certificate) (*tls.Config, error)
func RandStr ¶
RandStr generates a random string with the given length. The string can be made of differente sets of characters: see CharSet type
func SetFileCacheTTL ¶ added in v2.11.0
Types ¶
type BeforeServeFunction ¶ added in v2.1.0
BeforeServeFunction defines the type of the function executed whenever a connection is received in the domain, even before any error handling (like domain and subdomain check), so every field inside Route is ready. The function returns a boolean, which will tell if the connection was handled by it and so the serve function must not be called. One use case for this function is in this setup: imagine having two server, one HTTP on port 80 and one HTTPS on port 443, and you want to redirect every connection that is not internal from HTTP to HTTPS (see Router.SetInternalConnFilter function)
insecureServer := route.Server(80) insecureDomain := insecureServer.Domain("mydomain.com") insecureDomain.SetBeforeServeF(func(route *server.Route) bool { if route.IsInternalConn() { return false // this is an internal connection, so it must continue inside the HTTP server } dest := "https://" + route.R.Host + route.R.RequestURI route.AvoidLogging = true http.Redirect(route.W, route.R, dest, http.StatusPermanentRedirect) return true // the external connection already received a redirect, so nothing else should happen })
type Certificate ¶
type Certificate struct { CertPemPath string // CertPemPath is the path to the full chain public key KeyPemPath string // KeyPemPath is the path to the private key }
Certificate rapresents a standard PEM certicate composed of a full chain public key and a private key. This is used when creating an HTTPS server
type CharSet ¶ added in v2.2.0
type CharSet int
CharSet groups the possible output of the function RandStr. For the possible values see the constants
const ( NUM CharSet = iota // Digits from 0 to 9 ALPHA // Latin letters from A to z (Uppercase and Lowercase) ALPHA_LOW // Latin letters from a to z (Lowercase) ALPHA_NUM // Combination of NUM and ALPHA ALPHA_LOW_NUM // Combination of NUM and ALPHA_LOW ALPHA_NUM_SPECIAL // Combines ALPHA_LOW with this special character: !?+*-_=.&%$€#@ )
type ConnHandlerFunc ¶ added in v2.5.0
type Domain ¶
type Domain struct { Name string // BeforeServeF sets a function that will be executed before every connection. // If this function returns true, the serve function of the subdomain will not be // executed BeforeServeF BeforeServeFunction // contains filtered or unexported fields }
Domain rapresents a website domain with all its subdomains. It's possible to set:
- a function that will be executed (in case there are no errors) before every other logic
- global headers, that will be applied in every connection
- an error template, that will be used in case your logic will throw any error, so you will have a constant look
func (*Domain) DefaultSubdomain ¶
DefaultSubdomain returns the default subdomain, if set
func (*Domain) DisableSubdomain ¶
DisableSubdomain sets a subdomain to offline state
func (*Domain) EnableSubdomain ¶
EnableSubdomain sets a subdomain to online state
func (*Domain) RegisterDefaultSubdomain ¶
func (d *Domain) RegisterDefaultSubdomain(c SubdomainConfig) (*Subdomain, error)
RegisterDefaultSubdomain registers a subdomain that is called if no other one matches perfectly the incoming connection for the same domain
func (*Domain) RegisterSubdomain ¶
func (d *Domain) RegisterSubdomain(subdomain string, c SubdomainConfig) (*Subdomain, error)
RegisterSubdomain registers a subdomain in the domain. It's asked to specify the subdomain name (with or without trailing dot) and its configuration. It the Website Dir field is empty it will be used the default value of "<srv.Path>/public", instead if it's not absolute it will be relative to the srv.Path
func (*Domain) RemoveHeader ¶
RemoveHeader removes a header with the given name
func (*Domain) SetErrorTemplate ¶
SetErrorTemplate sets the error template used server-wise. It's required an HTML that contains two specific fields, a .Code one and a .Message one, for example like so:
<h2>Error {{ .Code }}</h2> <p>{{ .Message }}</p>
func (*Domain) SetHeader ¶
SetHeader adds a header to the collection of headers used in every connection
func (*Domain) SetHeaders ¶
SetHeaders adds headers to the collection of headers used in every connection. This is a faster way to set multiple headers at the same time, instead of using domain.SetHeader. The headers must be provided in this way:
headers := [][2]string { { "name1", "value1" }, { "name2", "value2" }, } d.SetHeaders(headers)
type HTTPServer ¶ added in v2.5.0
type HTTPServer struct { // Secure is set to indicate whether the server is using // the HTTP or HTTPS protocol Secure bool // Online tells wheter the server is responding to external requests Online bool // OnlineTime reports the last time the server was activated or resumed OnlineTime time.Time // Server is the underlying HTTP server from the standard library Server *http.Server // HTTP3Server is the QUIC Server, if is nil if the Server is not secure HTTP3Server *http3.Server // Router is a reference to the Router (is the server was created through it). // This should not be set by hand. Router *Router Logger logger.Logger // IsInternalConn can be used to additionally add rules used to determine whether // an incoming connection must be treated as from a client in the local network or not. // This is used both for the method route.IsInternalConn and for accessing other domains // via the http queries from desired IPs. By default, only the connection coming from // "localhost", "127.0.0.1" and "::1" are treated as local connections. IsInternalConn func(remoteAddress string) bool IsLocalhost func(host string) bool // ServerPath is the path provided on server creation. It is used as the log location // for this specific server ServerPath string // contains filtered or unexported fields }
HTTPServer is a single HTTP server listening on a TCP port. It can handle multiple domains and subdomains. To manage multiple servers listening on different ports use a Router.
Before creating any server you should change the HashKeyString and BlockKeyString global variables: see Route.SetCookiePerm method
func NewHTTPServer ¶ added in v2.5.0
func NewHTTPServer(address string, port int, secure bool, path string, certs ...Certificate) (*HTTPServer, error)
NewServer creates a new server
func (*HTTPServer) DefaultDomain ¶ added in v2.5.0
func (srv *HTTPServer) DefaultDomain() *Domain
DefaultDomain returns the default domain, if set
func (*HTTPServer) Domain ¶ added in v2.5.0
func (srv *HTTPServer) Domain(domain string) *Domain
Domain returns the domain with the given name registered in the server, if found
func (*HTTPServer) Header ¶ added in v2.5.0
func (srv *HTTPServer) Header() http.Header
Header returns the underlying http.Header intance
func (*HTTPServer) IsRunning ¶ added in v2.5.0
func (srv *HTTPServer) IsRunning() bool
IsRunning tells whether the server is running or not
func (*HTTPServer) Port ¶ added in v2.5.0
func (srv *HTTPServer) Port() int
Port returns the TCP port listened by the server
func (*HTTPServer) RegisterDefaultDomain ¶ added in v2.5.0
func (srv *HTTPServer) RegisterDefaultDomain(displayName string) (*Domain, error)
RegisterDefaultDomain registers a domain that is called if no other domain matches perfectly the incoming connection
func (*HTTPServer) RegisterDefaultRoute ¶ added in v2.5.0
func (srv *HTTPServer) RegisterDefaultRoute(displayName string, c SubdomainConfig) (*Domain, *Subdomain, error)
RegisterDefaultRoute is a shortcut for registering the default logic applied for every connection not matching any other specific domain and subdomain. It's the combination of srv.RegisterDefaultDomain(displayName).RegisterDefaultSubdomain(c)
func (*HTTPServer) RegisterDomain ¶ added in v2.5.0
func (srv *HTTPServer) RegisterDomain(displayName, domain string) (*Domain, error)
RegisterDomain registers a domain in the server. It's asked to specify a display name used in the logs and the effective URL of the domain (do not specify any protocol or port). If the domain name is an empy string it will be treated as the default domain (see srv.RegisterDefaultDomain)
func (*HTTPServer) RemoveHeader ¶ added in v2.5.0
func (srv *HTTPServer) RemoveHeader(name string) *HTTPServer
RemoveHeader removes an HTTP header with the given name from the Server specific headers
func (*HTTPServer) SetErrorTemplate ¶ added in v2.5.0
func (srv *HTTPServer) SetErrorTemplate(content string) error
SetErrorTemplate sets the error template used server-wise. It's required an HTML that contains two specific fields, a .Code one and a .Message one, for example like so:
<h2>Error {{ .Code }}</h2> <p>{{ .Message }}</p>
func (*HTTPServer) SetHeader ¶ added in v2.5.0
func (srv *HTTPServer) SetHeader(name, value string) *HTTPServer
SetHeader adds an HTTP header that will be set at every connection accepted by the Server
func (*HTTPServer) SetHeaders ¶ added in v2.5.0
func (srv *HTTPServer) SetHeaders(headers [][2]string) *HTTPServer
SetHeaders accepts a matrix made of couples of string, each of one rapresents an HTTP header with its key and value. This is a shorthand for not calling multiple times Server.SetHeader. It can be used like this:
srv.SetHeaders([][2]string{ {"header_name_1", "header_value_1"}, {"header_name_2", "header_value_2"}, })
func (*HTTPServer) Start ¶ added in v2.5.0
func (srv *HTTPServer) Start()
Start prepares every domain and subdomain and starts listening on the TCP port
func (*HTTPServer) Stop ¶ added in v2.5.0
func (srv *HTTPServer) Stop()
Stop cleans up every domain and subdomain and stops listening on the TCP port
type InitCloseFunction ¶
type InitCloseFunction func(srv *HTTPServer, domain *Domain, subdomain *Subdomain) error
InitCloseFunction defines the type of the function executed when a new subdomain is created or removed, usually when the relative server is started or stopped. Bear in mind that if you use the same function on multiple subdomain, maybe belonging to different servers, you could have to manually check that this function is done only once
type LifeCycle ¶ added in v2.5.0
type LifeCycle struct {
// contains filtered or unexported fields
}
func NewLifeCycleState ¶ added in v2.5.0
func NewLifeCycleState() *LifeCycle
func (*LifeCycle) AlreadyStarted ¶ added in v2.5.0
func (*LifeCycle) AlreadyStopped ¶ added in v2.5.0
func (*LifeCycle) GetState ¶ added in v2.5.0
func (state *LifeCycle) GetState() LifeCycleState
func (*LifeCycle) SetState ¶ added in v2.5.0
func (state *LifeCycle) SetState(s LifeCycleState)
type LifeCycleState ¶ added in v2.5.0
type LifeCycleState int
const ( LCS_STOPPED LifeCycleState = iota LCS_STOPPING LCS_STARTING LCS_STARTED )
type PHPProcessor ¶ added in v2.4.0
type PHPProcessor struct { Process *process.Process Logger logger.Logger // contains filtered or unexported fields }
func NewPHPProcessor ¶ added in v2.5.2
func NewPHPProcessor(port int, args ...string) (php *PHPProcessor, err error)
func (*PHPProcessor) Start ¶ added in v2.4.0
func (php *PHPProcessor) Start() error
func (*PHPProcessor) Stop ¶ added in v2.4.0
func (php *PHPProcessor) Stop() error
type ResponseWriter ¶
type ResponseWriter struct {
// contains filtered or unexported fields
}
ResponseWriter is just a wrapper for the standard http.ResponseWriter interface, the only difference is that it keeps track of the bytes written and the status code, so that can be logged
func (*ResponseWriter) Header ¶
func (w *ResponseWriter) Header() http.Header
Header is the equivalent of the http.ResponseWriter method
func (*ResponseWriter) Write ¶
func (w *ResponseWriter) Write(data []byte) (int, error)
Write is the equivalent of the http.ResponseWriter method
func (*ResponseWriter) WriteHeader ¶
func (w *ResponseWriter) WriteHeader(statusCode int)
WriteHeader is the equivalent of the http.ResponseWriter method but handles multiple calls, using only the first one used
type Route ¶
type Route struct { // W wraps the [http.ResponseWriter] returned by the standard // [http.Server], handles multiple W.WriteHeader calls and captures // metrics for the connection (time, bytes written and status code). // Can be used in conjunction with R to use the standard functions // provided by Go, such as [http.ServeContent] W *ResponseWriter // R is the standard [http.Request] without any modification R *http.Request // Srv is a reference to the server this connection went through Srv *HTTPServer // Router is a reference to the router this server connection belongs to Router *Router Logger logger.Logger // Secure is set to tell wheather the current connection is using HTTP (false) // or HTTPS(true), so you can use one Routing function for secure and unsecure // websites Secure bool // Secure is set to tell wheather the current connection is using HTTP/3 IsHTTP3 bool // Host contains the address used by the client, so it could be an IP address // or a domain name. This is generated from the R field Host string // Remote address contains the IP address of the client connecting (without ports). // This is generated from the R field RemoteAddress string // Website is a reference to the Website structure provided at startup when registering // the server domains and subdomains Website *Website // DomainName contains the request domain name parsed from the Host DomainName string // SubdomainName contains the request subdomain name parsed from the Host SubdomainName string // Domain is the domain the connection went through Domain *Domain // Subdomain is the subdomain the connection went through inside the domain Subdomain *Subdomain // RequestURI is the http.Request uri sanitized, parsed and separated from the queries // in order to have a clean path RequestURI string // Method tells which HTTP method the connection is using Method string // QueryMap contains all the queries retreived from the request uri QueryMap map[string]string // ConnectionTime is the timestamp that refers to the request arrival ConnectionTime time.Time // AvoidLogging is a flag that can be set by the user to tell that this connection should not be logged; // however this only happens when the connection returns a successful status code AvoidLogging bool // contains filtered or unexported fields }
Route wraps the incoming HTTP connection and provides simple and commonly use HTTP functions, some coming also from the standard library. It contains the standard http.ResponseWriter and *http.Request elements used to handle a connection with the standard library, but adds more to integrate with the router -> server -> website structure of this package
func (*Route) CompressedServe ¶ added in v2.9.2
func (route *Route) CompressedServe(serveF http.HandlerFunc, compressLevel int)
func (*Route) DecodeCookie ¶
DecodeCookie decodes a previously set cookie with the given name using the method route.SetCookie.
If the cookie was not found, it will return false and the relative error (probably an http.ErrNoCookie), otherwise it will return true and, possibly, the decode error. It happends when:
- the server was restarted, so the keys used for decoding are different
- you provided the wrong value type
- the cookie was not set by the server
The argument value must be a pointer, otherwise the value will not be returned. A workaround might be using the type parametric function server.DecodeCookie
func (*Route) DecodeCookiePerm ¶
DecodeCookiePerm decodes a previously set cookie with the given name using the method route.SetCookiePerm.
If the cookie was not found, it will return false and the relative error (probably an http.ErrNoCookie), otherwise it will return true and, possibly, the decode error. It happends when:
- you provided the wrong value type
- the cookie was not set by the server
The argument value must be a pointer, otherwise the value will not be returned. A workaround might be using the type parametric function server.DecodeCookiePerm
func (*Route) DeleteCookie ¶
DeleteCookie instantly removes a cookie with the given name before set with route.SetCookie or route.SetCookiePerm
func (*Route) Error ¶
Error is used to manually report an HTTP error to send to the client.
It sets the http status code (so it should not be set before) and if the connection is done via a GET request, it will try to serve the html error template with the status code and error message in it, otherwise if the error template does not exist or the request is done via another method (like POST), the error message will be sent as a plain text.
The last optional list of elements can be used just for logging or debugging: the elements will be saved in the logs
func (*Route) Errorf ¶ added in v2.2.2
Errorf is like the method Route.Error but you can format the output to the Log. Like the Route.Logf, everything that is after the first line feed will be used to populate the extra field of the Log
func (*Route) IsInternalConn ¶
IsInternalConn tells wheather the incoming connection should be treated as a local connection. The user can add a filter that can extend this selection to match their needs
func (*Route) IsLocalhost ¶ added in v2.5.3
func (*Route) NewReverseProxy ¶ added in v2.5.3
func (route *Route) NewReverseProxy(dest string) (*httputil.ReverseProxy, error)
func (*Route) RespBodyString ¶ added in v2.2.0
RespBodyString returns the response body as a string
func (*Route) ReverseProxy ¶
ReverseProxy runs a reverse proxy to the provided url. Returns an error is the url could not be parsed or if an error has occurred during the connection
func (*Route) ServeCompressedContent ¶ added in v2.9.2
func (*Route) ServeContent ¶ added in v2.11.0
func (*Route) ServeCustomFile ¶
ServeCustomFile serves a pseudo-file saved in memory. The name of the file is important for MIME type detection
func (*Route) ServeCustomFileWithTime ¶
ServeCustomFileWithTime will serve a pseudo-file saved in memory specifing the last modification time. The name of the file is important for MIME type detection
func (*Route) ServeFile ¶
ServeFile will serve a file in the file system. If the path is not absolute, it will first try to complete it with the website directory (if set) or with the server path
func (*Route) ServePHP ¶ added in v2.4.0
func (route *Route) ServePHP(php *PHPProcessor)
func (*Route) SetCookie ¶
SetCookie creates a new cookie with the given name and value, maxAge can be used to sex the expiration date:
- maxAge = 0 means no expiration specified
- maxAge > 0 sets the expiration date from the current date adding the given time in seconds (- maxAge < 0 will remove the cookie instantly, like route.DeleteCookie)
The cookie value is encoded and encrypted using a pair of keys created randomly at server creation, so if the same cookie is later decoded between server restart, it can't be decoded. To have such a behaviour see SetCookiePerm.
The encoding of the value is managed by the package encoding/gob. If you are just encoding and decoding plain structs and each field type is a primary type or a struct (with the same rules), nothing should be done, but if you are dealing with interfaces, you must first register every concrete structure or type implementing that interface before encoding or decoding
func (*Route) SetCookiePerm ¶
SetCookiePerm creates a new cookie with the given name and value, maxAge can be used to sex the expiration date:
- maxAge = 0 means no expiration specified
- maxAge > 0 sets the expiration date from the current date adding the given time in seconds (- maxAge < 0 will remove the cookie instantly, like route.DeleteCookie)
The cookie value is encoded and encrypted using a pair of keys at package level that MUST be set at program startup. This differs for the method route.SetCookie to ensure that even after server restart these cookies can still be decoded.
func (*Route) SetErrorCapture ¶ added in v2.9.12
func (*Route) StaticServe ¶
StaticServe tries to serve a file for every connection done via a GET request, following all the options provided in the Website configuration. This means it will not serve any file inside (also nested) a hidden folder, it will serve an HTML file only with the flag argument set to true, it will serve index.html automatically for connection with request uri empty or equal to "/", it will serve every file inside the AllFolders field of the Website
type Router ¶
type Router struct { // The Path provided when creating the Router or the working directory // if not provided. This defines the path for every server registered Path string TaskManager *TaskManager Logger logger.Logger // contains filtered or unexported fields }
Router is the main element of this package and is used to manage all the servers and the background tasks.
func NewRouter ¶
NewRouter returns a new Router ready to be set up. If routerPath is not provided, the router will try to get the working directory; if logger is nil, the standard logger.DefaultLogger will be used
func (*Router) HTTPServer ¶ added in v2.5.0
func (router *Router) HTTPServer(port int) *HTTPServer
Server returns the HTTP server running on the given port
func (*Router) NewHTTPServer ¶ added in v2.5.0
func (router *Router) NewHTTPServer(address string, port int, secure bool, path string, certs ...Certificate) (*HTTPServer, error)
NewServer creates a new HTTP/HTTPS Server linked to the Router. See NewServer function for more information
func (*Router) NewTCPServer ¶ added in v2.5.0
func (router *Router) NewTCPServer(address string, port int, secure bool, certs ...Certificate) (*TCPServer, error)
NewServer creates a new TCP Server linked to the Router. See NewTCPServer function for more information
func (*Router) Start ¶
func (router *Router) Start()
Start starts all the registered servers and the background task manager
type ServeFunction ¶
type ServeFunction func(route *Route)
ServeFunction defines the type of the function that is executed every time a connection is directed to the relative website / subdomain. It just takes the Route as a parameter, but it can be used for everything, like the method, a parsed request uri, a map with all the queries (removed from the request uri so the latter is a clean relative path) and even the http.ResponseWriter and *http.Request in order to use the standard HTTP package functions. However, Route provide a lot of prebuild functions for comodity, all base on the HTTP standard package
type Subdomain ¶
Subdomain rapresents a particular subdomain in a domain with all the logic. It's required a serve function, which will determine the logic of the website, and a Website, with all its options. It's possible to set:
- default headers, that will be applied in every connection
- an error template, that will be used in case your logic will throw any error, so you will have a constant look
- the subdomain offline state
- an initializer function, called when the server is starting up
- a cleanup function, called when the server is shutting down
func (*Subdomain) Disable ¶
func (sd *Subdomain) Disable()
Disable sets the subdomain to offline state
func (*Subdomain) RemoveHeader ¶
RemoveHeader removes a header with the given name
func (*Subdomain) SetErrorTemplate ¶
SetErrorTemplate sets the error template used server-wise. It's required an HTML that contains two specific fields, a .Code one and a .Message one, for example like so:
<h2>Error {{ .Code }}</h2> <p>{{ .Message }}</p>
func (*Subdomain) SetHeader ¶
SetHeader adds a header to the collection of headers used in every connection
func (*Subdomain) SetHeaders ¶
SetHeaders adds headers to the collection of headers used in every connection. This is a faster way to set multiple headers at the same time, instead of using subdomain.SetHeader. The headers must be provided in this way:
headers := [][2]string { { "name1", "value1" }, { "name2", "value2" }, } d.SetHeaders(headers)
type SubdomainConfig ¶
type SubdomainConfig struct { // Website will be copied to the subdomain and later will be // linked in every connection Website Website // ServeF is the function holding the logic behind the website ServeF ServeFunction // InitF is the function called upon server startup InitF InitCloseFunction // CloseF is the function called upon server shutdown CloseF InitCloseFunction }
SubdomainConfig is used to create a Subdomain. The Website should not be an empty struct and if ServeF is not set the server will serve every content inside the Website.Dir folder (see Route.StaticServe(true) for the logic and Domain.RegisterSubdomain for the folder behaviour), however InitF and CloseF are optional
type TCPServer ¶ added in v2.5.0
type TCPServer struct { Online bool ConnHandler ConnHandlerFunc Router *Router Logger logger.Logger // contains filtered or unexported fields }
func NewTCPServer ¶ added in v2.5.0
type Task ¶
type Task struct { InitF TaskFunc // InitF is the function called when the Task is started ExecF TaskFunc // ExecF is the function called every time the Task must be executed (from the timer or manually) CleanupF TaskFunc // CleanupF is the function called when the Task is removed from the TaskManager or when the TaskManager is stopped (e.g. on Router shutdown) Timer TaskTimer // TaskTimer is the Task execution interval, that is how often the function ExecF is called TaskManager *TaskManager Logger logger.Logger // contains filtered or unexported fields }
Task is composed of a name set upon creation and of 3 functions necessary of the correct execution of a kind of program. Every function is panic-protected, this means that the entire server will not crash when some parts of the task fails badly; this does not mean that you can't handle panics by yourself, but if they are not handled its like catching them and returning their message as an error. If a function returns an error, this will be logged, providing the task name and which function called it automatically (the task will be disabled if the initialization fails, but you can do it manually, see router.SetBackgroundTaskState)
func (*Task) Init ¶ added in v2.6.0
Init runs the initialization function, catching every possible error or panic, and then sets the flag Task.initDone to true. If the function fails it deactivates the task
func (*Task) ListenForExit ¶
ListenForExit waits until the exit signal is received from the manager. This signal is sent when you manually stop a task or the server is shutting down: in the last case the manager will wait for a maximum of 10 seconds, after those, if the execution is not finished, it will first kill the task and then call the cleanup function. This function is intended to be called in a goroutine listening for the signal: considering that the goroutine could stay alive even after the task exec function has exited, if this function returns true, this means that the signal is received correctly for that execution and you should exit, otherwise this means that the execution of the task has already terminated and thus you should not do anything Example:
execF = func(tm *server.TaskManager, t *server.Task) error { go func () { if !t.ListenForExit() { return // doing nothing because it returned false } // DO SOME FAST RECOVERY }() // SOME LONG RUNNING EXECUTION }
type TaskFunc ¶
TaskFunc is the executable part of the program. You can modify the timer of the task and also the functions themselves! See TaskInitFunc, NewTask and router.RegisterBackgroundTask for the creation of a Task
type TaskInitFunc ¶
type TaskInitFunc func() (initF, execF, cleanupF TaskFunc)
TaskInitFunc is called when creating a task and is provided by the user. This function need to return 3 TaskFunc (they can be nil) and they will be set to the created task. The kind of functions are:
- the initialization function: called only upon creation, if it fails (panics or returns an error) the task will be disabled automatically
- the exec function: called every time, could be interrupted if the server is shutting down; in this case, you will receive a signal on Task.ListenForExit, after that you will have 10 seconds before the server will call the cleanup function and exit
- the cleanup function: called when the server is shutting down, this must not be potentially blocking (must end in a reasonable time)
Example of usage:
func() { taskInitF := func() (initF, execF, cleanupF TaskFunc) { var myNeededValiable package.AnyType initF = func(tm *server.TaskManager, t *server.Task) { myNeededVariable = package.InitializeNewValiable() // DO SOME OTHER STUFF WITH router AND t } execF = func(tm *server.TaskManager, t *server.Task) { myNeededVariable.UseValiable() // DO SOME OTHER STUFF WITH router AND t } cleaunpF = func(tm *server.TaskManager, t *server.Task) { // DO SOME OTHER STUFF WITH router AND t myNeededVariable.DestroyValiable() } return } task := tm.NewTask("myTask", taskInitF, server.TaskTimerInactive) }
type TaskManager ¶
type TaskManager struct { Router *Router Logger logger.Logger // contains filtered or unexported fields }
TaskManager is a component of the Router that controls the execution of external processes and tasks registered by the user
func (*TaskManager) ExecTask ¶
func (tm *TaskManager) ExecTask(name string) error
ExecTask runs the Task immediatly
func (*TaskManager) FindProcess ¶ added in v2.3.0
func (tm *TaskManager) FindProcess(name string) (*process.Process, error)
FindProcess finds if a process with the given name is registered in the process map
func (*TaskManager) GetProcess ¶ added in v2.6.0
func (tm *TaskManager) GetProcess(name string) *process.Process
GetProcess returns the process registered with the given name, or nil if not found
func (*TaskManager) GetProcessesNames ¶ added in v2.3.0
func (tm *TaskManager) GetProcessesNames() []string
GetProcessesNames returns a slice containing all the names of the registered processes
func (*TaskManager) GetTask ¶ added in v2.6.0
func (tm *TaskManager) GetTask(name string) *Task
func (*TaskManager) GetTasksNames ¶
func (tm *TaskManager) GetTasksNames() []string
GetTasksNames returns all the names of the registered tasks in the TaskManager
func (*TaskManager) KillProcess ¶ added in v2.3.0
func (tm *TaskManager) KillProcess(name string) error
KillProcess forcibly kills the process with the given name
func (*TaskManager) KillTask ¶ added in v2.6.0
func (tm *TaskManager) KillTask(name string) error
RemoveTask runs the cleanup function provided and removes the Task from the TaskManager
func (*TaskManager) NewProcess ¶ added in v2.3.0
func (tm *TaskManager) NewProcess(name, dir string, execName string, args ...string) error
NewProcess creates a new Process with the given parameters. The process name must be a unique. It's possible to wait for its termination on multiple goroutines by calling the Wait method, and craceful shutdown is implemented in every operating system
The underlying process is described in the package github.com/nixpare/process
func (*TaskManager) NewTask ¶
func (tm *TaskManager) NewTask(name string, f TaskInitFunc, timer TaskTimer) error
NewTask creates and registers a new Task with the given name, displayName, initialization function (f TaskInitFunc) and execution timer, the TaskManager initialize it calling the initF function provided by f (if any). If it returns an error the Task will not be registered in the TaskManager.
func (*TaskManager) ProcessIsRunning ¶ added in v2.3.0
func (tm *TaskManager) ProcessIsRunning(name string) (bool, error)
ProcessIsRunning tells if the process is running or not
func (*TaskManager) RemoveTask ¶
func (tm *TaskManager) RemoveTask(name string) error
RemoveTask runs the cleanup function provided and removes the Task from the TaskManager
func (*TaskManager) RestartProcess ¶ added in v2.3.0
func (tm *TaskManager) RestartProcess(name string) error
RestartProcess first gracefully stops the process and then starts it again
func (*TaskManager) StartProcess ¶ added in v2.3.0
func (tm *TaskManager) StartProcess(name string) error
StartProcess starts an already registered process if it's not running. This method just waits for the successful start-up of the process, but It does not wait for the termination. For this, call the Wait method.
Also, this function starts the Process enabling the pipe for the standard input and the capture of the standard output, disables any real input/output and automatically logs an error if the exit status is not successfull. You can always manually call the Start method on the Process
func (*TaskManager) StopProcess ¶ added in v2.3.0
func (tm *TaskManager) StopProcess(name string) error
StopProcess tries to gracefully stop the process with the given name
func (*TaskManager) WaitProcess ¶ added in v2.3.0
func (tm *TaskManager) WaitProcess(name string) (process.ExitStatus, error)
WaitProcess waits for the termination of the process and returns process information
type TaskTimer ¶
type TaskTimer int
TaskTimer tells the TaskManager how often a Task should be executed. See the constants for the values accepted by the TaskManager
type VirtualFile ¶ added in v2.11.0
type VirtualFile struct {
// contains filtered or unexported fields
}
func NewVirtualFile ¶ added in v2.11.0
func NewVirtualFile(size int) *VirtualFile
func (*VirtualFile) Len ¶ added in v2.11.0
func (vf *VirtualFile) Len() int
func (*VirtualFile) NewReader ¶ added in v2.11.0
func (vf *VirtualFile) NewReader() io.ReadSeeker
func (*VirtualFile) Size ¶ added in v2.11.0
func (vf *VirtualFile) Size() int
type Website ¶
type Website struct { // Name is used in the log information Name string // Dir is the root folder of the Website. If it's not set // while creating the Website, this is set to the server path + /public // folder of the domain in which is registered, otherwise if it's a // relative path, it's considered relative to the server path. // This is also used by the function Route.ServeStatic to // automatically serve any content (see AllFolders attribute) Dir string // MainPages sets which are the pages that we want to keep track // of the statistics. For now the logic is not there yet, so this // field is not used MainPages []string // NoLogPages sets which are the requestURIs that we do not want to // register logs about NoLogPages []string // AllFolders specify which folders can be used by the route.ServeStatic // to automatically serve content. The selection is recursive. If you want // to serve any content in the Website.Dir is possible to fill this field // with just an empty string AllFolders []string // HiddenFolders it's the opposite of AllFolders, but has a higher priority: // it specifies which folders must not be used by the route.ServeStatic // to automatically serve content, even if a AllFolder entry could match HiddenFolders []string // PageHeaders is used to set automatic HTTP headers to the corresponding // requestURI. This can be set like so: /* var w Website w.PageHeader = map[string][][2]string { "/": { {"Header name", "Header value"}, {"Header name 1", "Header value 1"}, }, "/address": {{"Header name 2", "Header value 2"}}, } */ PageHeaders map[string][][2]string // XFiles maps requested file paths to existing files that can be used to create and serve XFile // virtual files. If the value in the map is an empty string, this means that the file used to // create the XFile corresponds, otherwise you can map it to a completely different file. The value // can be a relative path (to the Website.Dir) or an absolute path, but the key part must be the // desired request uri to match for the resource // // For example: if the XFiles attribute is set to // XFiles: map[string]string{ "/assets/css/index.css": "assets/css/X_INDEX.css" } // and a request comes with a URI of https://<my_domain>/assets/css/index.css, the server will // use the file Website.Dir + / + assets/css/X_INDEX.css to create the XFile and then serve it XFiles map[string]string // AvoidMetricsAndLogging disables any type of log for every connection and error regarding // this website (if not explicitly done by the logic calling Route.Log) AvoidMetricsAndLogging bool }
Website is used in a subdomain to serve content with its logic, in combination with a ServeFunction and (optionally) a pair of InitCloseFunction