Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Views: 39536
1
###
2
Express HTTP server
3
###
4
5
fs = require('fs')
6
async = require('async')
7
express = require('express')
8
express_index = require('serve-index')
9
body_parser = require('body-parser')
10
misc_node = require('smc-util-node/misc_node')
11
12
{defaults, required} = require('smc-util/misc')
13
14
{jupyter_router} = require('./jupyter/jupyter')
15
16
{directory_listing_router} = require('./directory-listing')
17
18
{upload_endpoint} = require('./upload')
19
20
kucalc = require('./kucalc')
21
22
exports.start_raw_server = (opts) ->
23
opts = defaults opts,
24
project_id : required
25
base_url : required
26
host : required
27
data_path : required
28
home : required
29
port : undefined
30
logger : undefined
31
cb : cb
32
{project_id, base_url, host, data_path, home, cb} = opts
33
opts.logger?.info("starting express http server...")
34
35
raw_port_file = misc_node.abspath("#{data_path}/raw.port")
36
raw_server = express()
37
38
# Needed for POST file to custom path.
39
raw_server.use(body_parser.urlencoded({ extended: true }))
40
41
port = opts.port # either undefined or the port number
42
43
async.series([
44
(cb) ->
45
# create the root symbolic link, so that it is possible to
46
# browse the entire filesystem, including tmp
47
target = process.env.SMC + '/root'
48
fs.exists target, (exists) ->
49
if exists
50
cb()
51
else
52
# make symbolic link from / to target
53
fs.symlink '/', target, (err) ->
54
if err
55
# nonfatal error
56
opts.logger?.debug("WARNING: error creating root symlink -- #{err}")
57
cb()
58
(cb) ->
59
if port # 0 or undefined
60
cb()
61
else
62
misc_node.free_port (err, _port) ->
63
port = _port; cb(err)
64
(cb) ->
65
fs.writeFile(raw_port_file, port, cb)
66
(cb) ->
67
base = "#{base_url}/#{project_id}/raw/"
68
opts.logger?.info("raw server: port=#{port}, host='#{host}', base='#{base}'")
69
70
if kucalc.IN_KUCALC
71
# Add a /health handler, which is used as a health check for Kubernetes.
72
kucalc.init_health_metrics(raw_server, project_id)
73
74
# Setup the /.smc/jupyter/... server, which is used by our jupyter server for blobs, etc.
75
raw_server.use(base, jupyter_router(express))
76
77
# Setup the /.smc/directory_listing/... server, which is used to provide directory listings
78
# to the hub (at least in KuCalc).
79
raw_server.use(base, directory_listing_router(express))
80
81
# Setup the upload POST endpoint
82
raw_server.use(base, upload_endpoint(express, opts.logger))
83
84
# Setup the static raw HTTP server. This must happen after anything above!!
85
raw_server.use base, (req, res, next) ->
86
# this middleware function has to come before the express.static server!
87
# it sets the content type to octet-stream (aka "download me") if URL query ?download exists
88
if req.query.download?
89
res.setHeader('Content-Type', 'application/octet-stream')
90
# Disable optimistic caching -- cloudflare obeys these headers
91
res.setHeader('Cache-Control', 'private, no-cache, must-revalidate')
92
return next()
93
raw_server.use(base, express_index(home, {hidden:true, icons:true}))
94
raw_server.use(base, express.static(home, {hidden:true}))
95
96
97
# NOTE: It is critical to only listen on the host interface (not localhost),
98
# since otherwise other users on the same VM could listen in.
99
# We also firewall connections from the other VM hosts above
100
# port 1024, so this is safe without authentication. TODO: should we add some sort of
101
# auth (?) just in case?
102
raw_server.listen(port, host, cb)
103
], (err) ->
104
if err
105
opts.logger?.debug("error starting raw_server: err = #{misc.to_json(err)}")
106
cb(err)
107
)
108