-
Notifications
You must be signed in to change notification settings - Fork 1
/
preload_http.go
102 lines (85 loc) · 2.88 KB
/
preload_http.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package main
import (
"fmt"
"io"
"net"
"net/http"
"strings"
pmgr "github.com/ipfs-shipyard/go-libp2p-kitsune/peer_manager"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/peer"
)
func startPreloadHandler(peerMgr *pmgr.PeerManager, port uint64) {
portStr := fmt.Sprintf(":%v", port)
log.Infof("Preload mode enabled with HTTP /api/v0/refs endpoint on port %v", port)
mux := http.NewServeMux()
mux.HandleFunc("/api/v0/refs", preloadRefsHandler(peerMgr))
go func() {
err := http.ListenAndServe(portStr, mux)
if err != nil {
log.Errorf("Error starting Prometheus listener: %s", err)
return
}
}()
}
func preloadRefsHandler(peerMgr *pmgr.PeerManager) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
cStr, found := r.URL.Query()["arg"]
remoteIP := getRemoteIP(r)
if found {
c, err := cid.Parse(cStr[0])
if err != nil {
w.WriteHeader(http.StatusBadRequest)
log.Debugf("IP %s requested bad %v", remoteIP, c)
return
}
log.Debugf("IP %s requested %v", remoteIP, c)
w.Header().Set("Access-Control-Allow-Origin", "*")
var peerId peer.ID
upPeers := peerMgr.UpstreamPeersForIP(remoteIP)
if len(upPeers) > 0 {
// We already have some peers for this IP - send the request to the same downstream
// peer. The main issue with this is that if the upstream peer is behind a NAT, all
// upstream peers behind that same IP will be assigned to the same downstream peer.
peerId = peerMgr.DownstreamForPeer(upPeers[0])[0]
} else {
// This peer's IP is not associated with any upstream peer, so just grab the last one
// we used (perhaps a random one would be better?). This should only happen when
// js-ipfs starts up and sends a `refs` request before libp2p is fully connected,
// or if the libp2p channel disconnects.
peerId = peerMgr.CurrentDownPeer()
}
peerInfo, found := peerMgr.DownPeerInfo(peerId)
if !found {
log.Errorf("Peer %s not found in downstream peers, not sending refs request", peerId)
return
}
url := fmt.Sprintf("http://%s:%v/api/v0/refs?recursive=true&arg=%s", peerInfo.IP, peerInfo.HttpPort, c)
log.Debugf("Fetching %s", url)
peerMgr.AddRefCid(remoteIP, c)
resp, err := http.Post(url, "application/json", nil)
if err != nil {
log.Errorf("HTTP error while fetching %s", err)
return
}
io.Copy(w, resp.Body)
}
}
}
func getRemoteIP(r *http.Request) net.IP {
var remoteIP string
if len(r.Header.Get("X-Forwarded-For")) > 1 {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
remoteIP = r.Header.Get("X-Forwarded-For")
if strings.Contains(remoteIP, ",") {
remoteIP = strings.Split(remoteIP, ",")[0]
}
} else {
if strings.Contains(r.RemoteAddr, ":") {
remoteIP = strings.Split(r.RemoteAddr, ":")[0]
} else {
remoteIP = r.RemoteAddr
}
}
return net.ParseIP(remoteIP)
}