UNPKG

@jupyterlab/services

Version:

Client APIs for the Jupyter services REST APIs

245 lines 9.37 kB
"use strict"; // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. Object.defineProperty(exports, "__esModule", { value: true }); exports.ServerConnection = void 0; const coreutils_1 = require("@jupyterlab/coreutils"); const serialize_1 = require("./kernel/serialize"); let WEBSOCKET; if (typeof window === 'undefined') { // Mangle the require statements so it does not get picked up in the // browser assets. WEBSOCKET = require('ws'); } else { WEBSOCKET = WebSocket; } /** * The namespace for ServerConnection functions. * * #### Notes * This is only intended to manage communication with the Jupyter server. * * The default values can be used in a JupyterLab or Jupyter Notebook context. * * We use `token` authentication if available, falling back on an XSRF * cookie if one has been provided on the `document`. * * A content type of `'application/json'` is added when using authentication * and there is no body data to allow the server to prevent malicious forms. */ var ServerConnection; (function (ServerConnection) { /** * Create a settings object given a subset of options. * * @param options - An optional partial set of options. * * @returns The full settings object. */ function makeSettings(options) { return Private.makeSettings(options); } ServerConnection.makeSettings = makeSettings; /** * Make an request to the notebook server. * * @param url - The url for the request. * * @param init - The initialization options for the request. * * @param settings - The server settings to apply to the request. * * @returns a Promise that resolves with the response. * * @throws If the url of the request is not a notebook server url. * * #### Notes * The `url` must start with `settings.baseUrl`. The `init` settings are * merged with `settings.init`, with `init` taking precedence. * The headers in the two objects are not merged. * If there is no body data, we set the content type to `application/json` * because it is required by the Notebook server. */ function makeRequest(url, init, settings) { return Private.handleRequest(url, init, settings); } ServerConnection.makeRequest = makeRequest; /** * A wrapped error for a fetch response. */ class ResponseError extends Error { /** * Create a ResponseError from a response, handling the traceback and message * as appropriate. * * @param response The response object. * * @returns A promise that resolves with a `ResponseError` object. */ static async create(response) { try { const data = await response.json(); const { message, traceback } = data; if (traceback) { console.error(traceback); } return new ResponseError(response, message !== null && message !== void 0 ? message : ResponseError._defaultMessage(response), traceback !== null && traceback !== void 0 ? traceback : ''); } catch (e) { console.debug(e); return new ResponseError(response); } } /** * Create a new response error. */ constructor(response, message = ResponseError._defaultMessage(response), traceback = '') { super(message); this.response = response; this.traceback = traceback; } static _defaultMessage(response) { return `Invalid response: ${response.status} ${response.statusText}`; } } ServerConnection.ResponseError = ResponseError; /** * A wrapped error for a network error. */ class NetworkError extends TypeError { /** * Create a new network error. */ constructor(original) { super(original.message); this.stack = original.stack; } } ServerConnection.NetworkError = NetworkError; })(ServerConnection || (exports.ServerConnection = ServerConnection = {})); /** * The namespace for module private data. */ var Private; (function (Private) { /** * Handle the server connection settings, returning a new value. */ function makeSettings(options = {}) { var _a; const pageBaseUrl = coreutils_1.PageConfig.getBaseUrl(); const pageWsUrl = coreutils_1.PageConfig.getWsUrl(); const baseUrl = coreutils_1.URLExt.normalize(options.baseUrl) || pageBaseUrl; let wsUrl = options.wsUrl; // Prefer the default wsUrl if we are using the default baseUrl. if (!wsUrl && baseUrl === pageBaseUrl) { wsUrl = pageWsUrl; } // Otherwise convert the baseUrl to a wsUrl if possible. if (!wsUrl && baseUrl.indexOf('http') === 0) { wsUrl = 'ws' + baseUrl.slice(4); } // Otherwise fall back on the default wsUrl. wsUrl = wsUrl !== null && wsUrl !== void 0 ? wsUrl : pageWsUrl; const appendTokenConfig = coreutils_1.PageConfig.getOption('appendToken').toLowerCase(); let appendToken; if (appendTokenConfig === '') { appendToken = typeof window === 'undefined' || (typeof process !== 'undefined' && ((_a = process === null || process === void 0 ? void 0 : process.env) === null || _a === void 0 ? void 0 : _a.JEST_WORKER_ID) !== undefined) || coreutils_1.URLExt.getHostName(pageBaseUrl) !== coreutils_1.URLExt.getHostName(wsUrl); } else { appendToken = appendTokenConfig === 'true'; } return { init: { cache: 'no-store', credentials: 'same-origin' }, fetch, Headers, Request, WebSocket: WEBSOCKET, token: coreutils_1.PageConfig.getToken(), appUrl: coreutils_1.PageConfig.getOption('appUrl'), appendToken, serializer: { serialize: serialize_1.serialize, deserialize: serialize_1.deserialize }, ...options, baseUrl, wsUrl }; } Private.makeSettings = makeSettings; /** * Handle a request. * * @param url - The url for the request. * * @param init - The overrides for the request init. * * @param settings - The settings object for the request. * * #### Notes * The `url` must start with `settings.baseUrl`. The `init` settings * take precedence over `settings.init`. */ function handleRequest(url, init, settings) { var _a; // Handle notebook server requests. if (url.indexOf(settings.baseUrl) !== 0) { throw new Error('Can only be used for notebook server requests'); } // Use explicit cache buster when `no-store` is set since // not all browsers use it properly. const cache = (_a = init.cache) !== null && _a !== void 0 ? _a : settings.init.cache; if (cache === 'no-store') { // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache url += (/\?/.test(url) ? '&' : '?') + new Date().getTime(); } const request = new settings.Request(url, { ...settings.init, ...init }); // Handle authentication. Authentication can be overdetermined by // settings token and XSRF token. let authenticated = false; if (settings.token) { authenticated = true; request.headers.append('Authorization', `token ${settings.token}`); } if (typeof document !== 'undefined') { const xsrfToken = getCookie('_xsrf'); if (xsrfToken !== undefined) { authenticated = true; request.headers.append('X-XSRFToken', xsrfToken); } } // Set the content type if there is no given data and we are // using an authenticated connection. if (!request.headers.has('Content-Type') && authenticated) { request.headers.set('Content-Type', 'application/json'); } // Use `call` to avoid a `TypeError` in the browser. return settings.fetch.call(null, request).catch((e) => { // Convert the TypeError into a more specific error. throw new ServerConnection.NetworkError(e); }); // TODO: *this* is probably where we need a system-wide connectionFailure // signal we can hook into. } Private.handleRequest = handleRequest; /** * Get a cookie from the document. */ function getCookie(name) { // From http://www.tornadoweb.org/en/stable/guide/security.html let cookie = ''; try { cookie = document.cookie; } catch (e) { // e.g. SecurityError in case of CSP Sandbox return; } const matches = cookie.match('\\b' + name + '=([^;]*)\\b'); return matches === null || matches === void 0 ? void 0 : matches[1]; } })(Private || (Private = {})); //# sourceMappingURL=serverconnection.js.map