mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 03:07:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			274 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			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.Pretty())
 | 
						|
	// 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
 | 
						|
	}
 | 
						|
 | 
						|
}
 |