http

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jan 7, 2024 License: Apache-2.0 Imports: 20 Imported by: 3

README

ContainerSSH HTTP Library

This library provides a common layer for HTTP clients and servers in use by ContainerSSH.

Using this library

This library provides a much simplified API for both the HTTP client and server.

Using the client

The client library takes a request object that can be marshalled into JSON format and sends it to the server. It then fills a response object with the response received from the server. In code:

// Logger is from the github.com/containerssh/libcontainerssh/log package
logger := standard.New()
clientConfig := http.ClientConfiguration{
    URL:        "http://127.0.0.1:8080/",
    Timeout:    2 * time.Second,
    // You can add TLS configuration here:
    CaCert:     "Add expected CA certificate(s) here.",
                // CaCert is required for https:// URLs on Windows due to golang#16736
    // Optionally, for client authentication:
    ClientCert: "Client certificate in PEM format or file name",
    ClientKey:  "Client key in PEM format or file name",
    // Optional: switch to www-urlencoded request body
    RequestEncoding: http.RequestEncodingWWWURLEncoded,
}
client, err := http.NewClient(clientConfig, logger)
if err != nil {
    // Handle validation error
}

request := yourRequestStruct{}
response := yourResponseStruct{}

responseStatus, err := client.Post(
    context.TODO(),
    "/relative/path/from/base/url",
    &request,
    &response,
)
if err != nil {
    // Handle connection error
    clientError := &http.ClientError{}
    if errors.As(err, clientError) {
        // Grab additional information here
    } else {
    	// This should never happen
    }
}

if responseStatus > 399 {
    // Handle error
}

The logger parameter is a logger from the github.com/containerssh/libcontainerssh/log package.

Using the server

The server consist of two parts: the HTTP server and the handler. The HTTP server can be used as follows:

server, err := http.NewServer(
    "service name",
    http.ServerConfiguration{
        Listen:       "127.0.0.1:8080",
        // You can also add TLS configuration
        // and certificates here:
        Key:          "PEM-encoded key or file name to cert here.",
        Cert:         "PEM-encoded certificate chain or file name here",
        // Authenticate clients with certificates:
        ClientCACert: "PEM-encoded client CA certificate or file name here",
    },
    handler,
    logger,
    func (url string) {
        fmt.Printf("Server is now ready at %s", url)
    }
)
if err != nil {
    // Handle configuration error
}
// Lifecycle from the github.com/containerssh/service package
lifecycle := service.NewLifecycle(server)
go func() {
    if err := lifecycle.Run(); err != nil {
        // Handle error
    }
}()
// Do something else, then shut down the server.
// You can pass a context for the shutdown deadline.
lifecycle.Shutdown(context.Background())

Like before, the logger parameter is a logger from the github.com/containerssh/libcontainerssh/log package. The handler is a regular go HTTP handler that satisfies this interface:

type Handler interface {
    ServeHTTP(http.ResponseWriter, *http.Request)
}

The lifecycle object is one from the ContainerSSH service package.

Using a simplified handler

This package also provides a simplified handler that helps with encoding and decoding JSON messages. It can be created as follows:

handler := http.NewServerHandler(yourController, logger)

The yourController variable then only needs to implement the following interface:

type RequestHandler interface {
	OnRequest(request ServerRequest, response ServerResponse) error
}

For example:

type MyRequest struct {
    Message string `json:"message"`
}

type MyResponse struct {
    Message string `json:"message"`
}

type myController struct {
}

func (c *myController) OnRequest(request http.ServerRequest, response http.ServerResponse) error {
    req := MyRequest{}
	if err := request.Decode(&req); err != nil {
		return err
	}
	if req.Message == "Hi" {
		response.SetBody(&MyResponse{
			Message: "Hello world!",
		})
	} else {
        response.SetStatus(400)
		response.SetBody(&MyResponse{
			Message: "Be nice and greet me!",
		})
	}
	return nil
}

In other words, the ServerRequest object gives you the ability to decode the request into a struct of your choice. The ServerResponse, conversely, encodes a struct into the response body and provides the ability to enter a status code.

Content negotiation

If you wish to perform content negotiation on the server side, this library now supports switching between text and JSON output. This can be invoked using the NewServerHandlerNegotiate method instead of NewServerHandler. This handler will attempt to switch based on the Accept header sent by the client. You can marshal objects to text by implementing the following interface:

type TextMarshallable interface {
	MarshalText() string
}

Using multiple handlers

This is a very simple handler example. You can use utility like gorilla/mux as an intermediate handler between the simplified handler and the server itself.

Documentation

Overview

Package http provides a much simplified API for both the HTTP client and server. It is meant for use in conjunction with ContainerSSH and should not be used for other purposes as the implementation is very ContainerSSH-specific.

Index

Constants

View Source
const RequestEncodingDefault = ""

RequestEncodingDefault is the default encoding and encodes the body to JSON.

View Source
const RequestEncodingJSON = "JSON"

RequestEncodingJSON encodes the body to JSON.

View Source
const RequestEncodingWWWURLEncoded = "WWW-URLENCODED"

RequestEncodingWWWURLEncoded encodes the body via www-urlencoded.

Variables

This section is empty.

Functions

func NewServerHandler

func NewServerHandler(
	requestHandler RequestHandler,
	logger log.Logger,
) goHttp.Handler

NewServerHandler creates a new simplified HTTP handler that decodes JSON requests and encodes JSON responses.

func NewServerHandlerNegotiate

func NewServerHandlerNegotiate(
	requestHandler RequestHandler,
	logger log.Logger,
) goHttp.Handler

NewServerHandlerNegotiate creates a simplified HTTP handler that supports content negotiation for responses.

Types

type Client

type Client interface {
	// Request queries the specified path on the configured endpoint with the specified method.
	Request(
		method string,
		path string,
		requestBody interface{},
		responseBody interface{},
	) (statusCode int, err error)

	// RequestURL requests a URL irrespective of the endpoint configured on the client.
	RequestURL(
		method string,
		url string,
		requestBody interface{},
		responseBody interface{},
	) (statusCode int, err error)

	// Get queries the configured endpoint with the path providing the response in the responseBody structure. It
	// returns the HTTP status code and any potential errors.
	Get(
		path string,
		responseBody interface{},
	) (statusCode int, err error)

	// Post queries the configured endpoint with the path, sending the requestBody and providing the
	// response in the responseBody structure. It returns the HTTP status code and any potential errors.
	Post(
		path string,
		requestBody interface{},
		responseBody interface{},
	) (statusCode int, err error)

	// Put queries the configured endpoint with the path, sending the requestBody and providing the
	// response in the responseBody structure. It returns the HTTP status code and any potential errors.
	Put(
		path string,
		requestBody interface{},
		responseBody interface{},
	) (statusCode int, err error)

	// Patch queries the configured endpoint with the path, sending the requestBody and providing the
	// response in the responseBody structure. It returns the HTTP status code and any potential errors.
	Patch(
		path string,
		requestBody interface{},
		responseBody interface{},
	) (statusCode int, err error)

	// Delete queries the configured endpoint with the path, sending the requestBody and providing the
	// response in the responseBody structure. It returns the HTTP status code and any potential errors.
	Delete(
		path string,
		requestBody interface{},
		responseBody interface{},
	) (statusCode int, err error)
}

Client is a simplified HTTP interface that ensures that a struct is transported to a remote endpoint properly encoded, and the response is decoded into the response struct.

func NewClient

func NewClient(
	config config.HTTPClientConfiguration,
	logger log.Logger,
) (Client, error)

NewClient creates a new HTTP client with the given configuration.

func NewClientWithHeaders

func NewClientWithHeaders(
	config config.HTTPClientConfiguration,
	logger log.Logger,
	extraHeaders map[string][]string,
	allowLaxDecoding bool,
) (Client, error)

NewClientWithHeaders creates a new HTTP client with added extra headers.

type RequestEncoding

type RequestEncoding string

RequestEncoding is the method by which the response is encoded.

func (RequestEncoding) Validate

func (r RequestEncoding) Validate() error

Validate validates the RequestEncoding

type RequestHandler

type RequestHandler interface {
	// OnRequest is a method receiving a request and is able to respond.
	OnRequest(request ServerRequest, response ServerResponse) error
}

RequestHandler is an interface containing a simple controller receiving a request and providing a response.

type Server

type Server interface {
	service.Service
}

Server is an interface that specifies the minimum requirements for the server.

func NewServer

func NewServer(
	name string,
	config config.HTTPServerConfiguration,
	handler goHttp.Handler,
	logger log.Logger,
	onReady func(string),
) (Server, error)

NewServer creates a new HTTP server with the given configuration and calling the provided handler.

type ServerRequest

type ServerRequest interface {
	// Decode decodes the raw request into the provided target from a JSON format. It provides an
	//        error if the decoding failed, which should be passed back through the request handler.
	Decode(target interface{}) error
}

ServerRequest is a testdata structure providing decoding from the raw request.

type ServerResponse

type ServerResponse interface {
	// SetStatus sets the HTTP status code
	SetStatus(statusCode uint16)
	// SetBody sets the object of the response which will be encoded as JSON.
	SetBody(interface{})
}

ServerResponse is a response structure that can be used by the RequestHandler to set the response details.

type TextMarshallable

type TextMarshallable interface {
	// MarshalText converts the object to a text representation safe to output on the web interface.
	MarshalText() string
}

TextMarshallable is an interface that lets an object be converted to user-facing text.

Jump to

Keyboard shortcuts

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