mirror of
https://github.com/ipfs/kubo.git
synced 2025-09-11 23:46:39 +08:00
351 lines
8.3 KiB
Go
351 lines
8.3 KiB
Go
package http
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
cmds "github.com/ipfs/go-ipfs/commands"
|
|
ipfscmd "github.com/ipfs/go-ipfs/core/commands"
|
|
coremock "github.com/ipfs/go-ipfs/core/mock"
|
|
)
|
|
|
|
const AllowedExposedHeaders = "X-Stream-Output, X-Chunked-Output"
|
|
|
|
func assertHeaders(t *testing.T, resHeaders http.Header, reqHeaders map[string]string) {
|
|
for name, value := range reqHeaders {
|
|
if resHeaders.Get(name) != value {
|
|
t.Errorf("Invalid header '%s', wanted '%s', got '%s'", name, value, resHeaders.Get(name))
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertStatus(t *testing.T, actual, expected int) {
|
|
if actual != expected {
|
|
t.Errorf("Expected status: %d got: %d", expected, actual)
|
|
}
|
|
}
|
|
|
|
func originCfg(origins []string) *ServerConfig {
|
|
cfg := NewServerConfig()
|
|
cfg.SetAllowedOrigins(origins...)
|
|
cfg.SetAllowedMethods("GET", "PUT", "POST")
|
|
return cfg
|
|
}
|
|
|
|
type testCase struct {
|
|
Method string
|
|
Path string
|
|
Code int
|
|
Origin string
|
|
Referer string
|
|
AllowOrigins []string
|
|
ReqHeaders map[string]string
|
|
ResHeaders map[string]string
|
|
}
|
|
|
|
var defaultOrigins = []string{
|
|
"http://localhost",
|
|
"http://127.0.0.1",
|
|
"https://localhost",
|
|
"https://127.0.0.1",
|
|
}
|
|
|
|
func getTestServer(t *testing.T, origins []string) *httptest.Server {
|
|
cmdsCtx, err := coremock.MockCmdsCtx()
|
|
if err != nil {
|
|
t.Error("failure to initialize mock cmds ctx", err)
|
|
return nil
|
|
}
|
|
|
|
cmdRoot := &cmds.Command{
|
|
Subcommands: map[string]*cmds.Command{
|
|
"version": ipfscmd.VersionCmd,
|
|
},
|
|
}
|
|
|
|
if len(origins) == 0 {
|
|
origins = defaultOrigins
|
|
}
|
|
|
|
handler := NewHandler(cmdsCtx, cmdRoot, originCfg(origins))
|
|
return httptest.NewServer(handler)
|
|
}
|
|
|
|
func (tc *testCase) test(t *testing.T) {
|
|
// defaults
|
|
method := tc.Method
|
|
if method == "" {
|
|
method = "GET"
|
|
}
|
|
|
|
path := tc.Path
|
|
if path == "" {
|
|
path = "/api/v0/version"
|
|
}
|
|
|
|
expectCode := tc.Code
|
|
if expectCode == 0 {
|
|
expectCode = 200
|
|
}
|
|
|
|
// request
|
|
req, err := http.NewRequest(method, path, nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
for k, v := range tc.ReqHeaders {
|
|
req.Header.Add(k, v)
|
|
}
|
|
if tc.Origin != "" {
|
|
req.Header.Add("Origin", tc.Origin)
|
|
}
|
|
if tc.Referer != "" {
|
|
req.Header.Add("Referer", tc.Referer)
|
|
}
|
|
|
|
// server
|
|
server := getTestServer(t, tc.AllowOrigins)
|
|
if server == nil {
|
|
return
|
|
}
|
|
defer server.Close()
|
|
|
|
req.URL, err = url.Parse(server.URL + path)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
// checks
|
|
t.Log("GET", server.URL+path, req.Header, res.Header)
|
|
assertHeaders(t, res.Header, tc.ResHeaders)
|
|
assertStatus(t, res.StatusCode, expectCode)
|
|
}
|
|
|
|
func TestDisallowedOrigins(t *testing.T) {
|
|
gtc := func(origin string, allowedOrigins []string) testCase {
|
|
return testCase{
|
|
Origin: origin,
|
|
AllowOrigins: allowedOrigins,
|
|
ResHeaders: map[string]string{
|
|
ACAOrigin: "",
|
|
ACAMethods: "",
|
|
ACACredentials: "",
|
|
"Access-Control-Max-Age": "",
|
|
"Access-Control-Expose-Headers": "",
|
|
},
|
|
Code: http.StatusForbidden,
|
|
}
|
|
}
|
|
|
|
tcs := []testCase{
|
|
gtc("http://barbaz.com", nil),
|
|
gtc("http://barbaz.com", []string{"http://localhost"}),
|
|
gtc("http://127.0.0.1", []string{"http://localhost"}),
|
|
gtc("http://localhost", []string{"http://127.0.0.1"}),
|
|
gtc("http://127.0.0.1:1234", nil),
|
|
gtc("http://localhost:1234", nil),
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
tc.test(t)
|
|
}
|
|
}
|
|
|
|
func TestAllowedOrigins(t *testing.T) {
|
|
gtc := func(origin string, allowedOrigins []string) testCase {
|
|
return testCase{
|
|
Origin: origin,
|
|
AllowOrigins: allowedOrigins,
|
|
ResHeaders: map[string]string{
|
|
ACAOrigin: origin,
|
|
ACAMethods: "",
|
|
ACACredentials: "",
|
|
"Access-Control-Max-Age": "",
|
|
"Access-Control-Expose-Headers": AllowedExposedHeaders,
|
|
},
|
|
Code: http.StatusOK,
|
|
}
|
|
}
|
|
|
|
tcs := []testCase{
|
|
gtc("http://barbaz.com", []string{"http://barbaz.com", "http://localhost"}),
|
|
gtc("http://localhost", []string{"http://barbaz.com", "http://localhost"}),
|
|
gtc("http://localhost", nil),
|
|
gtc("http://127.0.0.1", nil),
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
tc.test(t)
|
|
}
|
|
}
|
|
|
|
func TestWildcardOrigin(t *testing.T) {
|
|
gtc := func(origin string, allowedOrigins []string) testCase {
|
|
return testCase{
|
|
Origin: origin,
|
|
AllowOrigins: allowedOrigins,
|
|
ResHeaders: map[string]string{
|
|
ACAOrigin: origin,
|
|
ACAMethods: "",
|
|
ACACredentials: "",
|
|
"Access-Control-Max-Age": "",
|
|
"Access-Control-Expose-Headers": AllowedExposedHeaders,
|
|
},
|
|
Code: http.StatusOK,
|
|
}
|
|
}
|
|
|
|
tcs := []testCase{
|
|
gtc("http://barbaz.com", []string{"*"}),
|
|
gtc("http://barbaz.com", []string{"http://localhost", "*"}),
|
|
gtc("http://127.0.0.1", []string{"http://localhost", "*"}),
|
|
gtc("http://localhost", []string{"http://127.0.0.1", "*"}),
|
|
gtc("http://127.0.0.1", []string{"*"}),
|
|
gtc("http://localhost", []string{"*"}),
|
|
gtc("http://127.0.0.1:1234", []string{"*"}),
|
|
gtc("http://localhost:1234", []string{"*"}),
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
tc.test(t)
|
|
}
|
|
}
|
|
|
|
func TestDisallowedReferer(t *testing.T) {
|
|
gtc := func(referer string, allowedOrigins []string) testCase {
|
|
return testCase{
|
|
Origin: "http://localhost",
|
|
Referer: referer,
|
|
AllowOrigins: allowedOrigins,
|
|
ResHeaders: map[string]string{
|
|
ACAOrigin: "http://localhost",
|
|
ACAMethods: "",
|
|
ACACredentials: "",
|
|
"Access-Control-Max-Age": "",
|
|
"Access-Control-Expose-Headers": "",
|
|
},
|
|
Code: http.StatusForbidden,
|
|
}
|
|
}
|
|
|
|
tcs := []testCase{
|
|
gtc("http://foobar.com", nil),
|
|
gtc("http://localhost:1234", nil),
|
|
gtc("http://127.0.0.1:1234", nil),
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
tc.test(t)
|
|
}
|
|
}
|
|
|
|
func TestAllowedReferer(t *testing.T) {
|
|
gtc := func(referer string, allowedOrigins []string) testCase {
|
|
return testCase{
|
|
Origin: "http://localhost",
|
|
AllowOrigins: allowedOrigins,
|
|
ResHeaders: map[string]string{
|
|
ACAOrigin: "http://localhost",
|
|
ACAMethods: "",
|
|
ACACredentials: "",
|
|
"Access-Control-Max-Age": "",
|
|
"Access-Control-Expose-Headers": AllowedExposedHeaders,
|
|
},
|
|
Code: http.StatusOK,
|
|
}
|
|
}
|
|
|
|
tcs := []testCase{
|
|
gtc("http://barbaz.com", []string{"http://barbaz.com", "http://localhost"}),
|
|
gtc("http://localhost", []string{"http://barbaz.com", "http://localhost"}),
|
|
gtc("http://localhost", nil),
|
|
gtc("http://127.0.0.1", nil),
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
tc.test(t)
|
|
}
|
|
}
|
|
|
|
func TestWildcardReferer(t *testing.T) {
|
|
gtc := func(origin string, allowedOrigins []string) testCase {
|
|
return testCase{
|
|
Origin: origin,
|
|
AllowOrigins: allowedOrigins,
|
|
ResHeaders: map[string]string{
|
|
ACAOrigin: origin,
|
|
ACAMethods: "",
|
|
ACACredentials: "",
|
|
"Access-Control-Max-Age": "",
|
|
"Access-Control-Expose-Headers": AllowedExposedHeaders,
|
|
},
|
|
Code: http.StatusOK,
|
|
}
|
|
}
|
|
|
|
tcs := []testCase{
|
|
gtc("http://barbaz.com", []string{"*"}),
|
|
gtc("http://barbaz.com", []string{"http://localhost", "*"}),
|
|
gtc("http://127.0.0.1", []string{"http://localhost", "*"}),
|
|
gtc("http://localhost", []string{"http://127.0.0.1", "*"}),
|
|
gtc("http://127.0.0.1", []string{"*"}),
|
|
gtc("http://localhost", []string{"*"}),
|
|
gtc("http://127.0.0.1:1234", []string{"*"}),
|
|
gtc("http://localhost:1234", []string{"*"}),
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
tc.test(t)
|
|
}
|
|
}
|
|
|
|
func TestAllowedMethod(t *testing.T) {
|
|
gtc := func(method string, ok bool) testCase {
|
|
code := http.StatusOK
|
|
hdrs := map[string]string{
|
|
ACAOrigin: "http://localhost",
|
|
ACAMethods: method,
|
|
ACACredentials: "",
|
|
"Access-Control-Max-Age": "",
|
|
"Access-Control-Expose-Headers": "",
|
|
}
|
|
|
|
if !ok {
|
|
hdrs[ACAOrigin] = ""
|
|
hdrs[ACAMethods] = ""
|
|
}
|
|
|
|
return testCase{
|
|
Method: "OPTIONS",
|
|
Origin: "http://localhost",
|
|
AllowOrigins: []string{"*"},
|
|
ReqHeaders: map[string]string{
|
|
"Access-Control-Request-Method": method,
|
|
},
|
|
ResHeaders: hdrs,
|
|
Code: code,
|
|
}
|
|
}
|
|
|
|
tcs := []testCase{
|
|
gtc("PUT", true),
|
|
gtc("GET", true),
|
|
gtc("FOOBAR", false),
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
tc.test(t)
|
|
}
|
|
}
|