Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
gzupanda
code-server
提交
4cc181ce
C
code-server
项目概览
gzupanda
/
code-server
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
code-server
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
4cc181ce
编写于
2月 05, 2020
作者:
A
Asher
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Make routing base path agnostic
上级
a149c5fc
变更
13
隐藏空白更改
内联
并排
Showing
13 changed file
with
192 addition
and
215 deletion
+192
-215
src/browser/api.ts
src/browser/api.ts
+3
-7
src/browser/app.tsx
src/browser/app.tsx
+20
-9
src/browser/components/modal.tsx
src/browser/components/modal.tsx
+0
-1
src/browser/index.html
src/browser/index.html
+6
-6
src/common/api.ts
src/common/api.ts
+5
-0
src/common/util.ts
src/common/util.ts
+3
-2
src/node/api/server.ts
src/node/api/server.ts
+41
-39
src/node/app/server.tsx
src/node/app/server.tsx
+34
-31
src/node/http.ts
src/node/http.ts
+53
-71
src/node/vscode/error.html
src/node/vscode/error.html
+1
-6
src/node/vscode/server.ts
src/node/vscode/server.ts
+19
-36
src/node/vscode/workbench-build.html
src/node/vscode/workbench-build.html
+4
-4
src/node/vscode/workbench.html
src/node/vscode/workbench.html
+3
-3
未找到文件。
src/browser/api.ts
浏览文件 @
4cc181ce
import
{
getBasepath
}
from
"
hookrouter
"
import
{
Application
,
ApplicationsResponse
,
CreateSessionResponse
,
FilesResponse
,
RecentResponse
}
from
"
../common/api
"
import
{
ApiEndpoint
,
HttpCode
,
HttpError
}
from
"
../common/http
"
...
...
@@ -18,7 +19,7 @@ export function setAuthed(authed: boolean): void {
* Also set authed to false if the request returns unauthorized.
*/
const
tryRequest
=
async
(
endpoint
:
string
,
options
?:
RequestInit
):
Promise
<
Response
>
=>
{
const
response
=
await
fetch
(
"
/api
"
+
endpoint
+
"
/
"
,
options
)
const
response
=
await
fetch
(
getBasepath
()
+
"
/api
"
+
endpoint
+
"
/
"
,
options
)
if
(
response
.
status
===
HttpCode
.
Unauthorized
)
{
setAuthed
(
false
)
}
...
...
@@ -33,14 +34,9 @@ const tryRequest = async (endpoint: string, options?: RequestInit): Promise<Resp
* Try authenticating.
*/
export
const
authenticate
=
async
(
body
?:
AuthBody
):
Promise
<
void
>
=>
{
let
formBody
:
URLSearchParams
|
undefined
if
(
body
)
{
formBody
=
new
URLSearchParams
()
formBody
.
append
(
"
password
"
,
body
.
password
)
}
const
response
=
await
tryRequest
(
ApiEndpoint
.
login
,
{
method
:
"
POST
"
,
body
:
formBody
,
body
:
JSON
.
stringify
({
...
body
,
basePath
:
getBasepath
()
})
,
headers
:
{
"
Content-Type
"
:
"
application/x-www-form-urlencoded; charset=utf-8
"
,
},
...
...
src/browser/app.tsx
浏览文件 @
4cc181ce
import
{
getBasepath
,
navigate
}
from
"
hookrouter
"
import
{
getBasepath
,
navigate
,
setBasepath
}
from
"
hookrouter
"
import
*
as
React
from
"
react
"
import
{
Application
,
isExecutableApplication
}
from
"
../common/api
"
import
{
HttpError
}
from
"
../common/http
"
...
...
@@ -11,25 +11,36 @@ export interface AppProps {
}
const
App
:
React
.
FunctionComponent
<
AppProps
>
=
(
props
)
=>
{
const
[
authed
,
setAuthed
]
=
React
.
useState
<
boolean
>
(
!!
props
.
options
.
authed
)
const
[
authed
,
setAuthed
]
=
React
.
useState
<
boolean
>
(
props
.
options
.
authed
)
const
[
app
,
setApp
]
=
React
.
useState
<
Application
|
undefined
>
(
props
.
options
.
app
)
const
[
error
,
setError
]
=
React
.
useState
<
HttpError
|
Error
|
string
>
()
React
.
useEffect
(()
=>
{
if
(
app
&&
!
isExecutableApplication
(
app
))
{
navigate
(
normalize
(
`
${
getBasepath
()}
/
${
app
.
path
}
/`
,
true
))
}
},
[
app
])
if
(
typeof
window
!==
"
undefined
"
)
{
const
url
=
new
URL
(
window
.
location
.
origin
+
window
.
location
.
pathname
+
props
.
options
.
basePath
)
setBasepath
(
normalize
(
url
.
pathname
))
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(
window
as
any
).
setAuthed
=
(
a
:
boolean
):
void
=>
{
if
(
authed
!==
a
)
{
setAuthed
(
a
)
// TEMP: Remove when no longer auto-loading VS Code.
if
(
a
&&
!
app
)
{
setApp
({
name
:
"
VS Code
"
,
path
:
"
/
"
,
embedPath
:
"
/vscode-embed
"
,
})
}
}
}
}
React
.
useEffect
(()
=>
{
if
(
app
&&
!
isExecutableApplication
(
app
))
{
navigate
(
normalize
(
`
${
getBasepath
()}
/
${
app
.
path
}
/`
,
true
))
}
},
[
app
])
return
(
<>
{
!
app
||
!
app
.
loaded
?
(
...
...
@@ -41,7 +52,7 @@ const App: React.FunctionComponent<AppProps> = (props) => {
)
}
<
Modal
app
=
{
app
}
setApp
=
{
setApp
}
authed
=
{
authed
}
error
=
{
error
}
setError
=
{
setError
}
/>
{
authed
&&
app
&&
app
.
embedPath
?
(
<
iframe
id
=
"iframe"
src
=
{
normalize
(
`
${
getBasepath
()}
/
${
app
.
embedPath
}
/`
,
true
)
}
></
iframe
>
<
iframe
id
=
"iframe"
src
=
{
normalize
(
`
.
/
${
app
.
embedPath
}
/`
,
true
)
}
></
iframe
>
)
:
(
undefined
)
}
...
...
src/browser/components/modal.tsx
浏览文件 @
4cc181ce
...
...
@@ -128,7 +128,6 @@ export const Modal: React.FunctionComponent<ModalProps> = (props) => {
<
aside
className
=
"sidebar-nav"
>
<
nav
className
=
"links"
>
{
props
.
authed
?
(
// TEMP: Remove once we don't auto-load vscode.
<>
<
button
className
=
"link"
onClick
=
{
():
void
=>
setSection
(
Section
.
Recent
)
}
>
Recent
...
...
src/browser/index.html
浏览文件 @
4cc181ce
...
...
@@ -3,17 +3,17 @@
<head>
<meta
charset=
"utf-8"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
>
<
!-- <meta http-equiv="Content-Security-Policy" content="font-src 'self'; connect-src 'self'; default-src ws: wss:; style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:;"> --
>
<
meta
http-equiv=
"Content-Security-Policy"
content=
"font-src 'self' fonts.gstatic.com; connect-src 'self'; default-src ws: wss: 'self'; style-src 'self' fonts.googleapis.com; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:;"
>
<title>
code-server
</title>
<link
rel=
"icon"
href=
"/static-{{COMMIT}}/src/browser/media/favicon.ico"
type=
"image/x-icon"
/>
<link
rel=
"manifest"
href=
"/static-{{COMMIT}}/src/browser/media/manifest.json"
crossorigin=
"use-credentials"
>
<link
rel=
"apple-touch-icon"
href=
"/static-{{COMMIT}}/src/browser/media/code-server.png"
/>
<link
rel=
"icon"
href=
"
{{BASE}}
/static-{{COMMIT}}/src/browser/media/favicon.ico"
type=
"image/x-icon"
/>
<link
rel=
"manifest"
href=
"
{{BASE}}
/static-{{COMMIT}}/src/browser/media/manifest.json"
crossorigin=
"use-credentials"
>
<link
rel=
"apple-touch-icon"
href=
"
{{BASE}}
/static-{{COMMIT}}/src/browser/media/code-server.png"
/>
<link
href=
"https://fonts.googleapis.com/css?family=IBM+Plex+Sans&display=swap"
rel=
"stylesheet"
/>
<link
href=
"/static-{{COMMIT}}/dist/index.css"
rel=
"stylesheet"
>
<link
href=
"
{{BASE}}
/static-{{COMMIT}}/dist/index.css"
rel=
"stylesheet"
>
<meta
id=
"coder-options"
data-settings=
"{{OPTIONS}}"
>
</head>
<body>
<div
id=
"root"
>
{{COMPONENT}}
</div>
<script
src=
"/static-{{COMMIT}}/dist/index.js"
></script>
<script
src=
"
{{BASE}}
/static-{{COMMIT}}/dist/index.js"
></script>
</body>
</html>
src/common/api.ts
浏览文件 @
4cc181ce
...
...
@@ -22,6 +22,11 @@ export enum SessionError {
Unknown
,
}
export
interface
LoginRequest
{
password
:
string
basePath
:
string
}
export
interface
LoginResponse
{
success
:
boolean
}
...
...
src/common/util.ts
浏览文件 @
4cc181ce
...
...
@@ -3,8 +3,9 @@ import { Application } from "../common/api"
export
interface
Options
{
app
?:
Application
authed
?:
boolean
logLevel
?:
number
authed
:
boolean
basePath
:
string
logLevel
:
number
}
/**
...
...
src/node/api/server.ts
浏览文件 @
4cc181ce
import
{
field
,
logger
}
from
"
@coder/logger
"
import
*
as
http
from
"
http
"
import
*
as
net
from
"
net
"
import
*
as
querystring
from
"
querystring
"
import
*
as
ws
from
"
ws
"
import
{
ApplicationsResponse
,
ClientMessage
,
FilesResponse
,
LoginResponse
,
ServerMessage
}
from
"
../../common/api
"
import
{
ApplicationsResponse
,
ClientMessage
,
FilesResponse
,
LoginRequest
,
LoginResponse
,
ServerMessage
,
}
from
"
../../common/api
"
import
{
ApiEndpoint
,
HttpCode
}
from
"
../../common/http
"
import
{
HttpProvider
,
HttpProviderOptions
,
HttpResponse
,
HttpServer
,
PostData
}
from
"
../http
"
import
{
normalize
}
from
"
../../common/util
"
import
{
HttpProvider
,
HttpProviderOptions
,
HttpResponse
,
HttpServer
,
Route
}
from
"
../http
"
import
{
hash
}
from
"
../util
"
interface
LoginPayload
extends
PostData
{
password
?:
string
|
string
[]
}
/**
* API HTTP provider.
*/
...
...
@@ -22,13 +25,8 @@ export class ApiHttpProvider extends HttpProvider {
super
(
options
)
}
public
async
handleRequest
(
base
:
string
,
_requestPath
:
string
,
_query
:
querystring
.
ParsedUrlQuery
,
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
|
undefined
>
{
switch
(
base
)
{
public
async
handleRequest
(
route
:
Route
,
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
|
undefined
>
{
switch
(
route
.
base
)
{
case
ApiEndpoint
.
login
:
if
(
request
.
method
===
"
POST
"
)
{
return
this
.
login
(
request
)
...
...
@@ -38,7 +36,7 @@ export class ApiHttpProvider extends HttpProvider {
if
(
!
this
.
authenticated
(
request
))
{
return
{
code
:
HttpCode
.
Unauthorized
}
}
switch
(
base
)
{
switch
(
route
.
base
)
{
case
ApiEndpoint
.
applications
:
return
this
.
applications
()
case
ApiEndpoint
.
files
:
...
...
@@ -49,9 +47,7 @@ export class ApiHttpProvider extends HttpProvider {
}
public
async
handleWebSocket
(
_base
:
string
,
_requestPath
:
string
,
_query
:
querystring
.
ParsedUrlQuery
,
_route
:
Route
,
request
:
http
.
IncomingMessage
,
socket
:
net
.
Socket
,
head
:
Buffer
...
...
@@ -93,39 +89,45 @@ export class ApiHttpProvider extends HttpProvider {
* unauthorized.
*/
private
async
login
(
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
<
LoginResponse
>>
{
const
ok
=
(
password
:
string
|
true
):
HttpResponse
<
LoginResponse
>
=>
{
return
{
content
:
{
success
:
true
,
},
cookie
:
typeof
password
===
"
string
"
?
{
key
:
"
key
"
,
value
:
password
}
:
undefined
,
}
}
// Already authenticated via cookies?
const
providedPassword
=
this
.
authenticated
(
request
)
if
(
providedPassword
)
{
return
ok
(
providedPassword
)
return
{
code
:
HttpCode
.
Ok
}
}
const
data
=
await
this
.
getData
(
request
)
const
payload
:
Login
Payload
=
data
?
querystring
.
parse
(
data
)
:
{}
const
payload
:
Login
Request
=
data
?
JSON
.
parse
(
data
)
:
{}
const
password
=
this
.
authenticated
(
request
,
{
key
:
typeof
payload
.
password
===
"
string
"
?
[
hash
(
payload
.
password
)]
:
undefined
,
})
if
(
password
)
{
return
ok
(
password
)
return
{
content
:
{
success
:
true
,
},
cookie
:
typeof
password
===
"
string
"
?
{
key
:
"
key
"
,
value
:
password
,
path
:
normalize
(
payload
.
basePath
),
}
:
undefined
,
}
}
console
.
error
(
"
Failed login attempt
"
,
JSON
.
stringify
({
xForwardedFor
:
request
.
headers
[
"
x-forwarded-for
"
],
remoteAddress
:
request
.
connection
.
remoteAddress
,
userAgent
:
request
.
headers
[
"
user-agent
"
],
timestamp
:
Math
.
floor
(
new
Date
().
getTime
()
/
1000
),
})
)
// Only log if it was an actual login attempt.
if
(
payload
&&
payload
.
password
)
{
console
.
error
(
"
Failed login attempt
"
,
JSON
.
stringify
({
xForwardedFor
:
request
.
headers
[
"
x-forwarded-for
"
],
remoteAddress
:
request
.
connection
.
remoteAddress
,
userAgent
:
request
.
headers
[
"
user-agent
"
],
timestamp
:
Math
.
floor
(
new
Date
().
getTime
()
/
1000
),
})
)
}
return
{
code
:
HttpCode
.
Unauthorized
}
}
...
...
src/node/app/server.tsx
浏览文件 @
4cc181ce
import
{
logger
}
from
"
@coder/logger
"
import
*
as
http
from
"
http
"
import
*
as
querystring
from
"
querystring
"
import
*
as
React
from
"
react
"
import
*
as
ReactDOMServer
from
"
react-dom/server
"
import
App
from
"
../../browser/app
"
import
{
Options
}
from
"
../../common/util
"
import
{
HttpProvider
,
HttpResponse
}
from
"
../http
"
import
{
HttpProvider
,
HttpResponse
,
Route
}
from
"
../http
"
/**
* Top-level and fallback HTTP provider.
*/
export
class
MainHttpProvider
extends
HttpProvider
{
public
async
handleRequest
(
base
:
string
,
requestPath
:
string
,
_query
:
querystring
.
ParsedUrlQuery
,
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
|
undefined
>
{
if
(
base
===
"
/static
"
)
{
const
response
=
await
this
.
getResource
(
this
.
rootPath
,
requestPath
)
if
(
!
this
.
isDev
)
{
response
.
cache
=
true
public
async
handleRequest
(
route
:
Route
,
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
|
undefined
>
{
switch
(
route
.
base
)
{
case
"
/static
"
:
{
const
response
=
await
this
.
getResource
(
this
.
rootPath
,
route
.
requestPath
)
if
(
!
this
.
isDev
)
{
response
.
cache
=
true
}
return
response
}
return
response
}
// TEMP: Auto-load VS Code for now. In future versions we'll need to check
// the URL for the appropriate application to load, if any.
const
app
=
{
name
:
"
VS Code
"
,
path
:
"
/
"
,
embedPath
:
"
/vscode-embed
"
,
}
case
"
/
"
:
{
const
options
:
Options
=
{
authed
:
!!
this
.
authenticated
(
request
),
basePath
:
this
.
base
(
route
),
logLevel
:
logger
.
level
,
}
if
(
options
.
authed
)
{
// TEMP: Auto-load VS Code for now. In future versions we'll need to check
// the URL for the appropriate application to load, if any.
options
.
app
=
{
name
:
"
VS Code
"
,
path
:
"
/
"
,
embedPath
:
"
/vscode-embed
"
,
}
}
const
options
:
Options
=
{
app
,
authed
:
!!
this
.
authenticated
(
request
),
logLevel
:
logger
.
level
,
const
response
=
await
this
.
getUtf8Resource
(
this
.
rootPath
,
"
src/browser/index.html
"
)
response
.
content
=
response
.
content
.
replace
(
/{{COMMIT}}/g
,
this
.
options
.
commit
)
.
replace
(
/{{BASE}}/g
,
this
.
base
(
route
))
.
replace
(
/"{{OPTIONS}}"/g
,
`'
${
JSON
.
stringify
(
options
)}
'`
)
.
replace
(
/{{COMPONENT}}/g
,
ReactDOMServer
.
renderToString
(<
App
options
=
{
options
}
/>))
return
response
}
}
const
response
=
await
this
.
getUtf8Resource
(
this
.
rootPath
,
"
src/browser/index.html
"
)
response
.
content
=
response
.
content
.
replace
(
/{{COMMIT}}/g
,
this
.
options
.
commit
)
.
replace
(
/"{{OPTIONS}}"/g
,
`'
${
JSON
.
stringify
(
options
)}
'`
)
.
replace
(
/{{COMPONENT}}/g
,
ReactDOMServer
.
renderToString
(<
App
options
=
{
options
}
/>))
return
response
return
undefined
}
public
async
handleWebSocket
():
Promise
<
undefined
>
{
...
...
src/node/http.ts
浏览文件 @
4cc181ce
...
...
@@ -47,8 +47,9 @@ export interface HttpResponse<T = string | Buffer | object> {
content
?:
T
/**
* Cookie to write with the response.
* NOTE: Cookie paths must be absolute. The default is /.
*/
cookie
?:
{
key
:
string
;
value
:
string
}
cookie
?:
{
key
:
string
;
value
:
string
;
path
?:
string
}
/**
* Used to automatically determine the appropriate mime type.
*/
...
...
@@ -64,7 +65,7 @@ export interface HttpResponse<T = string | Buffer | object> {
/**
* Redirect to this path. Will rewrite against the base path but NOT the
* provider endpoint so you must include it. This allows redirecting outside
* of your endpoint.
Use `withBase()` to redirect within your endpoint.
* of your endpoint.
*/
redirect
?:
string
/**
...
...
@@ -87,9 +88,12 @@ export interface HttpStringFileResponse extends HttpResponse {
filePath
:
string
}
export
interface
RedirectResponse
extends
HttpResponse
{
redirect
:
string
}
export
interface
HttpServerOptions
{
readonly
auth
?:
AuthType
readonly
basePath
?:
string
readonly
cert
?:
string
readonly
certKey
?:
string
readonly
commit
?:
string
...
...
@@ -99,15 +103,18 @@ export interface HttpServerOptions {
readonly
socket
?:
string
}
interface
Provider
Route
{
export
interface
Route
{
base
:
string
requestPath
:
string
query
:
querystring
.
ParsedUrlQuery
provider
:
HttpProvider
fullPath
:
string
originalPath
:
string
}
interface
ProviderRoute
extends
Route
{
provider
:
HttpProvider
}
export
interface
HttpProviderOptions
{
readonly
base
:
string
readonly
auth
:
AuthType
...
...
@@ -132,9 +139,7 @@ export abstract class HttpProvider {
* Handle web sockets on the registered endpoint.
*/
public
abstract
handleWebSocket
(
base
:
string
,
requestPath
:
string
,
query
:
querystring
.
ParsedUrlQuery
,
route
:
Route
,
request
:
http
.
IncomingMessage
,
socket
:
net
.
Socket
,
head
:
Buffer
...
...
@@ -143,22 +148,18 @@ export abstract class HttpProvider {
/**
* Handle requests to the registered endpoint.
*/
public
abstract
handleRequest
(
base
:
string
,
requestPath
:
string
,
query
:
querystring
.
ParsedUrlQuery
,
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
|
undefined
>
protected
get
isDev
():
boolean
{
return
this
.
options
.
commit
===
"
development
"
}
public
abstract
handleRequest
(
route
:
Route
,
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
|
undefined
>
/**
*
Return the specified path with the base path prepended
.
*
Get the base relative to the provided route
.
*/
protected
withBase
(
path
:
string
):
string
{
return
normalize
(
`
${
this
.
options
.
base
}
/
${
path
}
`
)
public
base
(
route
:
Route
):
string
{
const
depth
=
route
.
fullPath
?
(
route
.
fullPath
.
match
(
/
\/
/g
)
||
[]).
length
:
1
return
normalize
(
"
./
"
+
(
depth
>
1
?
"
../
"
.
repeat
(
depth
-
1
)
:
""
))
}
protected
get
isDev
():
boolean
{
return
this
.
options
.
commit
===
"
development
"
}
/**
...
...
@@ -346,19 +347,14 @@ export class HttpServer {
private
listenPromise
:
Promise
<
string
|
null
>
|
undefined
public
readonly
protocol
:
"
http
"
|
"
https
"
private
readonly
providers
=
new
Map
<
string
,
HttpProvider
>
()
private
readonly
options
:
HttpServerOptions
private
readonly
heart
:
Heart
public
constructor
(
options
:
HttpServerOptions
)
{
public
constructor
(
private
readonly
options
:
HttpServerOptions
)
{
this
.
heart
=
new
Heart
(
path
.
join
(
xdgLocalDir
,
"
heartbeat
"
),
async
()
=>
{
const
connections
=
await
this
.
getConnections
()
logger
.
trace
(
`
${
connections
}
active connection
${
plural
(
connections
)}
`
)
return
connections
!==
0
})
this
.
options
=
{
...
options
,
basePath
:
options
.
basePath
?
options
.
basePath
.
replace
(
/
\/
+$/
,
""
)
:
""
,
}
this
.
protocol
=
this
.
options
.
cert
?
"
https
"
:
"
http
"
if
(
this
.
protocol
===
"
https
"
)
{
this
.
server
=
httpolyglot
.
createServer
(
...
...
@@ -452,30 +448,19 @@ export class HttpServer {
try
{
this
.
heart
.
beat
()
const
route
=
this
.
parseUrl
(
request
)
const
payload
=
this
.
maybeRedirect
(
request
,
route
)
||
(
await
route
.
provider
.
handleRequest
(
route
.
base
,
route
.
requestPath
,
route
.
query
,
request
))
const
payload
=
this
.
maybeRedirect
(
request
,
route
)
||
(
await
route
.
provider
.
handleRequest
(
route
,
request
))
if
(
!
payload
)
{
throw
new
HttpError
(
"
Not found
"
,
HttpCode
.
NotFound
)
}
const
basePath
=
this
.
options
.
basePath
||
"
/
"
response
.
writeHead
(
payload
.
redirect
?
HttpCode
.
Redirect
:
payload
.
code
||
HttpCode
.
Ok
,
{
"
Content-Type
"
:
payload
.
mime
||
getMediaMime
(
payload
.
filePath
),
...(
payload
.
redirect
?
{
Location
:
this
.
constructRedirect
(
request
.
headers
.
host
as
string
,
route
.
fullPath
,
normalize
(
`
${
basePath
}
/
${
payload
.
redirect
}
`
)
+
"
/
"
,
{
...
route
.
query
,
...(
payload
.
query
||
{})
}
),
}
:
{}),
...(
request
.
headers
[
"
service-worker
"
]
?
{
"
Service-Worker-Allowed
"
:
basePath
}
:
{}),
...(
payload
.
redirect
?
{
Location
:
payload
.
redirect
}
:
{}),
...(
request
.
headers
[
"
service-worker
"
]
?
{
"
Service-Worker-Allowed
"
:
route
.
provider
.
base
(
route
)
}
:
{}),
...(
payload
.
cache
?
{
"
Cache-Control
"
:
"
public, max-age=31536000
"
}
:
{}),
...(
payload
.
cookie
?
{
"
Set-Cookie
"
:
`
${
payload
.
cookie
.
key
}
=
${
payload
.
cookie
.
value
}
; Path=
${
basePath
}
; HttpOnly; SameSite=strict`
,
"
Set-Cookie
"
:
`
${
payload
.
cookie
.
key
}
=
${
payload
.
cookie
.
value
}
; Path=
${
payload
.
cookie
.
path
||
"
/
"
}
; HttpOnly; SameSite=strict`
,
}
:
{}),
...
payload
.
headers
,
...
...
@@ -497,9 +482,8 @@ export class HttpServer {
let
e
=
error
if
(
error
.
code
===
"
ENOENT
"
||
error
.
code
===
"
EISDIR
"
)
{
e
=
new
HttpError
(
"
Not found
"
,
HttpCode
.
NotFound
)
}
else
{
logger
.
error
(
error
.
stack
)
}
logger
.
debug
(
error
.
stack
)
response
.
writeHead
(
typeof
e
.
code
===
"
number
"
?
e
.
code
:
HttpCode
.
ServerError
)
response
.
end
(
error
.
message
)
}
...
...
@@ -509,14 +493,29 @@ export class HttpServer {
* Return any necessary redirection before delegating to a provider.
*/
private
maybeRedirect
(
request
:
http
.
IncomingMessage
,
route
:
ProviderRoute
):
HttpResponse
|
undefined
{
// Redirect to HTTPS.
const
redirect
=
(
path
:
string
):
string
=>
{
Object
.
keys
(
route
.
query
).
forEach
((
key
)
=>
{
if
(
typeof
route
.
query
[
key
]
===
"
undefined
"
)
{
delete
route
.
query
[
key
]
}
})
// If we're handling TLS ensure all requests are redirected to HTTPS.
return
this
.
options
.
cert
?
`
${
this
.
protocol
}
://
${
request
.
headers
.
host
}
`
:
""
+
normalize
(
`
${
route
.
provider
.
base
(
route
)}
/
${
path
}
`
,
true
)
+
(
Object
.
keys
(
route
.
query
).
length
>
0
?
`?
${
querystring
.
stringify
(
route
.
query
)}
`
:
""
)
}
// Redirect to HTTPS if we're handling the TLS.
if
(
this
.
options
.
cert
&&
!
(
request
.
connection
as
tls
.
TLSSocket
).
encrypted
)
{
return
{
redirect
:
r
oute
.
fullPath
}
return
{
redirect
:
r
edirect
(
route
.
fullPath
)
}
}
// Redirect indexes to a trailing slash so relative paths will operate
// against the provider.
if
(
route
.
requestPath
===
"
/index.html
"
&&
!
route
.
originalPath
.
endsWith
(
"
/
"
))
{
return
{
redirect
:
route
.
fullPath
}
// Redirect always includes a trailing slash.
// Redirect our indexes to a trailing slash so relative paths in the served
// HTML will operate against the base path properly.
if
(
route
.
requestPath
===
"
/index.html
"
&&
!
route
.
originalPath
.
endsWith
(
"
/
"
)
&&
this
.
providers
.
has
(
route
.
base
))
{
return
{
redirect
:
redirect
(
route
.
fullPath
+
"
/
"
)
}
}
return
undefined
}
...
...
@@ -534,12 +533,12 @@ export class HttpServer {
throw
new
HttpError
(
"
HTTP/1.1 400 Bad Request
"
,
HttpCode
.
BadRequest
)
}
const
{
base
,
requestPath
,
query
,
provider
}
=
this
.
parseUrl
(
request
)
if
(
!
provider
)
{
const
route
=
this
.
parseUrl
(
request
)
if
(
!
route
.
provider
)
{
throw
new
HttpError
(
"
Not found
"
,
HttpCode
.
NotFound
)
}
if
(
!
(
await
provider
.
handleWebSocket
(
base
,
requestPath
,
query
,
request
,
socket
,
head
)))
{
if
(
!
(
await
route
.
provider
.
handleWebSocket
(
route
,
request
,
socket
,
head
)))
{
throw
new
HttpError
(
"
Not found
"
,
HttpCode
.
NotFound
)
}
}
catch
(
error
)
{
...
...
@@ -593,21 +592,4 @@ export class HttpServer {
}
return
{
base
,
fullPath
,
requestPath
,
query
:
parsedUrl
.
query
,
provider
,
originalPath
}
}
/**
* Return the request URL with the specified base and new path.
*/
private
constructRedirect
(
host
:
string
,
oldPath
:
string
,
newPath
:
string
,
query
:
Query
):
string
{
if
(
oldPath
&&
oldPath
!==
"
/
"
&&
!
query
.
to
&&
/
\/
login
(\/
|$
)
/
.
test
(
newPath
)
&&
!
/
\/
login
(\/
|$
)
/
.
test
(
oldPath
))
{
query
.
to
=
oldPath
}
Object
.
keys
(
query
).
forEach
((
key
)
=>
{
if
(
typeof
query
[
key
]
===
"
undefined
"
)
{
delete
query
[
key
]
}
})
return
(
`
${
this
.
protocol
}
://
${
host
}${
newPath
}
`
+
(
Object
.
keys
(
query
).
length
>
0
?
`?
${
querystring
.
stringify
(
query
)}
`
:
""
)
)
}
}
src/node/vscode/error.html
浏览文件 @
4cc181ce
...
...
@@ -3,13 +3,8 @@
<head>
<meta
charset=
"utf-8"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
>
<
!-- <meta http-equiv="Content-Security-Policy" content="font-src 'self'; connect-src 'self'; default-src ws: wss:; style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:;"> --
>
<
meta
http-equiv=
"Content-Security-Policy"
content=
"font-src 'self' fonts.gstatic.com; connect-src 'self'; default-src ws: wss: 'self'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:;"
>
<title>
code-server
</title>
<link
rel=
"icon"
href=
"./static-{{COMMIT}}/src/browser/media/favicon.ico"
type=
"image/x-icon"
/>
<link
rel=
"manifest"
href=
"./static-{{COMMIT}}/src/browser/media/manifest.json"
crossorigin=
"use-credentials"
>
<link
rel=
"apple-touch-icon"
href=
"./static-{{COMMIT}}/src/browser/media/code-server.png"
/>
<link
href=
"https://fonts.googleapis.com/css?family=IBM+Plex+Sans&display=swap"
rel=
"stylesheet"
/>
<meta
id=
"coder-options"
data-settings=
"{{OPTIONS}}"
>
</head>
<body>
<div
id=
"root"
style=
"color:#f4f4f4;padding:20px;max-width:700px;"
>
...
...
src/node/vscode/server.ts
浏览文件 @
4cc181ce
...
...
@@ -4,7 +4,6 @@ import * as crypto from "crypto"
import
*
as
http
from
"
http
"
import
*
as
net
from
"
net
"
import
*
as
path
from
"
path
"
import
*
as
querystring
from
"
querystring
"
import
{
CodeServerMessage
,
Settings
,
...
...
@@ -13,7 +12,7 @@ import {
WorkbenchOptions
,
}
from
"
../../../lib/vscode/src/vs/server/ipc
"
import
{
generateUuid
}
from
"
../../common/util
"
import
{
HttpProvider
,
HttpProviderOptions
,
HttpResponse
}
from
"
../http
"
import
{
HttpProvider
,
HttpProviderOptions
,
HttpResponse
,
Route
}
from
"
../http
"
import
{
SettingsProvider
}
from
"
../settings
"
import
{
xdgLocalDir
}
from
"
../util
"
...
...
@@ -76,13 +75,7 @@ export class VscodeHttpProvider extends HttpProvider {
return
this
.
_vscode
}
public
async
handleWebSocket
(
_base
:
string
,
_requestPath
:
string
,
query
:
querystring
.
ParsedUrlQuery
,
request
:
http
.
IncomingMessage
,
socket
:
net
.
Socket
):
Promise
<
true
>
{
public
async
handleWebSocket
(
route
:
Route
,
request
:
http
.
IncomingMessage
,
socket
:
net
.
Socket
):
Promise
<
true
>
{
if
(
!
this
.
authenticated
(
request
))
{
throw
new
Error
(
"
not authenticated
"
)
}
...
...
@@ -105,7 +98,7 @@ export class VscodeHttpProvider extends HttpProvider {
)
const
vscode
=
await
this
.
_vscode
this
.
send
({
type
:
"
socket
"
,
query
},
vscode
,
socket
)
this
.
send
({
type
:
"
socket
"
,
query
:
route
.
query
},
vscode
,
socket
)
return
true
}
...
...
@@ -116,27 +109,20 @@ export class VscodeHttpProvider extends HttpProvider {
vscode
.
send
(
message
,
socket
)
}
public
async
handleRequest
(
base
:
string
,
requestPath
:
string
,
query
:
querystring
.
ParsedUrlQuery
,
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
|
undefined
>
{
public
async
handleRequest
(
route
:
Route
,
request
:
http
.
IncomingMessage
):
Promise
<
HttpResponse
|
undefined
>
{
this
.
ensureGet
(
request
)
switch
(
base
)
{
this
.
ensureAuthenticated
(
request
)
switch
(
route
.
base
)
{
case
"
/
"
:
if
(
!
this
.
authenticated
(
request
))
{
return
{
redirect
:
"
/login
"
}
}
try
{
return
await
this
.
getRoot
(
request
,
query
)
return
await
this
.
getRoot
(
request
,
route
)
}
catch
(
error
)
{
return
this
.
getErrorRoot
(
error
)
}
case
"
/static
"
:
{
switch
(
requestPath
)
{
switch
(
r
oute
.
r
equestPath
)
{
case
"
/out/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js
"
:
{
const
response
=
await
this
.
getUtf8Resource
(
this
.
vsRootPath
,
requestPath
)
const
response
=
await
this
.
getUtf8Resource
(
this
.
vsRootPath
,
r
oute
.
r
equestPath
)
response
.
content
=
response
.
content
.
replace
(
/{{COMMIT}}/g
,
this
.
workbenchOptions
?
this
.
workbenchOptions
.
commit
:
""
...
...
@@ -145,40 +131,37 @@ export class VscodeHttpProvider extends HttpProvider {
return
response
}
}
const
response
=
await
this
.
getResource
(
this
.
vsRootPath
,
requestPath
)
const
response
=
await
this
.
getResource
(
this
.
vsRootPath
,
r
oute
.
r
equestPath
)
response
.
cache
=
true
return
response
}
case
"
/resource
"
:
case
"
/vscode-remote-resource
"
:
this
.
ensureAuthenticated
(
request
)
if
(
typeof
query
.
path
===
"
string
"
)
{
return
this
.
getResource
(
query
.
path
)
if
(
typeof
route
.
query
.
path
===
"
string
"
)
{
return
this
.
getResource
(
route
.
query
.
path
)
}
break
case
"
/tar
"
:
this
.
ensureAuthenticated
(
request
)
if
(
typeof
query
.
path
===
"
string
"
)
{
return
this
.
getTarredResource
(
query
.
path
)
if
(
typeof
route
.
query
.
path
===
"
string
"
)
{
return
this
.
getTarredResource
(
route
.
query
.
path
)
}
break
case
"
/webview
"
:
this
.
ensureAuthenticated
(
request
)
if
(
/^
\/
vscode-resource/
.
test
(
requestPath
))
{
return
this
.
getResource
(
requestPath
.
replace
(
/^
\/
vscode-resource
(\/
file
)?
/
,
""
))
if
(
/^
\/
vscode-resource/
.
test
(
route
.
requestPath
))
{
return
this
.
getResource
(
route
.
requestPath
.
replace
(
/^
\/
vscode-resource
(\/
file
)?
/
,
""
))
}
return
this
.
getResource
(
this
.
vsRootPath
,
"
out/vs/workbench/contrib/webview/browser/pre
"
,
requestPath
)
return
this
.
getResource
(
this
.
vsRootPath
,
"
out/vs/workbench/contrib/webview/browser/pre
"
,
r
oute
.
r
equestPath
)
}
return
undefined
}
private
async
getRoot
(
request
:
http
.
IncomingMessage
,
query
:
querystring
.
ParsedUrlQuery
):
Promise
<
HttpResponse
>
{
private
async
getRoot
(
request
:
http
.
IncomingMessage
,
route
:
Route
):
Promise
<
HttpResponse
>
{
const
settings
=
await
this
.
settings
.
read
()
const
[
response
,
options
]
=
await
Promise
.
all
([
await
this
.
getUtf8Resource
(
this
.
rootPath
,
`src/node/vscode/workbench
${
!
this
.
isDev
?
"
-build
"
:
""
}
.html`
),
this
.
initialize
({
args
:
this
.
args
,
query
,
query
:
route
.
query
,
remoteAuthority
:
request
.
headers
.
host
as
string
,
settings
,
}),
...
...
src/node/vscode/workbench-build.html
浏览文件 @
4cc181ce
...
...
@@ -19,10 +19,10 @@
<meta
id=
"vscode-remote-nls-configuration"
data-settings=
"{{NLS_CONFIGURATION}}"
>
<!-- Workbench Icon/Manifest/CSS -->
<link
rel=
"icon"
href=
"./static-{{COMMIT}}/src/browser/media/favicon.ico"
type=
"image/x-icon"
/>
<link
rel=
"manifest"
href=
"./static-{{COMMIT}}/src/browser/media/manifest.json"
crossorigin=
"use-credentials"
>
<link
data-name=
"vs/workbench/workbench.web.api"
rel=
"stylesheet"
href=
"./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css"
>
<link
rel=
"apple-touch-icon"
href=
"./static-{{COMMIT}}/src/browser/media/code-server.png"
/>
<link
rel=
"icon"
href=
".
.
/static-{{COMMIT}}/src/browser/media/favicon.ico"
type=
"image/x-icon"
/>
<link
rel=
"manifest"
href=
".
.
/static-{{COMMIT}}/src/browser/media/manifest.json"
crossorigin=
"use-credentials"
>
<link
data-name=
"vs/workbench/workbench.web.api"
rel=
"stylesheet"
href=
".
.
/static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css"
>
<link
rel=
"apple-touch-icon"
href=
".
.
/static-{{COMMIT}}/src/browser/media/code-server.png"
/>
<meta
name=
"apple-mobile-web-app-capable"
content=
"yes"
>
<!-- Prefetch to avoid waterfall -->
...
...
src/node/vscode/workbench.html
浏览文件 @
4cc181ce
...
...
@@ -19,9 +19,9 @@
<meta
id=
"vscode-remote-nls-configuration"
data-settings=
"{{NLS_CONFIGURATION}}"
>
<!-- Workbench Icon/Manifest/CSS -->
<link
rel=
"icon"
href=
"./static/src/browser/media/favicon.ico"
type=
"image/x-icon"
/>
<link
rel=
"manifest"
href=
"./static/src/browser/media/manifest.json"
crossorigin=
"use-credentials"
>
<link
rel=
"apple-touch-icon"
href=
"./static/src/browser/media/code-server.png"
/>
<link
rel=
"icon"
href=
".
.
/static/src/browser/media/favicon.ico"
type=
"image/x-icon"
/>
<link
rel=
"manifest"
href=
".
.
/static/src/browser/media/manifest.json"
crossorigin=
"use-credentials"
>
<link
rel=
"apple-touch-icon"
href=
".
.
/static/src/browser/media/code-server.png"
/>
<meta
name=
"apple-mobile-web-app-capable"
content=
"yes"
>
</head>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录