aboutsummaryrefslogtreecommitdiff
path: root/main.go
blob: 7bea7e5992457511fce3cbad75636bd2dce8e020 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package main

import (
	"flag"
	"log"
	"math/rand"
	"net/http"
	"net/http/httputil"
	"net/url"
)

func main() {
	host := flag.String("host", "localhost:6969", "host to bind to")
	upstreamFlag := flag.String("upstream", "", "upstream host to proxy to (required)")
	only404 := flag.Bool("only404", false, "only change status code to 404")
	flag.Parse()

	if *upstreamFlag == "" {
		log.Fatal("--upstream is required")
	}
	upstream, err := url.Parse(*upstreamFlag)
	if err != nil {
		log.Fatal(err)
	}
	if upstream.Host == "" {
		log.Fatal("upstream host is required (you may be missing the protocol, ie http://)")
	}
	if upstream.Scheme == "" {
		upstream.Scheme = "http"
	}

	statusCodes := [...]int{
		http.StatusContinue,
		http.StatusSwitchingProtocols,
		http.StatusProcessing,
		http.StatusEarlyHints,
		http.StatusOK,
		http.StatusCreated,
		http.StatusAccepted,
		http.StatusNonAuthoritativeInfo,
		http.StatusNoContent,
		http.StatusResetContent,
		http.StatusPartialContent,
		http.StatusMultiStatus,
		http.StatusAlreadyReported,
		http.StatusIMUsed,
		http.StatusMultipleChoices,
		http.StatusMovedPermanently,
		http.StatusFound,
		http.StatusSeeOther,
		http.StatusNotModified,
		http.StatusUseProxy,
		http.StatusTemporaryRedirect,
		http.StatusPermanentRedirect,
		http.StatusBadRequest,
		http.StatusUnauthorized,
		http.StatusPaymentRequired,
		http.StatusForbidden,
		http.StatusNotFound,
		http.StatusMethodNotAllowed,
		http.StatusNotAcceptable,
		http.StatusProxyAuthRequired,
		http.StatusRequestTimeout,
		http.StatusConflict,
		http.StatusGone,
		http.StatusLengthRequired,
		http.StatusPreconditionFailed,
		http.StatusRequestEntityTooLarge,
		http.StatusRequestURITooLong,
		http.StatusUnsupportedMediaType,
		http.StatusRequestedRangeNotSatisfiable,
		http.StatusExpectationFailed,
		http.StatusTeapot,
		http.StatusMisdirectedRequest,
		http.StatusUnprocessableEntity,
		http.StatusLocked,
		http.StatusFailedDependency,
		http.StatusTooEarly,
		http.StatusUpgradeRequired,
		http.StatusPreconditionRequired,
		http.StatusTooManyRequests,
		http.StatusRequestHeaderFieldsTooLarge,
		http.StatusUnavailableForLegalReasons,
		http.StatusInternalServerError,
		http.StatusNotImplemented,
		http.StatusBadGateway,
		http.StatusServiceUnavailable,
		http.StatusGatewayTimeout,
		http.StatusHTTPVersionNotSupported,
		http.StatusVariantAlsoNegotiates,
		http.StatusInsufficientStorage,
		http.StatusLoopDetected,
		http.StatusNotExtended,
		http.StatusNetworkAuthenticationRequired,
	}

	proxy := &httputil.ReverseProxy{
		Rewrite: func(req *httputil.ProxyRequest) {
			req.SetURL(upstream)
		},
	}

	proxy.ModifyResponse = func(resp *http.Response) error {
		originStatus := resp.StatusCode
		if *only404 {
			resp.StatusCode = http.StatusNotFound
		} else {
			resp.StatusCode = statusCodes[rand.Intn(len(statusCodes))]
		}
		log.Printf("status code %d (%s) changed to %d (%s) for path %s", originStatus, http.StatusText(originStatus), resp.StatusCode, http.StatusText(resp.StatusCode), resp.Request.URL.Path)
		return nil
	}

	log.Println("listening on", *host)
	log.Println("proxying requests to", upstream.String())
	log.Fatal(http.ListenAndServe(*host, proxy))
}