mirror of
https://github.com/ipfs/kubo.git
synced 2025-08-06 19:44:01 +08:00
check api version in corehttp
- add comments, trim api path prefix - corehttp: add option to set HTTP header "Server" - daemon: use new corehttp options License: MIT Signed-off-by: keks <keks@cryptoscope.co>
This commit is contained in:
@ -17,6 +17,7 @@ import (
|
||||
corehttp "github.com/ipfs/go-ipfs/core/corehttp"
|
||||
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
|
||||
nodeMount "github.com/ipfs/go-ipfs/fuse/node"
|
||||
config "github.com/ipfs/go-ipfs/repo/config"
|
||||
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
|
||||
migrate "github.com/ipfs/go-ipfs/repo/fsrepo/migrations"
|
||||
|
||||
@ -432,6 +433,8 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (error, <-chan error
|
||||
var opts = []corehttp.ServeOption{
|
||||
corehttp.MetricsCollectionOption("api"),
|
||||
corehttp.CommandsOption(*cctx),
|
||||
corehttp.CheckVersionOption(),
|
||||
corehttp.ServerNameOption("go-ipfs/" + config.CurrentVersionNumber),
|
||||
corehttp.WebUIOption,
|
||||
gatewayOpt,
|
||||
corehttp.VersionOption(),
|
||||
@ -529,6 +532,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (error, <-chan e
|
||||
corehttp.VersionOption(),
|
||||
corehttp.IPNSHostnameOption(),
|
||||
corehttp.GatewayOption(writable, "/ipfs", "/ipns"),
|
||||
corehttp.CheckVersionOption(),
|
||||
}
|
||||
|
||||
if len(cfg.Gateway.RootRedirect) > 0 {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package corehttp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -10,12 +12,18 @@ import (
|
||||
oldcmds "github.com/ipfs/go-ipfs/commands"
|
||||
core "github.com/ipfs/go-ipfs/core"
|
||||
corecommands "github.com/ipfs/go-ipfs/core/commands"
|
||||
path "github.com/ipfs/go-ipfs/path"
|
||||
config "github.com/ipfs/go-ipfs/repo/config"
|
||||
|
||||
cmds "gx/ipfs/QmTwKPLyeRKuDawuy6CAn1kRj1FVoqBEM8sviAUWN7NW9K/go-ipfs-cmds"
|
||||
cmdsHttp "gx/ipfs/QmTwKPLyeRKuDawuy6CAn1kRj1FVoqBEM8sviAUWN7NW9K/go-ipfs-cmds/http"
|
||||
)
|
||||
|
||||
var (
|
||||
errApiVersionMismatch = errors.New("api version mismatch")
|
||||
)
|
||||
|
||||
const apiPath = "/api/v0"
|
||||
const originEnvKey = "API_ORIGIN"
|
||||
const originEnvKeyDeprecate = `You are using the ` + originEnvKey + `ENV Variable.
|
||||
This functionality is deprecated, and will be removed in future versions.
|
||||
@ -131,3 +139,42 @@ func CommandsOption(cctx oldcmds.Context) ServeOption {
|
||||
func CommandsROOption(cctx oldcmds.Context) ServeOption {
|
||||
return commandsOption(cctx, corecommands.RootRO)
|
||||
}
|
||||
|
||||
// CheckVersionOption returns a ServeOption that checks whether the client ipfs version matches. Does nothing when the user agent string does not contain `/go-ipfs/`
|
||||
func CheckVersionOption() ServeOption {
|
||||
daemonVersion := config.ApiVersion
|
||||
|
||||
return ServeOption(func(n *core.IpfsNode, l net.Listener, next *http.ServeMux) (*http.ServeMux, error) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(APIPath+"/", func(w http.ResponseWriter, r *http.Request) {
|
||||
pth := path.SplitList(r.URL.Path[len(APIPath):])
|
||||
// backwards compatibility to previous version check
|
||||
if pth[1] != "version" {
|
||||
clientVersion := r.UserAgent()
|
||||
// skips check if client is not go-ipfs
|
||||
if clientVersion != "" && strings.Contains(clientVersion, "/go-ipfs/") && daemonVersion != clientVersion {
|
||||
http.Error(w, fmt.Sprintf("%s (%s != %s)", errApiVersionMismatch, daemonVersion, clientVersion), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
mux.HandleFunc("/", next.ServeHTTP)
|
||||
|
||||
return mux, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ServerNameOption returns a ServeOption that makes the http server set the Server HTTP header.
|
||||
func ServerNameOption(name string) ServeOption {
|
||||
return ServeOption(func(n *core.IpfsNode, l net.Listener, next *http.ServeMux) (*http.ServeMux, error) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Server", name)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
return mux, nil
|
||||
})
|
||||
}
|
||||
|
113
core/corehttp/option_test.go
Normal file
113
core/corehttp/option_test.go
Normal file
@ -0,0 +1,113 @@
|
||||
package corehttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
config "github.com/ipfs/go-ipfs/repo/config"
|
||||
)
|
||||
|
||||
type testcasecheckversion struct {
|
||||
userAgent string
|
||||
uri string
|
||||
shouldHandle bool
|
||||
responseBody string
|
||||
responseCode int
|
||||
}
|
||||
|
||||
func (tc testcasecheckversion) body() string {
|
||||
if !tc.shouldHandle && tc.responseBody == "" {
|
||||
return fmt.Sprintf("%s (%s != %s)\n", errApiVersionMismatch, config.ApiVersion, tc.userAgent)
|
||||
}
|
||||
|
||||
return tc.responseBody
|
||||
}
|
||||
|
||||
func TestCheckVersionOption(t *testing.T) {
|
||||
tcs := []testcasecheckversion{
|
||||
{"/go-ipfs/0.1/", APIPath + "/test/", false, "", http.StatusBadRequest},
|
||||
{"/go-ipfs/0.1/", APIPath + "/version", true, "check!", http.StatusOK},
|
||||
{config.ApiVersion, APIPath + "/test", true, "check!", http.StatusOK},
|
||||
{"Mozilla Firefox/no go-ipfs node", APIPath + "/test", true, "check!", http.StatusOK},
|
||||
{"/go-ipfs/0.1/", "/webui", true, "check!", http.StatusOK},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Logf("%#v", tc)
|
||||
r := httptest.NewRequest("POST", tc.uri, nil)
|
||||
r.Header.Add("User-Agent", tc.userAgent) // old version, should fail
|
||||
|
||||
called := false
|
||||
inner := http.NewServeMux()
|
||||
inner.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
if !tc.shouldHandle {
|
||||
t.Error("handler was called even though version didn't match")
|
||||
} else {
|
||||
io.WriteString(w, "check!")
|
||||
}
|
||||
})
|
||||
|
||||
mux, err := CheckVersionOption()(nil, nil, inner)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
mux.ServeHTTP(w, r)
|
||||
|
||||
if tc.shouldHandle && !called {
|
||||
t.Error("handler wasn't called even though it should have")
|
||||
}
|
||||
|
||||
if w.Code != tc.responseCode {
|
||||
t.Errorf("expected code %d but got %d", tc.responseCode, w.Code)
|
||||
}
|
||||
|
||||
if w.Body.String() != tc.body() {
|
||||
t.Errorf("expected error message %q, got %q", tc.body(), w.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerNameOption(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
}
|
||||
|
||||
tcs := []testcase{
|
||||
{"go-ipfs/0.4.13"},
|
||||
{"go-ipfs/" + config.CurrentVersionNumber},
|
||||
}
|
||||
|
||||
assert := func(name string, exp, got interface{}) {
|
||||
if got != exp {
|
||||
t.Errorf("%s: got %q, expected %q", name, got, exp)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Logf("%#v", tc)
|
||||
r := httptest.NewRequest("POST", "/", nil)
|
||||
|
||||
inner := http.NewServeMux()
|
||||
inner.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// this block is intentionally left blank.
|
||||
})
|
||||
|
||||
mux, err := ServerNameOption(tc.name)(nil, nil, inner)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
mux.ServeHTTP(w, r)
|
||||
srvHdr := w.Header().Get("Server")
|
||||
assert("Server header", tc.name, srvHdr)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user