ceremonyclient/go-libp2p/examples/http-proxy/proxy.go
Cassandra Heart 9e1d07d1a0
Squashed commit of the following:
commit d05a4d5f688dbd09900ceccdcc5f8109dd0671c2
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Wed Jun 12 00:50:16 2024 -0500

    merge

commit db57ff1f191f9dedc87ca77da1c71244dd2325bd
Merge: 7b43494 2e3279a
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Wed Jun 12 00:49:32 2024 -0500

    Merge branch 'v1.4.19' into not-release

commit 7b43494246e28152b46710c8c9821429d4231f7e
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Wed Jun 12 00:49:13 2024 -0500

    pull from release site

commit 2e3279ac930ac630d9ca2b26cf4f3232abe79823
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 06:31:02 2024 -0500

    remove binaries

commit 2768a8778b3860c5736352c8aa950e3496a46e56
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 06:24:44 2024 -0500

    signatory #8 added

commit 6a944628575ccadd17c9f9f4a11a49c032fa0c1d
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 06:08:26 2024 -0500

    signatory #6 added

commit b401fb65e5ddbe0340fe85aab1182d6120a4e161
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 05:39:03 2024 -0500

    signatory #3 added

commit e5700913c0f6246fb607bcd3e219c257cb4a80e9
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 05:31:24 2024 -0500

    signatory #15 added

commit 9b1da6c03e517135bfcd59226f900adab42f3687
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 05:23:02 2024 -0500

    signatories #4 and #16 added

commit 9c97d1bbc399a070ac21b35ed9b1af127fa4c7ea
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 04:59:27 2024 -0500

    signatories #1 and #2 added

commit 905e3f78a8121eade1c331ae910ed25dd534f27a
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 04:40:32 2024 -0500

    build, binaries, signatory #13

commit ebfb57bc29d9ed1fb25d0dd100e38709354b3d84
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Sat Jun 8 03:38:53 2024 -0500

    tests pass, let's go

commit 5d4612c6c624c3dc18f9a5657936034ac9d9d8dd
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Fri Jun 7 03:53:15 2024 -0500

    update version info + readme

commit 6b0dd69e930d01b98acb8d7b56bb5d572e1a4324
Merge: 090d630 859221b
Author: Cassie Heart <cassandra@quilibrium.com>
Date:   Fri Jun 7 08:25:16 2024 +0000

    Merge branch 'feat-data-worker-direct-config' into 'v1.4.19'

    feat: support detached configuration mode for data workers

    See merge request quilibrium/ceremonyclient!7

commit 859221b179ab2631fa474be2494259afaaa6bd51
Author: Cassandra Heart <cassandra@quilibrium.com>
Date:   Fri Jun 7 03:24:22 2024 -0500

    feat: support detached configuration mode for data workers

commit 090d6301d44a2aa88886120783cd5a6e537aa6d1
Merge: 62db30c d1cae94
Author: Cassie Heart <cassandra@quilibrium.com>
Date:   Fri Jun 7 06:25:43 2024 +0000

    Merge branch 'feat-go-1-22' into 'v1.4.19'

    feat: go 1.22 support

    See merge request quilibrium/ceremonyclient!6

commit d1cae942165f4871f8051e266722c0ca717780cb
Author: Cassie Heart <cassandra@quilibrium.com>
Date:   Fri Jun 7 06:25:43 2024 +0000

    feat: go 1.22 support

commit 62db30c54f9258c92113c6664ce817670a339083
Merge: 0cbc0d0 f36cea3
Author: Cassie Heart <cassandra@quilibrium.com>
Date:   Fri Jun 7 03:52:17 2024 +0000

    Merge branch 'rust-vdf' into 'v1.4.19'

    Switch to Rust VDF

    See merge request quilibrium/ceremonyclient!2

commit f36cea323bfe5e56f519f59f9a0cce35f0f8b6ab
Author: Agost Biro <agostbiro@gmail.com>
Date:   Fri Jun 7 03:52:16 2024 +0000

    Switch to Rust VDF

commit 0cbc0d0d319713e20ca7f48588c4153833e58429
Merge: 986e12c 0c48a83
Author: Cassie Heart <cassandra@quilibrium.com>
Date:   Fri Jun 7 00:50:15 2024 +0000

    Merge branch 'release_image' into 'v1.4.19'

    create docker image based on release binaries

    See merge request quilibrium/ceremonyclient!4

commit 0c48a83bb5751abf7c8c0ff188bfdc2130631e78
Author: Marius Scurtescu <marius.scurtescu@gmail.com>
Date:   Fri Jun 7 00:50:15 2024 +0000

    create docker image based on release binaries

commit 986e12c88bb2d2b412b59f7db1ae39f828304dbe
Merge: 58456c1 a3ef5c6
Author: Cassie Heart <cassandra@quilibrium.com>
Date:   Wed Jun 5 22:01:37 2024 +0000

    Merge branch 'signature_check' into 'v1.4.19'

    add default of signature check from QUILIBRIUM_SIGNATURE_CHECK env var

    See merge request quilibrium/ceremonyclient!1

commit a3ef5c6af2d5de107d01c45a62d7324165e2551b
Author: Marius Scurtescu <marius.scurtescu@gmail.com>
Date:   Wed Jun 5 14:37:50 2024 -0700

    add default of signature check from QUILIBRIUM_SIGNATURE_CHECK env var
2024-06-12 00:51:16 -05:00

274 lines
8.5 KiB
Go

package main
import (
"bufio"
"context"
"flag"
"fmt"
"io"
"log"
"net/http"
"strings"
// We need to import libp2p's libraries that we use in this project.
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr/net"
)
// Protocol defines the libp2p protocol that we will use for the libp2p proxy
// service that we are going to provide. This will tag the streams used for
// this service. Streams are multiplexed and their protocol tag helps
// libp2p handle them to the right handler functions.
const Protocol = "/proxy-example/0.0.1"
// makeRandomHost creates a libp2p host with a randomly generated identity.
// This step is described in depth in other tutorials.
func makeRandomHost(port int) host.Host {
host, err := libp2p.New(libp2p.ListenAddrStrings(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", port)))
if err != nil {
log.Fatalln(err)
}
return host
}
// ProxyService provides HTTP proxying on top of libp2p by launching an
// HTTP server which tunnels the requests to a destination peer running
// ProxyService too.
type ProxyService struct {
host host.Host
dest peer.ID
proxyAddr ma.Multiaddr
}
// NewProxyService attaches a proxy service to the given libp2p Host.
// The proxyAddr parameter specifies the address on which the
// HTTP proxy server listens. The dest parameter specifies the peer
// ID of the remote peer in charge of performing the HTTP requests.
//
// ProxyAddr/dest may be nil/"" it is not necessary that this host
// provides a listening HTTP server (and instead its only function is to
// perform the proxied http requests it receives from a different peer.
//
// The addresses for the dest peer should be part of the host's peerstore.
func NewProxyService(h host.Host, proxyAddr ma.Multiaddr, dest peer.ID) *ProxyService {
// We let our host know that it needs to handle streams tagged with the
// protocol id that we have defined, and then handle them to
// our own streamHandling function.
h.SetStreamHandler(Protocol, streamHandler)
fmt.Println("Proxy server is ready")
fmt.Println("libp2p-peer addresses:")
for _, a := range h.Addrs() {
fmt.Printf("%s/ipfs/%s\n", a, h.ID())
}
return &ProxyService{
host: h,
dest: dest,
proxyAddr: proxyAddr,
}
}
// streamHandler is our function to handle any libp2p-net streams that belong
// to our protocol. The streams should contain an HTTP request which we need
// to parse, make on behalf of the original node, and then write the response
// on the stream, before closing it.
func streamHandler(stream network.Stream) {
// Remember to close the stream when we are done.
defer stream.Close()
// Create a new buffered reader, as ReadRequest needs one.
// The buffered reader reads from our stream, on which we
// have sent the HTTP request (see ServeHTTP())
buf := bufio.NewReader(stream)
// Read the HTTP request from the buffer
req, err := http.ReadRequest(buf)
if err != nil {
stream.Reset()
log.Println(err)
return
}
defer req.Body.Close()
// We need to reset these fields in the request
// URL as they are not maintained.
req.URL.Scheme = "http"
hp := strings.Split(req.Host, ":")
if len(hp) > 1 && hp[1] == "443" {
req.URL.Scheme = "https"
} else {
req.URL.Scheme = "http"
}
req.URL.Host = req.Host
outreq := new(http.Request)
*outreq = *req
// We now make the request
fmt.Printf("Making request to %s\n", req.URL)
resp, err := http.DefaultTransport.RoundTrip(outreq)
if err != nil {
stream.Reset()
log.Println(err)
return
}
// resp.Write writes whatever response we obtained for our
// request back to the stream.
resp.Write(stream)
}
// Serve listens on the ProxyService's proxy address. This effectively
// allows to set the listening address as http proxy.
func (p *ProxyService) Serve() {
_, serveArgs, _ := manet.DialArgs(p.proxyAddr)
fmt.Println("proxy listening on ", serveArgs)
if p.dest != "" {
http.ListenAndServe(serveArgs, p)
}
}
// ServeHTTP implements the http.Handler interface. WARNING: This is the
// simplest approach to a proxy. Therefore, we do not do any of the things
// that should be done when implementing a reverse proxy (like handling
// headers correctly). For how to do it properly, see:
// https://golang.org/src/net/http/httputil/reverseproxy.go?s=3845:3920#L121
//
// ServeHTTP opens a stream to the dest peer for every HTTP request.
// Streams are multiplexed over single connections so, unlike connections
// themselves, they are cheap to create and dispose of.
func (p *ProxyService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Printf("proxying request for %s to peer %s\n", r.URL, p.dest)
// We need to send the request to the remote libp2p peer, so
// we open a stream to it
stream, err := p.host.NewStream(context.Background(), p.dest, Protocol)
// If an error happens, we write an error for response.
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer stream.Close()
// r.Write() writes the HTTP request to the stream.
err = r.Write(stream)
if err != nil {
stream.Reset()
log.Println(err)
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
// Now we read the response that was sent from the dest
// peer
buf := bufio.NewReader(stream)
resp, err := http.ReadResponse(buf, r)
if err != nil {
stream.Reset()
log.Println(err)
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
// Copy any headers
for k, v := range resp.Header {
for _, s := range v {
w.Header().Add(k, s)
}
}
// Write response status and headers
w.WriteHeader(resp.StatusCode)
// Finally copy the body
io.Copy(w, resp.Body)
resp.Body.Close()
}
// addAddrToPeerstore parses a peer multiaddress and adds
// it to the given host's peerstore, so it knows how to
// contact it. It returns the peer ID of the remote peer.
func addAddrToPeerstore(h host.Host, addr string) peer.ID {
// The following code extracts target's the peer ID from the
// given multiaddress
ipfsaddr, err := ma.NewMultiaddr(addr)
if err != nil {
log.Fatalln(err)
}
pid, err := ipfsaddr.ValueForProtocol(ma.P_IPFS)
if err != nil {
log.Fatalln(err)
}
peerid, err := peer.Decode(pid)
if err != nil {
log.Fatalln(err)
}
// Decapsulate the /ipfs/<peerID> part from the target
// /ip4/<a.b.c.d>/ipfs/<peer> becomes /ip4/<a.b.c.d>
targetPeerAddr, _ := ma.NewMultiaddr(fmt.Sprintf("/ipfs/%s", peerid))
targetAddr := ipfsaddr.Decapsulate(targetPeerAddr)
// We have a peer ID and a targetAddr, so we add
// it to the peerstore so LibP2P knows how to contact it
h.Peerstore().AddAddr(peerid, targetAddr, peerstore.PermanentAddrTTL)
return peerid
}
const help = `
This example creates a simple HTTP Proxy using two libp2p peers. The first peer
provides an HTTP server locally which tunnels the HTTP requests with libp2p
to a remote peer. The remote peer performs the requests and
send the sends the response back.
Usage: Start remote peer first with: ./proxy
Then start the local peer with: ./proxy -d <remote-peer-multiaddress>
Then you can do something like: curl -x "localhost:9900" "http://ipfs.io".
This proxies sends the request through the local peer, which proxies it to
the remote peer, which makes it and sends the response back.`
func main() {
flag.Usage = func() {
fmt.Println(help)
flag.PrintDefaults()
}
// Parse some flags
destPeer := flag.String("d", "", "destination peer address")
port := flag.Int("p", 9900, "proxy port")
p2pport := flag.Int("l", 12000, "libp2p listen port")
flag.Parse()
// If we have a destination peer we will start a local server
if *destPeer != "" {
// We use p2pport+1 in order to not collide if the user
// is running the remote peer locally on that port
host := makeRandomHost(*p2pport + 1)
// Make sure our host knows how to reach destPeer
destPeerID := addAddrToPeerstore(host, *destPeer)
proxyAddr, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", *port))
if err != nil {
log.Fatalln(err)
}
// Create the proxy service and start the http server
proxy := NewProxyService(host, proxyAddr, destPeerID)
proxy.Serve() // serve hangs forever
} else {
host := makeRandomHost(*p2pport)
// In this case we only need to make sure our host
// knows how to handle incoming proxied requests from
// another peer.
_ = NewProxyService(host, nil, "")
<-make(chan struct{}) // hang forever
}
}