@cryptotaxi247 / kubo / commits / 90021c16d

[http_proxy_over_p2p]

This implements an http-proxy over p2p-streams. License: MIT Signed-off-by: Chris Boddy <chris@boddy.im>

Chris committed Aug 22, 2018 at 18:56 UTC 90021c16d6bde306564ff28fa13f01ec1ba3bae4
5 files changed +121 -7
cmd/ipfs/daemon.go
+1
@@ -461,6 +461,7 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
461 corehttp.MutexFractionOption("/debug/pprof-mutex/"),
462 corehttp.MetricsScrapingOption("/debug/metrics/prometheus"),
463 corehttp.LogOption(),
464 + corehttp.ProxyOption(),
465 }
466
467 if len(cfg.Gateway.RootRedirect) > 0 {
p2p/local.go
+1 -1
@@ -55,7 +55,7 @@ func (l *localListener) dial(ctx context.Context) (net.Stream, error) {
55 cctx, cancel := context.WithTimeout(ctx, time.Second*30) //TODO: configurable?
56 defer cancel()
57
58 - return l.p2p.peerHost.NewStream(cctx, l.peer, l.proto)
58 + return l.p2p.PeerHost.NewStream(cctx, l.peer, l.proto)
59 }
60
61 func (l *localListener) acceptConns() {
p2p/p2p.go
+6 -6
@@ -16,23 +16,23 @@ type P2P struct {
16 Streams *StreamRegistry
17
18 identity peer.ID
19 - peerHost p2phost.Host
19 + PeerHost p2phost.Host
20 peerstore pstore.Peerstore
21 }
22
23 // NewP2P creates new P2P struct
24 -func NewP2P(identity peer.ID, peerHost p2phost.Host, peerstore pstore.Peerstore) *P2P {
24 +func NewP2P(identity peer.ID, PeerHost p2phost.Host, peerstore pstore.Peerstore) *P2P {
25 return &P2P{
26 identity: identity,
27 - peerHost: peerHost,
27 + PeerHost: PeerHost,
28 peerstore: peerstore,
29
30 ListenersLocal: newListenersLocal(),
31 - ListenersP2P: newListenersP2P(peerHost),
31 + ListenersP2P: newListenersP2P(PeerHost),
32
33 Streams: &StreamRegistry{
34 Streams: map[uint64]*Stream{},
35 - ConnManager: peerHost.ConnManager(),
35 + ConnManager: PeerHost.ConnManager(),
36 conns: map[peer.ID]int{},
37 },
38 }
@@ -41,7 +41,7 @@ func NewP2P(identity peer.ID, peerHost p2phost.Host, peerstore pstore.Peerstore)
41 // CheckProtoExists checks whether a proto handler is registered to
42 // mux handler
43 func (p2p *P2P) CheckProtoExists(proto string) bool {
44 - protos := p2p.peerHost.Mux().Protocols()
44 + protos := p2p.PeerHost.Mux().Protocols()
45
46 for _, p := range protos {
47 if p != proto {
p2p/proxy.go new
+92
@@ -0,0 +1,92 @@
1 +package p2p
2 +
3 +import (
4 + "bufio"
5 + "fmt"
6 + "net"
7 + "net/http"
8 + "strings"
9 +
10 + core "github.com/ipfs/go-ipfs/core"
11 + protocol "gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol"
12 + peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer"
13 +)
14 +
15 +func ProxyOption() ServeOption {
16 + return func(ipfsNode *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
17 + mux.HandleFunc("/proxy/", func(w http.ResponseWriter, request *http.Request) {
18 + // parse request
19 + parsedRequest, err := parseRequest(request)
20 + if err != nil {
21 + handleError(w, "Failed to parse request", err, 400)
22 + return
23 + }
24 +
25 + // open connect to peer
26 + stream, err := ipfsNode.P2P.PeerHost.NewStream(ipfsNode.Context(), parsedRequest.target, protocol.ID("/x/"+parsedRequest.name))
27 + if err != nil {
28 + msg := fmt.Sprintf("Failed to open stream '%v' to target peer '%v'", parsedRequest.name, parsedRequest.target)
29 + handleError(w, msg, err, 500)
30 + return
31 + }
32 +
33 + // send request to peer
34 + proxyReq, err := http.NewRequest(request.Method, parsedRequest.httpPath, request.Body)
35 +
36 + if err != nil {
37 + handleError(w, "Failed to format proxy request", err, 500)
38 + return
39 + }
40 +
41 + proxyReq.Write(stream)
42 +
43 + s := bufio.NewReader(stream)
44 + proxyResponse, err := http.ReadResponse(s, proxyReq)
45 + defer func() { proxyResponse.Body.Close() }()
46 + if err != nil {
47 + msg := fmt.Sprintf("Failed to send request to stream '%v' to peer '%v'", parsedRequest.name, parsedRequest.target)
48 + handleError(w, msg, err, 500)
49 + return
50 + }
51 + // send client response
52 + proxyResponse.Write(w)
53 + })
54 + return mux, nil
55 + }
56 +}
57 +
58 +type proxyRequest struct {
59 + target peer.ID
60 + name string
61 + httpPath string // path to send to the proxy-host
62 +}
63 +
64 +// from the url path parse the peer-ID, name and http path
65 +// /http/$peer_id/$name/$http_path
66 +func parseRequest(request *http.Request) (*proxyRequest, error) {
67 + path := request.URL.Path
68 +
69 + split := strings.SplitN(path, "/", 6)
70 + if split[2] != "http" {
71 + return nil, fmt.Errorf("Invalid proxy request protocol '%s'", path)
72 + }
73 +
74 + if len(split) < 6 {
75 + return nil, fmt.Errorf("Invalid request path '%s'", path)
76 + }
77 +
78 + peerID, err := peer.IDB58Decode(split[3])
79 +
80 + if err != nil {
81 + return nil, err
82 + }
83 +
84 + return &proxyRequest{peerID, split[4], split[5]}, nil
85 +}
86 +
87 +// log error and send response to client
88 +func handleError(w http.ResponseWriter, msg string, err error, code int) {
89 + w.WriteHeader(code)
90 + fmt.Fprintf(w, "%s: %s\n", msg, err)
91 + log.Warningf("server error: %s: %s", err)
92 +}
p2p/proxy_test.go new
+21
@@ -0,0 +1,21 @@
1 +package p2p
2 +
3 +import (
4 + "github.com/ipfs/go-ipfs/thirdparty/assert"
5 + "net/http"
6 + "strings"
7 + "testing"
8 +)
9 +
10 +func TestParseRequest(t *testing.T) {
11 + url := "http://localhost:5001/proxy/http/QmT8JtU54XSmC38xSb1XHFSMm775VuTeajg7LWWWTAwzxT/test-name/path/to/index.txt"
12 + req, _ := http.NewRequest("GET", url, strings.NewReader(""))
13 +
14 + parsed, err := parseRequest(req)
15 + if err != nil {
16 + t.Error(err)
17 + }
18 + assert.True(parsed.httpPath == "path/to/index.txt", t, "proxy request path")
19 + assert.True(parsed.name == "test-name", t, "proxy request name")
20 + assert.True(parsed.target.Pretty() == "QmT8JtU54XSmC38xSb1XHFSMm775VuTeajg7LWWWTAwzxT", t, "proxy request peer-id")
21 +}