implement arbitrary HTTP header support
this commit adds the ability to specify arbitrary HTTP headers for either the Gateway or the API. simply set the desired headers on the config: ipfs config --json API.HTTPHeaders.X-MyHdr '["meow :)"]' ipfs config --json Gateway.HTTPHeaders.X-MyHdr '["meow :)"]' License: MIT Signed-off-by: Juan Batiz-Benet <juan@benet.ai>
Juan Batiz-Benet committed
Jul 28, 2015 at 07:50 UTC
4a571b099b013018703951c1c8f812ceadd1fe12
4 files changed
+41
-6
commands/http/handler.go
+22
-4
@@ -21,6 +21,7 @@ var log = u.Logger("commands/http")
21
type internalHandler struct {
22
ctx cmds.Context
23
root *cmds.Command
24
+ cfg *ServerConfig
25
}
26
27
// The Handler struct is funny because we want to wrap our internal handler
@@ -60,14 +61,24 @@ var mimeTypes = map[string]string{
61
}
62
63
type ServerConfig struct {
63
- // AddHeaders is an optional function that gets to write additional
64
- // headers to HTTP responses to the API requests.
65
- AddHeaders func(http.Header)
64
+ // Headers is an optional map of headers that is written out.
65
+ Headers map[string][]string
66
67
// CORSOpts is a set of options for CORS headers.
68
CORSOpts *cors.Options
69
}
70
71
+func skipAPIHeader(h string) bool {
72
+ switch h {
73
+ default:
74
+ return false
75
+ case "Access-Control-Allow-Origin":
76
+ case "Access-Control-Allow-Methods":
77
+ case "Access-Control-Allow-Credentials":
78
+ }
79
+ return true
80
+}
81
+
82
func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handler {
83
if cfg == nil {
84
cfg = &ServerConfig{}
@@ -89,7 +100,7 @@ func NewHandler(ctx cmds.Context, root *cmds.Command, cfg *ServerConfig) *Handle
100
101
// Wrap the internal handler with CORS handling-middleware.
102
// Create a handler for the API.
92
- internal := internalHandler{ctx, root}
103
+ internal := internalHandler{ctx, root, cfg}
104
c := cors.New(*cfg.CORSOpts)
105
return &Handler{internal, c.Handler(internal)}
106
}
@@ -132,6 +143,13 @@ func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
143
// call the command
144
res := i.root.Call(req)
145
146
+ // set user's headers first.
147
+ for k, v := range i.cfg.Headers {
148
+ if !skipAPIHeader(k) {
149
+ w.Header()[k] = v
150
+ }
151
+ }
152
+
153
// now handle responding to the client properly
154
sendResponse(w, r, req, res)
155
}
core/corehttp/commands.go
+4
-2
@@ -39,7 +39,7 @@ func addCORSFromEnv(c *cmdsHttp.ServerConfig) {
39
}
40
}
41
42
-func addCORSFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
42
+func addHeadersFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
43
log.Info("Using API.HTTPHeaders:", nc.API.HTTPHeaders)
44
45
if acao := nc.API.HTTPHeaders["Access-Control-Allow-Origin"]; acao != nil {
@@ -53,6 +53,8 @@ func addCORSFromConfig(c *cmdsHttp.ServerConfig, nc *config.Config) {
53
c.CORSOpts.AllowCredentials = (strings.ToLower(v) == "true")
54
}
55
}
56
+
57
+ c.Headers = nc.API.HTTPHeaders
58
}
59
60
func CommandsOption(cctx commands.Context) ServeOption {
@@ -64,7 +66,7 @@ func CommandsOption(cctx commands.Context) ServeOption {
66
},
67
}
68
67
- addCORSFromConfig(cfg, n.Repo.Config())
69
+ addHeadersFromConfig(cfg, n.Repo.Config())
70
addCORSFromEnv(cfg)
71
72
cmdHandler := cmdsHttp.NewHandler(cctx, corecommands.Root, cfg)
core/corehttp/gateway.go
+4
@@ -15,6 +15,7 @@ type Gateway struct {
15
}
16
17
type GatewayConfig struct {
18
+ Headers map[string][]string
19
BlockList *BlockList
20
Writable bool
21
}
@@ -27,6 +28,9 @@ func NewGateway(conf GatewayConfig) *Gateway {
28
29
func (g *Gateway) ServeOption() ServeOption {
30
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) {
31
+ // pass user's HTTP headers
32
+ g.Config.Headers = n.Repo.Config().Gateway.HTTPHeaders
33
+
34
gateway, err := newGatewayHandler(n, g.Config)
35
if err != nil {
36
return nil, err
core/corehttp/gateway_handler.go
+11
@@ -106,6 +106,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
106
return
107
}
108
109
+ i.addUserHeaders(w) // ok, _now_ write user's headers.
110
w.Header().Set("X-IPFS-Path", urlPath)
111
112
// Suborigin header, sandboxes apps from each other in the browser (even
@@ -229,6 +230,7 @@ func (i *gatewayHandler) postHandler(w http.ResponseWriter, r *http.Request) {
230
return
231
}
232
233
+ i.addUserHeaders(w) // ok, _now_ write user's headers.
234
w.Header().Set("IPFS-Hash", k.String())
235
http.Redirect(w, r, ipfsPathPrefix+k.String(), http.StatusCreated)
236
}
@@ -242,6 +244,7 @@ func (i *gatewayHandler) putEmptyDirHandler(w http.ResponseWriter, r *http.Reque
244
return
245
}
246
247
+ i.addUserHeaders(w) // ok, _now_ write user's headers.
248
w.Header().Set("IPFS-Hash", key.String())
249
http.Redirect(w, r, ipfsPathPrefix+key.String()+"/", http.StatusCreated)
250
}
@@ -340,6 +343,7 @@ func (i *gatewayHandler) putHandler(w http.ResponseWriter, r *http.Request) {
343
return
344
}
345
346
+ i.addUserHeaders(w) // ok, _now_ write user's headers.
347
w.Header().Set("IPFS-Hash", key.String())
348
http.Redirect(w, r, ipfsPathPrefix+key.String()+"/"+strings.Join(components, "/"), http.StatusCreated)
349
}
@@ -411,10 +415,17 @@ func (i *gatewayHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
415
return
416
}
417
418
+ i.addUserHeaders(w) // ok, _now_ write user's headers.
419
w.Header().Set("IPFS-Hash", key.String())
420
http.Redirect(w, r, ipfsPathPrefix+key.String()+"/"+strings.Join(components[:len(components)-1], "/"), http.StatusCreated)
421
}
422
423
+func (i *gatewayHandler) addUserHeaders(w http.ResponseWriter) {
424
+ for k, v := range i.config.Headers {
425
+ w.Header()[k] = v
426
+ }
427
+}
428
+
429
func webError(w http.ResponseWriter, message string, err error, defaultCode int) {
430
if _, ok := err.(path.ErrNoLink); ok {
431
webErrorWithCode(w, message, err, http.StatusNotFound)