httphelpers

package
v3.0.2 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2022 License: Apache-2.0 Imports: 21 Imported by: 2

Documentation

Overview

Package httphelpers contains convenience tools for testing HTTP client code.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BrokenConnectionHandler

func BrokenConnectionHandler() http.Handler

BrokenConnectionHandler creates an HTTP handler that will simulate an I/O error.

When used with an httptest.Server, the handler forces an early close of the connection. When used in a client created with ClientFromHandler, it causes a panic which is recovered and converted to an error result. However, do not use this with httptest.ResponseRecorder or your test will panic.

handler := BrokenConnectionHandler()
client := NewClientFromHandler(handler)
// All requests made with this client will return an error

func ClientFromHandler

func ClientFromHandler(handler http.Handler) *http.Client

ClientFromHandler returns an http.Client that does not do real network activity, but instead delegates to a http.Handler as if that handler were being used by a server.

This makes it possible to reuse the other handler-related functions in this package to control an http.Client rather than using the somewhat less convenient RoundTripper interface.

If the handler panics, it returns an error instead of a response. This can be used to simulate an I/O error (since the http.Handler interface does not provide any way *not* to return an actual HTTP response).

func HandlerForMethod

func HandlerForMethod(method string, handlerForMethod http.Handler, defaultHandler http.Handler) http.Handler

HandlerForMethod is a simple alternative to using an HTTP router. It delegates to the specified handler if the method matches; otherwise to the default handler, or a 405 if that is nil.

func HandlerForPath

func HandlerForPath(path string, handlerForPath http.Handler, defaultHandler http.Handler) http.Handler

HandlerForPath is a simple alternative to using an HTTP router. It delegates to the specified handler if the path matches; otherwise to the default handler, or a 404 if that is nil.

func HandlerForPathRegex

func HandlerForPathRegex(pathRegex string, handlerForPath http.Handler, defaultHandler http.Handler) http.Handler

HandlerForPathRegex is a simple alternative to using an HTTP router. It delegates to the specified handler if the path matches; otherwise to the default handler, or a 404 if that is nil.

func HandlerWithJSONResponse

func HandlerWithJSONResponse(contentToEncode interface{}, additionalHeaders http.Header) http.Handler

HandlerWithJSONResponse creates an HTTP handler that returns a 200 status and the JSON encoding of the specified object.

func HandlerWithResponse

func HandlerWithResponse(status int, headers http.Header, body []byte) http.Handler

HandlerWithResponse creates an HTTP handler that always returns the same status code, headers, and body.

func HandlerWithStatus

func HandlerWithStatus(status int) http.Handler

HandlerWithStatus creates an HTTP handler that always returns the same status code.

func MakeSelfSignedCert

func MakeSelfSignedCert(certFilePath, keyFilePath string) error

MakeSelfSignedCert generates a self-signed certificate and writes it to the specified files. See: https://golang.org/src/crypto/tls/generate_cert.go

func MakeServerWithCert

func MakeServerWithCert(certFilePath, keyFilePath string, handler http.Handler) (*httptest.Server, error)

MakeServerWithCert creates and starts a test HTTPS server using the specified certificate.

func RecordingHandler

func RecordingHandler(delegateToHandler http.Handler) (http.Handler, <-chan HTTPRequestInfo)

RecordingHandler wraps any HTTP handler in another handler that pushes received requests onto a channel.

handler, requestsCh := httphelpers.RecordingHandler(httphelpers.HandlerWithStatus(200))
httphelpers.WithServer(handler, func(server *http.TestServer) {
    doSomethingThatMakesARequest(server.URL) // request will receive a 200 status
    r := <-requestsCh
    verifyRequestPropertiesWereCorrect(r.Request, r.Body)
})

func SequentialHandler

func SequentialHandler(firstHandler http.Handler, remainingHandlers ...http.Handler) http.Handler

SequentialHandler creates an HTTP handler that delegates to one handler per request, in the order given. If there are more requests than parameters, all subsequent requests go to the last handler.

In this example, the first HTTP request will get a 503, and all subsequent requests will get a 200.

handler := httphelpers.SequentialHandler(
    httphelpers.HandlerWithStatus(503),
    httphelpers.HandlerWithStatus(200)
)

func WithSelfSignedServer

func WithSelfSignedServer(handler http.Handler, action func(*httptest.Server, []byte, *x509.CertPool))

WithSelfSignedServer is a convenience function for starting a test HTTPS server with a self-signed certificate, running the specified function, and then closing the server and cleaning up the temporary certificate files. If for some reason creating the server fails, it panics. The action function's second and third parameters provide the CA certificate for configuring the client, and a preconfigured CertPool in case that is more convenient to use.

func WithServer

func WithServer(handler http.Handler, action func(*httptest.Server))

WithServer creates an httptest.Server from the given handler, passes the server instance to the given function, and ensures that the server is closed afterward.

Types

type DelegatingHandler

type DelegatingHandler struct {
	Handler http.Handler
}

DelegatingHandler is a struct that behaves as an http.Handler by delegating to the handler it wraps. Use this if you want to change the handler's behavior dynamically during a test.

dh := &httphelpers.DelegatingHandler{httphelpers.HandlerWithStatus(200)}
server := httptest.NewServer(dh) // the server will return 200
dh.Handler = httphelpers.HandlerWithStatus(401) // now the server will return 401

func (*DelegatingHandler) ServeHTTP

func (d *DelegatingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type HTTPRequestInfo

type HTTPRequestInfo struct {
	Request *http.Request
	Body    []byte // body has to be captured separately by server because you can't read it after the response is sent
}

HTTPRequestInfo represents a request captured by NewRecordingHTTPHandler.

type SSEEvent

type SSEEvent struct {
	// ID is the optional unique ID of the event.
	ID string

	// Event is the message type, if any.
	Event string

	// Data is the message data.
	Data string

	// RetryMillis is an optional field that changes the client's reconnection delay to the specified number
	// of milliseconds. If zero or negative, the field will not be sent.
	RetryMillis int
}

SSEEvent is a simple representation of a Server-Sent Events message.

func (SSEEvent) Bytes

func (e SSEEvent) Bytes() []byte

Bytes returns the stream data for the event.

type SSEStreamControl

type SSEStreamControl interface {
	// Enqueue is the same as Send, except that if there are currently no open connections to this
	// endpoint, the event is enqueued and will be sent to the next client that connects.
	Enqueue(event SSEEvent)

	// Send sends an SSE event. If there are multiple open connections to this endpoint, the same
	// event is sent to all of them. If there are no open connections, the event is discarded.
	Send(event SSEEvent)

	// EnqueueComment is the same as Enqueue, except that it sends a comment line instead of an
	// event. A colon is prepended to the comment.
	EnqueueComment(comment string)

	// SendComment is the same as Send, except that it sends a comment line instead of an event.
	// A colon is prepended to the comment.
	SendComment(comment string)

	// EndAll terminates any existing connections to this endpoint, but allows new connections
	// afterward.
	EndAll()

	// Close terminates any existing connections to this endpoint and causes the handler to reject any
	// subsequent connections.
	Close() error
}

SSEStreamControl is the interface for manipulating streams created by SSEHandler.

func SSEHandler

func SSEHandler(initialEvent *SSEEvent) (http.Handler, SSEStreamControl)

SSEHandler creates an HTTP handler that streams Server-Sent Events data.

The initialData parameter, if not nil, specifies a starting event that should always be sent to any client that has connected to this endpoint. Then, any data provided via the SSEStreamControl interface is copied to all connected clients. Connections remain open until either EndAll or Close is called on on the SSEStreamControl.

In this example, every request to this endpoint will receive an initial initEvent, and then another event will be sent every second with a counter; every 30 seconds, all active stream connections are are closed:

initialEvent := httphelpers.SSEEvent{Data: "hello"}
handler, stream := httphelpers.SSEHandler(&initialEvent)
(start server with handler)
go func() {
    n := 1
    counter := time.NewTicker(time.Second)
    interrupter := time.NewTicker(time.Second * 10)
    for {
        select {
        case <-counter.C:
            stream.Send(httphelpers.SSEEvent{Data: fmt.Sprintf("%d\n", n)})
            n++
        case <-interrupter.C:
            stream.EndAll()
        }
    }
}()

type StreamControl

type StreamControl interface {
	// Enqueue is the same as Send, except that if there are currently no open connections to this
	// endpoint, the data is enqueued and will be sent to the next client that connects.
	Enqueue(data []byte)

	// Send sends a chunk of data. If there are multiple open connections to this endpoint, the same
	// data is sent to all of them. If there are no open connections, the data is discarded.
	Send(data []byte)

	// EndAll terminates any existing connections to this endpoint, but allows new connections
	// afterward.
	EndAll()

	// Close terminates any existing connections to this endpoint and causes the handler to reject any
	// subsequent connections.
	Close() error
}

StreamControl is the interface for manipulating streams created by ChunkedStreamingHandler.

func ChunkedStreamingHandler

func ChunkedStreamingHandler(initialChunk []byte, contentType string) (http.Handler, StreamControl)

ChunkedStreamingHandler creates an HTTP handler that streams arbitrary data using chunked encoding.

The initialData parameter, if not nil, specifies a starting chunk that should always be sent to any client that has connected to this endpoint. Then, any data provided via the StreamControl interface is copied to all connected clients. Connections remain open until either EndAll or Close is called on the StreamControl.

In this example, every request to this endpoint will receive an initial message of "hello\n", and then another line will be sent every second with a counter; every 30 seconds, all active stream connections are closed:

handler, stream := httphelpers.ChunkedStreamingHandler([]byte("hello\n"), "text/plain")
(start server with handler)
go func() {
    n := 1
    counter := time.NewTicker(time.Second)
    interrupter := time.NewTicker(time.Second * 10)
    for {
        select {
        case <-counter.C:
            stream.Send([]byte(fmt.Sprintf("%d\n", n)))
            n++
        case <-interrupter.C:
            stream.EndAll()
        }
    }
}()

Jump to

Keyboard shortcuts

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