Skip to content

Commit 988c310

Browse files
committed
Add readonly api to gateway
Issue #1322 Added the ability to expose individual commands to a read-only style handler that can be used on any http interface. Added the read-only handler to the gateway. License: MIT Signed-off-by: Travis Person <[email protected]>
1 parent fa23022 commit 988c310

File tree

3 files changed

+60
-6
lines changed

3 files changed

+60
-6
lines changed

cmd/ipfs/daemon.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func serveHTTPApi(req cmds.Request) (error, <-chan error) {
281281
var opts = []corehttp.ServeOption{
282282
corehttp.CommandsOption(*req.Context()),
283283
corehttp.WebUIOption,
284-
apiGw.ServeOption(),
284+
apiGw.ServeOption(nil),
285285
corehttp.VersionOption(),
286286
defaultMux("/debug/vars"),
287287
defaultMux("/debug/pprof/"),
@@ -339,7 +339,7 @@ func serveHTTPGateway(req cmds.Request) (error, <-chan error) {
339339
var opts = []corehttp.ServeOption{
340340
corehttp.VersionOption(),
341341
corehttp.IPNSHostnameOption(),
342-
corehttp.GatewayOption(writable),
342+
corehttp.GatewayOption(writable, req.Context()),
343343
}
344344

345345
if len(cfg.Gateway.RootRedirect) > 0 {

commands/http/handler.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
1313

1414
cmds "github.com/ipfs/go-ipfs/commands"
15+
commands "github.com/ipfs/go-ipfs/core/commands"
1516
u "github.com/ipfs/go-ipfs/util"
1617
)
1718

@@ -21,6 +22,7 @@ var log = u.Logger("commands/http")
2122
type internalHandler struct {
2223
ctx cmds.Context
2324
root *cmds.Command
25+
readOnly bool
2426
}
2527

2628
// The Handler struct is funny because we want to wrap our internal handler
@@ -47,14 +49,18 @@ var mimeTypes = map[string]string{
4749
cmds.Text: "text/plain",
4850
}
4951

52+
var readOnlyCmds = map[*cmds.Command]bool{
53+
commands.RefsCmd: true,
54+
}
55+
5056
func NewHandler(ctx cmds.Context, root *cmds.Command, allowedOrigin string) *Handler {
5157
// allow whitelisted origins (so we can make API requests from the browser)
5258
if len(allowedOrigin) > 0 {
5359
log.Info("Allowing API requests from origin: " + allowedOrigin)
5460
}
5561

5662
// Create a handler for the API.
57-
internal := internalHandler{ctx, root}
63+
internal := internalHandler{ctx, root, false}
5864

5965
// Create a CORS object for wrapping the internal handler.
6066
c := cors.New(cors.Options{
@@ -71,6 +77,32 @@ func NewHandler(ctx cmds.Context, root *cmds.Command, allowedOrigin string) *Han
7177
return &Handler{internal, c.Handler(internal)}
7278
}
7379

80+
func NewReadOnlyHandler(ctx cmds.Context, root *cmds.Command, allowedOrigin string) *Handler {
81+
// allow whitelisted origins (so we can make API requests from the browser)
82+
if len(allowedOrigin) > 0 {
83+
log.Info("Allowing API requests from origin: " + allowedOrigin)
84+
}
85+
86+
// Create a handler for the API.
87+
internal := internalHandler{ctx, root, true}
88+
89+
// Create a CORS object for wrapping the internal handler.
90+
c := cors.New(cors.Options{
91+
AllowedMethods: []string{"GET"},
92+
93+
// use AllowOriginFunc instead of AllowedOrigins because we want to be
94+
// restrictive by default.
95+
AllowOriginFunc: func(origin string) bool {
96+
return (allowedOrigin == "*") || (origin == allowedOrigin)
97+
},
98+
})
99+
100+
fmt.Println("Read Only API")
101+
102+
// Wrap the internal handler with CORS handling-middleware.
103+
return &Handler{internal, c.Handler(internal)}
104+
}
105+
74106
func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
75107
log.Debug("Incoming API request: ", r.URL)
76108

@@ -89,6 +121,7 @@ func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
89121
}
90122

91123
req, err := Parse(r, i.root)
124+
92125
if err != nil {
93126
if err == ErrNotFound {
94127
w.WriteHeader(http.StatusNotFound)
@@ -99,6 +132,15 @@ func (i internalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
99132
return
100133
}
101134

135+
if i.readOnly == true {
136+
if _, ok := readOnlyCmds[req.Command()]; !ok {
137+
// Or a 404?
138+
w.WriteHeader(http.StatusForbidden)
139+
w.Write([]byte("You may not execute this request on the read-only api."))
140+
return
141+
}
142+
}
143+
102144
// get the node's context to pass into the commands.
103145
node, err := i.ctx.GetNode()
104146
if err != nil {

core/corehttp/gateway.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import (
44
"fmt"
55
"net/http"
66
"sync"
7+
"os"
78

9+
commands "github.com/ipfs/go-ipfs/commands"
10+
cmdsHttp "github.com/ipfs/go-ipfs/commands/http"
11+
corecommands "github.com/ipfs/go-ipfs/core/commands"
812
core "github.com/ipfs/go-ipfs/core"
913
id "github.com/ipfs/go-ipfs/p2p/protocol/identify"
1014
)
@@ -25,26 +29,34 @@ func NewGateway(conf GatewayConfig) *Gateway {
2529
}
2630
}
2731

28-
func (g *Gateway) ServeOption() ServeOption {
32+
func (g *Gateway) ServeOption(cctx * commands.Context) ServeOption {
2933
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) {
3034
gateway, err := newGatewayHandler(n, g.Config)
3135
if err != nil {
3236
return nil, err
3337
}
3438
mux.Handle("/ipfs/", gateway)
3539
mux.Handle("/ipns/", gateway)
40+
41+
if cctx != nil {
42+
origin := os.Getenv(originEnvKey)
43+
cmdHandler := cmdsHttp.NewReadOnlyHandler(*cctx, corecommands.Root, origin)
44+
mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)
45+
}
3646
return mux, nil
3747
}
3848
}
3949

40-
func GatewayOption(writable bool) ServeOption {
50+
51+
func GatewayOption(writable bool, cctx * commands.Context) ServeOption {
4152
g := NewGateway(GatewayConfig{
4253
Writable: writable,
4354
BlockList: &BlockList{},
4455
})
45-
return g.ServeOption()
56+
return g.ServeOption(cctx)
4657
}
4758

59+
4860
func VersionOption() ServeOption {
4961
return func(n *core.IpfsNode, mux *http.ServeMux) (*http.ServeMux, error) {
5062
mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)