commit
01d47ec78f
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// Not exported from index.
|
||||
export class BinaryMessageFormat {
|
||||
|
||||
// The length prefix of binary messages is encoded as VarInt. Read the comment in
|
||||
|
|
|
|||
|
|
@ -8,20 +8,53 @@ import { CompletionMessage, HubMessage, IHubProtocol, ILogger, InvocationMessage
|
|||
|
||||
import { BinaryMessageFormat } from "./BinaryMessageFormat";
|
||||
|
||||
// TypeDoc's @inheritDoc and @link don't work across modules :(
|
||||
|
||||
/** Implements the MessagePack Hub Protocol */
|
||||
export class MessagePackHubProtocol implements IHubProtocol {
|
||||
|
||||
/** The name of the protocol. This is used by SignalR to resolve the protocol between the client and server. */
|
||||
public readonly name: string = "messagepack";
|
||||
/** The version of the protocol. */
|
||||
public readonly version: number = 1;
|
||||
|
||||
/** The TransferFormat of the protocol. */
|
||||
public readonly transferFormat: TransferFormat = TransferFormat.Binary;
|
||||
|
||||
/** Creates an array of HubMessage objects from the specified serialized representation.
|
||||
*
|
||||
* @param {ArrayBuffer} input An ArrayBuffer containing the serialized representation.
|
||||
* @param {ILogger} logger A logger that will be used to log messages that occur during parsing.
|
||||
*/
|
||||
public parseMessages(input: ArrayBuffer, logger: ILogger): HubMessage[] {
|
||||
// The interface does allow "string" to be passed in, but this implementation does not. So let's throw a useful error.
|
||||
if (!(input instanceof ArrayBuffer)) {
|
||||
throw new Error("Invalid input for MessagePack hub protocol. Expected an ArrayBuffer.");
|
||||
}
|
||||
|
||||
if (logger === null) {
|
||||
logger = NullLogger.instance;
|
||||
}
|
||||
return BinaryMessageFormat.parse(input).map((m) => this.parseMessage(m, logger));
|
||||
}
|
||||
|
||||
/** Writes the specified HubMessage to an ArrayBuffer and returns it.
|
||||
*
|
||||
* @param {HubMessage} message The message to write.
|
||||
* @returns {ArrayBuffer} An ArrayBuffer containing the serialized representation of the message.
|
||||
*/
|
||||
public writeMessage(message: HubMessage): ArrayBuffer {
|
||||
switch (message.type) {
|
||||
case MessageType.Invocation:
|
||||
return this.writeInvocation(message as InvocationMessage);
|
||||
case MessageType.StreamInvocation:
|
||||
return this.writeStreamInvocation(message as StreamInvocationMessage);
|
||||
case MessageType.StreamItem:
|
||||
case MessageType.Completion:
|
||||
throw new Error(`Writing messages of type '${message.type}' is not supported.`);
|
||||
default:
|
||||
throw new Error("Invalid message type.");
|
||||
}
|
||||
}
|
||||
|
||||
private parseMessage(input: Uint8Array, logger: ILogger): HubMessage {
|
||||
if (input.length === 0) {
|
||||
throw new Error("Invalid payload.");
|
||||
|
|
@ -154,20 +187,6 @@ export class MessagePackHubProtocol implements IHubProtocol {
|
|||
return completionMessage as CompletionMessage;
|
||||
}
|
||||
|
||||
public writeMessage(message: HubMessage): ArrayBuffer {
|
||||
switch (message.type) {
|
||||
case MessageType.Invocation:
|
||||
return this.writeInvocation(message as InvocationMessage);
|
||||
case MessageType.StreamInvocation:
|
||||
return this.writeStreamInvocation(message as StreamInvocationMessage);
|
||||
case MessageType.StreamItem:
|
||||
case MessageType.Completion:
|
||||
throw new Error(`Writing messages of type '${message.type}' is not supported.`);
|
||||
default:
|
||||
throw new Error("Invalid message type.");
|
||||
}
|
||||
}
|
||||
|
||||
private writeInvocation(invocationMessage: InvocationMessage): ArrayBuffer {
|
||||
const msgpack = msgpack5();
|
||||
const payload = msgpack.encode([MessageType.Invocation, invocationMessage.headers || {}, invocationMessage.invocationId || null,
|
||||
|
|
|
|||
|
|
@ -197,15 +197,15 @@ class TestProtocol implements IHubProtocol {
|
|||
public name: string = "test";
|
||||
public version: number = 1;
|
||||
public transferFormat: TransferFormat = TransferFormat.Text;
|
||||
public parseMessages(input: any, logger: ILogger): HubMessage[] {
|
||||
public parseMessages(input: string | ArrayBuffer, logger: ILogger): HubMessage[] {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
public writeMessage(message: HubMessage) {
|
||||
public writeMessage(message: HubMessage): string | ArrayBuffer {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
function createConnectionBuilder(logger?: ILogger | LogLevel): HubConnectionBuilder {
|
||||
function createConnectionBuilder(logger?: ILogger): HubConnectionBuilder {
|
||||
// We don't want to spam test output with logs. This can be changed as needed
|
||||
return new HubConnectionBuilder()
|
||||
.configureLogging(logger || NullLogger.instance);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
// We don't actually ever use the API being polyfilled, we always use the polyfill because
|
||||
// it's a very new API right now.
|
||||
|
||||
// Not exported from index.
|
||||
export class AbortController implements AbortSignal {
|
||||
private isAborted: boolean = false;
|
||||
public onabort: () => void;
|
||||
|
|
@ -27,7 +28,10 @@ export class AbortController implements AbortSignal {
|
|||
}
|
||||
}
|
||||
|
||||
/** Represents a signal that can be monitored to determine if a request has been aborted. */
|
||||
export interface AbortSignal {
|
||||
/** Indicates if the request has been aborted. */
|
||||
aborted: boolean;
|
||||
/** Set this to a handler that will be invoked when the request is aborted. */
|
||||
onabort: () => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
/** Error thrown when an HTTP request fails. */
|
||||
export class HttpError extends Error {
|
||||
// tslint:disable-next-line:variable-name
|
||||
private __proto__: Error;
|
||||
|
||||
/** The HTTP status code represented by this error. */
|
||||
public statusCode: number;
|
||||
|
||||
/** Constructs a new instance of {@link HttpError}.
|
||||
*
|
||||
* @param {string} errorMessage A descriptive error message.
|
||||
* @param {number} statusCode The HTTP status code represented by this error.
|
||||
*/
|
||||
constructor(errorMessage: string, statusCode: number) {
|
||||
const trueProto = new.target.prototype;
|
||||
super(errorMessage);
|
||||
|
|
@ -16,9 +25,15 @@ export class HttpError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
/** Error thrown when a timeout elapses. */
|
||||
export class TimeoutError extends Error {
|
||||
// tslint:disable-next-line:variable-name
|
||||
private __proto__: Error;
|
||||
|
||||
/** Constructs a new instance of {@link TimeoutError}.
|
||||
*
|
||||
* @param {string} errorMessage A descriptive error message.
|
||||
*/
|
||||
constructor(errorMessage: string = "A timeout occurred.") {
|
||||
const trueProto = new.target.prototype;
|
||||
super(errorMessage);
|
||||
|
|
|
|||
|
|
@ -5,20 +5,59 @@ import { AbortSignal } from "./AbortController";
|
|||
import { HttpError, TimeoutError } from "./Errors";
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
|
||||
/** Represents an HTTP request. */
|
||||
export interface HttpRequest {
|
||||
/** The HTTP method to use for the request. */
|
||||
method?: string;
|
||||
|
||||
/** The URL for the request. */
|
||||
url?: string;
|
||||
|
||||
/** The body content for the request. May be a string or an ArrayBuffer (for binary data). */
|
||||
content?: string | ArrayBuffer;
|
||||
|
||||
/** An object describing headers to apply to the request. */
|
||||
headers?: { [key: string]: string };
|
||||
|
||||
/** The XMLHttpRequestResponseType to apply to the request. */
|
||||
responseType?: XMLHttpRequestResponseType;
|
||||
|
||||
/** An AbortSignal that can be monitored for cancellation. */
|
||||
abortSignal?: AbortSignal;
|
||||
|
||||
/** The time to wait for the request to complete before throwing a TimeoutError. Measured in milliseconds. */
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
/** Represents an HTTP response. */
|
||||
export class HttpResponse {
|
||||
/** Constructs a new instance of {@link HttpResponse} with the specified status code.
|
||||
*
|
||||
* @param {number} statusCode The status code of the response.
|
||||
*/
|
||||
constructor(statusCode: number);
|
||||
|
||||
/** Constructs a new instance of {@link HttpResponse} with the specified status code and message.
|
||||
*
|
||||
* @param {number} statusCode The status code of the response.
|
||||
* @param {string} statusText The status message of the response.
|
||||
*/
|
||||
constructor(statusCode: number, statusText: string);
|
||||
|
||||
/** Constructs a new instance of {@link HttpResponse} with the specified status code, message and string content.
|
||||
*
|
||||
* @param {number} statusCode The status code of the response.
|
||||
* @param {string} statusText The status message of the response.
|
||||
* @param {string} content The content of the response.
|
||||
*/
|
||||
constructor(statusCode: number, statusText: string, content: string);
|
||||
|
||||
/** Constructs a new instance of {@link HttpResponse} with the specified status code, message and binary content.
|
||||
*
|
||||
* @param {number} statusCode The status code of the response.
|
||||
* @param {string} statusText The status message of the response.
|
||||
* @param {ArrayBuffer} content The content of the response.
|
||||
*/
|
||||
constructor(statusCode: number, statusText: string, content: ArrayBuffer);
|
||||
constructor(
|
||||
public readonly statusCode: number,
|
||||
|
|
@ -27,8 +66,24 @@ export class HttpResponse {
|
|||
}
|
||||
}
|
||||
|
||||
/** Abstraction over an HTTP client.
|
||||
*
|
||||
* This class provides an abstraction over an HTTP client so that a different implementation can be provided on different platforms.
|
||||
*/
|
||||
export abstract class HttpClient {
|
||||
/** Issues an HTTP GET request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
|
||||
*
|
||||
* @param {string} url The URL for the request.
|
||||
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
|
||||
*/
|
||||
public get(url: string): Promise<HttpResponse>;
|
||||
|
||||
/** Issues an HTTP GET request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
|
||||
*
|
||||
* @param {string} url The URL for the request.
|
||||
* @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.
|
||||
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
|
||||
*/
|
||||
public get(url: string, options: HttpRequest): Promise<HttpResponse>;
|
||||
public get(url: string, options?: HttpRequest): Promise<HttpResponse> {
|
||||
return this.send({
|
||||
|
|
@ -38,7 +93,19 @@ export abstract class HttpClient {
|
|||
});
|
||||
}
|
||||
|
||||
/** Issues an HTTP POST request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
|
||||
*
|
||||
* @param {string} url The URL for the request.
|
||||
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
|
||||
*/
|
||||
public post(url: string): Promise<HttpResponse>;
|
||||
|
||||
/** Issues an HTTP POST request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
|
||||
*
|
||||
* @param {string} url The URL for the request.
|
||||
* @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.
|
||||
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
|
||||
*/
|
||||
public post(url: string, options: HttpRequest): Promise<HttpResponse>;
|
||||
public post(url: string, options?: HttpRequest): Promise<HttpResponse> {
|
||||
return this.send({
|
||||
|
|
@ -48,7 +115,19 @@ export abstract class HttpClient {
|
|||
});
|
||||
}
|
||||
|
||||
/** Issues an HTTP DELETE request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
|
||||
*
|
||||
* @param {string} url The URL for the request.
|
||||
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
|
||||
*/
|
||||
public delete(url: string): Promise<HttpResponse>;
|
||||
|
||||
/** Issues an HTTP DELETE request to the specified URL, returning a Promise that resolves with an {@link HttpResponse} representing the result.
|
||||
*
|
||||
* @param {string} url The URL for the request.
|
||||
* @param {HttpRequest} options Additional options to configure the request. The 'url' field in this object will be overridden by the url parameter.
|
||||
* @returns {Promise<HttpResponse>} A Promise that resolves with an {@link HttpResponse} describing the response, or rejects with an Error indicating a failure.
|
||||
*/
|
||||
public delete(url: string, options: HttpRequest): Promise<HttpResponse>;
|
||||
public delete(url: string, options?: HttpRequest): Promise<HttpResponse> {
|
||||
return this.send({
|
||||
|
|
@ -58,17 +137,25 @@ export abstract class HttpClient {
|
|||
});
|
||||
}
|
||||
|
||||
/** Issues an HTTP request to the specified URL, returning a {@link Promise} that resolves with an {@link HttpResponse} representing the result.
|
||||
*
|
||||
* @param {HttpRequest} request An {@link HttpRequest} describing the request to send.
|
||||
* @returns {Promise<HttpResponse>} A Promise that resolves with an HttpResponse describing the response, or rejects with an Error indicating a failure.
|
||||
*/
|
||||
public abstract send(request: HttpRequest): Promise<HttpResponse>;
|
||||
}
|
||||
|
||||
/** Default implementation of {@link HttpClient}. */
|
||||
export class DefaultHttpClient extends HttpClient {
|
||||
private readonly logger: ILogger;
|
||||
|
||||
constructor(logger: ILogger) {
|
||||
/** Creates a new instance of the {@link DefaultHttpClient}, using the provided {@link ILogger} to log messages. */
|
||||
public constructor(logger: ILogger) {
|
||||
super();
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public send(request: HttpRequest): Promise<HttpResponse> {
|
||||
return new Promise<HttpResponse>((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { Arg, Subject } from "./Utils";
|
|||
|
||||
const DEFAULT_TIMEOUT_IN_MS: number = 30 * 1000;
|
||||
|
||||
/** Represents a connection to a SignalR Hub. */
|
||||
export class HubConnection {
|
||||
private readonly connection: IConnection;
|
||||
private readonly logger: ILogger;
|
||||
|
|
@ -22,6 +23,11 @@ export class HubConnection {
|
|||
private timeoutHandle: NodeJS.Timer;
|
||||
private receivedHandshakeResponse: boolean;
|
||||
|
||||
/** The server timeout in milliseconds.
|
||||
*
|
||||
* If this timeout elapses without receiving any messages from the server, the connection will be terminated with an error.
|
||||
* The default timeout value is 30,000 milliseconds (30 seconds).
|
||||
*/
|
||||
public serverTimeoutInMilliseconds: number;
|
||||
|
||||
/** @internal */
|
||||
|
|
@ -54,6 +60,10 @@ export class HubConnection {
|
|||
this.id = 0;
|
||||
}
|
||||
|
||||
/** Starts the connection.
|
||||
*
|
||||
* @returns {Promise<void>} A Promise that resolves when the connection has been successfully established, or rejects with an error.
|
||||
*/
|
||||
public async start(): Promise<void> {
|
||||
const handshakeRequest: HandshakeRequestMessage = {
|
||||
protocol: this.protocol.name,
|
||||
|
|
@ -77,6 +87,10 @@ export class HubConnection {
|
|||
this.configureTimeout();
|
||||
}
|
||||
|
||||
/** Stops the connection.
|
||||
*
|
||||
* @returns {Promise<void>} A Promise that resolves when the connection has been successfully terminated, or rejects with an error.
|
||||
*/
|
||||
public stop(): Promise<void> {
|
||||
this.logger.log(LogLevel.Debug, "Stopping HubConnection.");
|
||||
|
||||
|
|
@ -84,6 +98,13 @@ export class HubConnection {
|
|||
return this.connection.stop();
|
||||
}
|
||||
|
||||
/** Invokes a streaming hub method on the server using the specified name and arguments.
|
||||
*
|
||||
* @typeparam T The type of the items returned by the server.
|
||||
* @param {string} methodName The name of the server method to invoke.
|
||||
* @param {any[]} args The arguments used to invoke the server method.
|
||||
* @returns {IStreamResult<T>} An object that yields results from the server as they are received.
|
||||
*/
|
||||
public stream<T = any>(methodName: string, ...args: any[]): IStreamResult<T> {
|
||||
const invocationDescriptor = this.createStreamInvocation(methodName, args);
|
||||
|
||||
|
|
@ -124,6 +145,15 @@ export class HubConnection {
|
|||
return subject;
|
||||
}
|
||||
|
||||
/** Invokes a hub method on the server using the specified name and arguments. Does not wait for a response from the receiver.
|
||||
*
|
||||
* The Promise returned by this method resolves when the client has sent the invocation to the server. The server may still
|
||||
* be processing the invocation.
|
||||
*
|
||||
* @param {string} methodName The name of the server method to invoke.
|
||||
* @param {any[]} args The arguments used to invoke the server method.
|
||||
* @returns {Promise<void>} A Promise that resolves when the invocation has been successfully sent, or rejects with an error.
|
||||
*/
|
||||
public send(methodName: string, ...args: any[]): Promise<void> {
|
||||
const invocationDescriptor = this.createInvocation(methodName, args, true);
|
||||
|
||||
|
|
@ -132,6 +162,17 @@ export class HubConnection {
|
|||
return this.connection.send(message);
|
||||
}
|
||||
|
||||
/** Invokes a hub method on the server using the specified name and arguments.
|
||||
*
|
||||
* The Promise returned by this method resolves when the server indicates it has finished invoking the method. When the promise
|
||||
* resolves, the server has finished invoking the method. If the server method returns a result, it is produced as the result of
|
||||
* resolving the Promise.
|
||||
*
|
||||
* @typeparam T The expected return type.
|
||||
* @param {string} methodName The name of the server method to invoke.
|
||||
* @param {any[]} args The arguments used to invoke the server method.
|
||||
* @returns {Promise<T>} A Promise that resolves with the result of the server method (if any), or rejects with an error.
|
||||
*/
|
||||
public invoke<T = any>(methodName: string, ...args: any[]): Promise<T> {
|
||||
const invocationDescriptor = this.createInvocation(methodName, args, false);
|
||||
|
||||
|
|
@ -165,6 +206,11 @@ export class HubConnection {
|
|||
return p;
|
||||
}
|
||||
|
||||
/** Registers a handler that will be invoked when the hub method with the specified method name is invoked.
|
||||
*
|
||||
* @param {string} methodName The name of the hub method to define.
|
||||
* @param {Function} newMethod The handler that will be raised when the hub method is invoked.
|
||||
*/
|
||||
public on(methodName: string, newMethod: (...args: any[]) => void) {
|
||||
if (!methodName || !newMethod) {
|
||||
return;
|
||||
|
|
@ -183,7 +229,22 @@ export class HubConnection {
|
|||
this.methods[methodName].push(newMethod);
|
||||
}
|
||||
|
||||
public off(methodName: string, method?: (...args: any[]) => void) {
|
||||
/** Removes all handlers for the specified hub method.
|
||||
*
|
||||
* @param {string} methodName The name of the method to remove handlers for.
|
||||
*/
|
||||
public off(methodName: string): void;
|
||||
|
||||
/** Removes the specified handler for the specified hub method.
|
||||
*
|
||||
* You must pass the exact same Function instance as was previously passed to {@link on}. Passing a different instance (even if the function
|
||||
* body is the same) will not remove the handler.
|
||||
*
|
||||
* @param {string} methodName The name of the method to remove handlers for.
|
||||
* @param {Function} method The handler to remove. This must be the same Function instance as the one passed to {@link on}.
|
||||
*/
|
||||
public off(methodName: string, method: (...args: any[]) => void): void;
|
||||
public off(methodName: string, method?: (...args: any[]) => void): void {
|
||||
if (!methodName) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -207,6 +268,10 @@ export class HubConnection {
|
|||
|
||||
}
|
||||
|
||||
/** Registers a handler that will be invoked when the connection is closed.
|
||||
*
|
||||
* @param {Function} callback The handler that will be invoked when the connection is closed. Optionally receives a single argument containing the error that caused the connection to close (if any).
|
||||
*/
|
||||
public onclose(callback: (error?: Error) => void) {
|
||||
if (callback) {
|
||||
this.closedCallbacks.push(callback);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { JsonHubProtocol } from "./JsonHubProtocol";
|
|||
import { NullLogger } from "./Loggers";
|
||||
import { Arg, ConsoleLogger } from "./Utils";
|
||||
|
||||
/** A builder for configuring {@link HubConnection} instances. */
|
||||
export class HubConnectionBuilder {
|
||||
/** @internal */
|
||||
public protocol: IHubProtocol;
|
||||
|
|
@ -21,6 +22,19 @@ export class HubConnectionBuilder {
|
|||
/** @internal */
|
||||
public logger: ILogger;
|
||||
|
||||
/** Configures console logging for the {@link HubConnection}.
|
||||
*
|
||||
* @param {LogLevel} logLevel The minimum level of messages to log. Anything at this level, or a more severe level, will be logged.
|
||||
* @returns The {@link HubConnectionBuilder} instance, for chaining.
|
||||
*/
|
||||
public configureLogging(logLevel: LogLevel): HubConnectionBuilder;
|
||||
|
||||
/** Configures custom logging for the {@link HubConnection}.
|
||||
*
|
||||
* @param {ILogger} logger An object implementing the {@link ILogger} interface, which will be used to write all log messages.
|
||||
* @returns The {@link HubConnectionBuilder} instance, for chaining.
|
||||
*/
|
||||
public configureLogging(logger: ILogger): HubConnectionBuilder;
|
||||
public configureLogging(logging: LogLevel | ILogger): HubConnectionBuilder {
|
||||
Arg.isRequired(logging, "logging");
|
||||
|
||||
|
|
@ -33,9 +47,30 @@ export class HubConnectionBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Configures the {@link HubConnection} to use HTTP-based transports to connect to the specified URL.
|
||||
*
|
||||
* The transport will be selected automatically based on what the server and client support.
|
||||
*
|
||||
* @param {string} url The URL the connection will use.
|
||||
* @returns The {@link HubConnectionBuilder} instance, for chaining.
|
||||
*/
|
||||
public withUrl(url: string): HubConnectionBuilder;
|
||||
public withUrl(url: string, options: IHttpConnectionOptions): HubConnectionBuilder;
|
||||
|
||||
/** Configures the {@link HubConnection} to use the specified HTTP-based transport to connect to the specified URL.
|
||||
*
|
||||
* @param {string} url The URL the connection will use.
|
||||
* @param {HttpTransportType} transportType The specific transport to use.
|
||||
* @returns The {@link HubConnectionBuilder} instance, for chaining.
|
||||
*/
|
||||
public withUrl(url: string, transportType: HttpTransportType): HubConnectionBuilder;
|
||||
|
||||
/** Configures the {@link HubConnection} to use HTTP-based transports to connect to the specified URL.
|
||||
*
|
||||
* @param {string} url The URL the connection will use.
|
||||
* @param {IHttpConnectionOptions} options An options object used to configure the connection.
|
||||
* @returns The {@link HubConnectionBuilder} instance, for chaining.
|
||||
*/
|
||||
public withUrl(url: string, options: IHttpConnectionOptions): HubConnectionBuilder;
|
||||
public withUrl(url: string, transportTypeOrOptions?: IHttpConnectionOptions | HttpTransportType): HubConnectionBuilder {
|
||||
Arg.isRequired(url, "url");
|
||||
|
||||
|
|
@ -54,6 +89,10 @@ export class HubConnectionBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Configures the {@link HubConnection} to use the specified Hub Protocol.
|
||||
*
|
||||
* @param {IHubProtocol} protocol The {@link IHubProtocol} implementation to use.
|
||||
*/
|
||||
public withHubProtocol(protocol: IHubProtocol): HubConnectionBuilder {
|
||||
Arg.isRequired(protocol, "protocol");
|
||||
|
||||
|
|
@ -61,6 +100,10 @@ export class HubConnectionBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Creates a {@link HubConnection} from the configuration options specified in this builder.
|
||||
*
|
||||
* @returns {HubConnection} The configured {@link HubConnection}.
|
||||
*/
|
||||
public build(): HubConnection {
|
||||
// If httpConnectionOptions has a logger, use it. Otherwise, override it with the one
|
||||
// provided to configureLogger
|
||||
|
|
|
|||
|
|
@ -5,11 +5,37 @@ import { HttpClient } from "./HttpClient";
|
|||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { HttpTransportType, ITransport } from "./ITransport";
|
||||
|
||||
/** Options provided to the 'withUrl' method on {@link HubConnectionBuilder} to configure options for the HTTP-based transports. */
|
||||
export interface IHttpConnectionOptions {
|
||||
/** An {@link HttpClient} that will be used to make HTTP requests. */
|
||||
httpClient?: HttpClient;
|
||||
|
||||
/** An {@link HttpTransportType} value specifying the transport to use for the connection. */
|
||||
transport?: HttpTransportType | ITransport;
|
||||
|
||||
/** Configures the logger used for logging.
|
||||
*
|
||||
* Provide an {@link ILogger} instance, and log messages will be logged via that instance. Alternatively, provide a value from
|
||||
* the {@link LogLevel} enumeration and a default logger which logs to the Console will be configured to log messages of the specified
|
||||
* level (or higher).
|
||||
*/
|
||||
logger?: ILogger | LogLevel;
|
||||
accessTokenFactory?: () => string | Promise<string>;
|
||||
|
||||
/** A function that provides an access token required for HTTP Bearer authentication.
|
||||
*
|
||||
* @returns {string | Promise<string>} A string containing the access token, or a Promise that resolves to a string containing the access token.
|
||||
*/
|
||||
accessTokenFactory?(): string | Promise<string>;
|
||||
|
||||
/** A boolean indicating if message content should be logged.
|
||||
*
|
||||
* Message content can contain sensitive user data, so this is disabled by default.
|
||||
*/
|
||||
logMessageContent?: boolean;
|
||||
|
||||
/** A boolean indicating if negotiation should be skipped.
|
||||
*
|
||||
* Negotiation can only be skipped when the {@link transport} property is set to 'HttpTransportType.WebSockets'.
|
||||
*/
|
||||
skipNegotiation?: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,69 +4,162 @@
|
|||
import { ILogger } from "./ILogger";
|
||||
import { TransferFormat } from "./ITransport";
|
||||
|
||||
export const enum MessageType {
|
||||
/** Defines the type of a Hub Message. */
|
||||
export enum MessageType {
|
||||
/** Indicates the message is an Invocation message and implements the {@link InvocationMessage} interface. */
|
||||
Invocation = 1,
|
||||
/** Indicates the message is a StreamItem message and implements the {@link StreamItemMessage} interface. */
|
||||
StreamItem = 2,
|
||||
/** Indicates the message is a Completion message and implements the {@link CompletionMessage} interface. */
|
||||
Completion = 3,
|
||||
/** Indicates the message is a Stream Invocation message and implements the {@link StreamInvocationMessage} interface. */
|
||||
StreamInvocation = 4,
|
||||
/** Indicates the message is a Cancel Invocation message and implements the {@link CancelInvocationMessage} interface. */
|
||||
CancelInvocation = 5,
|
||||
/** Indicates the message is a Ping message and implements the {@link PingMessage} interface. */
|
||||
Ping = 6,
|
||||
/** Indicates the message is a Close message and implements the {@link CloseMessage} interface. */
|
||||
Close = 7,
|
||||
}
|
||||
|
||||
export interface MessageHeaders { [key: string]: string; }
|
||||
/** Defines a dictionary of string keys and string values representing headers attached to a Hub message. */
|
||||
export interface MessageHeaders {
|
||||
/** Gets or sets the header with the specified key. */
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export type HubMessage = InvocationMessage | StreamInvocationMessage | StreamItemMessage | CompletionMessage | CancelInvocationMessage | PingMessage | CloseMessage;
|
||||
/** Union type of all known Hub messages. */
|
||||
export type HubMessage =
|
||||
InvocationMessage |
|
||||
StreamInvocationMessage |
|
||||
StreamItemMessage |
|
||||
CompletionMessage |
|
||||
CancelInvocationMessage |
|
||||
PingMessage |
|
||||
CloseMessage;
|
||||
|
||||
/** Defines properties common to all Hub messages. */
|
||||
export interface HubMessageBase {
|
||||
/** A {@link MessageType} value indicating the type of this message. */
|
||||
readonly type: MessageType;
|
||||
}
|
||||
|
||||
/** Defines properties common to all Hub messages relating to a specific invocation. */
|
||||
export interface HubInvocationMessage extends HubMessageBase {
|
||||
/** A {@link MessageHeaders} dictionary containing headers attached to the message. */
|
||||
readonly headers?: MessageHeaders;
|
||||
/** The ID of the invocation relating to this message.
|
||||
*
|
||||
* This is expected to be present for {@link StreamInvocationMessage} and {@link CompletionMessage}. It may
|
||||
* be 'undefined' for an {@link InvocationMessage} if the sender does not expect a response.
|
||||
*/
|
||||
readonly invocationId?: string;
|
||||
}
|
||||
|
||||
/** A hub message representing a non-streaming invocation. */
|
||||
export interface InvocationMessage extends HubInvocationMessage {
|
||||
readonly type: MessageType.Invocation;
|
||||
/** The target method name. */
|
||||
readonly target: string;
|
||||
/** The target method arguments. */
|
||||
readonly arguments: any[];
|
||||
}
|
||||
|
||||
/** A hub message representing a streaming invocation. */
|
||||
export interface StreamInvocationMessage extends HubInvocationMessage {
|
||||
/** @inheritDoc */
|
||||
readonly type: MessageType.StreamInvocation;
|
||||
|
||||
/** The invocation ID. */
|
||||
readonly invocationId: string;
|
||||
/** The target method name. */
|
||||
readonly target: string;
|
||||
/** The target method arguments. */
|
||||
readonly arguments: any[];
|
||||
}
|
||||
|
||||
/** A hub message representing a single item produced as part of a result stream. */
|
||||
export interface StreamItemMessage extends HubInvocationMessage {
|
||||
/** @inheritDoc */
|
||||
readonly type: MessageType.StreamItem;
|
||||
|
||||
/** The invocation ID. */
|
||||
readonly invocationId: string;
|
||||
|
||||
/** The item produced by the server. */
|
||||
readonly item?: any;
|
||||
}
|
||||
|
||||
/** A hub message representing the result of an invocation. */
|
||||
export interface CompletionMessage extends HubInvocationMessage {
|
||||
/** @inheritDoc */
|
||||
readonly type: MessageType.Completion;
|
||||
/** The invocation ID. */
|
||||
readonly invocationId: string;
|
||||
/** The error produced by the invocation, if any.
|
||||
*
|
||||
* Either {@link error} or {@link result} must be defined, but not both.
|
||||
*/
|
||||
readonly error?: string;
|
||||
/** The result produced by the invocation, if any.
|
||||
*
|
||||
* Either {@link error} or {@link result} must be defined, but not both.
|
||||
*/
|
||||
readonly result?: any;
|
||||
}
|
||||
|
||||
/** A hub message indicating that the sender is still active. */
|
||||
export interface PingMessage extends HubMessageBase {
|
||||
/** @inheritDoc */
|
||||
readonly type: MessageType.Ping;
|
||||
}
|
||||
|
||||
/** A hub message indicating that the sender is closing the connection.
|
||||
*
|
||||
* If {@link error} is defined, the sender is closing the connection due to an error.
|
||||
*/
|
||||
export interface CloseMessage extends HubMessageBase {
|
||||
/** @inheritDoc */
|
||||
readonly type: MessageType.Close;
|
||||
/** The error that triggered the close, if any.
|
||||
*
|
||||
* If this property is undefined, the connection was closed normally and without error.
|
||||
*/
|
||||
readonly error?: string;
|
||||
}
|
||||
|
||||
/** A hub message sent to request that a streaming invocation be canceled. */
|
||||
export interface CancelInvocationMessage extends HubInvocationMessage {
|
||||
/** @inheritDoc */
|
||||
readonly type: MessageType.CancelInvocation;
|
||||
/** The invocation ID. */
|
||||
readonly invocationId: string;
|
||||
}
|
||||
|
||||
/** A protocol abstraction for communicating with SignalR Hubs. */
|
||||
export interface IHubProtocol {
|
||||
/** The name of the protocol. This is used by SignalR to resolve the protocol between the client and server. */
|
||||
readonly name: string;
|
||||
/** The version of the protocol. */
|
||||
readonly version: number;
|
||||
/** The {@link TransferFormat} of the protocol. */
|
||||
readonly transferFormat: TransferFormat;
|
||||
parseMessages(input: any, logger: ILogger): HubMessage[];
|
||||
writeMessage(message: HubMessage): any;
|
||||
|
||||
/** Creates an array of {@link HubMessage} objects from the specified serialized representation.
|
||||
*
|
||||
* If {@link transferFormat} is 'Text', the {@link input} parameter must be a string, otherwise it must be an ArrayBuffer.
|
||||
*
|
||||
* @param {string | ArrayBuffer} input A string, or ArrayBuffer containing the serialized representation.
|
||||
* @param {ILogger} logger A logger that will be used to log messages that occur during parsing.
|
||||
*/
|
||||
parseMessages(input: string | ArrayBuffer, logger: ILogger): HubMessage[];
|
||||
|
||||
/** Writes the specified {@link HubMessage} to a string or ArrayBuffer and returns it.
|
||||
*
|
||||
* If {@link transferFormat} is 'Text', the result of this method will be a string, otherwise it will be an ArrayBuffer.
|
||||
*
|
||||
* @param {HubMessage} message The message to write.
|
||||
* @returns {string | ArrayBuffer} A string or ArrayBuffer containing the serialized representation of the message.
|
||||
*/
|
||||
writeMessage(message: HubMessage): string | ArrayBuffer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,33 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// These values are designed to match the ASP.NET Log Levels since that's the pattern we're emulating here.
|
||||
/** Indicates the severity of a log message.
|
||||
*
|
||||
* Log Levels are ordered in increasing severity. So `Debug` is more severe than `Trace`, etc.
|
||||
*/
|
||||
export enum LogLevel {
|
||||
/** Log level for very low severity diagnostic messages. */
|
||||
Trace = 0,
|
||||
/** Log level for low severity diagnostic messages. */
|
||||
Debug = 1,
|
||||
/** Log level for informational diagnostic messages. */
|
||||
Information = 2,
|
||||
/** Log level for diagnostic messages that indicate a non-fatal problem. */
|
||||
Warning = 3,
|
||||
/** Log level for diagnostic messages that indicate a failure in the current operation. */
|
||||
Error = 4,
|
||||
/** Log level for diagnostic messages that indicate a failure that will terminate the entire application. */
|
||||
Critical = 5,
|
||||
/** The highest possible log level. Used when configuring logging to indicate that no log messages should be emitted. */
|
||||
None = 6,
|
||||
}
|
||||
|
||||
/** An abstraction that provides a sink for diagnostic messages. */
|
||||
export interface ILogger {
|
||||
/** Called by the framework to emit a diagnostic message.
|
||||
*
|
||||
* @param {LogLevel} logLevel The severity level of the message.
|
||||
* @param {string} message The message.
|
||||
*/
|
||||
log(logLevel: LogLevel, message: string): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
/** Specifies a specific HTTP transport type. */
|
||||
export enum HttpTransportType {
|
||||
/** Specifies the WebSockets transport. */
|
||||
WebSockets,
|
||||
/** Specifies the Server-Sent Events transport. */
|
||||
ServerSentEvents,
|
||||
/** Specifies the Long Polling transport. */
|
||||
LongPolling,
|
||||
}
|
||||
|
||||
/** Specifies the transfer format for a connection. */
|
||||
export enum TransferFormat {
|
||||
/** Specifies that only text data will be transmitted over the connection. */
|
||||
Text = 1,
|
||||
/** Specifies that binary data will be transmitted over the connection. */
|
||||
Binary,
|
||||
}
|
||||
|
||||
/** An abstraction over the behavior of transports. This is designed to support the framework and not intended for use by applications. */
|
||||
export interface ITransport {
|
||||
connect(url: string, transferFormat: TransferFormat): Promise<void>;
|
||||
send(data: any): Promise<void>;
|
||||
|
|
|
|||
|
|
@ -7,16 +7,30 @@ import { TransferFormat } from "./ITransport";
|
|||
import { NullLogger } from "./Loggers";
|
||||
import { TextMessageFormat } from "./TextMessageFormat";
|
||||
|
||||
export const JSON_HUB_PROTOCOL_NAME: string = "json";
|
||||
const JSON_HUB_PROTOCOL_NAME: string = "json";
|
||||
|
||||
/** Implements the JSON Hub Protocol. */
|
||||
export class JsonHubProtocol implements IHubProtocol {
|
||||
|
||||
/** @inheritDoc */
|
||||
public readonly name: string = JSON_HUB_PROTOCOL_NAME;
|
||||
/** @inheritDoc */
|
||||
public readonly version: number = 1;
|
||||
|
||||
/** @inheritDoc */
|
||||
public readonly transferFormat: TransferFormat = TransferFormat.Text;
|
||||
|
||||
/** Creates an array of {@link HubMessage} objects from the specified serialized representation.
|
||||
*
|
||||
* @param {string} input A string containing the serialized representation.
|
||||
* @param {ILogger} logger A logger that will be used to log messages that occur during parsing.
|
||||
*/
|
||||
public parseMessages(input: string, logger: ILogger): HubMessage[] {
|
||||
// The interface does allow "ArrayBuffer" to be passed in, but this implementation does not. So let's throw a useful error.
|
||||
if (typeof input !== "string") {
|
||||
throw new Error("Invalid input for JSON hub protocol. Expected a string.");
|
||||
}
|
||||
|
||||
if (!input) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -61,6 +75,11 @@ export class JsonHubProtocol implements IHubProtocol {
|
|||
return hubMessages;
|
||||
}
|
||||
|
||||
/** Writes the specified {@link HubMessage} to a string and returns it.
|
||||
*
|
||||
* @param {HubMessage} message The message to write.
|
||||
* @returns {string} A string containing the serialized representation of the message.
|
||||
*/
|
||||
public writeMessage(message: HubMessage): string {
|
||||
return TextMessageFormat.write(JSON.stringify(message));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@
|
|||
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
|
||||
/** A logger that does nothing when log messages are sent to it. */
|
||||
export class NullLogger implements ILogger {
|
||||
/** The singleton instance of the {@link NullLogger}. */
|
||||
public static instance: ILogger = new NullLogger();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/** @inheritDoc */
|
||||
public log(logLevel: LogLevel, message: string): void {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,17 +7,45 @@
|
|||
// depend on RxJS in the core library, so instead we duplicate the minimum logic needed and then users can easily adapt these into
|
||||
// proper RxJS observables if they want.
|
||||
|
||||
/** Defines the expected type for a receiver of results streamed by the server.
|
||||
*
|
||||
* @typeparam T The type of the items being sent by the server.
|
||||
*/
|
||||
export interface IStreamSubscriber<T> {
|
||||
/** A boolean that will be set by the {@link IStreamResult} when the stream is closed. */
|
||||
closed?: boolean;
|
||||
/** Called by the framework when a new item is available. */
|
||||
next(value: T): void;
|
||||
/** Called by the framework when an error has occurred.
|
||||
*
|
||||
* After this method is called, no additional methods on the {@link IStreamSubscriber} will be called.
|
||||
*/
|
||||
error(err: any): void;
|
||||
/** Called by the framework when the end of the stream is reached.
|
||||
*
|
||||
* After this method is called, no additional methods on the {@link IStreamSubscriber} will be called.
|
||||
*/
|
||||
complete(): void;
|
||||
}
|
||||
|
||||
/** Defines the result of a streaming hub method.
|
||||
*
|
||||
* @typeparam T The type of the items being sent by the server.
|
||||
*/
|
||||
export interface IStreamResult<T> {
|
||||
subscribe(observer: IStreamSubscriber<T>): ISubscription<T>;
|
||||
/** Attaches a {@link IStreamSubscriber}, which will be invoked when new items are available from the stream.
|
||||
*
|
||||
* @param {IStreamSubscriber<T>} observer The subscriber to attach.
|
||||
* @returns {ISubscription<T>} A subscription that can be disposed to terminate the stream and stop calling methods on the {@link IStreamSubscriber}.
|
||||
*/
|
||||
subscribe(subscriber: IStreamSubscriber<T>): ISubscription<T>;
|
||||
}
|
||||
|
||||
/** An interface that allows an {@link IStreamSubscriber} to be disconnected from a stream.
|
||||
*
|
||||
* @typeparam T The type of the items being sent by the server.
|
||||
*/
|
||||
export interface ISubscription<T> {
|
||||
/** Disconnects the {@link IStreamSubscriber} associated with this subscription from the stream. */
|
||||
dispose(): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// Not exported from index
|
||||
export class TextMessageFormat {
|
||||
public static RecordSeparatorCode = 0x1e;
|
||||
public static RecordSeparator = String.fromCharCode(TextMessageFormat.RecordSeparatorCode);
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// Everything that users need to access must be exported here. Including interfaces.
|
||||
export * from "./Errors";
|
||||
export * from "./HttpClient";
|
||||
export * from "./IHttpConnectionOptions";
|
||||
export * from "./HubConnection";
|
||||
export * from "./HubConnectionBuilder";
|
||||
export * from "./IHubProtocol";
|
||||
export * from "./ILogger";
|
||||
export * from "./ITransport";
|
||||
export * from "./Stream";
|
||||
export * from "./Loggers";
|
||||
export * from "./JsonHubProtocol";
|
||||
export { AbortSignal } from "./AbortController";
|
||||
export { HttpError, TimeoutError } from "./Errors";
|
||||
export { DefaultHttpClient, HttpClient, HttpRequest, HttpResponse } from "./HttpClient";
|
||||
export { IHttpConnectionOptions } from "./IHttpConnectionOptions";
|
||||
export { HubConnection } from "./HubConnection";
|
||||
export { HubConnectionBuilder } from "./HubConnectionBuilder";
|
||||
export { MessageType, MessageHeaders, HubMessage, HubMessageBase, HubInvocationMessage, InvocationMessage, StreamInvocationMessage, StreamItemMessage, CompletionMessage, PingMessage, CloseMessage, CancelInvocationMessage, IHubProtocol } from "./IHubProtocol";
|
||||
export { ILogger, LogLevel } from "./ILogger";
|
||||
export { HttpTransportType, TransferFormat, ITransport } from "./ITransport";
|
||||
export { IStreamSubscriber, IStreamResult, ISubscription } from "./Stream";
|
||||
export { NullLogger } from "./Loggers";
|
||||
export { JsonHubProtocol } from "./JsonHubProtocol";
|
||||
|
|
|
|||
Loading…
Reference in New Issue