@cryptotaxi247 / kubo / commits / f4d87419c

feat: make corehttp a reusable component

Ian Davis committed Jun 30, 2022 at 10:12 UTC f4d87419ce702ac127943d43a1844e55b6fdab52
2 files changed +77 -48
core/corehttp/gateway.go
+68 -39
@@ -1,18 +1,20 @@
1 package corehttp
2
3 import (
4 + "context"
5 "fmt"
6 "net"
7 "net/http"
8 "sort"
9
10 + coreiface "github.com/ipfs/interface-go-ipfs-core"
11 + options "github.com/ipfs/interface-go-ipfs-core/options"
12 + path "github.com/ipfs/interface-go-ipfs-core/path"
13 version "github.com/ipfs/kubo"
14 core "github.com/ipfs/kubo/core"
15 coreapi "github.com/ipfs/kubo/core/coreapi"
12 - "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
13 -
14 - options "github.com/ipfs/interface-go-ipfs-core/options"
16 id "github.com/libp2p/go-libp2p/p2p/protocol/identify"
17 + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
18 )
19
20 type GatewayConfig struct {
@@ -22,6 +24,21 @@ type GatewayConfig struct {
24 FastDirIndexThreshold int
25 }
26
27 +// NodeAPI defines the minimal set of API services required by a gateway handler
28 +type NodeAPI interface {
29 + // Unixfs returns an implementation of Unixfs API
30 + Unixfs() coreiface.UnixfsAPI
31 +
32 + // Block returns an implementation of Block API
33 + Block() coreiface.BlockAPI
34 +
35 + // Dag returns an implementation of Dag API
36 + Dag() coreiface.APIDagService
37 +
38 + // ResolvePath resolves the path using Unixfs resolver
39 + ResolvePath(context.Context, path.Path) (path.Resolved, error)
40 +}
41 +
42 // A helper function to clean up a set of headers:
43 // 1. Canonicalizes.
44 // 2. Deduplicates.
@@ -59,49 +76,22 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
76 headers[http.CanonicalHeaderKey(h)] = v
77 }
78
62 - // Hard-coded headers.
63 - const ACAHeadersName = "Access-Control-Allow-Headers"
64 - const ACEHeadersName = "Access-Control-Expose-Headers"
65 - const ACAOriginName = "Access-Control-Allow-Origin"
66 - const ACAMethodsName = "Access-Control-Allow-Methods"
67 -
68 - if _, ok := headers[ACAOriginName]; !ok {
69 - // Default to *all*
70 - headers[ACAOriginName] = []string{"*"}
79 + acheaders := AccessControlHeaders()
80 + for k, v := range acheaders {
81 + headers[k] = v
82 }
72 - if _, ok := headers[ACAMethodsName]; !ok {
73 - // Default to GET
74 - headers[ACAMethodsName] = []string{http.MethodGet}
83 +
84 + offlineApi, err := api.WithOptions(options.Api.Offline(true))
85 + if err != nil {
86 + return nil, err
87 }
88
77 - headers[ACAHeadersName] = cleanHeaderSet(
78 - append([]string{
79 - "Content-Type",
80 - "User-Agent",
81 - "Range",
82 - "X-Requested-With",
83 - }, headers[ACAHeadersName]...))
84 -
85 - headers[ACEHeadersName] = cleanHeaderSet(
86 - append([]string{
87 - "Content-Length",
88 - "Content-Range",
89 - "X-Chunked-Output",
90 - "X-Stream-Output",
91 - "X-Ipfs-Path",
92 - "X-Ipfs-Roots",
93 - }, headers[ACEHeadersName]...))
94 -
95 - var gateway http.Handler
96 - gateway, err = newGatewayHandler(GatewayConfig{
89 + gateway := NewGatewayHandler(GatewayConfig{
90 Headers: headers,
91 Writable: writable,
92 PathPrefixes: cfg.Gateway.PathPrefixes,
93 FastDirIndexThreshold: int(cfg.Gateway.FastDirIndexThreshold.WithDefault(100)),
101 - }, api)
102 - if err != nil {
103 - return nil, err
104 - }
94 + }, api, offlineApi)
95
96 gateway = otelhttp.NewHandler(gateway, "Gateway.Request")
97
@@ -112,6 +102,45 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
102 }
103 }
104
105 +func AccessControlHeaders() map[string][]string {
106 + headers := make(map[string][]string)
107 +
108 + // Hard-coded headers.
109 + const ACAHeadersName = "Access-Control-Allow-Headers"
110 + const ACEHeadersName = "Access-Control-Expose-Headers"
111 + const ACAOriginName = "Access-Control-Allow-Origin"
112 + const ACAMethodsName = "Access-Control-Allow-Methods"
113 +
114 + if _, ok := headers[ACAOriginName]; !ok {
115 + // Default to *all*
116 + headers[ACAOriginName] = []string{"*"}
117 + }
118 + if _, ok := headers[ACAMethodsName]; !ok {
119 + // Default to GET
120 + headers[ACAMethodsName] = []string{http.MethodGet}
121 + }
122 +
123 + headers[ACAHeadersName] = cleanHeaderSet(
124 + append([]string{
125 + "Content-Type",
126 + "User-Agent",
127 + "Range",
128 + "X-Requested-With",
129 + }, headers[ACAHeadersName]...))
130 +
131 + headers[ACEHeadersName] = cleanHeaderSet(
132 + append([]string{
133 + "Content-Length",
134 + "Content-Range",
135 + "X-Chunked-Output",
136 + "X-Stream-Output",
137 + "X-Ipfs-Path",
138 + "X-Ipfs-Roots",
139 + }, headers[ACEHeadersName]...))
140 +
141 + return headers
142 +}
143 +
144 func VersionOption() ServeOption {
145 return func(_ *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
146 mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {
core/corehttp/gateway_handler.go
+9 -9
@@ -24,7 +24,6 @@ import (
24 path "github.com/ipfs/go-path"
25 "github.com/ipfs/go-path/resolver"
26 coreiface "github.com/ipfs/interface-go-ipfs-core"
27 - options "github.com/ipfs/interface-go-ipfs-core/options"
27 ipath "github.com/ipfs/interface-go-ipfs-core/path"
28 routing "github.com/libp2p/go-libp2p-core/routing"
29 prometheus "github.com/prometheus/client_golang/prometheus"
@@ -68,8 +67,8 @@ type redirectTemplateData struct {
67 // (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link)
68 type gatewayHandler struct {
69 config GatewayConfig
71 - api coreiface.CoreAPI
72 - offlineApi coreiface.CoreAPI
70 + api NodeAPI
71 + offlineApi NodeAPI
72
73 // generic metrics
74 firstContentBlockGetMetric *prometheus.HistogramVec
@@ -213,11 +212,12 @@ func newGatewayHistogramMetric(name string, help string) *prometheus.HistogramVe
212 return histogramMetric
213 }
214
216 -func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) (*gatewayHandler, error) {
217 - offlineApi, err := api.WithOptions(options.Api.Offline(true))
218 - if err != nil {
219 - return nil, err
220 - }
215 +// NewGatewayHandler returns an http.Handler that can act as a gateway to IPFS content
216 +func NewGatewayHandler(c GatewayConfig, api NodeAPI, offlineApi NodeAPI) http.Handler {
217 + return newGatewayHandler(c, api, offlineApi)
218 +}
219 +
220 +func newGatewayHandler(c GatewayConfig, api NodeAPI, offlineApi NodeAPI) *gatewayHandler {
221 i := &gatewayHandler{
222 config: c,
223 api: api,
@@ -262,7 +262,7 @@ func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) (*gatewayHandler,
262 "The time to receive the first UnixFS node on a GET from the gateway.",
263 ),
264 }
265 - return i, nil
265 + return i
266 }
267
268 func parseIpfsPath(p string) (cid.Cid, string, error) {