CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/project/jupyter/http-server.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
HTTP server for getting various information from Jupyter, without
8
having to go through the websocket connection and messaging. This is
9
useful, e.g., for big images, general info about all available
10
kernels, sending signals, doing tab completions, and so on.
11
*/
12
13
import { Router } from "express";
14
import * as os_path from "node:path";
15
import getLogger from "@cocalc/backend/logger";
16
import { BlobStoreInterface } from "@cocalc/jupyter/types/project-interface";
17
import { startswith, to_json } from "@cocalc/util/misc";
18
import { exists } from "@cocalc/backend/misc/async-utils-node";
19
import { get_existing_kernel } from "@cocalc/jupyter/kernel";
20
import {
21
BlobStoreDisk,
22
get_blob_store,
23
BlobStoreSqlite,
24
} from "@cocalc/jupyter/blobs";
25
import { get_kernel_data } from "@cocalc/jupyter/kernel/kernel-data";
26
import { get_ProjectStatusServer } from "@cocalc/project/project-status/server";
27
import { delay } from "awaiting";
28
29
const log = getLogger("jupyter-http-server");
30
31
const BASE = "/.smc/jupyter/";
32
33
function get_kernel(kernel_data, name) {
34
for (const k of kernel_data) {
35
if (k.name == name) return k;
36
}
37
return null;
38
}
39
40
function jupyter_kernel_info_handler(router): void {
41
router.get(
42
BASE + "ipywidgets-get-buffer",
43
async function (req, res): Promise<void> {
44
try {
45
const { path, model_id, buffer_path } = req.query;
46
const kernel = get_existing_kernel(path);
47
if (kernel == null) {
48
res.status(404).send(`kernel associated to ${path} does not exist`);
49
return;
50
}
51
const buffer = kernel.ipywidgetsGetBuffer(model_id, buffer_path);
52
if (buffer == null) {
53
res
54
.status(404)
55
.send(
56
`buffer associated to model ${model_id} at ${buffer_path} not known`
57
);
58
return;
59
}
60
res.status(200).send(buffer);
61
} catch (err) {
62
res.status(500).send(`Error getting ipywidgets buffer - ${err}`);
63
}
64
}
65
);
66
67
// we are only actually using this to serve up the logo.
68
router.get(BASE + "kernelspecs/*", async function (req, res): Promise<void> {
69
try {
70
const kernel_data = await get_kernel_data();
71
let path = req.path.slice((BASE + "kernelspecs/").length).trim();
72
if (path.length === 0) {
73
res.json(kernel_data);
74
return;
75
}
76
const segments = path.split("/");
77
const name = segments[0];
78
const kernel = get_kernel(kernel_data, name);
79
if (kernel == null) {
80
const msg = `no such kernel '${name}'`;
81
throw Error(msg);
82
}
83
const resource_dir = kernel.resource_dir;
84
path = os_path.join(resource_dir, segments.slice(1).join("/"));
85
path = os_path.resolve(path);
86
87
if (!startswith(path, resource_dir)) {
88
// don't let user use .. or something to get any file on the server...!
89
// (this really can't happen due to url rules already; just being super paranoid.)
90
throw Error(`suspicious path '${path}'`);
91
}
92
if (await exists(path)) {
93
res.sendFile(path);
94
} else {
95
throw Error(`no such path '${path}'`);
96
}
97
} catch (err) {
98
res.status(500).send(err.toString());
99
}
100
});
101
}
102
103
export default async function init(): Promise<Router> {
104
// this might take infinitely long, obviously:
105
let blob_store: BlobStoreSqlite | BlobStoreDisk;
106
let d = 3000;
107
while (true) {
108
try {
109
// This call right here causes the configured blobstore to be initialized in the file
110
// packages/jupyter/blobs/get.ts
111
blob_store = await get_blob_store();
112
get_ProjectStatusServer().clearComponentAlert("BlobStore");
113
break;
114
} catch (err) {
115
get_ProjectStatusServer().setComponentAlert("BlobStore");
116
log.warn(`unable to instantiate BlobStore -- ${err}`);
117
}
118
await delay(d);
119
d = Math.min(30000, 1.2 * d);
120
}
121
122
log.debug("got blob store, setting up jupyter http server");
123
const router = Router();
124
125
// Install handling for the blob store
126
jupyter_blobstore_handler(router, blob_store);
127
128
// Handler for Jupyter kernel info
129
jupyter_kernel_info_handler(router);
130
131
return router;
132
}
133
134
function jupyter_blobstore_handler(
135
router: Router,
136
blob_store: BlobStoreInterface
137
): void {
138
const base = BASE + "blobs/";
139
140
router.get(base, async (_, res) => {
141
res.setHeader("Content-Type", "application/json");
142
res.end(to_json(await blob_store.keys()));
143
});
144
145
router.get(base + "*", async (req, res) => {
146
const filename: string = req.path.slice(base.length);
147
const sha1: string = `${req.query.sha1}`;
148
res.type(filename);
149
res.send(await blob_store.get(sha1));
150
});
151
}
152
153