Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
myhjmzy
code-server
提交
97167e75
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 搜索 >>
未验证
提交
97167e75
编写于
7月 12, 2019
作者:
A
Asher
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add authentication
上级
2b2aa9a2
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
400 addition
and
91 deletion
+400
-91
README.md
README.md
+1
-1
package.json
package.json
+1
-1
src/cli.ts
src/cli.ts
+42
-7
src/favicon/favicon.ico
src/favicon/favicon.ico
+0
-0
src/login/login.css
src/login/login.css
+94
-0
src/login/login.html
src/login/login.html
+26
-0
src/server.ts
src/server.ts
+218
-82
src/util.ts
src/util.ts
+18
-0
未找到文件。
README.md
浏览文件 @
97167e75
...
...
@@ -73,7 +73,7 @@ yarn patch:apply
yarn
yarn watch
# Wait for the initial compilation to complete (it will say "Finished compilation").
yarn start
yarn start
--allow-http --no-auth
# Visit http://localhost:8443
```
...
...
package.json
浏览文件 @
97167e75
...
...
@@ -4,7 +4,7 @@
"ensure-in-vscode"
:
"bash ./scripts/tasks.bash ensure-in-vscode"
,
"preinstall"
:
"yarn ensure-in-vscode && cd ../../../ && yarn || true"
,
"postinstall"
:
"rm -rf node_modules/@types/node"
,
"start"
:
"yarn ensure-in-vscode && nodemon
../../../out/vs/server/main.js --watch ../../../out --verbose
"
,
"start"
:
"yarn ensure-in-vscode && nodemon
--watch ../../../out --verbose ../../../out/vs/server/main.js
"
,
"watch"
:
"yarn ensure-in-vscode && cd ../../../ && yarn watch"
,
"build"
:
"bash ./scripts/tasks.bash build"
,
"package"
:
"bash ./scripts/tasks.bash package"
,
...
...
src/cli.ts
浏览文件 @
97167e75
import
*
as
os
from
"
os
"
;
import
*
as
path
from
"
path
"
;
import
{
validatePaths
}
from
"
vs/code/node/paths
"
;
import
{
parseMainProcessArgv
}
from
"
vs/platform/environment/node/argvHelper
"
;
...
...
@@ -9,16 +10,16 @@ import pkg from "vs/platform/product/node/package";
import
{
MainServer
,
WebviewServer
}
from
"
vs/server/src/server
"
;
import
"
vs/server/src/tar
"
;
import
{
generateCertificate
}
from
"
vs/server/src/util
"
;
import
{
generateCertificate
,
generatePassword
}
from
"
vs/server/src/util
"
;
interface
Args
extends
ParsedArgs
{
"
allow-http
"
?:
boolean
;
auth
?:
boolean
;
cert
?:
string
;
"
cert-key
"
?:
string
;
"
extra-builtin-extensions-dir
"
?:
string
;
"
extra-extensions-dir
"
?:
string
;
host
?:
string
;
"
no-auth
"
?:
boolean
;
open
?:
string
;
port
?:
string
;
socket
?:
string
;
...
...
@@ -58,7 +59,7 @@ options.push({ id: "cert-key", type: "string", cat: "o", description: "Path to c
options
.
push
({
id
:
"
extra-builtin-extensions-dir
"
,
type
:
"
string
"
,
cat
:
"
o
"
,
description
:
"
Path to extra builtin extension directory.
"
});
options
.
push
({
id
:
"
extra-extensions-dir
"
,
type
:
"
string
"
,
cat
:
"
o
"
,
description
:
"
Path to extra user extension directory.
"
});
options
.
push
({
id
:
"
host
"
,
type
:
"
string
"
,
cat
:
"
o
"
,
description
:
"
Host for the main and webview servers.
"
});
options
.
push
({
id
:
"
no-auth
"
,
type
:
"
string
"
,
cat
:
"
o
"
,
description
:
"
Disable password authentication.
"
});
options
.
push
({
id
:
"
no-auth
"
,
type
:
"
boolean
"
,
cat
:
"
o
"
,
description
:
"
Disable password authentication.
"
});
options
.
push
({
id
:
"
open
"
,
type
:
"
boolean
"
,
cat
:
"
o
"
,
description
:
"
Open in the browser on startup.
"
});
options
.
push
({
id
:
"
port
"
,
type
:
"
string
"
,
cat
:
"
o
"
,
description
:
"
Port for the main server.
"
});
options
.
push
({
id
:
"
socket
"
,
type
:
"
string
"
,
cat
:
"
o
"
,
description
:
"
Listen on a socket instead of host:port.
"
});
...
...
@@ -115,17 +116,32 @@ const main = async (): Promise<void> => {
}
const
options
=
{
host
:
args
[
"
host
"
]
||
(
args
[
"
no-auth
"
]
||
args
[
"
allow-http
"
]
?
"
localhost
"
:
"
0.0.0.0
"
),
host
:
args
.
host
,
allowHttp
:
args
[
"
allow-http
"
],
cert
:
args
[
"
cert
"
],
certKey
:
args
[
"
cert
"
],
cert
:
args
.
cert
,
certKey
:
args
[
"
cert-key
"
],
auth
:
typeof
args
.
auth
!==
"
undefined
"
?
args
.
auth
:
true
,
password
:
process
.
env
.
PASSWORD
,
};
if
(
!
options
.
host
)
{
options
.
host
=
!
options
.
auth
||
options
.
allowHttp
?
"
localhost
"
:
"
0.0.0.0
"
;
}
let
usingGeneratedCert
=
false
;
if
(
!
options
.
allowHttp
&&
(
!
options
.
cert
||
!
options
.
certKey
))
{
const
{
cert
,
certKey
}
=
await
generateCertificate
();
options
.
cert
=
cert
;
options
.
certKey
=
certKey
;
usingGeneratedCert
=
true
;
}
let
usingGeneratedPassword
=
false
;
if
(
options
.
auth
&&
!
options
.
password
)
{
options
.
password
=
await
generatePassword
();
usingGeneratedPassword
=
true
;
}
const
webviewPort
=
typeof
args
[
"
webview-port
"
]
!==
"
undefined
"
...
...
@@ -149,6 +165,25 @@ const main = async (): Promise<void> => {
]);
console
.
log
(
`Main server listening on
${
serverAddress
}
`
);
console
.
log
(
`Webview server listening on
${
webviewAddress
}
`
);
if
(
usingGeneratedPassword
)
{
console
.
log
(
"
- Password is
"
,
options
.
password
);
console
.
log
(
"
- To use your own password, set the PASSWORD environment variable
"
);
}
else
if
(
options
.
auth
)
{
console
.
log
(
"
- Using custom password for authentication
"
);
}
else
{
console
.
log
(
"
- No authentication
"
);
}
if
(
!
options
.
allowHttp
&&
options
.
cert
&&
options
.
certKey
)
{
console
.
log
(
usingGeneratedCert
?
` - Using generated certificate and key in
${
path
.
dirname
(
options
.
cert
)}
for HTTPS`
:
"
- Using provided certificate and key for HTTPS
"
,
);
}
else
{
console
.
log
(
"
- Not serving HTTPS
"
);
}
};
main
().
catch
((
error
)
=>
{
...
...
src/favicon/favicon.ico
0 → 100644
浏览文件 @
97167e75
2.0 KB
src/login/login.css
0 → 100644
浏览文件 @
97167e75
html
{
box-sizing
:
border-box
;
}
*,
*
:before
,
*
:after
{
box-sizing
:
inherit
;
}
html
,
body
{
background-color
:
#FFFFFF
;
height
:
100%
;
min-height
:
100%
;
}
body
{
align-items
:
center
;
display
:
flex
;
font-family
:
"monospace"
;
justify-content
:
center
;
margin
:
0
;
padding
:
10px
;
}
.login-form
{
border-radius
:
5px
;
box-shadow
:
0
18px
80px
10px
rgba
(
69
,
65
,
78
,
0.08
);
color
:
#575962
;
margin-top
:
-10%
;
max-width
:
328px
;
padding
:
40px
;
position
:
relative
;
width
:
100%
;
}
.login-form
>
.title
{
text-align
:
center
;
text-transform
:
uppercase
;
font-size
:
12px
;
font-weight
:
500
;
letter-spacing
:
1.5px
;
line-height
:
15px
;
margin-bottom
:
0px
;
margin-bottom
:
5px
;
margin-top
:
0px
;
}
.login-form
>
.subtitle
{
font-size
:
19px
;
font-weight
:
bold
;
line-height
:
25px
;
margin-bottom
:
45px
;
margin
:
0
;
text-align
:
center
;
}
.login-form
>
.field
{
text-align
:
left
;
font-size
:
12px
;
color
:
#797E84
;
margin
:
16px
0
;
}
.login-form
>
.field
>
.input
{
background
:
none
!important
;
border
:
1px
solid
#ccc
;
border-radius
:
2px
;
padding
:
5px
;
width
:
100%
;
}
.login-form
>
.button
{
border
:
none
;
border-radius
:
24px
;
box-shadow
:
0
12px
17px
2px
rgba
(
171
,
173
,
163
,
0.14
),
0
5px
22px
4px
rgba
(
171
,
173
,
163
,
0.12
),
0
7px
8px
-4px
rgba
(
171
,
173
,
163
,
0.2
);
cursor
:
pointer
;
display
:
block
;
padding
:
15px
5px
;
width
:
100%
;
}
.login-form
>
.button
:hover
{
background-color
:
rgb
(
0
,
122
,
204
);
color
:
#fff
;
}
.error-display
{
box-sizing
:
border-box
;
color
:
#bb2d0f
;
font-size
:
14px
;
font-weight
:
400
;
line-height
:
12px
;
padding
:
20px
8px
0
;
text-align
:
center
;
}
src/login/login.html
0 → 100644
浏览文件 @
97167e75
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"
>
<title>
Authenticate: code-server
</title>
<link
href=
"/login/login.css"
rel=
"stylesheet"
>
</head>
<body>
<form
class=
"login-form"
action=
"/login"
method=
"post"
>
<h4
class=
"title"
>
code-server
</h4>
<h2
class=
"subtitle"
>
Enter server password
</h2>
<div
class=
"field"
>
<!-- The onfocus code places the cursor at the end of the value. -->
<input
name=
"password"
type=
"password"
class=
"input"
value=
""
required
autofocus
onfocus=
"const value=this.value;this.value='';this.value=value;"
>
</div>
<button
class=
"button"
type=
"submit"
>
<span
class=
"label"
>
Enter IDE
</span>
</button>
<div
class=
"error-display"
style=
"display:none"
>
{{ERROR}}
</div>
</form>
</body>
</html>
src/server.ts
浏览文件 @
97167e75
...
...
@@ -6,11 +6,10 @@ import * as path from "path";
import
*
as
tls
from
"
tls
"
;
import
*
as
util
from
"
util
"
;
import
*
as
url
from
"
url
"
;
import
*
as
querystring
from
"
querystring
"
;
import
{
Emitter
}
from
"
vs/base/common/event
"
;
import
{
sanitizeFilePath
}
from
"
vs/base/common/extpath
"
;
import
{
getMediaMime
}
from
"
vs/base/common/mime
"
;
import
{
extname
}
from
"
vs/base/common/path
"
;
import
{
UriComponents
,
URI
}
from
"
vs/base/common/uri
"
;
import
{
IPCServer
,
ClientConnectionEvent
,
StaticRouter
}
from
"
vs/base/parts/ipc/common/ipc
"
;
import
{
mkdirp
}
from
"
vs/base/node/pfs
"
;
...
...
@@ -49,12 +48,16 @@ import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api";
import
{
Connection
,
ManagementConnection
,
ExtensionHostConnection
}
from
"
vs/server/src/connection
"
;
import
{
ExtensionEnvironmentChannel
,
FileProviderChannel
,
}
from
"
vs/server/src/channel
"
;
import
{
Protocol
}
from
"
vs/server/src/protocol
"
;
import
{
getUriTransformer
,
useHttpsTransformer
}
from
"
vs/server/src/util
"
;
import
{
get
MediaMime
,
get
UriTransformer
,
useHttpsTransformer
}
from
"
vs/server/src/util
"
;
export
enum
HttpCode
{
Ok
=
200
,
Redirect
=
302
,
NotFound
=
404
,
BadRequest
=
400
,
Unauthorized
=
401
,
LargePayload
=
413
,
ServerError
=
500
,
}
export
interface
Options
{
...
...
@@ -65,9 +68,15 @@ export interface Options {
}
export
interface
Response
{
content
?:
string
|
Buffer
;
code
?:
number
;
headers
:
http
.
OutgoingHttpHeaders
;
content
?:
string
|
Buffer
;
filePath
?:
string
;
headers
?:
http
.
OutgoingHttpHeaders
;
redirect
?:
string
;
}
export
interface
LoginPayload
{
password
?:
string
;
}
export
class
HttpError
extends
Error
{
...
...
@@ -80,19 +89,21 @@ export class HttpError extends Error {
}
export
interface
ServerOptions
{
readonly
port
:
number
;
readonly
host
:
string
;
readonly
port
?
:
number
;
readonly
host
?
:
string
;
readonly
socket
?:
string
;
readonly
allowHttp
?:
boolean
;
readonly
cert
?:
string
;
readonly
certKey
?:
string
;
readonly
auth
?:
boolean
;
readonly
password
?:
string
;
}
export
abstract
class
Server
{
// The underlying web server.
protected
readonly
server
:
http
.
Server
|
https
.
Server
;
protected
rootPath
=
path
.
resolve
(
__dirname
,
"
../../..
"
);
protected
rootPath
=
path
.
resolve
(
__dirname
,
"
../../..
/..
"
);
private
listenPromise
:
Promise
<
string
>
|
undefined
;
...
...
@@ -113,7 +124,7 @@ export abstract class Server {
if
(
!
this
.
listenPromise
)
{
this
.
listenPromise
=
new
Promise
((
resolve
,
reject
)
=>
{
this
.
server
.
on
(
"
error
"
,
reject
);
const
onListen
=
()
=>
resolve
(
this
.
address
(
this
.
server
,
this
.
options
.
allowHttp
));
const
onListen
=
()
=>
resolve
(
this
.
address
());
if
(
this
.
options
.
socket
)
{
this
.
server
.
listen
(
this
.
options
.
socket
,
onListen
);
}
else
{
...
...
@@ -124,6 +135,22 @@ export abstract class Server {
return
this
.
listenPromise
;
}
/**
* The local address of the server. If you pass in a request, it will use the
* request's host if listening on a port (rather than a socket). This enables
* accessing the webview server from the same host as the main server.
*/
public
address
(
request
?:
http
.
IncomingMessage
):
string
{
const
address
=
this
.
server
.
address
();
const
endpoint
=
typeof
address
!==
"
string
"
?
(
request
?
request
.
headers
.
host
!
.
split
(
"
:
"
,
1
)[
0
]
:
(
address
.
address
===
"
::
"
?
"
localhost
"
:
address
.
address
)
)
+
"
:
"
+
address
.
port
:
address
;
return
`
${
this
.
options
.
allowHttp
?
"
http
"
:
"
https
"
}
://
${
endpoint
}
`
;
}
protected
abstract
handleRequest
(
base
:
string
,
requestPath
:
string
,
...
...
@@ -133,68 +160,192 @@ export abstract class Server {
protected
async
getResource
(
filePath
:
string
):
Promise
<
Response
>
{
const
content
=
await
util
.
promisify
(
fs
.
readFile
)(
filePath
);
return
{
content
,
headers
:
{
"
Content-Type
"
:
getMediaMime
(
filePath
)
||
{
"
.css
"
:
"
text/css
"
,
"
.html
"
:
"
text/html
"
,
"
.js
"
:
"
text/javascript
"
,
"
.json
"
:
"
application/json
"
,
}[
extname
(
filePath
)]
||
"
text/plain
"
,
},
};
return
{
content
,
filePath
};
}
private
onRequest
=
async
(
request
:
http
.
IncomingMessage
,
response
:
http
.
ServerResponse
):
Promise
<
void
>
=>
{
try
{
const
payload
=
await
this
.
preHandleRequest
(
request
);
response
.
writeHead
(
payload
.
redirect
?
HttpCode
.
Redirect
:
payload
.
code
||
HttpCode
.
Ok
,
{
"
Cache-Control
"
:
"
max-age=86400
"
,
// TODO: ETag?
"
Content-Type
"
:
getMediaMime
(
payload
.
filePath
),
...(
payload
.
redirect
?
{
Location
:
payload
.
redirect
}
:
{}),
...
payload
.
headers
,
});
response
.
end
(
payload
.
content
);
}
catch
(
error
)
{
if
(
error
.
code
===
"
ENOENT
"
||
error
.
code
===
"
EISDIR
"
)
{
error
=
new
HttpError
(
"
Not found
"
,
HttpCode
.
NotFound
);
}
response
.
writeHead
(
typeof
error
.
code
===
"
number
"
?
error
.
code
:
HttpCode
.
ServerError
);
response
.
end
(
error
.
message
);
}
}
private
async
preHandleRequest
(
request
:
http
.
IncomingMessage
):
Promise
<
Response
>
{
const
secure
=
(
request
.
connection
as
tls
.
TLSSocket
).
encrypted
;
if
(
!
this
.
options
.
allowHttp
&&
!
secure
)
{
response
.
writeHead
(
302
,
{
Location
:
"
https://
"
+
request
.
headers
.
host
+
request
.
url
,
});
return
response
.
end
();
return
{
redirect
:
"
https://
"
+
request
.
headers
.
host
+
request
.
url
};
}
try
{
if
(
request
.
method
!==
"
GET
"
)
{
throw
new
HttpError
(
`Unsupported method
${
request
.
method
}
`
,
HttpCode
.
BadRequest
,
);
}
const
parsedUrl
=
url
.
parse
(
request
.
url
||
""
,
true
);
const
fullPath
=
decodeURIComponent
(
parsedUrl
.
pathname
||
"
/
"
);
const
match
=
fullPath
.
match
(
/^
(\/?[^/]
*
)(
.*
)
$/
);
let
[,
base
,
requestPath
]
=
match
?
match
.
map
((
p
)
=>
p
.
replace
(
/
\/
$/
,
""
))
:
[
""
,
""
,
""
];
if
(
base
.
indexOf
(
"
.
"
)
!==
-
1
)
{
// Assume it's a file at the root.
requestPath
=
base
;
base
=
"
/
"
;
}
else
if
(
base
===
""
)
{
// Happens if it's a plain `domain.com`.
base
=
"
/
"
;
}
if
(
requestPath
===
"
/
"
)
{
// Trailing slash, like `domain.com/login/`.
requestPath
=
""
;
}
else
if
(
requestPath
!==
""
)
{
// "" will become "." with normalize.
requestPath
=
path
.
normalize
(
requestPath
);
}
base
=
path
.
normalize
(
base
);
switch
(
base
)
{
case
"
/
"
:
this
.
ensureGet
(
request
);
if
(
!
this
.
authenticate
(
request
))
{
return
{
redirect
:
"
https://
"
+
request
.
headers
.
host
+
"
/login
"
};
}
break
;
case
"
/login
"
:
if
(
!
this
.
options
.
auth
)
{
throw
new
HttpError
(
"
Not found
"
,
HttpCode
.
NotFound
);
}
if
(
requestPath
===
""
)
{
return
this
.
tryLogin
(
request
);
}
this
.
ensureGet
(
request
);
return
this
.
getResource
(
path
.
join
(
this
.
rootPath
,
"
/out/vs/server/src/login
"
,
requestPath
));
case
"
/favicon.ico
"
:
this
.
ensureGet
(
request
);
return
this
.
getResource
(
path
.
join
(
this
.
rootPath
,
"
/out/vs/server/src/favicon
"
,
base
));
default
:
this
.
ensureGet
(
request
);
if
(
!
this
.
authenticate
(
request
))
{
throw
new
HttpError
(
`Unauthorized`
,
HttpCode
.
Unauthorized
);
}
break
;
}
const
parsedUrl
=
url
.
parse
(
request
.
url
||
""
,
true
);
return
this
.
handleRequest
(
base
,
requestPath
,
parsedUrl
,
request
);
}
const
fullPath
=
decodeURIComponent
(
parsedUrl
.
pathname
||
"
/
"
);
const
match
=
fullPath
.
match
(
/^
(\/?[^/]
*
)(
.*
)
$/
);
const
[,
base
,
requestPath
]
=
match
?
match
.
map
((
p
)
=>
p
!==
"
/
"
?
p
.
replace
(
/
\/
$/
,
""
)
:
p
)
:
[
""
,
""
,
""
];
private
async
tryLogin
(
request
:
http
.
IncomingMessage
):
Promise
<
Response
>
{
if
(
this
.
authenticate
(
request
))
{
this
.
ensureGet
(
request
);
return
{
redirect
:
"
https://
"
+
request
.
headers
.
host
+
"
/
"
};
}
const
{
content
,
headers
,
code
}
=
await
this
.
handleRequest
(
base
,
requestPath
,
parsedUrl
,
request
,
);
response
.
writeHead
(
code
||
HttpCode
.
Ok
,
{
"
Cache-Control
"
:
"
max-age=86400
"
,
// TODO: ETag?
...
headers
,
});
response
.
end
(
content
);
}
catch
(
error
)
{
if
(
error
.
code
===
"
ENOENT
"
||
error
.
code
===
"
EISDIR
"
)
{
error
=
new
HttpError
(
"
Not found
"
,
HttpCode
.
NotFound
);
if
(
request
.
method
===
"
POST
"
)
{
const
data
=
await
this
.
getData
<
LoginPayload
>
(
request
);
if
(
this
.
authenticate
(
request
,
data
))
{
return
{
redirect
:
"
https://
"
+
request
.
headers
.
host
+
"
/
"
,
headers
:
{
"
Set-Cookie
"
:
`password=
${
data
.
password
}
`
,
}
};
}
response
.
writeHead
(
typeof
error
.
code
===
"
number
"
?
error
.
code
:
500
);
response
.
end
(
error
.
message
);
let
userAgent
=
request
.
headers
[
"
user-agent
"
];
const
timestamp
=
Math
.
floor
(
new
Date
().
getTime
()
/
1000
);
if
(
Array
.
isArray
(
userAgent
))
{
userAgent
=
userAgent
.
join
(
"
,
"
);
}
console
.
error
(
"
Failed login attempt
"
,
JSON
.
stringify
({
xForwardedFor
:
request
.
headers
[
"
x-forwarded-for
"
],
remoteAddress
:
request
.
connection
.
remoteAddress
,
userAgent
,
timestamp
,
}));
return
this
.
getLogin
(
"
Invalid password
"
,
data
);
}
this
.
ensureGet
(
request
);
return
this
.
getLogin
();
}
private
address
(
server
:
net
.
Server
,
http
?:
boolean
):
string
{
const
address
=
server
.
address
();
const
endpoint
=
typeof
address
!==
"
string
"
?
((
address
.
address
===
"
::
"
?
"
localhost
"
:
address
.
address
)
+
"
:
"
+
address
.
port
)
:
address
;
return
`
${
http
?
"
http
"
:
"
https
"
}
://
${
endpoint
}
`
;
private
async
getLogin
(
error
:
string
=
""
,
payload
?:
LoginPayload
):
Promise
<
Response
>
{
const
filePath
=
path
.
join
(
this
.
rootPath
,
"
out/vs/server/src/login/login.html
"
);
let
content
=
await
util
.
promisify
(
fs
.
readFile
)(
filePath
,
"
utf8
"
);
if
(
error
)
{
content
=
content
.
replace
(
"
{{ERROR}}
"
,
error
)
.
replace
(
"
display:none
"
,
"
display:block
"
);
}
if
(
payload
&&
payload
.
password
)
{
content
=
content
.
replace
(
'
value=""
'
,
`value="
${
payload
.
password
}
"`
);
}
return
{
content
,
filePath
};
}
private
ensureGet
(
request
:
http
.
IncomingMessage
):
void
{
if
(
request
.
method
!==
"
GET
"
)
{
throw
new
HttpError
(
`Unsupported method
${
request
.
method
}
`
,
HttpCode
.
BadRequest
,
);
}
}
private
getData
<
T
extends
object
>
(
request
:
http
.
IncomingMessage
):
Promise
<
T
>
{
return
request
.
method
===
"
POST
"
?
new
Promise
<
T
>
((
resolve
,
reject
)
=>
{
let
body
=
""
;
const
onEnd
=
():
void
=>
{
off
();
resolve
(
querystring
.
parse
(
body
)
as
T
);
};
const
onError
=
(
error
:
Error
):
void
=>
{
off
();
reject
(
error
);
};
const
onData
=
(
d
:
Buffer
):
void
=>
{
body
+=
d
;
if
(
body
.
length
>
1
e6
)
{
onError
(
new
HttpError
(
"
Payload is too large
"
,
HttpCode
.
LargePayload
,
));
request
.
connection
.
destroy
();
}
};
const
off
=
():
void
=>
{
request
.
off
(
"
error
"
,
onError
);
request
.
off
(
"
data
"
,
onError
);
request
.
off
(
"
end
"
,
onEnd
);
};
request
.
on
(
"
error
"
,
onError
);
request
.
on
(
"
data
"
,
onData
);
request
.
on
(
"
end
"
,
onEnd
);
})
:
Promise
.
resolve
({}
as
T
);
}
private
authenticate
(
request
:
http
.
IncomingMessage
,
payload
?:
LoginPayload
):
boolean
{
if
(
!
this
.
options
.
auth
)
{
return
true
;
}
const
safeCompare
=
require
.
__$__nodeRequire
(
path
.
resolve
(
__dirname
,
"
../node_modules/safe-compare/index
"
))
as
typeof
import
(
"
safe-compare
"
);
if
(
typeof
payload
===
"
undefined
"
)
{
payload
=
this
.
parseCookies
<
LoginPayload
>
(
request
);
}
return
!!
this
.
options
.
password
&&
safeCompare
(
payload
.
password
||
""
,
this
.
options
.
password
);
}
private
parseCookies
<
T
extends
object
>
(
request
:
http
.
IncomingMessage
):
T
{
const
cookies
:
{
[
key
:
string
]:
string
}
=
{};
if
(
request
.
headers
.
cookie
)
{
request
.
headers
.
cookie
.
split
(
"
;
"
).
forEach
((
keyValue
)
=>
{
const
[
key
,
value
]
=
keyValue
.
split
(
"
=
"
,
2
);
cookies
[
key
.
trim
()]
=
decodeURI
(
value
);
});
}
return
cookies
as
T
;
}
}
...
...
@@ -281,8 +432,7 @@ export class MainServer extends Server {
request
:
http
.
IncomingMessage
,
):
Promise
<
Response
>
{
switch
(
base
)
{
case
"
/
"
:
return
this
.
getRoot
(
request
,
parsedUrl
);
case
"
/
"
:
return
this
.
getRoot
(
request
,
parsedUrl
);
case
"
/node_modules
"
:
case
"
/out
"
:
return
this
.
getResource
(
path
.
join
(
this
.
rootPath
,
base
,
requestPath
));
...
...
@@ -292,23 +442,19 @@ export class MainServer extends Server {
// resources are requested by the browser (like the extension icon) and
// some by the file provider (like the extension README). Maybe add a
// /resource prefix and a file provider that strips that prefix?
default
:
return
this
.
getResource
(
path
.
join
(
base
,
requestPath
));
default
:
return
this
.
getResource
(
path
.
join
(
base
,
requestPath
));
}
}
private
async
getRoot
(
request
:
http
.
IncomingMessage
,
parsedUrl
:
url
.
UrlWithParsedQuery
):
Promise
<
Response
>
{
const
htmlPath
=
path
.
join
(
this
.
rootPath
,
'
out/vs/code/browser/workbench/workbench.html
'
,
);
let
content
=
await
util
.
promisify
(
fs
.
readFile
)(
htmlPath
,
"
utf8
"
);
const
filePath
=
path
.
join
(
this
.
rootPath
,
"
out/vs/code/browser/workbench/workbench.html
"
);
let
content
=
await
util
.
promisify
(
fs
.
readFile
)(
filePath
,
"
utf8
"
);
const
remoteAuthority
=
request
.
headers
.
host
as
string
;
const
transformer
=
getUriTransformer
(
remoteAuthority
);
const
webviewEndpoint
=
await
this
.
webviewServer
.
listen
();
await
this
.
webviewServer
.
listen
();
const
webviewEndpoint
=
this
.
webviewServer
.
address
(
request
);
const
cwd
=
process
.
env
.
VSCODE_CWD
||
process
.
cwd
();
const
workspacePath
=
parsedUrl
.
query
.
workspace
as
string
|
undefined
;
...
...
@@ -338,12 +484,7 @@ export class MainServer extends Server {
content
=
content
.
replace
(
'
{{WEBVIEW_ENDPOINT}}
'
,
webviewEndpoint
);
return
{
content
,
headers
:
{
"
Content-Type
"
:
"
text/html
"
,
},
};
return
{
content
,
filePath
};
}
private
createProtocol
(
request
:
http
.
IncomingMessage
,
socket
:
net
.
Socket
):
Protocol
{
...
...
@@ -444,15 +585,10 @@ export class WebviewServer extends Server {
base
:
string
,
requestPath
:
string
,
):
Promise
<
Response
>
{
const
webviewPath
=
path
.
join
(
this
.
rootPath
,
"
out/vs/workbench/contrib/webview/browser/pre
"
,
);
if
(
base
===
"
/
"
)
{
base
=
"
/index.html
"
;
const
webviewPath
=
path
.
join
(
this
.
rootPath
,
"
out/vs/workbench/contrib/webview/browser/pre
"
);
if
(
requestPath
===
""
)
{
requestPath
=
"
/index.html
"
;
}
return
this
.
getResource
(
path
.
join
(
webviewPath
,
base
,
requestPath
));
}
}
src/util.ts
浏览文件 @
97167e75
import
*
as
crypto
from
"
crypto
"
;
import
*
as
fs
from
"
fs
"
;
import
*
as
os
from
"
os
"
;
import
*
as
path
from
"
path
"
;
import
*
as
util
from
"
util
"
;
import
{
getPathFromAmdModule
}
from
"
vs/base/common/amd
"
;
import
{
getMediaMime
as
vsGetMediaMime
}
from
"
vs/base/common/mime
"
;
import
{
extname
}
from
"
vs/base/common/path
"
;
import
{
URITransformer
,
IRawURITransformer
}
from
"
vs/base/common/uriIpc
"
;
import
{
mkdirp
}
from
"
vs/base/node/pfs
"
;
...
...
@@ -58,3 +61,18 @@ export const getUriTransformer = (remoteAuthority: string): URITransformer => {
const
rawURITransformer
=
<
IRawURITransformer
>
rawURITransformerFactory
(
remoteAuthority
);
return
new
URITransformer
(
rawURITransformer
);
};
export
const
generatePassword
=
async
(
length
:
number
=
24
):
Promise
<
string
>
=>
{
const
buffer
=
Buffer
.
alloc
(
Math
.
ceil
(
length
/
2
));
await
util
.
promisify
(
crypto
.
randomFill
)(
buffer
);
return
buffer
.
toString
(
"
hex
"
).
substring
(
0
,
length
);
};
export
const
getMediaMime
=
(
filePath
?:
string
):
string
=>
{
return
filePath
&&
(
vsGetMediaMime
(
filePath
)
||
{
"
.css
"
:
"
text/css
"
,
"
.html
"
:
"
text/html
"
,
"
.js
"
:
"
text/javascript
"
,
"
.json
"
:
"
application/json
"
,
}[
extname
(
filePath
)])
||
"
text/plain
"
;
};
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录