private async Task DispatchMessagesAsync(HubConnectionContext connection) { var input = GetInput(connection); var protocol = connection.Protocol; var binder = InvocationBinder.Instance; while (true) { var result = await input.ReadAsync(); var buffer = result.Buffer; try { if (result.IsCanceled) { break; } if (!buffer.IsEmpty) { InvokeResetClientTimeout(connection); // No message limit, just parse and dispatch while (TryParse(protocol, binder, ref buffer, out var message)) { if (message is InvocationMessage invocationMessage) { var invocation = new ServerlessProtocol.InvocationMessage(new ReadOnlySequence <byte>(protocol.GetMessageBytes(invocationMessage)), invocationMessage.Target, invocationMessage.InvocationId); var response = await((IUpstreamMessageHandler)_upstream).WriteMessageAsync(connection, invocation); if (response.Length > 0) { await((EmulatorHubConnectionContext)connection).ConnectionContext.Transport.Output.WriteAsync(response.First); } } } } if (result.IsCompleted) { if (!buffer.IsEmpty) { throw new InvalidDataException("Connection terminated while reading a message."); } break; } } finally { // The buffer was sliced up to where it was consumed, so we can just advance to the start. // We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data // before yielding the read again. input.AdvanceTo(buffer.Start, buffer.End); } } }
public async Task <ReadOnlySequence <byte> > WriteMessageAsync(HubConnectionContext connectionContext, ServerlessProtocol.InvocationMessage message, CancellationToken token) { var parameters = new InvokeUpstreamParameters(_hubName, ServerlessProtocol.Constants.Categories.MessageCategory, message.Target); if (!_httpUpstreamTrigger.TryGetMatchedUpstreamContext(parameters, out var context)) { // Upstream for this event is not set, ignore the event return(ReadOnlySequence <byte> .Empty); } var mediaType = connectionContext.Protocol.Name.Equals("json", StringComparison.OrdinalIgnoreCase) ? JsonMediaType : MessagePackMediaType; using (var response = await _httpUpstreamTrigger.GetResponseAsync( context, connectionContext, message, parameters, mediaType, token)) { response.CheckResponse(parameters, _logger, IsExpectedResponse); if (!string.IsNullOrEmpty(message.InvocationId)) { if (response.StatusCode == HttpStatusCode.OK) { var contentLength = response.Content.Headers.ContentLength; if (contentLength == 0) { // same as no content. await connectionContext.WriteAsync(CompletionMessage.Empty(message.InvocationId)); } else if (contentLength > MaxAllowedResponseLength) { // We don't support response large than 16M, fast fail. await connectionContext.WriteAsync(CompletionMessage.WithError(message.InvocationId, $"Invocation failed, response too large.")); } else { var ls = new LimitedStream(MaxAllowedResponseLength); try { await response.Content.CopyToAsync(ls); return(new ReadOnlySequence <byte>(ls.ToMemory())); } catch (InvalidDataException) { await connectionContext.WriteAsync(CompletionMessage.WithError(message.InvocationId, $"Invocation failed, response too large.")); } catch (OperationCanceledException) { await connectionContext.WriteAsync(CompletionMessage.WithError(message.InvocationId, $"Invocation failed, response cancelled.")); } catch (Exception ex) { await connectionContext.WriteAsync(CompletionMessage.WithError(message.InvocationId, $"Invocation failed, error: {ex.Message}.")); } } } else if (response.StatusCode == HttpStatusCode.NoContent) { await connectionContext.WriteAsync(CompletionMessage.Empty(message.InvocationId)); } else { await connectionContext.WriteAsync(CompletionMessage.WithError(message.InvocationId, $"Invocation failed, status code {(int)response.StatusCode}")); } } } return(ReadOnlySequence <byte> .Empty); }
public async Task <ReadOnlySequence <byte> > WriteMessageAsync(HubConnectionContext connectionContext, ServerlessProtocol.InvocationMessage message, CancellationToken token) { var parameters = new InvokeUpstreamParameters(_hubName, ServerlessProtocol.Constants.Categories.MessageCategory, message.Target); if (!_httpUpstreamTrigger.TryGetMatchedUpstreamContext(parameters, out var context)) { // Upstream for this event is not set, ignore the event return(ReadOnlySequence <byte> .Empty); } var mediaType = connectionContext.Protocol.Name.Equals("json", StringComparison.OrdinalIgnoreCase) ? JsonMediaType : MessagePackMediaType; using (var response = await _httpUpstreamTrigger.GetResponseAsync( context, connectionContext, message, parameters, mediaType, token)) { response.CheckResponse(parameters, _logger, IsExpectedResponse); if (!string.IsNullOrEmpty(message.InvocationId)) { if (response.StatusCode == HttpStatusCode.OK) { return(message.Payload); } else if (response.StatusCode == HttpStatusCode.NoContent) { await connectionContext.WriteAsync(CompletionMessage.Empty(message.InvocationId)); } else { await connectionContext.WriteAsync(CompletionMessage.WithError(message.InvocationId, $"Invocation failed, status code {(int)response.StatusCode}")); } } } return(ReadOnlySequence <byte> .Empty); }
public static async Task <HttpResponseMessage> GetResponseAsync(this IHttpUpstreamTrigger httpUpstream, UpstreamContext context, HubConnectionContext connectionContext, ServerlessProtocol.InvocationMessage message, InvokeUpstreamParameters parameters, MediaTypeHeaderValue mediaType = null, CancellationToken token = default) { var upstream = connectionContext.Features.Get <IHttpUpstreamPropertiesFeature>(); if (upstream == null) { // defensive code, should not happen throw new InvalidOperationException("IHttpUpstreamPropertiesFeature expected"); } if (message.Payload.IsSingleSegment) { return(await httpUpstream.TriggerAsync( context, upstream, parameters, request => { request.Content = new ReadOnlyMemoryContent(message.Payload.First); request.Content.Headers.ContentType = mediaType; }, token)); } using (var owner = ExactSizeMemoryPool.Shared.Rent((int)message.Payload.Length)) { message.Payload.CopyTo(owner.Memory.Span); return(await httpUpstream.TriggerAsync( context, upstream, parameters, request => { request.Content = new ReadOnlyMemoryContent(owner.Memory); request.Content.Headers.ContentType = mediaType; }, token)); } }