From dd35ad43caab407cc70ef3b153b3f94d57242ed9 Mon Sep 17 00:00:00 2001 From: Jesse Hallam Date: Thu, 21 Jun 2018 14:31:51 -0400 Subject: MM-10370: serve subpath (#8968) * factor out GetSubpathFromConfig * mv web/subpath.go to utils/subpath.go * serve up web, api and ws on /subpath if configured * pass config to utils.RenderWeb(App)?Error This allows the methods to extract the configured subpath and redirect to the appropriate `/subpath/error` handler. * ensure GetSubpathFromConfig returns trailing slashes deterministically * fix error 404 handling * redirect /subpath to /subpath/ This is necessary for the static handler to match, otherwise none of the registered routes find anything. This also makes it no longer necessary to add trailing slashes in the root router. --- utils/subpath_test.go | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 utils/subpath_test.go (limited to 'utils/subpath_test.go') diff --git a/utils/subpath_test.go b/utils/subpath_test.go new file mode 100644 index 000000000..ee518d5f6 --- /dev/null +++ b/utils/subpath_test.go @@ -0,0 +1,192 @@ +package utils_test + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/utils" +) + +func TestUpdateAssetsSubpath(t *testing.T) { + t.Run("no client dir", func(t *testing.T) { + tempDir, err := ioutil.TempDir("", "test_update_assets_subpath") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + os.Chdir(tempDir) + + err = utils.UpdateAssetsSubpath("/") + require.Error(t, err) + }) + + t.Run("valid", func(t *testing.T) { + tempDir, err := ioutil.TempDir("", "test_update_assets_subpath") + require.NoError(t, err) + defer os.RemoveAll(tempDir) + os.Chdir(tempDir) + + err = os.Mkdir(model.CLIENT_DIR, 0700) + require.NoError(t, err) + + testCases := []struct { + Description string + RootHTML string + MainCSS string + Subpath string + ExpectedRootHTML string + ExpectedMainCSS string + }{ + { + "no changes required, empty subpath provided", + baseRootHtml, + baseCss, + "", + baseRootHtml, + baseCss, + }, + { + "no changes required", + baseRootHtml, + baseCss, + "/", + baseRootHtml, + baseCss, + }, + { + "subpath", + baseRootHtml, + baseCss, + "/subpath", + subpathRootHtml, + subpathCss, + }, + { + "new subpath from old", + subpathRootHtml, + subpathCss, + "/nested/subpath", + newSubpathRootHtml, + newSubpathCss, + }, + { + "resetting to /", + subpathRootHtml, + subpathCss, + "/", + resetRootHtml, + baseCss, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + ioutil.WriteFile(filepath.Join(tempDir, model.CLIENT_DIR, "root.html"), []byte(testCase.RootHTML), 0700) + ioutil.WriteFile(filepath.Join(tempDir, model.CLIENT_DIR, "main.css"), []byte(testCase.MainCSS), 0700) + err := utils.UpdateAssetsSubpath(testCase.Subpath) + require.NoError(t, err) + + contents, err := ioutil.ReadFile(filepath.Join(tempDir, model.CLIENT_DIR, "root.html")) + require.NoError(t, err) + require.Equal(t, testCase.ExpectedRootHTML, string(contents)) + + contents, err = ioutil.ReadFile(filepath.Join(tempDir, model.CLIENT_DIR, "main.css")) + require.NoError(t, err) + require.Equal(t, testCase.ExpectedMainCSS, string(contents)) + + }) + } + }) +} + +func TestGetSubpathFromConfig(t *testing.T) { + sToP := func(s string) *string { + return &s + } + + testCases := []struct { + Description string + SiteURL *string + ExpectedError bool + ExpectedSubpath string + }{ + { + "empty SiteURL", + sToP(""), + false, + "/", + }, + { + "invalid SiteURL", + sToP("cache_object:foo/bar"), + true, + "", + }, + { + "nil SiteURL", + nil, + false, + "/", + }, + { + "no trailing slash", + sToP("http://localhost:8065"), + false, + "/", + }, + { + "trailing slash", + sToP("http://localhost:8065/"), + false, + "/", + }, + { + "subpath, no trailing slash", + sToP("http://localhost:8065/subpath"), + false, + "/subpath", + }, + { + "trailing slash", + sToP("http://localhost:8065/subpath/"), + false, + "/subpath", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.Description, func(t *testing.T) { + config := &model.Config{ + ServiceSettings: model.ServiceSettings{ + SiteURL: testCase.SiteURL, + }, + } + + subpath, err := utils.GetSubpathFromConfig(config) + if testCase.ExpectedError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + + require.Equal(t, testCase.ExpectedSubpath, subpath) + }) + } +} + +const baseRootHtml = ` Mattermost

Cannot connect to Mattermost


We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.


` + +const baseCss = `@font-face{font-family:FontAwesome;src:url(/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}` + +const subpathRootHtml = ` Mattermost

Cannot connect to Mattermost


We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.


` + +const subpathCss = `@font-face{font-family:FontAwesome;src:url(/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/subpath/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/subpath/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/subpath/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/subpath/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}` + +const newSubpathRootHtml = ` Mattermost

Cannot connect to Mattermost


We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.


` + +const newSubpathCss = `@font-face{font-family:FontAwesome;src:url(/nested/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/nested/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/nested/subpath/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/nested/subpath/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/nested/subpath/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/nested/subpath/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}` + +const resetRootHtml = ` Mattermost

Cannot connect to Mattermost


We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.


` -- cgit v1.2.3-1-g7c22