mirror of https://github.com/grpc/grpc-node.git
Merge pull request #231 from murgatroid99/pure_js_credentials_service_url
Pure JS: Pass real service_url to call credentials
This commit is contained in:
commit
fa32b718e3
|
@ -7,13 +7,27 @@ import {BaseFilter, Filter, FilterFactory} from './filter';
|
|||
import {Metadata} from './metadata';
|
||||
|
||||
export class CallCredentialsFilter extends BaseFilter implements Filter {
|
||||
constructor(private readonly credentials: CallCredentials) {
|
||||
private serviceUrl: string;
|
||||
constructor(
|
||||
private readonly credentials: CallCredentials,
|
||||
private readonly host: string,
|
||||
private readonly path: string) {
|
||||
super();
|
||||
let splitPath: string[] = path.split('/');
|
||||
let serviceName: string = '';
|
||||
/* 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];
|
||||
}
|
||||
/* Currently, call credentials are only allowed on HTTPS connections, so we
|
||||
* can assume that the scheme is "https" */
|
||||
this.serviceUrl = `https://${host}/${serviceName}`;
|
||||
}
|
||||
|
||||
async sendMetadata(metadata: Promise<Metadata>): Promise<Metadata> {
|
||||
// TODO(kjin): pass real service URL to generateMetadata
|
||||
let credsMetadata = this.credentials.generateMetadata({ service_url: '' });
|
||||
let credsMetadata = this.credentials.generateMetadata({ service_url: this.serviceUrl });
|
||||
let resultMetadata = await metadata;
|
||||
resultMetadata.merge(await credsMetadata);
|
||||
return resultMetadata;
|
||||
|
@ -29,6 +43,8 @@ export class CallCredentialsFilterFactory implements
|
|||
|
||||
createFilter(callStream: CallStream): CallCredentialsFilter {
|
||||
return new CallCredentialsFilter(
|
||||
this.credentials.compose(callStream.getCredentials()));
|
||||
this.credentials.compose(callStream.getCredentials()),
|
||||
callStream.getHost(),
|
||||
callStream.getMethod());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ export interface CallStreamOptions {
|
|||
deadline: Deadline;
|
||||
credentials: CallCredentials;
|
||||
flags: number;
|
||||
host: string;
|
||||
}
|
||||
|
||||
export type CallOptions = Partial<CallStreamOptions>;
|
||||
|
@ -44,6 +45,8 @@ export type CallStream = {
|
|||
/* If the return value is null, the call has not ended yet. Otherwise, it has
|
||||
* ended with the specified status */
|
||||
getStatus(): StatusObject|null;
|
||||
getMethod(): string;
|
||||
getHost(): string;
|
||||
} & EmitterAugmentation1<'metadata', Metadata>
|
||||
& EmitterAugmentation1<'status', StatusObject>
|
||||
& ObjectDuplex<WriteObject, Buffer>;
|
||||
|
@ -355,6 +358,14 @@ export class Http2CallStream extends Duplex implements CallStream {
|
|||
throw new Error('Not yet implemented');
|
||||
}
|
||||
|
||||
getMethod(): string {
|
||||
return this.methodName;
|
||||
}
|
||||
|
||||
getHost(): string {
|
||||
return this.options.host;
|
||||
}
|
||||
|
||||
_read(size: number) {
|
||||
if (this.http2Stream === null) {
|
||||
this.pendingRead = true;
|
||||
|
|
|
@ -82,7 +82,8 @@ export interface Channel extends EventEmitter {
|
|||
|
||||
export class Http2Channel extends EventEmitter implements Channel {
|
||||
private readonly userAgent: string;
|
||||
private readonly authority: url.URL;
|
||||
private readonly target: url.URL;
|
||||
private readonly defaultAuthority: string;
|
||||
private connectivityState: ConnectivityState = ConnectivityState.IDLE;
|
||||
/* For now, we have up to one subchannel, which will exist as long as we are
|
||||
* connecting or trying to connect */
|
||||
|
@ -146,7 +147,7 @@ export class Http2Channel extends EventEmitter implements Channel {
|
|||
let subChannel: http2.ClientHttp2Session;
|
||||
let secureContext = this.credentials.getSecureContext();
|
||||
if (secureContext === null) {
|
||||
subChannel = http2.connect(this.authority);
|
||||
subChannel = http2.connect(this.target);
|
||||
} else {
|
||||
const connectionOptions: http2.SecureClientSessionOptions = {
|
||||
secureContext,
|
||||
|
@ -161,7 +162,7 @@ export class Http2Channel extends EventEmitter implements Channel {
|
|||
}
|
||||
connectionOptions.servername = sslTargetNameOverride;
|
||||
}
|
||||
subChannel = http2.connect(this.authority, connectionOptions);
|
||||
subChannel = http2.connect(this.target, connectionOptions);
|
||||
}
|
||||
this.subChannel = subChannel;
|
||||
let now = new Date();
|
||||
|
@ -195,9 +196,15 @@ export class Http2Channel extends EventEmitter implements Channel {
|
|||
private readonly options: Partial<ChannelOptions>) {
|
||||
super();
|
||||
if (credentials.getSecureContext() === null) {
|
||||
this.authority = new url.URL(`http://${address}`);
|
||||
this.target = new url.URL(`http://${address}`);
|
||||
} else {
|
||||
this.authority = new url.URL(`https://${address}`);
|
||||
this.target = new url.URL(`https://${address}`);
|
||||
}
|
||||
// TODO(murgatroid99): Add more centralized handling of channel options
|
||||
if (this.options['grpc.default_authority']) {
|
||||
this.defaultAuthority = this.options['grpc.default_authority'] as string;
|
||||
} else {
|
||||
this.defaultAuthority = this.target.host;
|
||||
}
|
||||
this.filterStackFactory = new FilterStackFactory([
|
||||
new CompressionFilterFactory(this),
|
||||
|
@ -220,20 +227,16 @@ export class Http2Channel extends EventEmitter implements Channel {
|
|||
}
|
||||
|
||||
private startHttp2Stream(
|
||||
methodName: string, stream: Http2CallStream, metadata: Metadata) {
|
||||
authority: string,
|
||||
methodName: string,
|
||||
stream: Http2CallStream,
|
||||
metadata: Metadata) {
|
||||
let finalMetadata: Promise<Metadata> =
|
||||
stream.filterStack.sendMetadata(Promise.resolve(metadata.clone()));
|
||||
Promise.all([finalMetadata, this.connect()])
|
||||
.then(([metadataValue]) => {
|
||||
let headers = metadataValue.toHttp2Headers();
|
||||
let host: string;
|
||||
// TODO(murgatroid99): Add more centralized handling of channel options
|
||||
if (this.options['grpc.default_authority']) {
|
||||
host = this.options['grpc.default_authority'] as string;
|
||||
} else {
|
||||
host = this.authority.hostname;
|
||||
}
|
||||
headers[HTTP2_HEADER_AUTHORITY] = host;
|
||||
headers[HTTP2_HEADER_AUTHORITY] = authority;
|
||||
headers[HTTP2_HEADER_USER_AGENT] = this.userAgent;
|
||||
headers[HTTP2_HEADER_CONTENT_TYPE] = 'application/grpc';
|
||||
headers[HTTP2_HEADER_METHOD] = 'POST';
|
||||
|
@ -249,7 +252,7 @@ export class Http2Channel extends EventEmitter implements Channel {
|
|||
/* In this case, we lost the connection while finalizing
|
||||
* metadata. That should be very unusual */
|
||||
setImmediate(() => {
|
||||
this.startHttp2Stream(methodName, stream, metadata);
|
||||
this.startHttp2Stream(authority, methodName, stream, metadata);
|
||||
});
|
||||
}
|
||||
}).catch((error: Error & { code: number }) => {
|
||||
|
@ -267,11 +270,12 @@ export class Http2Channel extends EventEmitter implements Channel {
|
|||
let finalOptions: CallStreamOptions = {
|
||||
deadline: options.deadline === undefined ? Infinity : options.deadline,
|
||||
credentials: options.credentials || CallCredentials.createEmpty(),
|
||||
flags: options.flags || 0
|
||||
flags: options.flags || 0,
|
||||
host: options.host || this.defaultAuthority
|
||||
};
|
||||
let stream: Http2CallStream =
|
||||
new Http2CallStream(methodName, finalOptions, this.filterStackFactory);
|
||||
this.startHttp2Stream(methodName, stream, metadata);
|
||||
this.startHttp2Stream(finalOptions.host, methodName, stream, metadata);
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,8 @@ describe('CallStream', () => {
|
|||
const callStreamArgs = {
|
||||
deadline: Infinity,
|
||||
credentials: CallCredentials.createEmpty(),
|
||||
flags: 0
|
||||
flags: 0,
|
||||
host: ''
|
||||
};
|
||||
const filterStackFactory = new FilterStackFactory([]);
|
||||
const message = 'eat this message'; // 16 bytes
|
||||
|
|
Loading…
Reference in New Issue