-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 08d10db
Showing
9 changed files
with
322 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dist/ | ||
cert.pem |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# This is an example .goreleaser.yml file with some sensible defaults. | ||
# Make sure to check the documentation at https://goreleaser.com | ||
before: | ||
hooks: | ||
# You may remove this if you don't use go modules. | ||
- go mod tidy | ||
# you may remove this if you don't need go generate | ||
- go generate ./... | ||
builds: | ||
- env: | ||
- CGO_ENABLED=0 | ||
goos: | ||
- linux | ||
- windows | ||
- darwin | ||
|
||
archives: | ||
- format: tar.gz | ||
# this name template makes the OS and Arch compatible with the results of uname. | ||
name_template: >- | ||
{{ .ProjectName }}_ | ||
{{- title .Os }}_ | ||
{{- if eq .Arch "amd64" }}x86_64 | ||
{{- else if eq .Arch "386" }}i386 | ||
{{- else }}{{ .Arch }}{{ end }} | ||
{{- if .Arm }}v{{ .Arm }}{{ end }} | ||
# use zip for windows archives | ||
format_overrides: | ||
- goos: windows | ||
format: zip | ||
checksum: | ||
name_template: 'checksums.txt' | ||
snapshot: | ||
name_template: "{{ incpatch .Version }}-next" | ||
changelog: | ||
sort: asc | ||
filters: | ||
exclude: | ||
- '^docs:' | ||
- '^test:' | ||
|
||
# The lines beneath this are called `modelines`. See `:help modeline` | ||
# Feel free to remove those if you don't want/use them. | ||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json | ||
# vim: set ts=2 sw=2 tw=0 fo=cnqoj |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
VERSION = $(shell git describe --tags --always --dirty) | ||
LDFLAGS=-ldflags "-X main.version=$(VERSION)" | ||
OSARCH=$(shell go env GOHOSTOS)-$(shell go env GOHOSTARCH) | ||
|
||
SIMPLEPROXYTOOL=\ | ||
go-hhtp-proxy-darwin-amd64 \ | ||
go-http-proxy-linux-amd64 \ | ||
go-http-proxy-windows-amd64.exe | ||
|
||
proxytool: go-http-proxy-$(OSARCH) | ||
|
||
$(SIMPLEPROXYTOOL): | ||
GOOS=$(word 2,$(subst -, ,$@)) GOARCH=$(word 3,$(subst -, ,$(subst .exe,,$@))) go build $(LDFLAGS) -o $@ ./$< | ||
|
||
%-$(VERSION).zip: %.exe | ||
rm -f $@ | ||
zip $@ $< | ||
|
||
%-$(VERSION).zip: % | ||
rm -f $@ | ||
zip $@ $< | ||
|
||
clean: | ||
rm -f go-http-proxy-* | ||
|
||
|
||
release: | ||
$(foreach bin,$(SIMPLEPROXYTOOL),$(subst .exe,,$(bin))-$(VERSION).zip) | ||
|
||
rm: | ||
$(foreach bin,$(SIMPLEPROXYTOOL),$(subst .exe,,$(bin))-$(VERSION).zip) | ||
|
||
|
||
build: proxytool $(SIMPLEPROXYTOOL) clean rm | ||
|
||
.PHONY: build | ||
all: build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Go HTTP Proxy | ||
|
||
Go HTTP Proxy is a simple proxy server written in Go. It provides the ability to proxy HTTP and HTTPS traffic leveraging the HTTP CONNECT Method. | ||
|
||
This tool is useful when you need to debug or inspect network requests, or to test your client code in presence of proxies. | ||
|
||
## Features | ||
|
||
- Supports both HTTP and HTTPS. Forwards HTTPS traffic using the HTTP CONNECT method. | ||
- Provides an option to increase verbosity to log detailed information about the traffic. | ||
- Supports custom TLS certificates (`-cert` and `-key` flags). | ||
|
||
## Limitations | ||
|
||
- This tool is primarily for debugging and development purposes. It might not be suited for production-level traffic or security demands. | ||
- The proxy does not modify or interfere with the content of the traffic, it merely passes it along. This means it will not remove certain headers or modify request/response bodies. | ||
|
||
## Usage | ||
|
||
First, download the latest binary for your platform from the GitHub releases page. Then you can run the proxy with: | ||
|
||
By default, the proxy will start an HTTP server on port 8888. By default, the proxy will start an HTTP server on port 8888. | ||
|
||
```bash | ||
./go-http-proxy --help | ||
-cert string | ||
Path to TLS certificate. If not provided and https is set, a self-signed certificate will be generated and saved to cert.pem in the current directory. | ||
-https | ||
Use HTTPS for proxy | ||
-key string | ||
Path to TLS key. If not provided and https is set, a self-signed certificate will be generated. | ||
-port string | ||
Port to listen on (default "8888") | ||
-v Increase verbosity | ||
``` | ||
|
||
To start an HTTPS server, use the `-https` flag and optionally provide paths to your certificate and key files. | ||
|
||
If `key` and `cert` paths are not passed, the program will generate a self-signed certificate and key with a CNAME and DNS SAN of `localhost` for easy DNS resolution during local tests. Then, you'd either have to add this cert to the `ca-certificates` store on your machine, or avoid verifying the cert in the client. | ||
|
||
```bash | ||
./go-http-proxy -https | ||
``` |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/go-http-proxy | ||
|
||
go 1.20 | ||
|
||
require github.com/hauke96/sigolo v1.0.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
github.com/hauke96/sigolo v1.0.1 h1:o4E1Qv5BPqHRxXfzpakRdIygTUUzpslqkwagaCbr0rg= | ||
github.com/hauke96/sigolo v1.0.1/go.mod h1:HjmtTXJhUyF8xPUnNt9i6oUkx7+Py5NZZiLc2V7khxg= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"crypto/x509/pkix" | ||
"encoding/pem" | ||
"flag" | ||
"io" | ||
"log" | ||
"math/big" | ||
"net" | ||
"net/http" | ||
"os" | ||
"time" | ||
) | ||
|
||
var ( | ||
verbose = flag.Bool("v", false, "Increase verbosity") | ||
port = flag.String("port", "8888", "Port to listen on") | ||
https = flag.Bool("https", false, "Use HTTPS for proxy") | ||
certificate = flag.String("cert", "", "Path to TLS certificate. If not provided and https is set, a self-signed certificate will be generated and saved to cert.pem in the current directory.") | ||
key = flag.String("key", "", "Path to TLS key. If not provided and https is set, a self-signed certificate will be generated.") | ||
) | ||
|
||
func verbosePrintln(logger *log.Logger, msg string) { | ||
if *verbose { | ||
logger.Println(msg) | ||
} | ||
} | ||
|
||
func handleTunneling(w http.ResponseWriter, r *http.Request) { | ||
destConn, err := net.DialTimeout("tcp", r.Host, 10*time.Second) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusServiceUnavailable) | ||
return | ||
} | ||
|
||
verbosePrintln(log.New(io.Discard, "", log.LstdFlags), "Handling CONNECT request for "+r.Host) | ||
|
||
w.WriteHeader(http.StatusOK) | ||
hijacker, ok := w.(http.Hijacker) | ||
if !ok { | ||
http.Error(w, "Hijacking not supported", http.StatusInternalServerError) | ||
return | ||
} | ||
clientConn, _, err := hijacker.Hijack() | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusServiceUnavailable) | ||
} | ||
go transfer(destConn, clientConn) | ||
go transfer(clientConn, destConn) | ||
} | ||
|
||
func transfer(destination io.WriteCloser, source io.ReadCloser) { | ||
defer destination.Close() | ||
defer source.Close() | ||
_, err := io.Copy(destination, source) | ||
if err != nil && *verbose { | ||
log.Println("Error while transferring data:", err) | ||
} | ||
} | ||
|
||
func handleHTTP(w http.ResponseWriter, r *http.Request) { | ||
verbosePrintln(log.New(io.Discard, "", log.LstdFlags), "Handling HTTP request for "+r.URL.String()) | ||
resp, err := http.DefaultTransport.RoundTrip(r) | ||
if err != nil { | ||
http.Error(w, err.Error(), http.StatusServiceUnavailable) | ||
return | ||
} | ||
defer resp.Body.Close() | ||
copyHeader(w.Header(), resp.Header) | ||
w.WriteHeader(resp.StatusCode) | ||
_, err = io.Copy(w, resp.Body) | ||
if err != nil && *verbose { | ||
log.Println("Error while copying response body:", err) | ||
} | ||
} | ||
|
||
func copyHeader(dst, src http.Header) { | ||
for k, vv := range src { | ||
for _, v := range vv { | ||
dst.Add(k, v) | ||
} | ||
} | ||
} | ||
|
||
|
||
func main() { | ||
flag.Parse() | ||
|
||
server := &http.Server{ | ||
Addr: ":" + *port, | ||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
if r.Method == http.MethodConnect { | ||
handleTunneling(w, r) | ||
} else { | ||
handleHTTP(w, r) | ||
} | ||
}), | ||
} | ||
|
||
if *https { | ||
var tlsConfig *tls.Config | ||
|
||
if *certificate != "" && *key != "" { | ||
cert, err := tls.LoadX509KeyPair(*certificate, *key) | ||
if err != nil { | ||
log.Fatalf("Failed to load certificate and key: %v", err) | ||
} | ||
|
||
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}} | ||
} else { | ||
// Generate a new self-signed certificate. | ||
priv, _ := rsa.GenerateKey(rand.Reader, 2048) | ||
template := x509.Certificate{ | ||
SerialNumber: big.NewInt(1), | ||
Subject: pkix.Name{ | ||
CommonName: "localhost", | ||
}, | ||
DNSNames : []string{"localhost"}, | ||
NotBefore: time.Now(), | ||
NotAfter: time.Now().Add(time.Hour * 24 * 365), // Valid for one year. | ||
BasicConstraintsValid: true, | ||
} | ||
|
||
certDER, _ := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) | ||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) | ||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) | ||
|
||
// Write the certificate and key to files. | ||
if err := os.WriteFile("cert.pem", certPEM, 0644); err != nil { | ||
log.Fatalf("Failed to write certificate: %v", err) | ||
} | ||
|
||
cert, _ := tls.X509KeyPair(certPEM, keyPEM) | ||
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}} | ||
} | ||
|
||
server.TLSConfig = tlsConfig | ||
|
||
log.Println("Starting HTTPS server on", server.Addr) | ||
log.Fatal(server.ListenAndServeTLS("", "")) | ||
} else { | ||
log.Println("Starting HTTP server on", server.Addr) | ||
log.Fatal(server.ListenAndServe()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"log" | ||
"net/http" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestCopyHeader(t *testing.T) { | ||
dst := make(http.Header) | ||
src := make(http.Header) | ||
|
||
src.Add("Content-Type", "text/html") | ||
src.Add("Content-Length", "123") | ||
|
||
copyHeader(dst, src) | ||
|
||
if dst.Get("Content-Type") != "text/html" || dst.Get("Content-Length") != "123" { | ||
t.Fatal("copyHeader did not correctly copy headers from src to dst") | ||
} | ||
} | ||
|
||
func TestVerbosePrintln(t *testing.T) { | ||
var buf bytes.Buffer | ||
logger := log.New(&buf, "", log.LstdFlags) | ||
|
||
*verbose = true | ||
|
||
verbosePrintln(logger, "Test message") | ||
|
||
if !strings.Contains(buf.String(), "Test message") { | ||
t.Fatal("verbosePrintln did not correctly output message to log") | ||
} | ||
|
||
*verbose = false | ||
} |