CallingContext.cs
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.IO;
6 using System.Linq;
7 using System.Net;
8 using System.Net.Http;
9 using System.Threading.Tasks;
10 
11 namespace Microsoft.Bot.Builder.Calling
12 {
16  public class ParsedCallingRequest
17  {
24  public HttpStatusCode ParseStatusCode { set; get; }
25 
29  public string Content { set; get; }
30 
34  public Task<Stream> AdditionalData { set; get; }
35 
39  public string SkypeChaindId { set; get; }
40 
45  public bool Faulted()
46  {
47  return ParseStatusCode != HttpStatusCode.OK;
48  }
49  }
50 
54  public enum CallRequestType
55  {
58  }
59 
63  public class CallingContext
64  {
68  public readonly HttpRequestMessage Request;
69 
74  public CallingContext(HttpRequestMessage request)
75  {
76  SetField.NotNull<HttpRequestMessage>(out this.Request, nameof(request), request);
77  }
78 
84  public virtual async Task<ParsedCallingRequest> ProcessRequest(CallRequestType callType)
85  {
86  ParsedCallingRequest parsedRequest;
87  switch (callType)
88  {
89  case CallRequestType.IncomingCall:
90  parsedRequest = await ProcessIncomingCallAsync();
91  break;
92  case CallRequestType.CallingEvent:
93  parsedRequest = await ProcessCallingEventAsync();
94  break;
95  default:
96  parsedRequest = GenerateParsedResults(HttpStatusCode.InternalServerError);
97  break;
98  }
99  parsedRequest.SkypeChaindId = ExtractSkypeChainId();
100  return parsedRequest;
101  }
102 
103  protected virtual string ExtractSkypeChainId()
104  {
105  string chaindId = null;
106  IEnumerable<string> headerValues;
107  if (Request.Headers.TryGetValues("X-Microsoft-Skype-Chain-ID", out headerValues))
108  {
109  chaindId = headerValues.FirstOrDefault();
110  }
111  return chaindId;
112  }
113 
114  protected virtual async Task<ParsedCallingRequest> ProcessIncomingCallAsync()
115  {
116  try
117  {
118  if (Request.Content == null)
119  {
120  Trace.TraceError("No content in the request");
121  return GenerateParsedResults(HttpStatusCode.BadRequest);
122  }
123 
124  var content = await Request.Content.ReadAsStringAsync().ConfigureAwait(false);
125  return GenerateParsedResults(HttpStatusCode.OK, content);
126  }
127  catch (Exception e)
128  {
129  Trace.TraceError($"Failed to process the incoming call, exception: {e}");
130  return GenerateParsedResults(HttpStatusCode.InternalServerError);
131  }
132  }
133 
134  protected virtual async Task<ParsedCallingRequest> ProcessCallingEventAsync()
135  {
136  try
137  {
138  if (Request.Content == null)
139  {
140  Trace.TraceError("No content in the request");
141  return GenerateParsedResults(HttpStatusCode.BadRequest);
142  }
143  if (Request.Content.IsMimeMultipartContent())
144  {
145  return await HandleMultipartRequest(Request).ConfigureAwait(false);
146  }
147 
148  var content = await Request.Content.ReadAsStringAsync().ConfigureAwait(false);
149  return GenerateParsedResults(HttpStatusCode.OK, content);
150  }
151  catch (Exception e)
152  {
153  Trace.TraceError($"Failed to process the callback request, exception: {e}");
154  return GenerateParsedResults(HttpStatusCode.InternalServerError);
155  }
156  }
157 
158  private ParsedCallingRequest GenerateParsedResults(HttpStatusCode statusCode, string content = null, Task<Stream> additionalData = null)
159  {
160  return new ParsedCallingRequest
161  {
162  Content = content,
163  ParseStatusCode = statusCode,
164  AdditionalData = additionalData
165  };
166  }
167 
168  private async Task<ParsedCallingRequest> HandleMultipartRequest(HttpRequestMessage request)
169  {
170  var streamProvider = await request.Content.ReadAsMultipartAsync().ConfigureAwait(false);
171  var jsonContent =
172  streamProvider.Contents.FirstOrDefault(content => content.Headers.ContentType?.MediaType == "application/json");
173  if (jsonContent == null)
174  {
175  Trace.TraceError("No json content in MultiPart content");
176  return GenerateParsedResults(HttpStatusCode.BadRequest);
177  }
178  var json = await jsonContent.ReadAsStringAsync().ConfigureAwait(false);
179 
180  var otherContent =
181  streamProvider.Contents.FirstOrDefault(content => content.Headers.ContentType?.MediaType != "application/json");
182  if (otherContent == null)
183  {
184  Trace.TraceError("MultiPart content does not contain non json content");
185  return GenerateParsedResults(HttpStatusCode.BadRequest);
186  }
187 
188  return GenerateParsedResults(HttpStatusCode.OK, json, otherContent.ReadAsStreamAsync());
189  }
190  }
191 }
readonly HttpRequestMessage Request
The calling request.
The context for this request. It is reponsible in parsing the calling request before calling into ICa...
CallingContext(HttpRequestMessage request)
Creates a new instance of calling context.
virtual async Task< ParsedCallingRequest > ProcessCallingEventAsync()
Namespace for internal machinery that is not useful for most developers and may change in the future...
string SkypeChaindId
The Skype chain Id, look at the documentation X-Microsoft-Skype-Chain-ID header value.
bool Faulted()
Check if the parser is faulted on parsing incoming request.
string Content
The calling request content.
virtual async Task< ParsedCallingRequest > ProcessRequest(CallRequestType callType)
Process the calling request and returns ParsedCallingRequest.
CallRequestType
The type of calling request
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
virtual async Task< ParsedCallingRequest > ProcessIncomingCallAsync()
Root namespace for the Microsoft Bot Builder SDK.