Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
myhjmzy
code-server
提交
55a7e8b5
C
code-server
项目概览
myhjmzy
/
code-server
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
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,体验更适合开发者的 AI 搜索 >>
未验证
提交
55a7e8b5
编写于
9月 09, 2020
作者:
A
Anmol Sethi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Implement automatic cloud proxying
上级
916e24e1
变更
4
显示空白变更内容
内联
并排
Showing
4 changed file
with
138 addition
and
3 deletion
+138
-3
package.json
package.json
+2
-1
src/node/coder-cloud.ts
src/node/coder-cloud.ts
+128
-1
src/node/entry.ts
src/node/entry.ts
+3
-1
yarn.lock
yarn.lock
+5
-0
未找到文件。
package.json
浏览文件 @
55a7e8b5
...
...
@@ -82,7 +82,8 @@
"
tar-fs
"
:
"
^2.0.0
"
,
"
ws
"
:
"
^7.2.0
"
,
"
xdg-basedir
"
:
"
^4.0.0
"
,
"
yarn
"
:
"
^1.22.4
"
"
yarn
"
:
"
^1.22.4
"
,
"
delay
"
:
"
^4.4.0
"
},
"bin"
:
{
"code-server"
:
"out/node/entry.js"
...
...
src/node/coder-cloud.ts
浏览文件 @
55a7e8b5
...
...
@@ -2,9 +2,14 @@ import { spawn } from "child_process"
import
path
from
"
path
"
import
{
logger
}
from
"
@coder/logger
"
import
split2
from
"
split2
"
import
delay
from
"
delay
"
import
fs
from
"
fs
"
import
{
promisify
}
from
"
util
"
import
xdgBasedir
from
"
xdg-basedir
"
const
coderCloudAgent
=
path
.
resolve
(
__dirname
,
"
../../lib/coder-cloud-agent
"
)
export
async
function
coderCloudExpose
(
serverName
:
string
):
Promise
<
void
>
{
const
coderCloudAgent
=
path
.
resolve
(
__dirname
,
"
../../lib/coder-cloud-agent
"
)
const
agent
=
spawn
(
coderCloudAgent
,
[
"
link
"
,
serverName
],
{
stdio
:
[
"
inherit
"
,
"
inherit
"
,
"
pipe
"
],
})
...
...
@@ -28,3 +33,125 @@ export async function coderCloudExpose(serverName: string): Promise<void> {
})
})
}
export
function
coderCloudProxy
(
addr
:
string
)
{
// addr needs to be in host:port format.
// So we trim the protocol.
addr
=
addr
.
replace
(
/^https
?
:
\/\/
/
,
""
)
if
(
!
xdgBasedir
.
config
)
{
return
}
const
sessionTokenPath
=
path
.
join
(
xdgBasedir
.
config
,
"
coder-cloud
"
,
"
session
"
)
const
_proxy
=
async
()
=>
{
await
waitForPath
(
sessionTokenPath
)
logger
.
info
(
"
exposing coder-server with coder-cloud
"
)
const
agent
=
spawn
(
coderCloudAgent
,
[
"
proxy
"
,
"
--code-server-addr
"
,
addr
],
{
stdio
:
[
"
inherit
"
,
"
inherit
"
,
"
pipe
"
],
})
agent
.
stderr
.
pipe
(
split2
()).
on
(
"
data
"
,
line
=>
{
line
=
line
.
replace
(
/^
[
0-9-
]
+
[
0-9:
]
+
[^
]
+
\t
/
,
""
)
logger
.
info
(
line
)
})
return
new
Promise
((
res
,
rej
)
=>
{
agent
.
on
(
"
error
"
,
rej
)
agent
.
on
(
"
close
"
,
code
=>
{
if
(
code
!==
0
)
{
rej
({
message
:
`coder cloud agent exited with
${
code
}
`
,
})
return
}
res
()
})
})
}
const
proxy
=
async
()
=>
{
try
{
await
_proxy
()
}
catch
(
err
)
{
logger
.
error
(
err
.
message
)
}
setTimeout
(
proxy
,
3000
)
}
proxy
()
}
/**
* waitForPath efficiently implements waiting for the existence of a path.
*
* We intentionally do not use fs.watchFile as it is very slow from testing.
* I believe it polls instead of watching.
*
* The way this works is for each level of the path it will check if it exists
* and if not, it will wait for it. e.g. if the path is /home/nhooyr/.config/coder-cloud/session
* then first it will check if /home exists, then /home/nhooyr and so on.
*
* The wait works by first creating a watch promise for the p segment.
* We call fs.watch on the dirname of the p segment. When the dirname has a change,
* we check if the p segment exists and if it does, we resolve the watch promise.
* On any error or the watcher being closed, we reject the watch promise.
*
* Once that promise is setup, we check if the p segment exists with fs.exists
* and if it does, we close the watcher and return.
*
* Now we race the watch promise and a 2000ms delay promise. Once the race
* is complete, we close the watcher.
*
* If the watch promise was the one to resolve, we return.
* Otherwise we setup the watch promise again and retry.
*
* This combination of polling and watching is very reliable and efficient.
*/
async
function
waitForPath
(
p
:
string
):
Promise
<
void
>
{
const
segs
=
p
.
split
(
path
.
sep
)
for
(
let
i
=
0
;
i
<
segs
.
length
;
i
++
)
{
const
s
=
path
.
join
(
"
/
"
,
...
segs
.
slice
(
0
,
i
+
1
))
// We need to wait for each segment to exist.
await
_waitForPath
(
s
)
}
}
async
function
_waitForPath
(
p
:
string
):
Promise
<
void
>
{
const
watchDir
=
path
.
dirname
(
p
)
logger
.
debug
(
`waiting for
${
p
}
`
)
for
(;;)
{
const
w
=
fs
.
watch
(
watchDir
)
const
watchPromise
=
new
Promise
<
void
>
((
res
,
rej
)
=>
{
w
.
on
(
"
change
"
,
async
()
=>
{
if
(
await
promisify
(
fs
.
exists
)(
p
))
{
res
()
}
})
w
.
on
(
"
close
"
,
()
=>
rej
(
new
Error
(
"
watcher closed
"
)))
w
.
on
(
"
error
"
,
rej
)
})
// We want to ignore any errors from this promise being rejected if the file
// already exists below.
watchPromise
.
catch
(()
=>
{})
if
(
await
promisify
(
fs
.
exists
)(
p
))
{
// The path exists!
w
.
close
()
return
}
// Now we wait for either the watch promise to resolve/reject or 2000ms.
const
s
=
await
Promise
.
race
([
watchPromise
.
then
(()
=>
"
exists
"
),
delay
(
2000
)])
w
.
close
()
if
(
s
===
"
exists
"
)
{
return
}
}
}
src/node/entry.ts
浏览文件 @
55a7e8b5
...
...
@@ -12,11 +12,11 @@ import { StaticHttpProvider } from "./app/static"
import
{
UpdateHttpProvider
}
from
"
./app/update
"
import
{
VscodeHttpProvider
}
from
"
./app/vscode
"
import
{
Args
,
bindAddrFromAllSources
,
optionDescriptions
,
parse
,
readConfigFile
,
setDefaults
}
from
"
./cli
"
import
{
coderCloudExpose
,
coderCloudProxy
}
from
"
./coder-cloud
"
import
{
AuthType
,
HttpServer
,
HttpServerOptions
}
from
"
./http
"
import
{
loadPlugins
}
from
"
./plugin
"
import
{
generateCertificate
,
hash
,
humanPath
,
open
}
from
"
./util
"
import
{
ipcMain
,
wrap
}
from
"
./wrapper
"
import
{
coderCloudExpose
}
from
"
./coder-cloud
"
process
.
on
(
"
uncaughtException
"
,
(
error
)
=>
{
logger
.
error
(
`Uncaught exception:
${
error
.
message
}
`
)
...
...
@@ -123,6 +123,8 @@ const main = async (args: Args, cliArgs: Args, configArgs: Args): Promise<void>
httpServer
.
proxyDomains
.
forEach
((
domain
)
=>
logger
.
info
(
` - *.
${
domain
}
`
))
}
coderCloudProxy
(
serverAddress
!
)
if
(
serverAddress
&&
!
options
.
socket
&&
args
.
open
)
{
// The web socket doesn't seem to work if browsing with 0.0.0.0.
const
openAddress
=
serverAddress
.
replace
(
/:
\/\/
0.0.0.0/
,
"
://localhost
"
)
...
...
yarn.lock
浏览文件 @
55a7e8b5
...
...
@@ -2525,6 +2525,11 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
delay@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/delay/-/delay-4.4.0.tgz#71abc745f3ce043fe7f450491236541edec4ad0c"
integrity sha512-txgOrJu3OdtOfTiEOT2e76dJVfG/1dz2NZ4F0Pyt4UGZJryssMRp5vdM5wQoLwSOBNdrJv3F9PAhp/heqd7vrA==
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录