Streaming Content constantly updating 8x8 streaming service - c#

I have tried to create a simple console application.
We have a call system from 8x8 that provide a web streaming API but their documentation is very limited and nothing in C#.
The api service streams call statuses in near real time and I would like to get that 'stream' and be able to read and process it in realtime if possible. The response or Content Type is 'text/html'. But the actual body of the response can be declared as json - sample below:
{"Interaction":{"attachedData":{"attachedDatum":[{"attachedDataKey":"#pri","attachedDataValue":100},{"attachedDataKey":"callingName","attachedDataValue":999999999999},{"attachedDataKey":"cha","attachedDataValue":99999999999},{"attachedDataKey":"cnt","attachedDataValue":0},{"attachedDataKey":"con","attachedDataValue":0},{"attachedDataKey":"med","attachedDataValue":"T"},{"attachedDataKey":"pho","attachedDataValue":9999999999},{"attachedDataKey":"phoneNum","attachedDataValue":9999999999},{"attachedDataKey":"tok","attachedDataValue":999999999}]},"event":"InteractionCreated","inboundChannelid":9999999999,"interactionEventTS":9999999,"interactionGUID":"int-15b875d0da2-DJOJkDhDsrh3AIaFP8VkICv9t-phone-01-testist","resourceType":0}}
I have seen several posts concerning httpClient and the GetAsync methods but none of these appear to work as they appear to be for calls when a response is made, not something that constantly has a response.
Using fiddler for the call it does not appear to close so the stream is constantly running, so fiddler does not display any data until a separate user or instance connects.
When I use a browser the content is 'streamed' to the page and updates automatically and shows all the content (as above).
The api contains authentication so when another client connects and retrieves data the connected client closes and finally I am able to see the data that was gathering.
This is the code so and does return the big stream when another client connects but ideally I want a real time response and appears to just get stuck in the GETASYNC method:
var response = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
string responseString = await responseContent.ReadAsStringAsync();
Console.WriteLine(responseString);
}
Hopefully that's enough information for one of you clever people to help me in my predicament.

I was also having an issue consuming their streaming API and the examples I found that worked with the Twitter and CouchBase streaming API's did not work with 8x8. Both Twitter and CouchBase send line terminators in their pushes so the solution relied on ReadLine to pull in the feed. Since 8x8 does not send terminators you'll need to use ReadBlock or better ReadBlockAsync.
The following code shows how to connect using credentials and consume their feed:
private static async Task StreamAsync(string url, string username, string password)
{
var handler = new HttpClientHandler()
{
Credentials = new NetworkCredential {UserName = username, Password = password},
PreAuthenticate = true
};
// Client can also be singleton
using (var client = new HttpClient(handler))
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Connection.Add("keep-alive");
using (var response = await client.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead))
{
using (var body = await response.Content.ReadAsStreamAsync())
{
using (var reader = new StreamReader(body))
{
while (!reader.EndOfStream)
{
var buffer = new char[1024];
await reader.ReadBlockAsync(buffer, 0, buffer.Length);
Console.WriteLine(new string(buffer));
}
}
}
}
}
}

Related

Azure Relay Hybrid Connections - how to send synchronous request/response

I'm using hybrid connections to request data from a listener. If I can write and read to the connection, how can I know that the response I've read from the connection matches the request I've given it? For example:
private HybridConnectionClient _client = new HybridConnectionClient(***);
public override async Task<RelayResponse> SendAsync(RelayRequest request)
{
var stream = await _client.CreateConnectionAsync();
var writer = new StreamWriter(stream) { AutoFlush = true };
var reader = new StreamReader(stream);
var reqestSerialized = JsonConvert.SerializeObject(request);
await writer.WriteLineAsync(reqestSerialized);
string responseSerialized = await reader.ReadLineAsync();
var response = JsonConvert.DeserializeObject<RelayResponse>(responseSerialized);
return response;
}
If the listener on this connection is reading and responding to many requests at the same time, is there anyway to know that the next Readline() we do on the client side to get the response is the one that is associated with the request? Or is that something that has to be managed?
Understanding a bit more about azure relay hybrid connections, I understand this now.
There isn't really any concept of a synchronous request/response in the framework, but if you use a new connection for each request, and respond on that same connection in the listener, you can be sure the response is for the request you sent.
Spawn a new connection for each request, then make sure the response is written to that connection. So looking at Microsoft's listener example code, whenever listener.AcceptConnectionAsync() fires do all the message response on relayConnection, then go back to waiting at await listener.AcceptConnectionAsync();
while (true)
{
var relayConnection = await listener.AcceptConnectionAsync();
if (relayConnection == null)
{
break;
}
ProcessMessagesOnConnection(relayConnection, cts);
}

Passing current authentication credentials from UWP app to web service to access resources on server with these credentials

My Windows 10 UWP app is calling a WebAPI web service that I have created. I need to pass the current credentials on the client side when calling the web service so that it can access other resources using these credentials.
I also need to do this without prompting the user for credentials so that the experience is seamless.
I am able to do this with using System.Net.Http and successfully pass the current credentials to the server to use for accessing resources. This sends the request and brings back the response without any prompt. I have enabled Enterprise Authentication and Private Networks capabilities on the UWP app to make this work.
Problem: This works fine for GET requests but not for POST requests to the same server. POST requests result in the following error:
This IRandomAccessStream does not support the GetInputStreamAt method
because it requires cloning and this stream does not support cloning.
I read that this was a bug on this link: PostAsync throwing IRandomAccessStream error when targeting windows 10 UWP. The workaround proposed in multiple locations for this bug is to use Windows.Web.Http instead. However, if I do this, how can I pass the default/current credentials to the server?
Here is the code that I am using to do a GET request using the current Windows credentials without prompting for it. It works flawlessly:
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler
{
UseDefaultCredentials = true
// Credentials = (NetworkCredential)System.Net.CredentialCache.DefaultNetworkCredentials
//using either one of the above enables me to have the web service use the current credentials without prompting
};
string responseContent = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler))
{
System.Net.Http.HttpRequestMessage requestMessage = new System.Net.Http.HttpRequestMessage();
requestMessage = new System.Net.Http.HttpRequestMessage
{
Method = System.Net.Http.HttpMethod.Get,
RequestUri = new Uri(strWebServiceURL)
};
using (System.Net.Http.HttpResponseMessage response = await client.SendAsync(requestMessage))
{
responseContent = await response.Content.ReadAsStringAsync();
}
//This also works fine
using (System.Net.Http.HttpResponseMessage response = await client.GetAsync(strWebServiceURL))
{
responseContent = await response.Content.ReadAsStringAsync();
}
Below is the code I use to do a POST request which results in the IRandomAccessStream error:
System.Net.Http.HttpClientHandler handler = new System.Net.Http.HttpClientHandler
{
UseDefaultCredentials = true
// Credentials = (NetworkCredential)System.Net.CredentialCache.DefaultNetworkCredentials
//using either one of the above enables me to have the web service use the current credentials without prompting
};
string responseContent = string.Empty;
using (System.Net.Http.HttpClient client = new System.Net.Http.HttpClient(handler))
{
System.Net.Http.HttpRequestMessage requestMessage = new System.Net.Http.HttpRequestMessage();
requestMessage = new System.Net.Http.HttpRequestMessage
{
Content = myMultipartFormDataContent,
Method = System.Net.Http.HttpMethod.Post,
RequestUri = new Uri(strWebServiceURL)
};
using (System.Net.Http.HttpResponseMessage response = await client.SendAsync(requestMessage))
{
responseContent = await response.Content.ReadAsStringAsync();
}
//No difference when using it this way as well
using (System.Net.Http.HttpResponseMessage response = await client.PostAsync(strWebServiceURL, myMultipartFormDataContent))
{
responseContent = await response.Content.ReadAsStringAsync();
}
I tried using Windows.Web.Http but I don't know how I can get it to pass the current/default credentials to the server without prompting.
I have also added the WebService URL to a IE Local Intranet zone and have that zone set to automatically log in with current user name and password:
Please help!
With the new Windows.Web.Http namespace in UWP app, if you want to use the DefaultCredentials, all you have to do is turn on enterprise credentials in the manifest and the uwp app will send them out as appropriate. You don't need to configure anything on the HttpClientto make it work. Details please reference this thread.
Since you already enable the enterprise credentials capability, you could just create HttpClient without configure. But to avoid the username and password prompt, you may need to disable the UI, for example:
var myFilter = new HttpBaseProtocolFilter();
myFilter.AllowUI = false;
Windows.Web.Http.HttpClient client = new Windows.Web.Http.HttpClient(myFilter);
Windows.Web.Http.HttpResponseMessage result = await client.GetAsync(new Uri("http://localhost:5132/api/values"));

Streaming HttpResponse through Owin

I have a HttpResponse object as a result of HttpClient.SendAsync() call. The response has a chunked transfer encoding and results in 1.5 GB of data.
I want to pass this data through OWIN pipeline. To do this I need to convert it to a stream. Simplified code to do this is:
public async Task Invoke(IDictionary<string, object> environment)
{
var httpContent = GetHttpContent();
var responseStream = (Stream)environment["owin.ResponseBody"];
await httpContent.CopyToAsync(responseStream);
}
However, the last line results in copying the entire stream to the memory. And when I use wget to download the data directly from the backend server, it is downloaded successfully and shows a progress bar (although it doesn't know the overall size since it is chunked). But when I use wget to download data from my OWIN-hosted application it sticks on sending the request.
How should I stream this data through an OWIN pipeline to prevent copying it to memory?
EDIT
This is how I get the HttpResponse:
var client = new HttpClient(new HttpClientHandler());
// …and then:
using (var request = new HttpRequestMessage { RequestUri = uri, Method = HttpMethod.Get })
{
return client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
}
I assume this is in IIS? System.Web also buffers responses: https://msdn.microsoft.com/en-us/library/system.web.httpresponse.bufferoutput(v=vs.110).aspx
See server.DisableResponseBuffering in
https://katanaproject.codeplex.com/wikipage?title=OWIN%20Keys&referringTitle=Documentation

HttpClient accessing a socket application

I started down the path of using HttpClient as I thought the service I was accessing is a REST service. Turns out it's a JSON service running on port 80 but is a socket application.
The HttpClient opens the remote port but when it sends the JSON request it never gets a response. I was having the hardest time getting fiddler to get a response back as well. But I was able to get wget and curl to send/receiving a response. That's when I talked to the original developer and he mentioned that it wasn't a true "REST" service, but just a socket application that sends/receives JSON.
Is there something I can do to tweak HttpClient to access a socket application or am I going to have to take a step back and use WebSockets?
This is the test code that sends/receives the JSON packet.
private async Task ProcessZone(string szIPAddress)
{
string responseData = string.Empty;
Uri baseAddress = new Uri(#"http://" + szIPAddress + "/player");
try
{
using (var httpClient = new HttpClient { BaseAddress = baseAddress })
{
var _req = new SendRequest();
_req.id = "rec-100";
_req.url = "/stable/av/";
_req.method = "browse";
var _json = JsonConvert.SerializeObject(_req);
using (var content = new StringContent(_json,Encoding.UTF8, "application/json"))
{
using (var response = await httpClient.PostAsync(baseAddress, content))
{
responseData = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
var _Response = JsonConvert.DeserializeObject<Response>(responseData);
var item = new ZoneInfo();
item.szIPAddress = szIPAddress;
item.szZoneName = _Response.result.item.title;
lstZones.Add(item);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
private async Task ProcessZones()
{
foreach (var item in ZoneSearch)
{
await ProcessZone(item.IPAddress);
}
}
The connection hangs on this line:
using (var response = await httpClient.PostAsync(baseAddress, content))
I should also mention that the code above does work fine on a true rest service...
That's when I talked to the original developer and he mentioned that it wasn't a true "REST" service, but just a socket application that sends/receives JSON.
Knowing the protocol is the first step towards making a working client.
Is there something I can do to tweak HttpClient to access a socket application or am I going to have to take a step back and use WebSockets?
Neither, unfortunately. HttpClient - as the name implies - only works with HTTP services. Since the server is not an HTTP server, it won't work with HttpClient. WebSockets have a rather confusing name, since they are not raw sockets but instead use the WebSocket protocol, which require an HTTP handshake to set up. Since the server is not an HTTP/WebSocket server, it won't work with WebSockets.
Your only choices are to either pressure the developer to write a real REST service (which makes your job orders of magnitude easier), or use raw sockets (e.g., Socket). Correctly using raw sockets is extremely difficult, so I recommend you pressure the developer to write a REST service like the entire rest of the world does today.

WebClient - UploadValues : Get Status Response

I'am trying to pass values from a controller to another controller in another domain. I'am adding data to a NameValueCollection and pass it to another controller [httppost] method and receiving data there mapped to a Model same as i passed from.
Currently i'am running it locally by opening two instance of VS simultaneously. When the both VS is opened the values are passed correctly and the information is written to db correctly and i receive a response like "{byte[0]}". Now when i try stopping the destination controller Project and try to submit data then it wont work but still i get the same response as "{byte[0]}". Can somebody please help me how to return the response command in this scenario. Is there a way a understand the UploadValues are completed or not completed.
.........
.........
NameValueCollection resumeDetails = new NameValueCollection();
resumeDetails.Add("FirstName", "KRIZTE");
byte[] res = this.Post(ConfigurationManager.AppSettings["RedirectionUrl"].ToString(), resumeDetails);
return View("Index");
}
public byte[] Post(string uri, NameValueCollection resumeDetails)
{
byte[] response = null;
WebClient client = new WebClient();
response = client.UploadValues(uri, resumeDetails);
return response;
}
You should not use the WebClient because of problems like this.
Microsoft implemented HttpClient class as a newer API and it has these benefits:
HttpClient is the newer of the APIs and it has the benefits of
has a good async programming model
1- being worked on by Henrik F Nielson who is basically one of the inventors of HTTP, and he designed the API so it is easy for you to follow the HTTP standard, e.g. generating standards-compliant headers
2- is in the .Net framework 4.5, so it has some guaranteed level of support for the forseeable future
3- also has the xcopyable/portable-framework version of the library if you want to use it on other platforms - .Net 4.0, Windows Phone etc.
so I'm gonna show you an example of using HttpClient:
var uri = "http://google.com";
var client = new HttpClient();
try
{
var values = new List<KeyValuePair<string, string>>();
// add values to data for post
values.Add(new KeyValuePair<string, string>("FirstName", "KRITZTE"));
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
// Post data
var result = await client.PostAsync(uri, content);
// Access content as stream which you can read into some string
Console.WriteLine(result.Content);
// Access the result status code
Console.WriteLine(result.StatusCode);
}
catch(AggregateException ex)
{
// get all possible exceptions which are thrown
foreach (var item in ex.Flatten().InnerExceptions)
{
Console.WriteLine(item.Message);
}
}

Categories

Resources