web

package module
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: 16 Imported by: 1

README

Web

Go Report Card Godoc Releases LICENSE

The web project provides two packages, web and router.

Web

Package web is a HTTP server for Golang applications.

It is suitable for both front-end and back-end use, being able to deliver static content, act as a REST-ful JSON server, and as a WebSocket server.

It includes simple controls to allow for user authentication with contextual data being available in every request, and provides simple per-user rate-limiting.

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.

Documentation & Examples

For full documentation including examples please see the official package documentation

Documentation

Overview

Package web is a HTTP server for Golang applications.

It is suitable for both front-end and back-end use, being able to deliver static content, act as a REST-ful JSON server, and as a WebSocket server.

It includes simple controls to allow for user authentication with contextual data being available in every request, and provides simple per-user rate-limiting.

Example (Authentication)
package main

import (
	"net/http"
	"time"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	type User struct {
		Username string `json:"username"`
	}

	// Login
	loginHandle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		// Do any authentication logic here

		// Assuming the user authenticated successfully...
		return true, &web.APIResponse{
			Cookies: []http.Cookie{
				{
					Name:    "session",
					Value:   "1",
					Path:    "/",
					Expires: time.Now().AddDate(0, 0, 1),
				},
			},
		}, nil
	}
	unauthenticatedOptions := web.HandleOptions{}
	server.API.GET("/login", loginHandle, unauthenticatedOptions)

	// Get User Info
	getUserHandle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		user := request.UserData.(User)
		return user, nil, nil
	}

	authenticatedOptions := web.HandleOptions{
		// The authenticate method is where you validate that a request if from an authenticated, or simple "logged in"
		// user. In this example, we validate that a cookie is present.
		// Any data returned by this method is provided into the request handler as Request.UserData
		// Returning nil results in a HTTP 403 response
		AuthenticateMethod: func(request *http.Request) interface{} {
			cookie, err := request.Cookie("session")
			if err != nil || cookie == nil {
				return nil
			}
			if cookie.Value != "1" {
				return nil
			}
			return map[string]string{
				"foo": "bar",
			}
		},
	}
	// Notice that we used a different HandleOptions instance with our AuthenticateMethod
	// an options without any AuthenticateMethod is considered unauthenticated
	server.API.GET("/user", getUserHandle, authenticatedOptions)

	if err := server.Start(); err != nil {
		panic(err)
	}
}
Output:

Example (File)
package main

import (
	"os"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		f, err := os.Open("/foo/bar")
		if err != nil {
			return web.HTTPResponse{
				Status: 500,
			}
		}
		return web.HTTPResponse{
			Reader: f,
		}
	}

	options := web.HandleOptions{}
	server.HTTPEasy.GET("/file", handle, options)

	if err := server.Start(); err != nil {
		panic(err)
	}
}
Output:

Example (Json)
package main

import (
	"time"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		return time.Now().Unix(), nil, nil
	}

	options := web.HandleOptions{}
	server.API.GET("/time", handle, options)

	if err := server.Start(); err != nil {
		panic(err)
	}
}
Output:

Example (Ratelimit)
package main

import (
	"net/http"
	"time"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	// Restrict each connecting IP address to a maximum of 5 requests per second
	server.Options.MaxRequestsPerSecond = 5

	// Handle called when a request is rejected due to rate limiting
	server.RateLimitedHandler = func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(429)
		w.Write([]byte("Too many requests"))
	}

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		return time.Now().Unix(), nil, nil
	}

	options := web.HandleOptions{}
	server.API.GET("/time", handle, options)

	if err := server.Start(); err != nil {
		panic(err)
	}
}
Output:

Example (Unixsocket)
package main

import (
	"net"
	"time"

	"github.com/ecnepsnai/web"
)

func main() {
	l, err := net.Listen("unix", "/example.socket")
	if err != nil {
		panic(err)
	}
	server := web.NewListener(l)

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		return time.Now().Unix(), nil, nil
	}

	options := web.HandleOptions{}
	server.API.GET("/time", handle, options)

	if err := server.Start(); err != nil {
		panic(err)
	}
}
Output:

Example (Websocket)
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	type questionType struct {
		Name string
	}

	type answerType struct {
		Reply string
	}

	handle := func(request web.Request, conn *web.WSConn) {
		question := questionType{}
		if err := conn.ReadJSON(&question); err != nil {
			return
		}

		reply := answerType{
			Reply: "Hello, " + question.Name,
		}
		if err := conn.WriteJSON(&reply); err != nil {
			return
		}
	}

	options := web.HandleOptions{}
	server.Socket("/greeting", handle, options)

	if err := server.Start(); err != nil {
		panic(err)
	}
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var CommonErrors = struct {
	NotFound        *Error
	BadRequest      *Error
	Unauthorized    *Error
	Forbidden       *Error
	ServerError     *Error
	TooManyRequests *Error
}{
	NotFound: &Error{
		Code:    404,
		Message: "Not Found",
	},
	BadRequest: &Error{
		Code:    400,
		Message: "Bad Request",
	},
	Unauthorized: &Error{
		Code:    403,
		Message: "Unauthorized",
	},
	Forbidden: &Error{
		Code:    403,
		Message: "Forbidden",
	},
	ServerError: &Error{
		Code:    500,
		Message: "Server Error",
	},
	TooManyRequests: &Error{
		Code:    429,
		Message: "Too Many Requests",
	},
}

CommonErrors are common errors types suitable for API endpoints

Functions

This section is empty.

Types

type API

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

API describes a JSON API server. API handles return data or an error, and all responses are wrapped in a common response object.

func (API) DELETE

func (a API) DELETE(path string, handle APIHandle, options HandleOptions)

DELETE register a new HTTP DELETE request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		username := request.Parameters["username"]

		return map[string]string{
			"username": username,
		}, nil, nil
	}
	server.API.DELETE("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (API) GET

func (a API) GET(path string, handle APIHandle, options HandleOptions)

GET register a new HTTP GET request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		username := request.Parameters["username"]

		return map[string]string{
			"username": username,
		}, nil, nil
	}
	server.API.GET("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (API) HEAD

func (a API) HEAD(path string, handle APIHandle, options HandleOptions)

HEAD register a new HTTP HEAD request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		return nil, nil, nil
	}
	server.API.HEAD("/users/user/", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (API) OPTIONS

func (a API) OPTIONS(path string, handle APIHandle, options HandleOptions)

OPTIONS register a new HTTP OPTIONS request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		return nil, nil, nil
	}
	server.API.OPTIONS("/users/user/", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (API) PATCH

func (a API) PATCH(path string, handle APIHandle, options HandleOptions)

PATCH register a new HTTP PATCH request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	type userRequestType struct {
		FirstName string `json:"first_name"`
	}

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		username := request.Parameters["username"]
		params := userRequestType{}
		if err := request.DecodeJSON(&params); err != nil {
			return nil, nil, err
		}

		return map[string]string{
			"first_name": params.FirstName,
			"username":   username,
		}, nil, nil
	}
	server.API.PATCH("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (API) POST

func (a API) POST(path string, handle APIHandle, options HandleOptions)

POST register a new HTTP POST request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	type userRequestType struct {
		FirstName string `json:"first_name"`
	}

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		username := request.Parameters["username"]
		params := userRequestType{}
		if err := request.DecodeJSON(&params); err != nil {
			return nil, nil, err
		}

		return map[string]string{
			"first_name": params.FirstName,
			"username":   username,
		}, nil, nil
	}
	server.API.POST("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (API) PUT

func (a API) PUT(path string, handle APIHandle, options HandleOptions)

PUT register a new HTTP PUT request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	type userRequestType struct {
		FirstName string `json:"first_name"`
	}

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		username := request.Parameters["username"]
		params := userRequestType{}
		if err := request.DecodeJSON(&params); err != nil {
			return nil, nil, err
		}

		return map[string]string{
			"first_name": params.FirstName,
			"username":   username,
		}, nil, nil
	}
	server.API.PUT("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

type APIHandle

type APIHandle func(request Request) (interface{}, *APIResponse, *Error)

APIHandle describes a method signature for handling an API request

type APIResponse added in v1.11.0

type APIResponse struct {
	// Additional headers to append to the response.
	Headers map[string]string
	// Cookies to set on the response.
	Cookies []http.Cookie
}

APIResponse describes additional response properties for API handles

type Error

type Error struct {
	Code    int    `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

Error describes an API error object

func ValidationError

func ValidationError(format string, v ...interface{}) *Error

ValidationError convenience method to make a error object for validation errors

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		username := request.Parameters["username"]

		return nil, nil, web.ValidationError("No user with username %s", username)
	}
	server.API.GET("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

type HTTP

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

HTTP describes a HTTP server. HTTP handles are exposed to the raw http request and response writers.

func (HTTP) DELETE

func (h HTTP) DELETE(path string, handle HTTPHandle, options HandleOptions)

DELETE register a new HTTP DELETE request handle

Example
package main

import (
	"net/http"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(w http.ResponseWriter, r web.Request) {
		username := r.Parameters["username"]

		w.Header().Set("X-Username", username)
		w.WriteHeader(200)
	}
	server.HTTP.DELETE("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTP) GET

func (h HTTP) GET(path string, handle HTTPHandle, options HandleOptions)

GET register a new HTTP GET request handle

Example
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(w http.ResponseWriter, r web.Request) {
		f, _ := os.Open("/foo/bar")
		info, _ := f.Stat()
		w.Header().Set("Content-Type", "text/plain")
		w.Header().Set("Content-Length", fmt.Sprintf("%d", info.Size()))
		io.Copy(w, f)
	}
	server.HTTP.GET("/users/user", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTP) HEAD

func (h HTTP) HEAD(path string, handle HTTPHandle, options HandleOptions)

HEAD register a new HTTP HEAD request handle

Example
package main

import (
	"net/http"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(w http.ResponseWriter, r web.Request) {
		w.Header().Set("X-Fancy-Header", "Some value")
		w.WriteHeader(204)
	}
	server.HTTP.HEAD("/users/user", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTP) OPTIONS

func (h HTTP) OPTIONS(path string, handle HTTPHandle, options HandleOptions)

OPTIONS register a new HTTP OPTIONS request handle

Example
package main

import (
	"net/http"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(w http.ResponseWriter, r web.Request) {
		w.Header().Set("X-Fancy-Header", "Some value")
		w.WriteHeader(200)
	}
	server.HTTP.OPTIONS("/users/user", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTP) PATCH

func (h HTTP) PATCH(path string, handle HTTPHandle, options HandleOptions)

PATCH register a new HTTP PATCH request handle

Example
package main

import (
	"net/http"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(w http.ResponseWriter, r web.Request) {
		username := r.Parameters["username"]

		w.Header().Set("X-Username", username)
		w.WriteHeader(200)
	}
	server.HTTP.PATCH("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTP) POST

func (h HTTP) POST(path string, handle HTTPHandle, options HandleOptions)

POST register a new HTTP POST request handle

Example
package main

import (
	"net/http"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(w http.ResponseWriter, r web.Request) {
		username := r.Parameters["username"]

		w.Header().Set("X-Username", username)
		w.WriteHeader(200)
	}
	server.HTTP.POST("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTP) PUT

func (h HTTP) PUT(path string, handle HTTPHandle, options HandleOptions)

PUT register a new HTTP PUT request handle

Example
package main

import (
	"net/http"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(w http.ResponseWriter, r web.Request) {
		username := r.Parameters["username"]

		w.Header().Set("X-Username", username)
		w.WriteHeader(200)
	}
	server.HTTP.PUT("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

type HTTPEasy added in v1.11.0

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

HTTPEasy describes a HTTPEasy server. HTTPEasy handles are expected to return a reader and specify the content type and length themselves.

The HTTPEasy server supports HTTPEasy range requests, should the client request it and the application provide a supported Reader (io.ReadSeekCloser).

func (HTTPEasy) DELETE added in v1.11.0

func (h HTTPEasy) DELETE(path string, handle HTTPEasyHandle, options HandleOptions)

DELETE register a new HTTP DELETE request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		username := request.Parameters["username"]
		return web.HTTPResponse{
			Headers: map[string]string{
				"X-Username": username,
			},
		}
	}
	server.HTTPEasy.DELETE("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTPEasy) GET added in v1.11.0

func (h HTTPEasy) GET(path string, handle HTTPEasyHandle, options HandleOptions)

GET register a new HTTP GET request handle

Example
package main

import (
	"os"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		f, err := os.Open("/foo/bar")
		info, ierr := f.Stat()
		if err != nil || ierr != nil {
			return web.HTTPResponse{
				Status: 500,
			}
		}
		return web.HTTPResponse{
			Reader:        f, // The file will be closed automatically
			ContentType:   "text/plain",
			ContentLength: uint64(info.Size()),
		}
	}
	server.HTTPEasy.GET("/users/user", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTPEasy) GETHEAD added in v1.11.0

func (h HTTPEasy) GETHEAD(path string, handle HTTPEasyHandle, options HandleOptions)

GETHEAD registers both a HTTP GET and HTTP HEAD request handle. Equal to calling HTTPEasy.GET and HTTPEasy.HEAD.

Handle responses can always return a reader, it will automatically be ignored for HEAD requests.

Example
package main

import (
	"os"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		f, err := os.Open("/foo/bar")
		info, ierr := f.Stat()
		if err != nil || ierr != nil {
			return web.HTTPResponse{
				Status: 500,
			}
		}
		return web.HTTPResponse{
			Reader:        f, // the file will not be read for HTTP HEAD requests, but it will be closed.
			ContentType:   "text/plain",
			ContentLength: uint64(info.Size()),
		}
	}
	server.HTTPEasy.GETHEAD("/users/user", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTPEasy) HEAD added in v1.11.0

func (h HTTPEasy) HEAD(path string, handle HTTPEasyHandle, options HandleOptions)

HEAD register a new HTTP HEAD request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		return web.HTTPResponse{
			Headers: map[string]string{
				"X-Fancy-Header": "some value",
			},
		}
	}
	server.HTTPEasy.HEAD("/users/user", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTPEasy) OPTIONS added in v1.11.0

func (h HTTPEasy) OPTIONS(path string, handle HTTPEasyHandle, options HandleOptions)

OPTIONS register a new HTTP OPTIONS request handle

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		return web.HTTPResponse{
			Headers: map[string]string{
				"X-Fancy-Header": "some value",
			},
		}
	}
	server.HTTPEasy.OPTIONS("/users/user", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTPEasy) PATCH added in v1.11.0

func (h HTTPEasy) PATCH(path string, handle HTTPEasyHandle, options HandleOptions)

PATCH register a new HTTP PATCH request handle

Example
package main

import (
	"os"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		username := request.Parameters["username"]

		f, err := os.Open("/foo/bar")
		if err != nil {
			return web.HTTPResponse{
				Status: 500,
			}
		}
		return web.HTTPResponse{
			Headers: map[string]string{
				"X-Username": username,
			},
			Reader: f,
		}
	}
	server.HTTPEasy.PATCH("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTPEasy) POST added in v1.11.0

func (h HTTPEasy) POST(path string, handle HTTPEasyHandle, options HandleOptions)

POST register a new HTTP POST request handle

Example
package main

import (
	"os"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		username := request.Parameters["username"]

		f, err := os.Open("/foo/bar")
		if err != nil {
			return web.HTTPResponse{
				Status: 500,
			}
		}
		return web.HTTPResponse{
			Headers: map[string]string{
				"X-Username": username,
			},
			Reader: f,
		}
	}
	server.HTTPEasy.POST("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTPEasy) PUT added in v1.11.0

func (h HTTPEasy) PUT(path string, handle HTTPEasyHandle, options HandleOptions)

PUT register a new HTTP PUT request handle

Example
package main

import (
	"os"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) web.HTTPResponse {
		username := request.Parameters["username"]

		f, err := os.Open("/foo/bar")
		if err != nil {
			return web.HTTPResponse{
				Status: 500,
			}
		}
		return web.HTTPResponse{
			Headers: map[string]string{
				"X-Username": username,
			},
			Reader: f,
		}
	}
	server.HTTPEasy.PUT("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (HTTPEasy) Static added in v1.11.0

func (h HTTPEasy) Static(path string, directory string)

Static registers a GET and HEAD handle for all requests under path to serve any files matching the directory.

For example:

directory = /usr/share/www/
path      = /static/

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

Will panic if any handle is registered under path. Attempting to register a new handle under path after calling Static 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.

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	server.HTTPEasy.Static("/static/*", "/path/to/static/files")

	server.Start()
}
Output:

type HTTPEasyHandle added in v1.11.0

type HTTPEasyHandle func(request Request) HTTPResponse

HTTPEasyHandle describes a method signature for handling an HTTP request

type HTTPHandle

type HTTPHandle func(w http.ResponseWriter, r Request)

HTTPHandle describes a method signature for handling an HTTP request

type HTTPResponse added in v1.9.0

type HTTPResponse struct {
	// The reader for the response. Will be closed when the HTTP response is finished. Can be nil.
	//
	// If a io.ReadSeekCloser is provided then ranged data may be provided for a HTTP range request.
	Reader io.ReadCloser
	// The status code for the response. If 0 then 200 is implied.
	Status int
	// Additional headers to append to the response.
	Headers map[string]string
	// Cookies to set on the response.
	Cookies []http.Cookie
	// The content type of the response. Will overwrite any 'content-type' header in Headers.
	ContentType string
	// The length of the content.
	ContentLength uint64
}

HTTPResponse describes a HTTP response

type HandleOptions

type HandleOptions struct {
	// AuthenticateMethod method called to determine if a request is properly authenticated or not.
	// Optional - Omit this entirely if no authentication is needed for the request.
	// Return nil to signal an unauthenticated request, which will be rejected.
	// Objects returned will be passed to the handle as the UserData object.
	AuthenticateMethod func(request *http.Request) interface{}
	// UnauthorizedMethod method called when an unauthenticated request occurs (AuthenticateMethod returned nil)
	// to customize the response seen by the user.
	// Optional - Omit this to have a default response.
	UnauthorizedMethod func(w http.ResponseWriter, request *http.Request)
	// MaxBodyLength defines the maximum length accepted for any HTTP request body. Requests that
	// exceed this limit will receive a 413 Payload Too Large response.
	// The default value of 0 will not reject requests with large bodies.
	MaxBodyLength uint64
	// DontLogRequests if true then requests to this handle are not logged
	DontLogRequests bool
}

HandleOptions describes options for a route

type JSONResponse

type JSONResponse struct {
	// The actual data of the response
	Data interface{} `json:"data,omitempty"`
	// If an error occured, details about the error
	Error *Error `json:"error,omitempty"`
	// The HTTP status code for the response
	//
	// Deprecated: will be removed in the next breaking update
	Code int `json:"code"`
}

JSONResponse describes an API response object

type MockRequestParameters added in v1.11.2

type MockRequestParameters struct {
	// User data to be passed into the handler. May be nil.
	UserData interface{}
	// URL parameters (not query parameters) to be populated into the request.Params object in the handler. May be nil.
	Parameters map[string]string
	// Object to be encoded with JSON as the body. May be nil. Exclusive to Body.
	JSONBody interface{}
	// Body data. May be nil. Exclusive to JSONBody.
	Body io.ReadCloser
	// Optional HTTP request to pass to the handler.
	Request *http.Request
}

Parameters for creating a mock request for uses in tests

type Request

type Request struct {
	HTTP       *http.Request
	Parameters map[string]string
	UserData   interface{}
}

Request describes an API request

func MockRequest

func MockRequest(parameters MockRequestParameters) Request

MockRequest will generate a mock request for testing your handlers. Will panic for invalid parameters.

func (Request) ClientIPAddress added in v1.7.0

func (r Request) ClientIPAddress() net.IP

ClientIPAddress returns the IP address of the client. It supports the 'X-Real-IP' and 'X-Forwarded-For' headers.

Example
package main

import (
	"fmt"

	"github.com/ecnepsnai/web"
)

func main() {
	server := web.New("127.0.0.1:8080")

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		clientAddr := request.ClientIPAddress().String()
		fmt.Printf("%s\n", clientAddr)
		return clientAddr, nil, nil
	}
	server.API.POST("/ip/my_ip", handle, web.HandleOptions{})

	server.Start()
}
Output:

func (Request) DecodeJSON added in v1.7.0

func (r Request) DecodeJSON(v interface{}) *Error

DecodeJSON unmarshal the JSON body to the provided interface

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	type userRequestType struct {
		FirstName string `json:"first_name"`
	}

	handle := func(request web.Request) (interface{}, *web.APIResponse, *web.Error) {
		username := request.Parameters["username"]
		params := userRequestType{}
		if err := request.DecodeJSON(&params); err != nil {
			return nil, nil, err
		}

		return map[string]string{
			"first_name": params.FirstName,
			"username":   username,
		}, nil, nil
	}
	server.API.POST("/users/user/:username", handle, web.HandleOptions{})

	server.Start()
}
Output:

type Server

type Server struct {
	// The socket address that the server is listening on. Only populated if the server was created with web.New().
	BindAddress string
	// The port that this server is listening on. Only populated if the server was created with web.New().
	ListenPort uint16
	// The JSON API server. API handles return data or an error, and all responses are wrapped in a common
	// response object.
	API API
	// HTTPEasy describes a easy HTTP server. HTTPEasy handles are expected to return a reader and specify the content
	// type and length themselves.
	//
	// The HTTPEasy server supports HTTP range requests, should the client request it and the application provide a
	// supported Reader (io.ReadSeekCloser).
	HTTPEasy HTTPEasy
	// The HTTP server. HTTP handles are exposed to the raw http request and response writers.
	HTTP HTTP
	// The handler called when a request that does not match a registered path occurs. Defaults to a plain
	// HTTP 404 with "Not found" as the body.
	NotFoundHandler func(w http.ResponseWriter, r *http.Request)
	// The handler called when a request that did match a router but with the incorrect method occurs. Defaults to a
	// plain HTTP 405 with "Method not allowed" as the body.
	MethodNotAllowedHandler func(w http.ResponseWriter, r *http.Request)
	// The handler called when a request exceed the configured maximum per second limit. Defaults to a plain HTTP 429
	// with "Too many requests" as the body.
	RateLimitedHandler func(w http.ResponseWriter, r *http.Request)
	// Additional options for the server
	Options ServerOptions
	// contains filtered or unexported fields
}

Server describes an web server

func New

func New(bindAddress string) *Server

New create a new server object that will bind to the provided address. Does not accept incoming connections until the server is started. Bind address must be in the format of "address:port", such as "localhost:8080" or "0.0.0.0:8080".

func NewListener added in v1.10.0

func NewListener(listener net.Listener) *Server

NewListener creates a new server object that will use the given listener. Does not accept incoming connections until the server is started.

func (*Server) Socket added in v1.4.0

func (s *Server) Socket(path string, handle SocketHandle, options HandleOptions)

Socket register a new websocket server at the given path

Example
package main

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

func main() {
	server := web.New("127.0.0.1:8080")

	type questionType struct {
		Name string
	}

	type answerType struct {
		Reply string
	}

	handle := func(request web.Request, conn *web.WSConn) {
		question := questionType{}
		if err := conn.ReadJSON(&question); err != nil {
			return
		}

		reply := answerType{
			Reply: "Hello, " + question.Name,
		}
		if err := conn.WriteJSON(&reply); err != nil {
			return
		}
	}

	options := web.HandleOptions{}
	server.Socket("/greeting", handle, options)

	server.Start()
}
Output:

func (*Server) Start

func (s *Server) Start() error

Start will start the web server and listen on the socket address. This method blocks. If a server is stopped using the Stop() method, this returns no error.

func (*Server) Stop

func (s *Server) Stop()

Stop will stop the server. The Start() method will return without an error after stopping.

type ServerOptions added in v1.9.0

type ServerOptions struct {
	// Specify the maximum number of requests any given client IP address can make per second. Requests that are rate
	// limited will call the RateLimitedHandler, which you can override to customize the response.
	// Setting this to 0 disables rate limiting.
	MaxRequestsPerSecond int
	// The level to use when logging out HTTP requests. Maps to github.com/ecnepsnai/logtic levels. Defaults to Debug.
	RequestLogLevel int
	// If true then the server will not try to reply with chunked data for a HTTP range request
	IgnoreHTTPRangeRequests bool
}

type SocketHandle added in v1.4.0

type SocketHandle func(request Request, conn *WSConn)

SocketHandle describes a method signature for handling a HTTP websocket request

type WSConn added in v1.4.0

type WSConn struct {
	*websocket.Conn
}

WSConn describes a websocket connection.

Directories

Path Synopsis
Package router provides a simple & efficient parametrized HTTP router.
Package router provides a simple & efficient parametrized HTTP router.

Jump to

Keyboard shortcuts

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