@cryptotaxi247 / kubo / commits / 4a571b099

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)