UNPKG

@databricks/sql

Version:

Driver for connection to Databricks SQL via Thrift API.

198 lines (197 loc) 8.02 kB
"use strict"; /** This file is created using node_modules/thrift/lib/nodejs/lib/thrift/http_connection.js as an example The code relies on thrift internals, so be careful when upgrading `thrift` library */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.THTTPException = void 0; const events_1 = require("events"); const thrift_1 = require("thrift"); const node_fetch_1 = __importStar(require("node-fetch")); // @ts-expect-error TS7016: Could not find a declaration file for module const input_buffer_underrun_error_1 = __importDefault(require("thrift/lib/nodejs/lib/thrift/input_buffer_underrun_error")); const NullRetryPolicy_1 = __importDefault(require("./NullRetryPolicy")); class THTTPException extends thrift_1.Thrift.TApplicationException { constructor(response) { super(thrift_1.Thrift.TApplicationExceptionType.PROTOCOL_ERROR, `Received a response with a bad HTTP status code: ${response.status}`); this.statusCode = response.status; this.response = response; } } exports.THTTPException = THTTPException; const retryableThriftMethods = new Set([ 'GetOperationStatus', 'CancelOperation', 'CloseOperation', 'GetResultSetMetadata', 'CloseSession', 'GetInfo', 'GetTypeInfo', 'GetCatalogs', 'GetSchemas', 'GetTables', 'GetTableTypes', 'GetColumns', 'GetFunctions', 'GetPrimaryKeys', 'GetCrossReference', ]); class ThriftHttpConnection extends events_1.EventEmitter { constructor(options, config = {}) { var _a, _b; super(); this.url = options.url; this.config = config; this.options = options; this.transport = (_a = options.transport) !== null && _a !== void 0 ? _a : thrift_1.TBufferedTransport; this.protocol = (_b = options.protocol) !== null && _b !== void 0 ? _b : thrift_1.TBinaryProtocol; } async getRetryPolicy(thriftMethodName) { // Allow retry behavior only for Thrift operations that are for sure safe to retry if (thriftMethodName && retryableThriftMethods.has(thriftMethodName)) { return this.options.getRetryPolicy(); } // Don't retry everything that is not explicitly allowed to retry return new NullRetryPolicy_1.default(); } setHeaders(headers) { this.config = { ...this.config, headers, }; } write(data, seqId) { const requestConfig = { ...this.config, method: 'POST', headers: { ...this.config.headers, Connection: 'keep-alive', 'Content-Length': `${data.length}`, 'Content-Type': 'application/x-thrift', }, body: data, }; this.getThriftMethodName(data) .then((thriftMethod) => this.getRetryPolicy(thriftMethod)) .then((retryPolicy) => { const makeRequest = () => { const request = new node_fetch_1.Request(this.url, requestConfig); return (0, node_fetch_1.default)(request).then((response) => ({ request, response })); }; return retryPolicy.invokeWithRetry(makeRequest); }) .then(({ response }) => { if (response.status !== 200) { throw new THTTPException(response); } return response.buffer(); }) .then((buffer) => { this.transport.receiver((transportWithData) => this.handleThriftResponse(transportWithData), seqId)(buffer); }) .catch((error) => { var _a; if (error instanceof node_fetch_1.FetchError) { if (error.type === 'request-timeout') { error = new thrift_1.Thrift.TApplicationException(thrift_1.Thrift.TApplicationExceptionType.PROTOCOL_ERROR, 'Request timed out'); } } const defaultErrorHandler = (err) => { this.emit('error', err); }; if (this.client) { const callback = (_a = this.client._reqs[seqId]) !== null && _a !== void 0 ? _a : defaultErrorHandler; delete this.client._reqs[seqId]; callback(error); } else { defaultErrorHandler(error); } }); } getThriftMethodName(thriftMessage) { return new Promise((resolve) => { try { const receiver = this.transport.receiver((transportWithData) => { const Protocol = this.protocol; const proto = new Protocol(transportWithData); const header = proto.readMessageBegin(); resolve(header.fname); }, 0 /* `seqId` could be any because it's ignored */); receiver(thriftMessage); } catch (_a) { resolve(undefined); } }); } handleThriftResponse(transportWithData) { if (!this.client) { throw new thrift_1.Thrift.TApplicationException(thrift_1.Thrift.TApplicationExceptionType.INTERNAL_ERROR, 'Client not available'); } const Protocol = this.protocol; const proto = new Protocol(transportWithData); try { // eslint-disable-next-line no-constant-condition while (true) { const header = proto.readMessageBegin(); const dummySeqId = header.rseqid * -1; const { client } = this; client._reqs[dummySeqId] = (err, success) => { transportWithData.commitPosition(); const clientCallback = client._reqs[header.rseqid]; delete client._reqs[header.rseqid]; if (clientCallback) { process.nextTick(() => { clientCallback(err, success); }); } }; if (client[`recv_${header.fname}`]) { client[`recv_${header.fname}`](proto, header.mtype, dummySeqId); } else { delete client._reqs[dummySeqId]; throw new thrift_1.Thrift.TApplicationException(thrift_1.Thrift.TApplicationExceptionType.WRONG_METHOD_NAME, 'Received a response to an unknown RPC function'); } } } catch (error) { if (error instanceof input_buffer_underrun_error_1.default) { transportWithData.rollbackPosition(); } else { throw error; } } } } exports.default = ThriftHttpConnection; //# sourceMappingURL=ThriftHttpConnection.js.map