/// <summary> /// Returns a handler for incoming Web stream requests. /// </summary> /// <param name="route"> /// The handler. /// </param> /// <param name="controller"> /// The controller. /// </param> /// <param name="args"> /// The request parameters. /// </param> /// <returns> /// A handler for incoming Web stream requests. /// </returns> public static Func <IDictionary <string, object>, Task> WebSocketRequestHandler( this ControllerRoute route, object controller, IDictionary <string, string> args) { return(async environment => { using (var socket = new WebSocket(environment)) { Task incomingMessagePump; Func <string, IObservable <string> > getObservable; if (route.ObservableParameters != null) { // Route has observable parameters. var observableParams = route.ObservableParameters.ToDictionary(_ => _, _ => new SingleSubscriptionObservable()); getObservable = name => observableParams[name].Observable; incomingMessagePump = IncomingMessagePump(socket, observableParams); } else { // No observable parameters. getObservable = _ => Observable.Empty <string>(); incomingMessagePump = Task.FromResult(0); } // Hook up the incoming and outgoing message pumps. var outgoing = GetObservableFromHandler(() => route.Invoke(controller, args, getObservable)); var outgoingMessagePump = OutgoingMessagePump(outgoing, socket); // Close the socket when both pumps finish. await Task.WhenAll(outgoingMessagePump, incomingMessagePump); } }); }
/// <summary> /// Returns an HTTP request handler for single-return routes. /// </summary> /// <param name="route">The route.</param> /// <param name="controller">The controller.</param> /// <param name="args">The route arguments.</param> /// <returns>An HTTP request handler for single-return routes.</returns> private static Func <IOwinContext, Task> GetIdiomaticHttpRequestHandler(ControllerRoute route, object controller, IDictionary <string, string> args) { return(async environment => { var response = environment.Response; int code; string body; try { body = await route.Invoke(controller, args, _ => Observable.Empty <string>()).FirstAsync(); code = 200; } catch (Exception e) { body = e.ToString(); code = 500; } response.StatusCode = code; response.ContentType = "application/json"; await response.WriteAsync(body); }); }
/// <summary> /// Returns an HTTP request handler for single-return routes. /// </summary> /// <param name="route">The route.</param> /// <param name="controller">The controller.</param> /// <param name="args">The route arguments.</param> /// <returns>An HTTP request handler for single-return routes.</returns> private static Func<IOwinContext, Task> GetIdiomaticHttpRequestHandler(ControllerRoute route, object controller, IDictionary<string, string> args) { return async environment => { var response = environment.Response; int code; string body; try { body = await route.Invoke(controller, args, _ => Observable.Empty<string>()).FirstAsync(); code = 200; } catch (Exception e) { body = e.ToString(); code = 500; } response.StatusCode = code; response.ContentType = "application/json"; await response.WriteAsync(body); }; }
/// <summary> /// Returns a handler for incoming Web stream requests. /// </summary> /// <param name="route"> /// The handler. /// </param> /// <param name="controller"> /// The controller. /// </param> /// <param name="args"> /// The request parameters. /// </param> /// <param name="environment"> /// The environment. /// </param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// A handler for incoming Web stream requests. /// </returns> public static async Task HttpRequestHandler( this ControllerRoute route, object controller, IDictionary <string, string> args, IOwinContext environment, CancellationToken cancellationToken) { var response = environment.Response; var pump = new MutuallyExclusiveTaskExecutor(); var outgoing = GetObservableFromHandler(() => route.Invoke(controller, args, _ => Observable.Empty <string>())); var headersWritten = new[] { false }; response.Headers.Set("Transfer-Encoding", "Chunked"); response.ContentType = "application/json"; // Hook up the outgoing message pump to the response body. var subscription = outgoing.Subscribe( msg => pump.Schedule( async() => { try { if (!headersWritten[0]) { headersWritten[0] = true; // Set the response headers. response.StatusCode = 200; } await response.WriteAsync(msg, cancellationToken); await response.Body.FlushAsync(cancellationToken); } catch { pump.Complete(); throw; } }), e => pump.Schedule( async() => { try { if (!headersWritten[0]) { // Set the response headers. response.StatusCode = 500; headersWritten[0] = true; } await response.WriteAsync(e.ToString(), cancellationToken); await response.Body.FlushAsync(cancellationToken); } finally { pump.Complete(); } }), () => pump.Schedule( () => { if (!headersWritten[0]) { // No content. response.StatusCode = 204; response.Body.Close(); response.Body = Stream.Null; headersWritten[0] = true; } pump.Complete(); return(Task.FromResult(0)); })); // When the cancellation token is cancelled, unsubscribe. cancellationToken.Register(subscription.Dispose); try { await pump.Run(cancellationToken); } finally { subscription.Dispose(); } }