UNPKG

9.37 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Jupyter Development Team.
3// Distributed under the terms of the Modified BSD License.
4Object.defineProperty(exports, "__esModule", { value: true });
5exports.ServerConnection = void 0;
6const coreutils_1 = require("@jupyterlab/coreutils");
7const serialize_1 = require("./kernel/serialize");
8let WEBSOCKET;
9if (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}
14else {
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 */
31var 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 */
124var 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