123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- "use strict";
- /*
- * Copyright 2022 gRPC authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.LoadBalancingCall = void 0;
- const connectivity_state_1 = require("./connectivity-state");
- const constants_1 = require("./constants");
- const deadline_1 = require("./deadline");
- const metadata_1 = require("./metadata");
- const picker_1 = require("./picker");
- const uri_parser_1 = require("./uri-parser");
- const logging = require("./logging");
- const control_plane_status_1 = require("./control-plane-status");
- const http2 = require("http2");
- const TRACER_NAME = 'load_balancing_call';
- class LoadBalancingCall {
- constructor(channel, callConfig, methodName, host, credentials, deadline, callNumber) {
- var _a, _b;
- this.channel = channel;
- this.callConfig = callConfig;
- this.methodName = methodName;
- this.host = host;
- this.credentials = credentials;
- this.deadline = deadline;
- this.callNumber = callNumber;
- this.child = null;
- this.readPending = false;
- this.pendingMessage = null;
- this.pendingHalfClose = false;
- this.pendingChildStatus = null;
- this.ended = false;
- this.metadata = null;
- this.listener = null;
- this.onCallEnded = null;
- const splitPath = this.methodName.split('/');
- let serviceName = '';
- /* The standard path format is "/{serviceName}/{methodName}", so if we split
- * by '/', the first item should be empty and the second should be the
- * service name */
- if (splitPath.length >= 2) {
- serviceName = splitPath[1];
- }
- const hostname = (_b = (_a = uri_parser_1.splitHostPort(this.host)) === null || _a === void 0 ? void 0 : _a.host) !== null && _b !== void 0 ? _b : 'localhost';
- /* Currently, call credentials are only allowed on HTTPS connections, so we
- * can assume that the scheme is "https" */
- this.serviceUrl = `https://${hostname}/${serviceName}`;
- }
- trace(text) {
- logging.trace(constants_1.LogVerbosity.DEBUG, TRACER_NAME, '[' + this.callNumber + '] ' + text);
- }
- outputStatus(status, progress) {
- var _a, _b;
- if (!this.ended) {
- this.ended = true;
- this.trace('ended with status: code=' + status.code + ' details="' + status.details + '"');
- const finalStatus = Object.assign(Object.assign({}, status), { progress });
- (_a = this.listener) === null || _a === void 0 ? void 0 : _a.onReceiveStatus(finalStatus);
- (_b = this.onCallEnded) === null || _b === void 0 ? void 0 : _b.call(this, finalStatus.code);
- }
- }
- doPick() {
- var _a, _b;
- if (this.ended) {
- return;
- }
- if (!this.metadata) {
- throw new Error('doPick called before start');
- }
- const pickResult = this.channel.doPick(this.metadata, this.callConfig.pickInformation);
- const subchannelString = pickResult.subchannel ?
- '(' + pickResult.subchannel.getChannelzRef().id + ') ' + pickResult.subchannel.getAddress() :
- '' + pickResult.subchannel;
- this.trace('Pick result: ' +
- picker_1.PickResultType[pickResult.pickResultType] +
- ' subchannel: ' +
- subchannelString +
- ' status: ' + ((_a = pickResult.status) === null || _a === void 0 ? void 0 : _a.code) +
- ' ' + ((_b = pickResult.status) === null || _b === void 0 ? void 0 : _b.details));
- switch (pickResult.pickResultType) {
- case picker_1.PickResultType.COMPLETE:
- this.credentials.generateMetadata({ service_url: this.serviceUrl }).then((credsMetadata) => {
- var _a, _b, _c;
- const finalMetadata = this.metadata.clone();
- finalMetadata.merge(credsMetadata);
- if (finalMetadata.get('authorization').length > 1) {
- this.outputStatus({
- code: constants_1.Status.INTERNAL,
- details: '"authorization" metadata cannot have multiple values',
- metadata: new metadata_1.Metadata()
- }, 'PROCESSED');
- }
- if (pickResult.subchannel.getConnectivityState() !== connectivity_state_1.ConnectivityState.READY) {
- this.trace('Picked subchannel ' +
- subchannelString +
- ' has state ' +
- connectivity_state_1.ConnectivityState[pickResult.subchannel.getConnectivityState()] +
- ' after getting credentials metadata. Retrying pick');
- this.doPick();
- return;
- }
- if (this.deadline !== Infinity) {
- finalMetadata.set('grpc-timeout', deadline_1.getDeadlineTimeoutString(this.deadline));
- }
- try {
- this.child = pickResult.subchannel.getRealSubchannel().createCall(finalMetadata, this.host, this.methodName, {
- onReceiveMetadata: metadata => {
- this.listener.onReceiveMetadata(metadata);
- },
- onReceiveMessage: message => {
- this.listener.onReceiveMessage(message);
- },
- onReceiveStatus: status => {
- if (status.code === http2.constants.NGHTTP2_REFUSED_STREAM) {
- this.outputStatus(status, 'REFUSED');
- }
- else {
- this.outputStatus(status, 'PROCESSED');
- }
- }
- });
- }
- catch (error) {
- this.trace('Failed to start call on picked subchannel ' +
- subchannelString +
- ' with error ' +
- error.message);
- this.outputStatus({
- code: constants_1.Status.INTERNAL,
- details: 'Failed to start HTTP/2 stream with error ' + error.message,
- metadata: new metadata_1.Metadata()
- }, 'NOT_STARTED');
- return;
- }
- (_b = (_a = this.callConfig).onCommitted) === null || _b === void 0 ? void 0 : _b.call(_a);
- (_c = pickResult.onCallStarted) === null || _c === void 0 ? void 0 : _c.call(pickResult);
- this.onCallEnded = pickResult.onCallEnded;
- this.trace('Created child call [' + this.child.getCallNumber() + ']');
- if (this.readPending) {
- this.child.startRead();
- }
- if (this.pendingMessage) {
- this.child.sendMessageWithContext(this.pendingMessage.context, this.pendingMessage.message);
- }
- if (this.pendingHalfClose) {
- this.child.halfClose();
- }
- }, (error) => {
- // We assume the error code isn't 0 (Status.OK)
- const { code, details } = control_plane_status_1.restrictControlPlaneStatusCode(typeof error.code === 'number' ? error.code : constants_1.Status.UNKNOWN, `Getting metadata from plugin failed with error: ${error.message}`);
- this.outputStatus({
- code: code,
- details: details,
- metadata: new metadata_1.Metadata()
- }, 'PROCESSED');
- });
- break;
- case picker_1.PickResultType.DROP:
- const { code, details } = control_plane_status_1.restrictControlPlaneStatusCode(pickResult.status.code, pickResult.status.details);
- this.outputStatus({ code, details, metadata: pickResult.status.metadata }, 'DROP');
- break;
- case picker_1.PickResultType.TRANSIENT_FAILURE:
- if (this.metadata.getOptions().waitForReady) {
- this.channel.queueCallForPick(this);
- }
- else {
- const { code, details } = control_plane_status_1.restrictControlPlaneStatusCode(pickResult.status.code, pickResult.status.details);
- this.outputStatus({ code, details, metadata: pickResult.status.metadata }, 'PROCESSED');
- }
- break;
- case picker_1.PickResultType.QUEUE:
- this.channel.queueCallForPick(this);
- }
- }
- cancelWithStatus(status, details) {
- var _a;
- this.trace('cancelWithStatus code: ' + status + ' details: "' + details + '"');
- (_a = this.child) === null || _a === void 0 ? void 0 : _a.cancelWithStatus(status, details);
- this.outputStatus({ code: status, details: details, metadata: new metadata_1.Metadata() }, 'PROCESSED');
- }
- getPeer() {
- var _a, _b;
- return (_b = (_a = this.child) === null || _a === void 0 ? void 0 : _a.getPeer()) !== null && _b !== void 0 ? _b : this.channel.getTarget();
- }
- start(metadata, listener) {
- this.trace('start called');
- this.listener = listener;
- this.metadata = metadata;
- this.doPick();
- }
- sendMessageWithContext(context, message) {
- this.trace('write() called with message of length ' + message.length);
- if (this.child) {
- this.child.sendMessageWithContext(context, message);
- }
- else {
- this.pendingMessage = { context, message };
- }
- }
- startRead() {
- this.trace('startRead called');
- if (this.child) {
- this.child.startRead();
- }
- else {
- this.readPending = true;
- }
- }
- halfClose() {
- this.trace('halfClose called');
- if (this.child) {
- this.child.halfClose();
- }
- else {
- this.pendingHalfClose = true;
- }
- }
- setCredentials(credentials) {
- throw new Error("Method not implemented.");
- }
- getCallNumber() {
- return this.callNumber;
- }
- }
- exports.LoadBalancingCall = LoadBalancingCall;
- //# sourceMappingURL=load-balancing-call.js.map
|