diff --git a/bosdyn-client/channel.js b/bosdyn-client/channel.js index eeb9faa..d2a7462 100644 --- a/bosdyn-client/channel.js +++ b/bosdyn-client/channel.js @@ -1,10 +1,11 @@ 'use strict'; +const process = require('node:process'); const { Buffer } = require('node:buffer'); + const grpc = require('@grpc/grpc-js'); const { - RpcError, ClientCancelledOperationError, InvalidAppTokenError, InvalidClientCertificateError, @@ -13,15 +14,18 @@ const { PermissionDeniedError, ProxyConnectionError, ResponseTooLargeError, + RetryableUnavailableError, + RpcError, ServiceFailedDuringExecutionError, ServiceUnavailableError, TimedOutError, + TooManyRequestsError, + TransientFailureError, UnableToConnectToRobotError, UnauthenticatedError, - UnknownDnsNameError, UnimplementedError, - TransientFailureError, -} = require('./exceptions.js'); + UnknownDnsNameError, +} = require('./exceptions'); /** * Set default max message length for sending and receiving to 100MB. This value is used when @@ -35,23 +39,19 @@ const DEFAULT_MAX_MESSAGE_LENGTH = 100 * 1024 ** 2; /** * Plugin to refresh access token. * @param {Function} token_cb Callable that returns an Object - * @param {boolean} add_app_token Whether to include an app token in the metadata. - * This is necessary for compatibility with old robot software. + * @param {boolean} [add_app_token] Deprecated. * @returns {Function} */ -function RefreshingAccessTokenAuthMetadataPlugin(token_cb, add_app_token) { +function RefreshingAccessTokenAuthMetadataPlugin(token_cb, add_app_token = null) { const _token_cb = token_cb; - const _add_app_token = add_app_token; + if (add_app_token !== null) { + process.emitWarning('add_app_token is deprecated for RefreshingAccessTokenAuthMetadataPlugin.', 'Do not set it'); + } return function setMetadata(context, callback) { - const { app_token, user_token } = _token_cb(); + const { user_token } = _token_cb(); const metadata = new grpc.Metadata(); - if (_add_app_token) { - metadata.set('authorization', `Bearer ${user_token}`); - metadata.set('x-bosdyn-apptoken', app_token); - } else { - metadata.set('authorization', `Bearer ${user_token}`); - } + metadata.set('authorization', `Bearer ${user_token}`); return callback(null, metadata); }; } @@ -60,11 +60,14 @@ function RefreshingAccessTokenAuthMetadataPlugin(token_cb, add_app_token) { * Returns credentials for establishing a secure channel. Uses previously set values on the linked Sdk and this. * @param {string|Buffer} cert The certificate to create channel credentials. * @param {Function} token_cb Callable that returns an Object - * @param {boolean} add_app_token Whether to include an app token in the metadata. - * This is necessary for compatibility with old robot software. + * @param {boolean} add_app_token Deprecated. * @returns {Object} */ -function create_secure_channel_creds(cert, token_cb, add_app_token) { +function create_secure_channel_creds(cert, token_cb, add_app_token = null) { + if (add_app_token !== null) { + process.emitWarning('add_app_token is deprecated for create_secure_channel_creds.', 'Do not set it'); + } + cert = Buffer.concat([Buffer.from(cert), Buffer.from('\0')]); const transport_creds = grpc.credentials.createSsl(cert); const plugin = RefreshingAccessTokenAuthMetadataPlugin(token_cb, add_app_token); @@ -113,8 +116,8 @@ function create_insecure_channel(address, port, authority = null, options = {}) * The list contains the values for max allowed message length for both sending and * receiving. If no values are provided, the default values of 100 MB are used. -* @param {?number} [max_send_message_length=104857600] Max message length allowed for message to send. -* @param {?number} [max_receive_message_length=104857600] Max message length allowed for message to receive. +* @param {?number} [max_send_message_length=104_857_600] Max message length allowed for message to send. +* @param {?number} [max_receive_message_length=104_857_600] Max message length allowed for message to receive. * @returns {Object} Object with values for channel options. */ function generate_channel_options(max_send_message_length = null, max_receive_message_length = null) { @@ -176,6 +179,15 @@ function translate_exception(rpc_error) { } if (code === grpc.status.UNAVAILABLE) { + if (msg.includes('Socket closed') || msg.includes('Connection reset by peer')) { + return new RetryableUnavailableError(msg); + } + if (msg.includes(502)) { + return new ServiceUnavailableError(msg); + } + if (msg.includes(429)) { + return new TooManyRequestsError(msg); + } return new UnableToConnectToRobotError(msg); } diff --git a/bosdyn-client/robot.js b/bosdyn-client/robot.js index c8fd667..6f3b4e8 100644 --- a/bosdyn-client/robot.js +++ b/bosdyn-client/robot.js @@ -139,6 +139,10 @@ class Robot { } } + get host() { + return this._name; + } + _get_token_id(username) { return `${this.serial_number}.${username}`; } @@ -216,19 +220,12 @@ class Robot { return this._robot_id; } - async _should_send_app_token_on_each_request() { - const robot_id = await this.get_cached_robot_id(); - const robot_software_version = robot_id.getSoftwareRelease().getVersion(); - if (robot_software_version.getMajorVersion() <= 1 && robot_software_version.getMinorVersion() <= 1) return true; - return false; - } - /** * Verify the right information exists before calling the ensure_secure_channel method. * @param {string} service_name Name of the service in the directory. * @param {boolean} [secure=true] Create a secure channel or not. * @param {Array} options Options of the grpc channel. - * @returns {Promise<*>|*} Existing channel if found, or newly created channel if not found. + * @returns {Promise<*>} Existing channel if found, or newly created channel if not found. */ async ensure_channel(service_name, secure = true, options = []) { const option = options.length ? options.map(x => x[0]) : null; @@ -260,16 +257,13 @@ class Robot { : this.ensure_insecure_channel(authority, options); } - async ensure_secure_channel(authority, skip_app_token_check = false, options = []) { + ensure_secure_channel(authority, skip_app_token_check = false, options = []) { if (authority in this.channels_by_authority) return this.channels_by_authority[authority]; - const should_send_app_token = skip_app_token_check ? false : await this._should_send_app_token_on_each_request(); - - const creds = channel.create_secure_channel_creds( - this.cert, - () => ({ app_token: this.app_token, user_token: this.user_token }), - should_send_app_token, - ); + const creds = channel.create_secure_channel_creds(this.cert, () => ({ + app_token: this.app_token, + user_token: this.user_token, + })); const channelData = channel.create_secure_channel( this.address, _DEFAULT_SECURE_CHANNEL_PORT, @@ -287,8 +281,8 @@ class Robot { ensure_insecure_channel(authority, options = []) { if (authority in this.channels_by_authority) return this.channels_by_authority[authority]; const channelData = channel.create_insecure_channel(this.address, _DEFAULT_SECURE_CHANNEL_PORT, authority, options); - this.logger.debug( - `[ROBOT] Created channel to ${this.address} at port ${_DEFAULT_SECURE_CHANNEL_PORT} with authority ${authority}`, + this.logger.warn( + `[ROBOT] Created insecure channel to ${this.address} at port ${_DEFAULT_SECURE_CHANNEL_PORT} with authority ${authority}`, ); this.channels_by_authority[authority] = channelData; return channelData; @@ -297,7 +291,7 @@ class Robot { async authenticate(username, password, timeout) { console.log('Pensez à modifier authenticate dans le fichier robot.js'); const default_service_name = AuthClient.default_service_name; - const auth_channel = this.ensure_insecure_channel(this._bootstrap_service_authorities[default_service_name]); + const auth_channel = this.ensure_secure_channel(this._bootstrap_service_authorities[default_service_name]); const auth_client = await this.ensure_client(default_service_name, auth_channel); const user_token = await auth_client.auth(username, password, this.app_token, { timeout }); this.update_user_token(user_token, username);