Documentation
¶
Overview ¶
HTTP wrecker which provides an ability to interrupt execution of requests to an upstream server.
Index ¶
Examples ¶
Constants ¶
const ( UnixSchemeHTTP = "http+unix" UnixSchemeHTTPS = "https+unix" )
const DefaultReadTimeout = 10 * time.Second
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Blocker ¶ added in v0.5.0
Decides whether to return an error or redirect a request to an upstream server.
If it returns true, an error will be sent to a sender and a request will not be forwarded to an upstream server.
Each blocker receives a copy of the request body, which it can read independently from other blockers and from the upstream server.
type Handler ¶ added in v0.4.0
type Handler struct {
// contains filtered or unexported fields
}
HTTP wrecker in the form of http.Handler.
Example ¶
package main
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"time"
"github.com/akramarenkov/wrecker/httpw"
)
func main() {
message := []byte("example message")
upstreamListener, err := net.Listen("tcp", "127.0.0.1:")
if err != nil {
panic(err)
}
wreckerListener, err := net.Listen("tcp", "127.0.0.1:")
if err != nil {
panic(err)
}
upstreamURL := url.URL{
Scheme: "http",
Host: upstreamListener.Addr().String(),
}
blocker := func(req *http.Request) bool {
return req.URL.Path == "/forbidden"
}
opts := httpw.HandlerOpts{
Upstream: upstreamURL.String(),
Blockers: []httpw.Blocker{blocker},
}
wrecker, err := httpw.NewHandler(opts)
if err != nil {
panic(err)
}
var upstreamRouter http.ServeMux
upstreamRouter.HandleFunc(
"/api",
func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write(message)
},
)
upstreamRouter.HandleFunc(
"/forbidden",
func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write(message)
},
)
upstreamServer := &http.Server{
Handler: &upstreamRouter,
ReadTimeout: 5 * time.Second,
}
wreckerServer := &http.Server{
Handler: wrecker,
ReadTimeout: 5 * time.Second,
}
serversErrs := make(chan error)
defer close(serversErrs)
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := upstreamServer.Shutdown(ctx); err != nil {
fmt.Println("Upstream server shutdown error:", err)
}
if err := <-serversErrs; !errors.Is(err, http.ErrServerClosed) {
fmt.Println("Upstream server has terminated abnormally:", err)
}
if err := wreckerServer.Shutdown(ctx); err != nil {
fmt.Println("Wrecker server shutdown error:", err)
}
if err := <-serversErrs; !errors.Is(err, http.ErrServerClosed) {
fmt.Println("Wrecker server has terminated abnormally:", err)
}
}()
go func() {
serversErrs <- upstreamServer.Serve(upstreamListener)
}()
go func() {
serversErrs <- wreckerServer.Serve(wreckerListener)
}()
apiURL := url.URL{
Scheme: "http",
Host: wreckerListener.Addr().String(),
Path: "/api",
}
resp, err := http.Get(apiURL.String())
if err != nil {
panic(err)
}
defer resp.Body.Close()
received, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(
"Is message sent by server equal to message received by client:",
bytes.Equal(received, message),
)
forbiddenURL := url.URL{
Scheme: "http",
Host: wreckerListener.Addr().String(),
Path: "/forbidden",
}
resp, err = http.Get(forbiddenURL.String())
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println(
"Is the request to a forbidden path aborted:",
resp.StatusCode == http.StatusForbidden,
)
}
Output: Is message sent by server equal to message received by client: true Is the request to a forbidden path aborted: true
func NewHandler ¶ added in v0.6.0
func NewHandler(opts HandlerOpts) (*Handler, error)
Creates a new HTTP wrecker in the form of http.Handler.
func (*Handler) ServeHTTP ¶ added in v0.4.0
func (hdl *Handler) ServeHTTP(wrt http.ResponseWriter, req *http.Request)
Implements the http.Handler interface.
type HandlerOpts ¶ added in v0.6.0
type HandlerOpts struct {
// URL of upstream server. Required parameter
Upstream string
// List of the blockers
Blockers []Blocker
// List of the spoilers
Spoilers []Spoiler
// Transport for proxied requests
ProxyTransport http.RoundTripper
}
type Opts ¶ added in v0.4.0
type Opts struct {
// Network to listen, as in [net.Listen]. Required parameter
Network string
// Address to listen, as in [net.Listen]. Required parameter
Address string
// URL of upstream server. Required parameter
Upstream string
// List of the blockers
Blockers []Blocker
// List of the spoilers
Spoilers []Spoiler
// Transport for proxied requests
ProxyTransport http.RoundTripper
// Parameters of server. Addr and Handler fields are ignored
Server *http.Server
}
Options of the created instance of the HTTP wrecker with an HTTP server inside.
type Response ¶ added in v0.7.0
type Response struct {
// Body of a response
Body []byte
// Header maps of a response
Header http.Header
// Request that was sent to obtain this Response
Request *http.Request
// Status code of a response
StatusCode int
}
Data of response from upstream server.
type Spoiler ¶ added in v0.6.0
Decides whether to return an error or pass response to an sender.
If it returns true, an error will be sent to a sender without response headers and body.
Each spoiler receives a copy of response headers and body, also a copy of the request body.
type Wrecker ¶
type Wrecker struct {
// contains filtered or unexported fields
}
HTTP wrecker with an HTTP server inside.
Example ¶
package main
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"time"
"github.com/akramarenkov/wrecker/httpw"
)
func main() {
message := []byte("example message")
upstreamListener, err := net.Listen("tcp", "127.0.0.1:")
if err != nil {
panic(err)
}
var upstreamRouter http.ServeMux
upstreamRouter.HandleFunc(
"/api",
func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write(message)
},
)
upstreamRouter.HandleFunc(
"/forbidden",
func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write(message)
},
)
upstreamServer := &http.Server{
Handler: &upstreamRouter,
ReadTimeout: 5 * time.Second,
}
upstreamErr := make(chan error)
defer close(upstreamErr)
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := upstreamServer.Shutdown(ctx); err != nil {
fmt.Println("Upstream server shutdown error:", err)
}
if err := <-upstreamErr; !errors.Is(err, http.ErrServerClosed) {
fmt.Println("Upstream server has terminated abnormally:", err)
}
}()
go func() {
upstreamErr <- upstreamServer.Serve(upstreamListener)
}()
upstreamURL := url.URL{
Scheme: "http",
Host: upstreamListener.Addr().String(),
}
opts := httpw.Opts{
Network: "tcp",
Address: "127.0.0.1:",
Upstream: upstreamURL.String(),
Blockers: []httpw.Blocker{
func(req *http.Request) bool {
return req.URL.Path == "/forbidden"
},
},
}
wrecker, err := httpw.New(opts)
if err != nil {
panic(err)
}
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := wrecker.Shutdown(ctx); err != nil {
fmt.Println("Wrecker shutdown error:", err)
}
if err := <-wrecker.Err(); !errors.Is(err, http.ErrServerClosed) {
fmt.Println("Wrecker has terminated abnormally:", err)
}
}()
apiURL := url.URL{
Scheme: "http",
Host: wrecker.Addr().String(),
Path: "/api",
}
resp, err := http.Get(apiURL.String())
if err != nil {
panic(err)
}
defer resp.Body.Close()
received, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(
"Is message sent by server equal to message received by client:",
bytes.Equal(received, message),
)
forbiddenURL := url.URL{
Scheme: "http",
Host: wrecker.Addr().String(),
Path: "/forbidden",
}
resp, err = http.Get(forbiddenURL.String())
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println(
"Is the request to a forbidden path aborted:",
resp.StatusCode == http.StatusForbidden,
)
}
Output: Is message sent by server equal to message received by client: true Is the request to a forbidden path aborted: true
func (*Wrecker) Close ¶ added in v0.4.0
Immediately closes all listeners and connections of the wrecker server, simply calling http.Server.Close.
func (*Wrecker) Err ¶ added in v0.4.0
Returns a channel with errors occurring in the wrecker server.
When the wrecker server is terminated by the Wrecker.Shutdown or Wrecker.Close methods, http.ErrServerClosed is returned.
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
gatherer
Internal package with Gatherer that gathers response body, stores headers and response status code.
|
Internal package with Gatherer that gathers response body, stores headers and response status code. |
|
unhijacked
Internal package with wrapper over http.ResponseWriter that does not implement the http.Hijacker interface.
|
Internal package with wrapper over http.ResponseWriter that does not implement the http.Hijacker interface. |