Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit35ee5d6

Browse files
committed
chore: Add subdomain parser for applications
1 parentc8f8c95 commit35ee5d6

File tree

2 files changed

+223
-0
lines changed

2 files changed

+223
-0
lines changed

‎coderd/subdomain.go‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package coderd
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"regexp"
7+
"strings"
8+
9+
"golang.org/x/xerrors"
10+
)
11+
12+
const (
13+
// XForwardedHostHeader is a header used by proxies to indicate the
14+
// original host of the request.
15+
XForwardedHostHeader="X-Forwarded-Host"
16+
xForwardedProto="X-Forwarded-Proto"
17+
)
18+
19+
typeApplicationstruct {
20+
AppURLstring
21+
AppNamestring
22+
Workspacestring
23+
Agentstring
24+
Userstring
25+
Pathstring
26+
27+
// Domain is used to output the url to reach the app.
28+
Domainstring
29+
}
30+
31+
func (api*API)handleSubdomain(next http.Handler) http.Handler {
32+
returnhttp.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
33+
34+
})
35+
}
36+
37+
var (
38+
nameRegex=`[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*`
39+
appURL=regexp.MustCompile(fmt.Sprintf(
40+
// {USERNAME}--{WORKSPACE_NAME}}--{{AGENT_NAME}}--{{PORT}}
41+
`^(?P<UserName>%[1]s)--(?P<WorkspaceName>%[1]s)(--(?P<AgentName>%[1]s))?--(?P<AppName>%[1]s)$`,
42+
nameRegex))
43+
)
44+
45+
funcParseSubdomainAppURL(r*http.Request) (Application,error) {
46+
host:=RequestHost(r)
47+
ifhost=="" {
48+
returnApplication{},xerrors.Errorf("no host header")
49+
}
50+
51+
subdomain,domain,err:=SplitSubdomain(host)
52+
iferr!=nil {
53+
returnApplication{},xerrors.Errorf("split host domain: %w",err)
54+
}
55+
56+
matches:=appURL.FindAllStringSubmatch(subdomain,-1)
57+
iflen(matches)==0 {
58+
returnApplication{},xerrors.Errorf("invalid application url format: %q",subdomain)
59+
}
60+
61+
iflen(matches)>1 {
62+
returnApplication{},xerrors.Errorf("multiple matches (%d) for application url: %q",len(matches),subdomain)
63+
}
64+
matchGroup:=matches[0]
65+
66+
returnApplication{
67+
AppURL:"",
68+
AppName:matchGroup[appURL.SubexpIndex("AppName")],
69+
Workspace:matchGroup[appURL.SubexpIndex("WorkspaceName")],
70+
Agent:matchGroup[appURL.SubexpIndex("AgentName")],
71+
User:matchGroup[appURL.SubexpIndex("UserName")],
72+
Path:r.URL.Path,
73+
Domain:domain,
74+
},nil
75+
}
76+
77+
// Parse parses a DevURL from the subdomain of r's Host header.
78+
// If DevURL is not valid, returns a non-nil error.
79+
//
80+
// devurls can be in two forms, each field separate by 2 hypthens:
81+
// 1) port-envname-user (eg. http://8080--myenv--johndoe.cdrdeploy.c8s.io)
82+
// 2) name-user (eg. http://demosvc--johndoe.cdrdeploy.c8s.io)
83+
//
84+
// Note that envname itself can contain hyphens.
85+
// If subdomain begins with a sequence of numbers, form 1 is assumed.
86+
// Otherwise, form 2 is assumed.
87+
//func Parse(r *http.Request, devurlSuffix string) (Application, error) {
88+
//
89+
//return d, nil
90+
//}
91+
92+
// RequestHost returns the name of the host from the request. It prioritizes
93+
// 'X-Forwarded-Host' over r.Host since most requests are being proxied.
94+
funcRequestHost(r*http.Request)string {
95+
host:=r.Header.Get(XForwardedHostHeader)
96+
ifhost!="" {
97+
returnhost
98+
}
99+
100+
returnr.Host
101+
}
102+
103+
// SplitSubdomain splits a subdomain from a domain. E.g.:
104+
// - "foo.bar.com" becomes "foo", "bar.com"
105+
// - "foo.bar.baz.com" becomes "foo", "bar.baz.com"
106+
//
107+
// An error is returned if the string doesn't contain a period.
108+
funcSplitSubdomain(hostnamestring) (string,string,error) {
109+
toks:=strings.SplitN(hostname,".",2)
110+
iflen(toks)<2 {
111+
return"","",xerrors.Errorf("no domain")
112+
}
113+
114+
returntoks[0],toks[1],nil
115+
}

‎coderd/subdomain_test.go‎

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package coderd_test
2+
3+
import (
4+
"net/http/httptest"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/coder/coder/coderd"
10+
)
11+
12+
funcTestParseSubdomainAppURL(t*testing.T) {
13+
t.Parallel()
14+
testCases:= []struct {
15+
Namestring
16+
URLstring
17+
Expected coderd.Application
18+
ExpectedErrorstring
19+
}{
20+
{
21+
Name:"Empty",
22+
URL:"https://example.com",
23+
Expected: coderd.Application{},
24+
ExpectedError:"invalid application url format",
25+
},
26+
{
27+
Name:"Workspace.Agent+App",
28+
URL:"https://workspace.agent--app.coder.com",
29+
Expected: coderd.Application{},
30+
ExpectedError:"invalid application url format",
31+
},
32+
{
33+
Name:"Workspace+App",
34+
URL:"https://workspace--app.coder.com",
35+
Expected: coderd.Application{},
36+
ExpectedError:"invalid application url format",
37+
},
38+
// Correct
39+
{
40+
Name:"User+Workspace+App",
41+
URL:"https://user--workspace--app.coder.com",
42+
Expected: coderd.Application{
43+
AppURL:"",
44+
AppName:"app",
45+
Workspace:"workspace",
46+
Agent:"",
47+
User:"user",
48+
Path:"",
49+
Domain:"coder.com",
50+
},
51+
},
52+
{
53+
Name:"User+Workspace+Port",
54+
URL:"https://user--workspace--8080.coder.com",
55+
Expected: coderd.Application{
56+
AppURL:"",
57+
AppName:"8080",
58+
Workspace:"workspace",
59+
Agent:"",
60+
User:"user",
61+
Path:"",
62+
Domain:"coder.com",
63+
},
64+
},
65+
{
66+
Name:"User+Workspace.Agent+App",
67+
URL:"https://user--workspace--agent--app.coder.com",
68+
Expected: coderd.Application{
69+
AppURL:"",
70+
AppName:"app",
71+
Workspace:"workspace",
72+
Agent:"agent",
73+
User:"user",
74+
Path:"",
75+
Domain:"coder.com",
76+
},
77+
},
78+
{
79+
Name:"User+Workspace.Agent+Port",
80+
URL:"https://user--workspace--agent--8080.coder.com",
81+
Expected: coderd.Application{
82+
AppURL:"",
83+
AppName:"8080",
84+
Workspace:"workspace",
85+
Agent:"agent",
86+
User:"user",
87+
Path:"",
88+
Domain:"coder.com",
89+
},
90+
},
91+
}
92+
93+
for_,c:=rangetestCases {
94+
c:=c
95+
t.Run(c.Name,func(t*testing.T) {
96+
t.Parallel()
97+
r:=httptest.NewRequest("GET",c.URL,nil)
98+
99+
app,err:=coderd.ParseSubdomainAppURL(r)
100+
ifc.ExpectedError=="" {
101+
require.NoError(t,err)
102+
require.Equal(t,c.Expected,app,"expected app")
103+
}else {
104+
require.ErrorContains(t,err,c.ExpectedError,"expected error")
105+
}
106+
})
107+
}
108+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp