public void ReceiveExtensions_ReadBodyAsString_Streams_Zero() { var request = new ReceiveRequest(); var result = request.ReadBodyAsString(); Assert.Empty(result); }
public override async Task <Response> ProcessRequestAsync(ReceiveRequest request) { var response = new Response(); var body = request.ReadBodyAsString().Result; if (string.IsNullOrEmpty(body)) { response.StatusCode = 400; return(response); } try { var activity = JsonConvert.DeserializeObject <Activity>(body, SerializationSettings.DefaultDeserializationSettings); var invokeResponse = await this.StreamingAdapter.ProcessActivityAsync(activity, new BotCallbackHandler(this.Bot.OnTurnAsync), CancellationToken.None).ConfigureAwait(false); if (invokeResponse == null) { response.StatusCode = 200; } else { response.StatusCode = invokeResponse.Status; if (invokeResponse.Body != null) { response.SetBody(invokeResponse.Body); } } invokeResponse = (InvokeResponse)null; } catch (Exception) { // TODO: Better exception handling. response.StatusCode = 403; } return(response); }
/// <summary> /// Handles incoming requests. /// </summary> /// <param name="request">A <see cref="ReceiveRequest"/> for this handler to process.</param> /// <param name="logger">Logger.</param> /// <param name="context">Optional context to process the request within.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>A <see cref="Task"/> that will produce a <see cref="StreamingResponse"/> on successful completion.</returns> public override async Task <StreamingResponse> ProcessRequestAsync(ReceiveRequest request, ILogger <RequestHandler> logger = null, object context = null, CancellationToken cancellationToken = default) { var response = new StreamingResponse(); // We accept all POSTs regardless of path, but anything else requires special treatment. if (!string.Equals(request?.Verb, StreamingRequest.POST, StringComparison.OrdinalIgnoreCase)) { return(HandleCustomPaths(request, response)); } // Convert the StreamingRequest into an activity the adapter can understand. string body; try { body = request.ReadBodyAsString(); } #pragma warning disable CA1031 // Do not catch general exception types (we log the exception and continue execution) catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { response.StatusCode = (int)HttpStatusCode.BadRequest; _logger.LogError("Request body missing or malformed: " + ex.Message); return(response); } try { var activity = JsonConvert.DeserializeObject <Activity>(body, SerializationSettings.DefaultDeserializationSettings); // All activities received by this StreamingRequestHandler will originate from the same channel, but we won't // know what that channel is until we've received the first request. if (string.IsNullOrWhiteSpace(ServiceUrl)) { ServiceUrl = activity.ServiceUrl; } // If this is the first time the handler has seen this conversation it needs to be added to the dictionary so the // adapter is able to route requests to the correct handler. if (!HasConversation(activity.Conversation.Id)) { _conversations.Add(activity.Conversation.Id, DateTime.Now); } /* * Any content sent as part of a StreamingRequest, including the request body * and inline attachments, appear as streams added to the same collection. The first * stream of any request will be the body, which is parsed and passed into this method * as the first argument, 'body'. Any additional streams are inline attachments that need * to be iterated over and added to the Activity as attachments to be sent to the Bot. */ if (request.Streams.Count > 1) { var streamAttachments = new List <Attachment>(); for (var i = 1; i < request.Streams.Count; i++) { streamAttachments.Add(new Attachment() { ContentType = request.Streams[i].ContentType, Content = request.Streams[i].Stream }); } if (activity.Attachments != null) { activity.Attachments = activity.Attachments.Concat(streamAttachments).ToArray(); } else { activity.Attachments = streamAttachments.ToArray(); } } // Now that the request has been converted into an activity we can send it to the adapter. var adapterResponse = await _activityProcessor.ProcessStreamingActivityAsync(activity, _bot.OnTurnAsync, cancellationToken).ConfigureAwait(false); // Now we convert the invokeResponse returned by the adapter into a StreamingResponse we can send back to the channel. if (adapterResponse == null) { response.StatusCode = (int)HttpStatusCode.OK; } else { response.StatusCode = adapterResponse.Status; if (adapterResponse.Body != null) { response.SetBody(adapterResponse.Body); } } } #pragma warning disable CA1031 // Do not catch general exception types (we logging the error and we send it back in the body of the response) catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { response.StatusCode = (int)HttpStatusCode.InternalServerError; response.SetBody(ex.ToString()); _logger.LogError(ex.ToString()); } return(response); }
/// <summary> /// Performs the actual processing of a request, handing it off to the adapter and returning the response. /// </summary> /// <param name="request">A ReceiveRequest from the connected channel.</param> /// <param name="response">The response to update and return, ultimately sent to client.</param> /// <param name="logger">Optional logger.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>The response ready to send to the client.</returns> private async Task <StreamingResponse> ProcessStreamingRequestAsync(ReceiveRequest request, StreamingResponse response, ILogger <RequestHandler> logger, CancellationToken cancellationToken) { var body = string.Empty; try { body = request.ReadBodyAsString(); } catch (Exception ex) { response.StatusCode = (int)HttpStatusCode.BadRequest; logger.LogError("Request body missing or malformed: " + ex.Message); return(response); } try { var adapter = new BotFrameworkStreamingExtensionsAdapter(_transportServer, _middlewareSet, logger); IBot bot = null; // First check if an IBot type definition is available from the service provider. if (_services != null) { /* Creating a new scope for each request allows us to support * bots that inject scoped services as dependencies. */ bot = _services.CreateScope().ServiceProvider.GetService <IBot>(); } // If no bot has been set, check if a singleton bot is associated with this request handler. if (bot == null) { bot = _bot; } // If a bot still hasn't been set, the request will not be handled correctly, so throw and terminate. if (bot == null) { throw new Exception("Unable to find bot when processing request."); } adapter.OnTurnError = _onTurnError; var invokeResponse = await adapter.ProcessActivityAsync(body, request.Streams, new BotCallbackHandler(bot.OnTurnAsync), cancellationToken).ConfigureAwait(false); if (invokeResponse == null) { response.StatusCode = (int)HttpStatusCode.OK; } else { response.StatusCode = invokeResponse.Status; if (invokeResponse.Body != null) { response.SetBody(invokeResponse.Body); } } invokeResponse = null; } catch (Exception ex) { response.StatusCode = (int)HttpStatusCode.InternalServerError; logger.LogError(ex.Message); } return(response); }
public async override Task <Response> ProcessRequestAsync(ReceiveRequest request, object context = null, ILogger <RequestHandler> logger = null) { if (Bot == null) { throw new ArgumentNullException(nameof(Bot)); } if (SkillWebSocketBotAdapter == null) { throw new ArgumentNullException(nameof(SkillWebSocketBotAdapter)); } var response = new Response(); var body = await request.ReadBodyAsString().ConfigureAwait(false); if (string.IsNullOrEmpty(body) || request.Streams?.Count == 0) { response.StatusCode = (int)HttpStatusCode.BadRequest; response.SetBody("Empty request body."); return(response); } if (request.Streams.Where(x => x.Type != "application/json; charset=utf-8").Any()) { response.StatusCode = (int)HttpStatusCode.NotAcceptable; return(response); } Activity activity = null; try { activity = JsonConvert.DeserializeObject <Activity>(body, Serialization.Settings); } catch (Exception ex) { _botTelemetryClient.TrackException(ex); response.StatusCode = (int)HttpStatusCode.BadRequest; response.SetBody("Request body is not an Activity instance."); return(response); } try { var cancellationTokenSource = new CancellationTokenSource(); _stopWatch.Start(); var invokeResponse = await this.SkillWebSocketBotAdapter.ProcessActivityAsync(activity, new BotCallbackHandler(this.Bot.OnTurnAsync), cancellationTokenSource.Token).ConfigureAwait(false); _stopWatch.Stop(); _botTelemetryClient.TrackEvent("SkillWebSocketProcessRequestLatency", null, new Dictionary <string, double> { { "Latency", _stopWatch.ElapsedMilliseconds }, }); // trigger cancel token after activity is handled. this will stop the typing indicator cancellationTokenSource.Cancel(); if (invokeResponse == null) { response.StatusCode = (int)HttpStatusCode.OK; } else { response.StatusCode = invokeResponse.Status; if (invokeResponse.Body != null) { response.SetBody(invokeResponse.Body); } } } catch (SkillWebSocketCallbackException ex) { _botTelemetryClient.TrackException(ex); response.StatusCode = (int)HttpStatusCode.InternalServerError; response.SetBody(ex.Message); return(response); } catch (Exception ex) { _botTelemetryClient.TrackException(ex); response.StatusCode = (int)HttpStatusCode.InternalServerError; } return(response); }