1 | ;
|
2 | // Copyright (c) Jupyter Development Team.
|
3 | // Distributed under the terms of the Modified BSD License.
|
4 | Object.defineProperty(exports, "__esModule", { value: true });
|
5 | exports.ServerConnection = void 0;
|
6 | const coreutils_1 = require("@jupyterlab/coreutils");
|
7 | const serialize_1 = require("./kernel/serialize");
|
8 | let WEBSOCKET;
|
9 | if (typeof window === 'undefined') {
|
10 | // Mangle the require statements so it does not get picked up in the
|
11 | // browser assets.
|
12 | WEBSOCKET = require('ws');
|
13 | }
|
14 | else {
|
15 | WEBSOCKET = WebSocket;
|
16 | }
|
17 | /**
|
18 | * The namespace for ServerConnection functions.
|
19 | *
|
20 | * #### Notes
|
21 | * This is only intended to manage communication with the Jupyter server.
|
22 | *
|
23 | * The default values can be used in a JupyterLab or Jupyter Notebook context.
|
24 | *
|
25 | * We use `token` authentication if available, falling back on an XSRF
|
26 | * cookie if one has been provided on the `document`.
|
27 | *
|
28 | * A content type of `'application/json'` is added when using authentication
|
29 | * and there is no body data to allow the server to prevent malicious forms.
|
30 | */
|
31 | var ServerConnection;
|
32 | (function (ServerConnection) {
|
33 | /**
|
34 | * Create a settings object given a subset of options.
|
35 | *
|
36 | * @param options - An optional partial set of options.
|
37 | *
|
38 | * @returns The full settings object.
|
39 | */
|
40 | function makeSettings(options) {
|
41 | return Private.makeSettings(options);
|
42 | }
|
43 | ServerConnection.makeSettings = makeSettings;
|
44 | /**
|
45 | * Make an request to the notebook server.
|
46 | *
|
47 | * @param url - The url for the request.
|
48 | *
|
49 | * @param init - The initialization options for the request.
|
50 | *
|
51 | * @param settings - The server settings to apply to the request.
|
52 | *
|
53 | * @returns a Promise that resolves with the response.
|
54 | *
|
55 | * @throws If the url of the request is not a notebook server url.
|
56 | *
|
57 | * #### Notes
|
58 | * The `url` must start with `settings.baseUrl`. The `init` settings are
|
59 | * merged with `settings.init`, with `init` taking precedence.
|
60 | * The headers in the two objects are not merged.
|
61 | * If there is no body data, we set the content type to `application/json`
|
62 | * because it is required by the Notebook server.
|
63 | */
|
64 | function makeRequest(url, init, settings) {
|
65 | return Private.handleRequest(url, init, settings);
|
66 | }
|
67 | ServerConnection.makeRequest = makeRequest;
|
68 | /**
|
69 | * A wrapped error for a fetch response.
|
70 | */
|
71 | class ResponseError extends Error {
|
72 | /**
|
73 | * Create a ResponseError from a response, handling the traceback and message
|
74 | * as appropriate.
|
75 | *
|
76 | * @param response The response object.
|
77 | *
|
78 | * @returns A promise that resolves with a `ResponseError` object.
|
79 | */
|
80 | static async create(response) {
|
81 | try {
|
82 | const data = await response.json();
|
83 | const { message, traceback } = data;
|
84 | if (traceback) {
|
85 | console.error(traceback);
|
86 | }
|
87 | return new ResponseError(response, message !== null && message !== void 0 ? message : ResponseError._defaultMessage(response), traceback !== null && traceback !== void 0 ? traceback : '');
|
88 | }
|
89 | catch (e) {
|
90 | console.debug(e);
|
91 | return new ResponseError(response);
|
92 | }
|
93 | }
|
94 | /**
|
95 | * Create a new response error.
|
96 | */
|
97 | constructor(response, message = ResponseError._defaultMessage(response), traceback = '') {
|
98 | super(message);
|
99 | this.response = response;
|
100 | this.traceback = traceback;
|
101 | }
|
102 | static _defaultMessage(response) {
|
103 | return `Invalid response: ${response.status} ${response.statusText}`;
|
104 | }
|
105 | }
|
106 | ServerConnection.ResponseError = ResponseError;
|
107 | /**
|
108 | * A wrapped error for a network error.
|
109 | */
|
110 | class NetworkError extends TypeError {
|
111 | /**
|
112 | * Create a new network error.
|
113 | */
|
114 | constructor(original) {
|
115 | super(original.message);
|
116 | this.stack = original.stack;
|
117 | }
|
118 | }
|
119 | ServerConnection.NetworkError = NetworkError;
|
120 | })(ServerConnection || (exports.ServerConnection = ServerConnection = {}));
|
121 | /**
|
122 | * The namespace for module private data.
|
123 | */
|
124 | var Private;
|
125 | (function (Private) {
|
126 | /**
|
127 | * Handle the server connection settings, returning a new value.
|
128 | */
|
129 | function makeSettings(options = {}) {
|
130 | var _a;
|
131 | const pageBaseUrl = coreutils_1.PageConfig.getBaseUrl();
|
132 | const pageWsUrl = coreutils_1.PageConfig.getWsUrl();
|
133 | const baseUrl = coreutils_1.URLExt.normalize(options.baseUrl) || pageBaseUrl;
|
134 | let wsUrl = options.wsUrl;
|
135 | // Prefer the default wsUrl if we are using the default baseUrl.
|
136 | if (!wsUrl && baseUrl === pageBaseUrl) {
|
137 | wsUrl = pageWsUrl;
|
138 | }
|
139 | // Otherwise convert the baseUrl to a wsUrl if possible.
|
140 | if (!wsUrl && baseUrl.indexOf('http') === 0) {
|
141 | wsUrl = 'ws' + baseUrl.slice(4);
|
142 | }
|
143 | // Otherwise fall back on the default wsUrl.
|
144 | wsUrl = wsUrl !== null && wsUrl !== void 0 ? wsUrl : pageWsUrl;
|
145 | const appendTokenConfig = coreutils_1.PageConfig.getOption('appendToken').toLowerCase();
|
146 | let appendToken;
|
147 | if (appendTokenConfig === '') {
|
148 | appendToken =
|
149 | typeof window === 'undefined' ||
|
150 | (typeof process !== 'undefined' &&
|
151 | ((_a = process === null || process === void 0 ? void 0 : process.env) === null || _a === void 0 ? void 0 : _a.JEST_WORKER_ID) !== undefined) ||
|
152 | coreutils_1.URLExt.getHostName(pageBaseUrl) !== coreutils_1.URLExt.getHostName(wsUrl);
|
153 | }
|
154 | else {
|
155 | appendToken = appendTokenConfig === 'true';
|
156 | }
|
157 | return {
|
158 | init: { cache: 'no-store', credentials: 'same-origin' },
|
159 | fetch,
|
160 | Headers,
|
161 | Request,
|
162 | WebSocket: WEBSOCKET,
|
163 | token: coreutils_1.PageConfig.getToken(),
|
164 | appUrl: coreutils_1.PageConfig.getOption('appUrl'),
|
165 | appendToken,
|
166 | serializer: { serialize: serialize_1.serialize, deserialize: serialize_1.deserialize },
|
167 | ...options,
|
168 | baseUrl,
|
169 | wsUrl
|
170 | };
|
171 | }
|
172 | Private.makeSettings = makeSettings;
|
173 | /**
|
174 | * Handle a request.
|
175 | *
|
176 | * @param url - The url for the request.
|
177 | *
|
178 | * @param init - The overrides for the request init.
|
179 | *
|
180 | * @param settings - The settings object for the request.
|
181 | *
|
182 | * #### Notes
|
183 | * The `url` must start with `settings.baseUrl`. The `init` settings
|
184 | * take precedence over `settings.init`.
|
185 | */
|
186 | function handleRequest(url, init, settings) {
|
187 | var _a;
|
188 | // Handle notebook server requests.
|
189 | if (url.indexOf(settings.baseUrl) !== 0) {
|
190 | throw new Error('Can only be used for notebook server requests');
|
191 | }
|
192 | // Use explicit cache buster when `no-store` is set since
|
193 | // not all browsers use it properly.
|
194 | const cache = (_a = init.cache) !== null && _a !== void 0 ? _a : settings.init.cache;
|
195 | if (cache === 'no-store') {
|
196 | // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
|
197 | url += (/\?/.test(url) ? '&' : '?') + new Date().getTime();
|
198 | }
|
199 | const request = new settings.Request(url, { ...settings.init, ...init });
|
200 | // Handle authentication. Authentication can be overdetermined by
|
201 | // settings token and XSRF token.
|
202 | let authenticated = false;
|
203 | if (settings.token) {
|
204 | authenticated = true;
|
205 | request.headers.append('Authorization', `token ${settings.token}`);
|
206 | }
|
207 | if (typeof document !== 'undefined') {
|
208 | const xsrfToken = getCookie('_xsrf');
|
209 | if (xsrfToken !== undefined) {
|
210 | authenticated = true;
|
211 | request.headers.append('X-XSRFToken', xsrfToken);
|
212 | }
|
213 | }
|
214 | // Set the content type if there is no given data and we are
|
215 | // using an authenticated connection.
|
216 | if (!request.headers.has('Content-Type') && authenticated) {
|
217 | request.headers.set('Content-Type', 'application/json');
|
218 | }
|
219 | // Use `call` to avoid a `TypeError` in the browser.
|
220 | return settings.fetch.call(null, request).catch((e) => {
|
221 | // Convert the TypeError into a more specific error.
|
222 | throw new ServerConnection.NetworkError(e);
|
223 | });
|
224 | // TODO: *this* is probably where we need a system-wide connectionFailure
|
225 | // signal we can hook into.
|
226 | }
|
227 | Private.handleRequest = handleRequest;
|
228 | /**
|
229 | * Get a cookie from the document.
|
230 | */
|
231 | function getCookie(name) {
|
232 | // From http://www.tornadoweb.org/en/stable/guide/security.html
|
233 | let cookie = '';
|
234 | try {
|
235 | cookie = document.cookie;
|
236 | }
|
237 | catch (e) {
|
238 | // e.g. SecurityError in case of CSP Sandbox
|
239 | return;
|
240 | }
|
241 | const matches = cookie.match('\\b' + name + '=([^;]*)\\b');
|
242 | return matches === null || matches === void 0 ? void 0 : matches[1];
|
243 | }
|
244 | })(Private || (Private = {}));
|
245 | //# sourceMappingURL=serverconnection.js.map |
\ | No newline at end of file |