Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
镜像
Coder
code-server
提交
055e0ef9
C
code-server
项目概览
镜像
/
Coder
/
code-server
2022-09-21 03:15:05同步失败
通知
15
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
code-server
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
055e0ef9
编写于
1月 20, 2021
作者:
A
Asher
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Provide WsRouter to plugins
上级
fb37473e
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
101 addition
and
34 deletion
+101
-34
src/node/plugin.ts
src/node/plugin.ts
+9
-5
src/node/routes/index.ts
src/node/routes/index.ts
+11
-11
src/node/routes/pathProxy.ts
src/node/routes/pathProxy.ts
+2
-2
src/node/wsRouter.ts
src/node/wsRouter.ts
+4
-15
test/httpserver.ts
test/httpserver.ts
+17
-0
test/plugin.test.ts
test/plugin.test.ts
+12
-1
test/test-plugin/src/index.ts
test/test-plugin/src/index.ts
+13
-0
typings/pluginapi.d.ts
typings/pluginapi.d.ts
+33
-0
未找到文件。
src/node/plugin.ts
浏览文件 @
055e0ef9
...
@@ -7,6 +7,7 @@ import * as pluginapi from "../../typings/pluginapi"
...
@@ -7,6 +7,7 @@ import * as pluginapi from "../../typings/pluginapi"
import
{
version
}
from
"
./constants
"
import
{
version
}
from
"
./constants
"
import
{
proxy
}
from
"
./proxy
"
import
{
proxy
}
from
"
./proxy
"
import
*
as
util
from
"
./util
"
import
*
as
util
from
"
./util
"
import
{
Router
as
WsRouter
,
WebsocketRouter
}
from
"
./wsRouter
"
const
fsp
=
fs
.
promises
const
fsp
=
fs
.
promises
/**
/**
...
@@ -21,6 +22,7 @@ require("module")._load = function (request: string, parent: object, isMain: boo
...
@@ -21,6 +22,7 @@ require("module")._load = function (request: string, parent: object, isMain: boo
express
,
express
,
field
,
field
,
proxy
,
proxy
,
WsRouter
,
}
}
}
}
return
originalLoad
.
apply
(
this
,
[
request
,
parent
,
isMain
])
return
originalLoad
.
apply
(
this
,
[
request
,
parent
,
isMain
])
...
@@ -103,14 +105,16 @@ export class PluginAPI {
...
@@ -103,14 +105,16 @@ export class PluginAPI {
}
}
/**
/**
* mount mounts all plugin routers onto r.
* mount mounts all plugin routers onto r
and websocket routers onto wr
.
*/
*/
public
mount
(
r
:
express
.
Router
):
void
{
public
mount
(
r
:
express
.
Router
,
wr
:
express
.
Router
):
void
{
for
(
const
[,
p
]
of
this
.
plugins
)
{
for
(
const
[,
p
]
of
this
.
plugins
)
{
if
(
!
p
.
router
)
{
if
(
p
.
router
)
{
continue
r
.
use
(
`
${
p
.
routerPath
}
`
,
p
.
router
())
}
if
(
p
.
wsRouter
)
{
wr
.
use
(
`
${
p
.
routerPath
}
`
,
(
p
.
wsRouter
()
as
WebsocketRouter
).
router
)
}
}
r
.
use
(
`
${
p
.
routerPath
}
`
,
p
.
router
())
}
}
}
}
...
...
src/node/routes/index.ts
浏览文件 @
055e0ef9
...
@@ -6,20 +6,20 @@ import { promises as fs } from "fs"
...
@@ -6,20 +6,20 @@ import { promises as fs } from "fs"
import
http
from
"
http
"
import
http
from
"
http
"
import
*
as
path
from
"
path
"
import
*
as
path
from
"
path
"
import
*
as
tls
from
"
tls
"
import
*
as
tls
from
"
tls
"
import
*
as
pluginapi
from
"
../../../typings/pluginapi
"
import
{
HttpCode
,
HttpError
}
from
"
../../common/http
"
import
{
HttpCode
,
HttpError
}
from
"
../../common/http
"
import
{
plural
}
from
"
../../common/util
"
import
{
plural
}
from
"
../../common/util
"
import
{
AuthType
,
DefaultedArgs
}
from
"
../cli
"
import
{
AuthType
,
DefaultedArgs
}
from
"
../cli
"
import
{
rootPath
}
from
"
../constants
"
import
{
rootPath
}
from
"
../constants
"
import
{
Heart
}
from
"
../heart
"
import
{
Heart
}
from
"
../heart
"
import
{
re
placeTemplates
,
redirect
}
from
"
../http
"
import
{
re
direct
,
replaceTemplates
}
from
"
../http
"
import
{
PluginAPI
}
from
"
../plugin
"
import
{
PluginAPI
}
from
"
../plugin
"
import
{
getMediaMime
,
paths
}
from
"
../util
"
import
{
getMediaMime
,
paths
}
from
"
../util
"
import
{
WebsocketRequest
}
from
"
../wsRouter
"
import
*
as
apps
from
"
./apps
"
import
*
as
apps
from
"
./apps
"
import
*
as
domainProxy
from
"
./domainProxy
"
import
*
as
domainProxy
from
"
./domainProxy
"
import
*
as
health
from
"
./health
"
import
*
as
health
from
"
./health
"
import
*
as
login
from
"
./login
"
import
*
as
login
from
"
./login
"
import
*
as
proxy
from
"
./pathProxy
"
import
*
as
p
athP
roxy
from
"
./pathProxy
"
// static is a reserved keyword.
// static is a reserved keyword.
import
*
as
_static
from
"
./static
"
import
*
as
_static
from
"
./static
"
import
*
as
update
from
"
./update
"
import
*
as
update
from
"
./update
"
...
@@ -104,21 +104,21 @@ export const register = async (
...
@@ -104,21 +104,21 @@ export const register = async (
wsApp
.
use
(
"
/
"
,
domainProxy
.
wsRouter
.
router
)
wsApp
.
use
(
"
/
"
,
domainProxy
.
wsRouter
.
router
)
app
.
all
(
"
/proxy/(:port)(/*)?
"
,
(
req
,
res
)
=>
{
app
.
all
(
"
/proxy/(:port)(/*)?
"
,
(
req
,
res
)
=>
{
proxy
.
proxy
(
req
,
res
)
p
athP
roxy
.
proxy
(
req
,
res
)
})
})
wsApp
.
get
(
"
/proxy/(:port)(/*)?
"
,
(
req
,
res
)
=>
{
wsApp
.
get
(
"
/proxy/(:port)(/*)?
"
,
(
req
)
=>
{
p
roxy
.
wsProxy
(
req
as
WebsocketRequest
)
p
athProxy
.
wsProxy
(
req
as
pluginapi
.
WebsocketRequest
)
})
})
// These two routes pass through the path directly.
// These two routes pass through the path directly.
// So the proxied app must be aware it is running
// So the proxied app must be aware it is running
// under /absproxy/<someport>/
// under /absproxy/<someport>/
app
.
all
(
"
/absproxy/(:port)(/*)?
"
,
(
req
,
res
)
=>
{
app
.
all
(
"
/absproxy/(:port)(/*)?
"
,
(
req
,
res
)
=>
{
proxy
.
proxy
(
req
,
res
,
{
p
athP
roxy
.
proxy
(
req
,
res
,
{
passthroughPath
:
true
,
passthroughPath
:
true
,
})
})
})
})
wsApp
.
get
(
"
/absproxy/(:port)(/*)?
"
,
(
req
,
res
)
=>
{
wsApp
.
get
(
"
/absproxy/(:port)(/*)?
"
,
(
req
)
=>
{
p
roxy
.
wsProxy
(
req
as
WebsocketRequest
,
{
p
athProxy
.
wsProxy
(
req
as
pluginapi
.
WebsocketRequest
,
{
passthroughPath
:
true
,
passthroughPath
:
true
,
})
})
})
})
...
@@ -146,7 +146,7 @@ export const register = async (
...
@@ -146,7 +146,7 @@ export const register = async (
const
papi
=
new
PluginAPI
(
logger
,
process
.
env
.
CS_PLUGIN
,
process
.
env
.
CS_PLUGIN_PATH
)
const
papi
=
new
PluginAPI
(
logger
,
process
.
env
.
CS_PLUGIN
,
process
.
env
.
CS_PLUGIN_PATH
)
await
papi
.
loadPlugins
()
await
papi
.
loadPlugins
()
papi
.
mount
(
app
)
papi
.
mount
(
app
,
wsApp
)
app
.
use
(
"
/api/applications
"
,
apps
.
router
(
papi
))
app
.
use
(
"
/api/applications
"
,
apps
.
router
(
papi
))
app
.
use
(()
=>
{
app
.
use
(()
=>
{
...
@@ -187,7 +187,7 @@ export const register = async (
...
@@ -187,7 +187,7 @@ export const register = async (
const
wsErrorHandler
:
express
.
ErrorRequestHandler
=
async
(
err
,
req
,
res
,
next
)
=>
{
const
wsErrorHandler
:
express
.
ErrorRequestHandler
=
async
(
err
,
req
,
res
,
next
)
=>
{
logger
.
error
(
`
${
err
.
message
}
${
err
.
stack
}
`
)
logger
.
error
(
`
${
err
.
message
}
${
err
.
stack
}
`
)
;(
req
as
WebsocketRequest
).
ws
.
end
()
;(
req
as
pluginapi
.
WebsocketRequest
).
ws
.
end
()
}
}
wsApp
.
use
(
wsErrorHandler
)
wsApp
.
use
(
wsErrorHandler
)
...
...
src/node/routes/pathProxy.ts
浏览文件 @
055e0ef9
import
{
Request
,
Response
}
from
"
express
"
import
{
Request
,
Response
}
from
"
express
"
import
*
as
path
from
"
path
"
import
*
as
path
from
"
path
"
import
qs
from
"
qs
"
import
qs
from
"
qs
"
import
*
as
pluginapi
from
"
../../../typings/pluginapi
"
import
{
HttpCode
,
HttpError
}
from
"
../../common/http
"
import
{
HttpCode
,
HttpError
}
from
"
../../common/http
"
import
{
normalize
}
from
"
../../common/util
"
import
{
normalize
}
from
"
../../common/util
"
import
{
authenticated
,
ensureAuthenticated
,
redirect
}
from
"
../http
"
import
{
authenticated
,
ensureAuthenticated
,
redirect
}
from
"
../http
"
import
{
proxy
as
_proxy
}
from
"
../proxy
"
import
{
proxy
as
_proxy
}
from
"
../proxy
"
import
{
WebsocketRequest
}
from
"
../wsRouter
"
const
getProxyTarget
=
(
req
:
Request
,
passthroughPath
?:
boolean
):
string
=>
{
const
getProxyTarget
=
(
req
:
Request
,
passthroughPath
?:
boolean
):
string
=>
{
if
(
passthroughPath
)
{
if
(
passthroughPath
)
{
...
@@ -46,7 +46,7 @@ export function proxy(
...
@@ -46,7 +46,7 @@ export function proxy(
}
}
export
function
wsProxy
(
export
function
wsProxy
(
req
:
WebsocketRequest
,
req
:
pluginapi
.
WebsocketRequest
,
opts
?:
{
opts
?:
{
passthroughPath
?:
boolean
passthroughPath
?:
boolean
},
},
...
...
src/node/wsRouter.ts
浏览文件 @
055e0ef9
import
*
as
express
from
"
express
"
import
*
as
express
from
"
express
"
import
*
as
expressCore
from
"
express-serve-static-core
"
import
*
as
expressCore
from
"
express-serve-static-core
"
import
*
as
http
from
"
http
"
import
*
as
http
from
"
http
"
import
*
as
net
from
"
net
"
import
*
as
pluginapi
from
"
../../typings/pluginapi
"
export
const
handleUpgrade
=
(
app
:
express
.
Express
,
server
:
http
.
Server
):
void
=>
{
export
const
handleUpgrade
=
(
app
:
express
.
Express
,
server
:
http
.
Server
):
void
=>
{
server
.
on
(
"
upgrade
"
,
(
req
,
socket
,
head
)
=>
{
server
.
on
(
"
upgrade
"
,
(
req
,
socket
,
head
)
=>
{
...
@@ -20,31 +20,20 @@ export const handleUpgrade = (app: express.Express, server: http.Server): void =
...
@@ -20,31 +20,20 @@ export const handleUpgrade = (app: express.Express, server: http.Server): void =
})
})
}
}
export
interface
WebsocketRequest
extends
express
.
Request
{
interface
InternalWebsocketRequest
extends
pluginapi
.
WebsocketRequest
{
ws
:
net
.
Socket
head
:
Buffer
}
interface
InternalWebsocketRequest
extends
WebsocketRequest
{
_ws_handled
:
boolean
_ws_handled
:
boolean
}
}
export
type
WebSocketHandler
=
(
req
:
WebsocketRequest
,
res
:
express
.
Response
,
next
:
express
.
NextFunction
,
)
=>
void
|
Promise
<
void
>
export
class
WebsocketRouter
{
export
class
WebsocketRouter
{
public
readonly
router
=
express
.
Router
()
public
readonly
router
=
express
.
Router
()
public
ws
(
route
:
expressCore
.
PathParams
,
...
handlers
:
WebSocketHandler
[]):
void
{
public
ws
(
route
:
expressCore
.
PathParams
,
...
handlers
:
pluginapi
.
WebSocketHandler
[]):
void
{
this
.
router
.
get
(
this
.
router
.
get
(
route
,
route
,
...
handlers
.
map
((
handler
)
=>
{
...
handlers
.
map
((
handler
)
=>
{
const
wrapped
:
express
.
Handler
=
(
req
,
res
,
next
)
=>
{
const
wrapped
:
express
.
Handler
=
(
req
,
res
,
next
)
=>
{
;(
req
as
InternalWebsocketRequest
).
_ws_handled
=
true
;(
req
as
InternalWebsocketRequest
).
_ws_handled
=
true
return
handler
(
req
as
WebsocketRequest
,
res
,
next
)
return
handler
(
req
as
pluginapi
.
WebsocketRequest
,
res
,
next
)
}
}
return
wrapped
return
wrapped
}),
}),
...
...
test/httpserver.ts
浏览文件 @
055e0ef9
import
*
as
express
from
"
express
"
import
*
as
http
from
"
http
"
import
*
as
http
from
"
http
"
import
*
as
nodeFetch
from
"
node-fetch
"
import
*
as
nodeFetch
from
"
node-fetch
"
import
Websocket
from
"
ws
"
import
*
as
util
from
"
../src/common/util
"
import
*
as
util
from
"
../src/common/util
"
import
{
ensureAddress
}
from
"
../src/node/app
"
import
{
ensureAddress
}
from
"
../src/node/app
"
import
{
handleUpgrade
}
from
"
../src/node/wsRouter
"
// Perhaps an abstraction similar to this should be used in app.ts as well.
// Perhaps an abstraction similar to this should be used in app.ts as well.
export
class
HttpServer
{
export
class
HttpServer
{
...
@@ -39,6 +42,13 @@ export class HttpServer {
...
@@ -39,6 +42,13 @@ export class HttpServer {
})
})
}
}
/**
* Send upgrade requests to an Express app.
*/
public
listenUpgrade
(
app
:
express
.
Express
):
void
{
handleUpgrade
(
app
,
this
.
hs
)
}
/**
/**
* close cleans up the server.
* close cleans up the server.
*/
*/
...
@@ -62,6 +72,13 @@ export class HttpServer {
...
@@ -62,6 +72,13 @@ export class HttpServer {
return
nodeFetch
.
default
(
`
${
ensureAddress
(
this
.
hs
)}${
requestPath
}
`
,
opts
)
return
nodeFetch
.
default
(
`
${
ensureAddress
(
this
.
hs
)}${
requestPath
}
`
,
opts
)
}
}
/**
* Open a websocket against the requset path.
*/
public
ws
(
requestPath
:
string
):
Websocket
{
return
new
Websocket
(
`
${
ensureAddress
(
this
.
hs
).
replace
(
"
http:
"
,
"
ws:
"
)}${
requestPath
}
`
)
}
public
port
():
number
{
public
port
():
number
{
const
addr
=
this
.
hs
.
address
()
const
addr
=
this
.
hs
.
address
()
if
(
addr
&&
typeof
addr
===
"
object
"
)
{
if
(
addr
&&
typeof
addr
===
"
object
"
)
{
...
...
test/plugin.test.ts
浏览文件 @
055e0ef9
...
@@ -21,11 +21,13 @@ describe("plugin", () => {
...
@@ -21,11 +21,13 @@ describe("plugin", () => {
await
papi
.
loadPlugins
(
false
)
await
papi
.
loadPlugins
(
false
)
const
app
=
express
.
default
()
const
app
=
express
.
default
()
papi
.
mount
(
app
)
const
wsApp
=
express
.
default
()
papi
.
mount
(
app
,
wsApp
)
app
.
use
(
"
/api/applications
"
,
apps
.
router
(
papi
))
app
.
use
(
"
/api/applications
"
,
apps
.
router
(
papi
))
s
=
new
httpserver
.
HttpServer
()
s
=
new
httpserver
.
HttpServer
()
await
s
.
listen
(
app
)
await
s
.
listen
(
app
)
s
.
listenUpgrade
(
wsApp
)
})
})
afterAll
(
async
()
=>
{
afterAll
(
async
()
=>
{
...
@@ -70,4 +72,13 @@ describe("plugin", () => {
...
@@ -70,4 +72,13 @@ describe("plugin", () => {
const
body
=
await
resp
.
text
()
const
body
=
await
resp
.
text
()
expect
(
body
).
toBe
(
indexHTML
)
expect
(
body
).
toBe
(
indexHTML
)
})
})
it
(
"
/test-plugin/test-app (websocket)
"
,
async
()
=>
{
const
ws
=
s
.
ws
(
"
/test-plugin/test-app
"
)
const
message
=
await
new
Promise
((
resolve
)
=>
{
ws
.
once
(
"
message
"
,
(
message
)
=>
resolve
(
message
))
})
ws
.
terminate
()
expect
(
message
).
toBe
(
"
hello
"
)
})
})
})
test/test-plugin/src/index.ts
浏览文件 @
055e0ef9
import
*
as
cs
from
"
code-server
"
import
*
as
cs
from
"
code-server
"
import
*
as
fspath
from
"
path
"
import
*
as
fspath
from
"
path
"
import
Websocket
from
"
ws
"
const
wss
=
new
Websocket
.
Server
({
noServer
:
true
})
export
const
plugin
:
cs
.
Plugin
=
{
export
const
plugin
:
cs
.
Plugin
=
{
displayName
:
"
Test Plugin
"
,
displayName
:
"
Test Plugin
"
,
...
@@ -22,6 +25,16 @@ export const plugin: cs.Plugin = {
...
@@ -22,6 +25,16 @@ export const plugin: cs.Plugin = {
return
r
return
r
},
},
wsRouter
()
{
const
wr
=
cs
.
WsRouter
()
wr
.
ws
(
"
/test-app
"
,
(
req
)
=>
{
wss
.
handleUpgrade
(
req
,
req
.
socket
,
req
.
head
,
(
ws
)
=>
{
ws
.
send
(
"
hello
"
)
})
})
return
wr
},
applications
()
{
applications
()
{
return
[
return
[
{
{
...
...
typings/pluginapi.d.ts
浏览文件 @
055e0ef9
...
@@ -3,6 +3,9 @@
...
@@ -3,6 +3,9 @@
*/
*/
import
{
field
,
Logger
}
from
"
@coder/logger
"
import
{
field
,
Logger
}
from
"
@coder/logger
"
import
*
as
express
from
"
express
"
import
*
as
express
from
"
express
"
import
*
as
expressCore
from
"
express-serve-static-core
"
import
ProxyServer
from
"
http-proxy
"
import
*
as
net
from
"
net
"
/**
/**
* Overlay
* Overlay
...
@@ -78,6 +81,27 @@ import * as express from "express"
...
@@ -78,6 +81,27 @@ import * as express from "express"
* ]
* ]
*/
*/
export
interface
WebsocketRequest
extends
express
.
Request
{
ws
:
net
.
Socket
head
:
Buffer
}
export
type
WebSocketHandler
=
(
req
:
WebsocketRequest
,
res
:
express
.
Response
,
next
:
express
.
NextFunction
,
)
=>
void
|
Promise
<
void
>
export
interface
WebsocketRouter
{
readonly
router
:
express
.
Router
ws
(
route
:
expressCore
.
PathParams
,
...
handlers
:
WebSocketHandler
[]):
void
}
/**
* Create a router for websocket routes.
*/
export
function
WsRouter
():
WebsocketRouter
/**
/**
* The Express import used by code-server.
* The Express import used by code-server.
*
*
...
@@ -152,6 +176,15 @@ export interface Plugin {
...
@@ -152,6 +176,15 @@ export interface Plugin {
*/
*/
router
?():
express
.
Router
router
?():
express
.
Router
/**
* Returns the plugin's websocket router.
*
* Mounted at <code-sever-root>/<plugin-path>
*
* If not present, the plugin provides no websockets.
*/
wsRouter
?():
WebsocketRouter
/**
/**
* code-server uses this to collect the list of applications that
* code-server uses this to collect the list of applications that
* the plugin can currently provide.
* the plugin can currently provide.
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录