Implement a basic HTTP proxy

This commit is contained in:
eyedeekay
2025-02-10 00:32:29 -05:00
parent 50fe3b1cf5
commit 729f9afcda
4 changed files with 74 additions and 6 deletions

2
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/go-i2p/go-i2ptunnel
go 1.23.5
require (
github.com/elazarl/goproxy v1.7.0
github.com/go-i2p/go-connfilter v0.0.0-20250205023438-0f2b889a80f6
github.com/go-i2p/go-forward v0.0.0-20250202052226-ee8a43dcb664
github.com/go-i2p/go-i2ptunnel-config v0.0.0-20250209030407-ba90db65df97
@@ -27,6 +28,7 @@ require (
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.29.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

7
go.sum
View File

@@ -7,6 +7,8 @@ github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbe
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/elazarl/goproxy v1.7.0 h1:EXv2nV4EjM60ZtsEVLYJG4oBXhDGutMKperpHsZ/v+0=
github.com/elazarl/goproxy v1.7.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
github.com/go-i2p/go-connfilter v0.0.0-20250205023438-0f2b889a80f6 h1:kOJH77NTMYNrG1+dX/T/ZO2RX1xp7ZA1eViqy4uZy5M=
github.com/go-i2p/go-connfilter v0.0.0-20250205023438-0f2b889a80f6/go.mod h1:qSZ3m4cEeyQc391rRXIGYEq9zakEPMJG9WfeQ49gByU=
github.com/go-i2p/go-forward v0.0.0-20250202052226-ee8a43dcb664 h1:j+RzLt8jZPT9CeiLFDWEXvJPb6Orn3UQgywTx8iL1O4=
@@ -42,8 +44,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0=
github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM=
github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 h1:d/Wr/Vl/wiJHc3AHYbYs5I3PucJvRuw3SvbmlIRf+oM=
@@ -91,6 +94,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@@ -23,13 +23,18 @@ Key features:
**/
import (
"context"
"net"
"net/http"
"strconv"
"sync"
httpinspector "github.com/go-i2p/go-connfilter/http"
i2pconv "github.com/go-i2p/go-i2ptunnel-config/lib"
i2ptunnel "github.com/go-i2p/go-i2ptunnel/lib/core"
"github.com/go-i2p/onramp"
"github.com/elazarl/goproxy"
)
var implementHTTPClient i2ptunnel.I2PTunnel = &HTTPClient{}
@@ -43,11 +48,19 @@ type HTTPClient struct {
i2ptunnel.I2PTunnelStatus
// The http filtering configuration
httpinspector.Config
// The proxy server
*goproxy.ProxyHttpServer
// The http server
*http.Server
// Channel for shutdown signaling
done chan struct{}
// Mutex for server operations
mu sync.Mutex
// Error history of the tunnel
Errors []i2ptunnel.I2PTunnelError
// Context for cleanup
ctx context.Context
cancel context.CancelFunc
}
func (h *HTTPClient) recordError(err error) {
@@ -80,7 +93,29 @@ func (h *HTTPClient) Name() string {
// Start the tunnel
func (h *HTTPClient) Start() error {
panic("unimplemented")
h.mu.Lock()
defer h.mu.Unlock()
if h.ProxyHttpServer != nil {
return nil // Already started
}
// Create context for managing goroutines
h.ctx, h.cancel = context.WithCancel(context.Background())
h.done = make(chan struct{})
proxy := goproxy.NewProxyHttpServer()
h.ProxyHttpServer = proxy
h.ProxyHttpServer.Tr.DialContext = h.DialContext
// set up local listener
listener, err := net.Listen("tcp", net.JoinHostPort(h.Interface, strconv.Itoa(h.Port)))
if err != nil {
return err
}
// set up httpinspector listener
listenerInspector := httpinspector.New(listener, h.Config)
h.Server = &http.Server{}
h.Server.Handler = h.ProxyHttpServer
return h.Server.Serve(listenerInspector)
}
// Get the tunnel's status
@@ -90,7 +125,21 @@ func (h *HTTPClient) Status() i2ptunnel.I2PTunnelStatus {
// Stop the tunnel
func (h *HTTPClient) Stop() error {
panic("unimplemented")
h.mu.Lock()
defer h.mu.Unlock()
if h.Server != nil {
h.I2PTunnelStatus = i2ptunnel.I2PTunnelStatusStopping
close(h.done)
if err := h.Server.Shutdown(h.ctx); err != nil {
h.recordError(err)
return err
}
h.cancel()
h.Server = nil
h.I2PTunnelStatus = i2ptunnel.I2PTunnelStatusStopped
}
return nil
}
// Get the tunnel's I2P target. Nil in the case of one-to-many clients like SOCKS5 and HTTP

View File

@@ -1,6 +1,8 @@
package httpclient
import (
"context"
"net"
"strings"
i2pconv "github.com/go-i2p/go-i2ptunnel-config/lib"
@@ -20,10 +22,20 @@ func NewHTTPClient(config i2pconv.TunnelConfig, samAddr string) (*HTTPClient, er
return nil, err
}
garlic.ServiceKeys = keys
return &HTTPClient{
h := &HTTPClient{
TunnelConfig: config,
Garlic: garlic,
I2PTunnelStatus: i2ptunnel.I2PTunnelStatusStopped,
done: make(chan struct{}),
}, nil
}
return h, nil
}
func (h *HTTPClient) DialContext(ctx context.Context, network, addr string) (c net.Conn, err error) {
return h.Garlic.DialContext(ctx, network, addr)
}
func (h *HTTPClient) Dial(network, addr string) (c net.Conn, err error) {
return h.Garlic.Dial(network, addr)
}