.Net/Flex: How to read partial results from URLRequest? - c#

I'm working on a batch process script that's executed from Flex. The batch script is in a .aspx Page and returns partial results through the following class:
public class ResponseLogger
{
private HttpResponse _response;
public ResponseLogger(HttpResponse response)
{
this._response = response;
}
public void Start()
{
_response.Clear();
_response.ContentType = "text/plain";
_response.ContentEncoding = System.Text.Encoding.GetEncoding("ISO-8859-1");
}
public void End()
{
_response.End();
}
public void Br()
{
Log("");
}
public void Underline(string message)
{
Log(message);
Log("".PadLeft(message.Length, '-'));
}
public void Log(string message)
{
_response.Write(message + "\n");
_response.Flush();
}
}
In my Flex application I'd like to show the result as soon as it's flushed on server side. Can this be done using Actionscript?

Short answer, no, you cannot do partial results over HTTP unless you do short/long polling (several http calls per minute). HTTP in it's essence is a request-response protocol.
What you want is a push technology, but I'm not sure if there's a .NET equivalent for this. On the Java side you got BlazeDS or GraniteDS for push messaging.
The other question is why are you results 'partial'?

Related

Send data to client OnMessage with WebSocket-sharp server

I've been trying to solve this simple problem, but I cannot make it work.
I am using WebSocketSharp.Server. The code can be seen below.
In my NetworkClass I have someData I would like to send to the client when a message is received. The problem is the OnMessage event gets fired in a different class, and I don't know how the access the instance of this class.
Broadcasting to all clients form the NetworkClass works fine and receiving messages form the client works fine as well.
public class IndexRoute : WebSocketBehavior {
protected override void OnMessage(MessageEventArgs e) {
Console.WriteLine("Received message form client: "+e.Data);
//TODO: send someData string to client
}
}
public class NetworkClass {
String someData = "TestData";
WebSocketServer server = new WebSocketServer("ws://127.0.0.1:5000");
public NetworkClass() {
server.AddWebSocketService<IndexRoute>("/");
server.Start();
Console.WriteLine("Server started");
}
public void broadcastData() {
server.WebSocketServices["/"].Sessions.Broadcast(someData);
Console.WriteLine("Broadcast");
}
}
I combined the answers of Jeroen van Langen and Jesper. The key was to pass the instance of the NetworkClass to the IndexRoute class and access the variables from there.
server.AddWebSocketService<IndexRoute>("/", () => new IndexRoute(this));
works but is marked as obsolete.
public class IndexRoute : WebSocketBehavior {
private NetworkClass _instanceOfNetworkClass;
public IndexRoute(NetworkClass instanceOfNetworkClass)
{
_instanceOfNetworkClass = instanceOfNetworkClass;
}
protected override void OnMessage(MessageEventArgs e) {
Console.WriteLine("Received message form client: "+e.Data);
//Broadcast someData string to clients
Sessions.Broadcast(_instanceOfNetworkClass.someData);
}
}
public class NetworkClass {
public String someData = "TestData";
WebSocketServer server = new WebSocketServer("ws://127.0.0.1:5000");
public NetworkClass() {
server.AddWebSocketService<IndexRoute>("/", () => new IndexRoute(this));
server.Start();
Console.WriteLine("Server started");
}
public void broadcastData() {
server.WebSocketServices["/"].Sessions.Broadcast(someData);
Console.WriteLine("Broadcast");
}
}
I don't have any experience with WebSocketSharp, but the docs says you can give an alternate WebSocketBehavior construction function to the AddWebSocketService<>
Let me know if it works: (else i'll remove it)
public class IndexRoute : WebSocketBehavior {
private string _someData;
// define the constructor which accepts the someData
// save it in a field.
public IndexRoute(string someData)
{
_someData = someData;
}
protected override void OnMessage(MessageEventArgs e) {
Console.WriteLine("Received message form client: "+e.Data);
//TODO: send someData string to client
// do something with _someData
}
}
public class NetworkClass {
String someData = "TestData";
WebSocketServer server = new WebSocketServer("ws://127.0.0.1:5000");
public NetworkClass() {
// pass the construction function.
// (construct the IndexRoute and pass the someData to it.)
server.AddWebSocketService<IndexRoute>("/", () => new IndexRoute(someData));
server.Start();
Console.WriteLine("Server started");
}
public void broadcastData() {
server.WebSocketServices["/"].Sessions.Broadcast(someData);
Console.WriteLine("Broadcast");
}
}
I didn't tested it, but it might give you a step in the right direction to solve it.
When you inherit WebSocketBehavior, you get methods and properties that allow you to find out information about the connection you are currently talking to. In this case, just use one of the Send methods to send to the connection that sent the message:
protected override void OnMessage(MessageEventArgs e) {
Console.WriteLine("Received message form client: "+e.Data);
Send(/* pick one of the overloads to suit your needs */);
}
You can also use the property Context to get a WebSocketContext with information about the connection, or ID to find the session (connection) ID, which you could pass to another object. If the question is how you find your instance of NetworkClass, you are already constructing the IndexRoute yourself, just pass this in as an additional parameter.

Best Practices for event driven WPF UI update and Async Receive in C#?

In an effort to learn c#, I'm writing an application that will continuously monitor UDP traffic on a particular port and update a WPF UI text block with received packet information. The following code works (UDP handler class instance d instantiated elsewhere in scope):
public MainWindow()
{
InitializeComponent();
Task.Run(async () =>
{
using (d.receiveClient)
{
while (true)
{
var receivedResults = await d.receiveClient.ReceiveAsync();
byte[] buffer = receivedResults.Buffer;
Console.Write("Receiving Data: ");
Console.WriteLine(buffer[0].ToString());
Dispatcher.BeginInvoke(new Action(delegate
{
MyTextBlock.Text = "Rx Data: " + buffer[0].ToString();
}));
}
}
});
}
While it works, it's certainly doesn't feel idiomatic or correct. I'd like to create a Task<byte[]> that contains the async receive logic in the class that currently contains receiveClient. Problem is it doesn't execute continuously; the task will execute once, then exit. I've tried restarting the task in .ContinueWith(), etc, and while the Task can be restarted, I seem to loose the hooks into the UI. What's the best way to accomplish an event driven, continuous receive that in turn updates WPF UI components in native c# (I'd rather not use WinPcap or its .Net equivalent)?
Rather than try to stick with an always running Task/Thread and passing messages back to the main application, I ultimately opted to use the BeginReceive() and EndReceive() methods with a custom event for handling data parsing. Essentially something like the following (distilled to essentials):
public void Listen()
{
receiveClient.BeginReceive(new AsyncCallback(ProcessIncoming), null);
}
private void ProcessIncoming(IAsyncResult res)
{
byte[] rec_bytes = receiveClient.EndReceive(res, ref rec_ep);
receiveClient.BeginReceive(new AsyncCallback(ProcessIncoming), null);
PacketReceivedEventArgs args = new PacketReceivedEventArgs();
args.IP = rec_ep.Address;
args.Port = rec_ep.Port;
args.Data = rec_bytes;
OnPacketReceived(args);
}
protected virtual void OnPacketReceived(PacketReceivedEventArgs e)
{
EventHandler<PacketReceivedEventArgs> handler = PacketReceived;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<PacketReceivedEventArgs> PacketReceived;
public class PacketReceivedEventArgs : EventArgs
{
public byte[] Data { get; set; }
public IPAddress IP { get; set; }
public int Port { get; set; }
}
In the main app, connecting, listening, and packet handling becomes (where d is an instance of the class implementing the above methods):
d.Connect(someIPString, portInt);
d.PacketReceived += _SomeHandler;
d.Listen();

Using CefGlue to return HTML page from an Url

I am attempting to write an implementation for the following (prototype) method:
var result = browser.GetHtml(string url);
The reason I need this is because there are a number of pages that push a mound of Javascript to the browser, and then the Javascript renders the page. The only way to retrieve such pages reliably is to allow the Javascript to execute in a browser environment before retrieving the resulting HTML.
My current attempt is using CefGlue. After downloading this project and combining it with the code in this answer I came up with the following code (included here for completeness):
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Xilium.CefGlue;
namespace OffScreenCefGlue
{
internal class Program
{
private static void Main(string[] args)
{
// Load CEF. This checks for the correct CEF version.
CefRuntime.Load();
// Start the secondary CEF process.
var cefMainArgs = new CefMainArgs(new string[0]);
var cefApp = new DemoCefApp();
// This is where the code path divereges for child processes.
if (CefRuntime.ExecuteProcess(cefMainArgs, cefApp) != -1)
{
Console.Error.WriteLine("CefRuntime could not create the secondary process.");
}
// Settings for all of CEF (e.g. process management and control).
var cefSettings = new CefSettings
{
SingleProcess = false,
MultiThreadedMessageLoop = true
};
// Start the browser process (a child process).
CefRuntime.Initialize(cefMainArgs, cefSettings, cefApp);
// Instruct CEF to not render to a window at all.
CefWindowInfo cefWindowInfo = CefWindowInfo.Create();
cefWindowInfo.SetAsOffScreen(IntPtr.Zero);
// Settings for the browser window itself (e.g. should JavaScript be enabled?).
var cefBrowserSettings = new CefBrowserSettings();
// Initialize some the cust interactions with the browser process.
// The browser window will be 1280 x 720 (pixels).
var cefClient = new DemoCefClient(1280, 720);
// Start up the browser instance.
string url = "http://www.reddit.com/";
CefBrowserHost.CreateBrowser(cefWindowInfo, cefClient, cefBrowserSettings, url);
// Hang, to let the browser do its work.
Console.Read();
// Clean up CEF.
CefRuntime.Shutdown();
}
}
internal class DemoCefApp : CefApp
{
}
internal class DemoCefClient : CefClient
{
private readonly DemoCefLoadHandler _loadHandler;
private readonly DemoCefRenderHandler _renderHandler;
public DemoCefClient(int windowWidth, int windowHeight)
{
_renderHandler = new DemoCefRenderHandler(windowWidth, windowHeight);
_loadHandler = new DemoCefLoadHandler();
}
protected override CefRenderHandler GetRenderHandler()
{
return _renderHandler;
}
protected override CefLoadHandler GetLoadHandler()
{
return _loadHandler;
}
}
internal class DemoCefLoadHandler : CefLoadHandler
{
public string Html { get; private set; }
protected override void OnLoadStart(CefBrowser browser, CefFrame frame)
{
// A single CefBrowser instance can handle multiple requests
// for a single URL if there are frames (i.e. <FRAME>, <IFRAME>).
if (frame.IsMain)
{
Console.WriteLine("START: {0}", browser.GetMainFrame().Url);
}
}
protected override async void OnLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode)
{
if (frame.IsMain)
{
Html = await browser.GetSourceAsync();
Console.WriteLine("END: {0}, {1}", browser.GetMainFrame().Url, httpStatusCode);
}
}
}
internal class DemoCefRenderHandler : CefRenderHandler
{
private readonly int _windowHeight;
private readonly int _windowWidth;
public DemoCefRenderHandler(int windowWidth, int windowHeight)
{
_windowWidth = windowWidth;
_windowHeight = windowHeight;
}
protected override bool GetRootScreenRect(CefBrowser browser, ref CefRectangle rect)
{
return GetViewRect(browser, ref rect);
}
protected override bool GetScreenPoint(CefBrowser browser, int viewX, int viewY, ref int screenX, ref int screenY)
{
screenX = viewX;
screenY = viewY;
return true;
}
protected override bool GetViewRect(CefBrowser browser, ref CefRectangle rect)
{
rect.X = 0;
rect.Y = 0;
rect.Width = _windowWidth;
rect.Height = _windowHeight;
return true;
}
protected override bool GetScreenInfo(CefBrowser browser, CefScreenInfo screenInfo)
{
return false;
}
protected override void OnPopupSize(CefBrowser browser, CefRectangle rect)
{
}
protected override void OnPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects, IntPtr buffer, int width, int height)
{
// Save the provided buffer (a bitmap image) as a PNG.
var bitmap = new Bitmap(width, height, width*4, PixelFormat.Format32bppRgb, buffer);
bitmap.Save("LastOnPaint.png", ImageFormat.Png);
}
protected override void OnCursorChange(CefBrowser browser, IntPtr cursorHandle)
{
}
protected override void OnScrollOffsetChanged(CefBrowser browser)
{
}
}
public class TaskStringVisitor : CefStringVisitor
{
private readonly TaskCompletionSource<string> taskCompletionSource;
public TaskStringVisitor()
{
taskCompletionSource = new TaskCompletionSource<string>();
}
protected override void Visit(string value)
{
taskCompletionSource.SetResult(value);
}
public Task<string> Task
{
get { return taskCompletionSource.Task; }
}
}
public static class CEFExtensions
{
public static Task<string> GetSourceAsync(this CefBrowser browser)
{
TaskStringVisitor taskStringVisitor = new TaskStringVisitor();
browser.GetMainFrame().GetSource(taskStringVisitor);
return taskStringVisitor.Task;
}
}
}
The relevant bit of code is here:
protected override async void OnLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode)
{
if (frame.IsMain)
{
Html = await browser.GetSourceAsync();
Console.WriteLine("END: {0}, {1}", browser.GetMainFrame().Url, httpStatusCode);
}
}
This actually appears to work; you can examine the Html variable with the debugger, and there is an HTML page in there. The problem is, the Html variable does me no good in that callback method; it's buried three layers deep in a class hierarchy, and I need to return it in the method I'm trying to write without creating a Schroedinbug.
(attempting to get the result from that string Html property, including trying to view it with the Html visualizer in the debugger, appears to cause a deadlock, something I'd really like to avoid, especially since this code is going to run on a server).
How do I achieve my var result = browser.GetHtml(string url); safely and reliably?
Bonus question: Could the callback mechanisms in the above code be converted to Tasks using this technique? What would that look like?
Keep in mind that current CefGlue versions did not provide any of synchronization contexts, so most of time you should not use async/await in callbacks, unless you are sure what you do.
"Reliable" code should be async, because most of CEF calls are async (with or without callbacks provided). Async/await is greatly simplifies this task, so i'm assume that this question can be simplified to: "how to write GetSourceAsync method correctly?". This is also relied to your's bonus question, and simple answer is of course no, and this technique should be consider harmful, because without knowledge of underlying code is lead to different effects.
So, regardless to GetSourceAsync method, and especially TaskStringVisitor i'm only propose you never execute TaskCompletionSource's methods directly, because it executes continuations synchronously (in .NET 4.6 it is have option to execute continuations asynchronously, but i'm personally did not inspect how it is done in 4.6 internally). This is needed to free one of CEF thread as soon as possible. Otherwise eventually you can obtain big continuation tree, loop or wait, what is actually block browser's thread forever. Also, note, that this kind extensions are also harmful, because they had same problems described above - the only choice to deal with is to have true async continuation.
protected override void Visit(string value)
{
System.Threading.Tasks.Task.Run(() => taskCompletionSource.SetResult(value));
}
Some CEF API are hybrid: they queue task to required thread if we already not on required thread, or execute synchronously. For this cases handling should be simplified, and it is better to avoid async stuff in that case. Again, just to avoid synchronous continuations, because them can lead to reentrancy problems and/or just your obtain unnecessary stack frames (with hope that only for short period of time, and code did not stuck somewhere).
One of easiest sample is, but it is also true for some other API calls:
internal static class CefTaskHelper
{
public static Task RunAsync(CefThreadId threadId, Action action)
{
if (CefRuntime.CurrentlyOn(threadId))
{
action();
return TaskHelpers.Completed();
}
else
{
var tcs = new TaskCompletionSource<FakeVoid>();
StartNew(threadId, () =>
{
try
{
action();
tcs.SetResultAsync(default(FakeVoid));
}
catch (Exception e)
{
tcs.SetExceptionAsync(e);
}
});
return tcs.Task;
}
}
public static void StartNew(CefThreadId threadId, Action action)
{
CefRuntime.PostTask(threadId, new CefActionTask(action));
}
}
UPDATE:
This actually appears to work; you can examine the Html variable with
the debugger, and there is an HTML page in there. The problem is, the
Html variable does me no good in that callback method; it's buried
three layers deep in a class hierarchy, and I need to return it in the
method I'm trying to write without creating a Schroedinbug.
You just need to implement CefLifeSpanHandler and then you can have direct access to CefBrowser once it will be created (it created asynchronously). There is exists CreateBrowserSync call, but is not preffered way.
PS: I'm on the way on CefGlue next generation, but right now nothing ready to use. Better async/await integration is planned. I'm personally use async/await stuff around it intensively, exactly at server side environment.

Is there any elegant way to call a web page asynchronously ,ignore the response and release resource of thread

I have a case in ASP.NET,that when a request got, a web page (naming it as service.aspx)will be called asynchronously, and I don't care the response of service.aspx, I just need call it .
Right now ,I have two ways.
First one is HttpWebRequest.BeginGetResponse, parameter callback is set as null. And it seems that it's not a good idea , there is no way to call EndGetResponse, so maybe the resource of calling service.aspx thread could not be released.
Second one is WebClient.OpenReadAsync, but I'm not sure whether it could release thread resource if I don't specify OpenReadCompleted event.
Or maybe there is other appropriate way to have what I want.
You can create a class that will make web requests in the background. Create a static instance of this class in your application's HttpApplication class (Global.asax) and then call a method to queue web requests as required.
Class that performs web requests in the background
public class SiteBackgroundCaller : IRegisteredObject, IDisposable
{
private BlockingCollection<string> requestList;
private CancellationTokenSource queueWorkerCts;
private Task queueWorkerThread;
public SiteBackgroundCaller()
{
// Register an instance of this class with the hosting environment, so we can terminate the task gracefully.
HostingEnvironment.RegisterObject(this);
requestList = new BlockingCollection<string>();
queueWorkerCts = new CancellationTokenSource();
queueWorkerThread = new Task(queueWorkerMethod, TaskCreationOptions.LongRunning);
queueWorkerThread.Start();
}
public void QueueBackgroundRequest(string uri)
{
requestList.Add(uri);
}
private void queueWorkerMethod()
{
while (!queueWorkerCts.IsCancellationRequested)
{
try
{
// This line will block until there is something in the collection
string uri = requestList.Take(queueWorkerCts.Token);
if (queueWorkerCts.IsCancellationRequested)
return;
// Make the request
HttpWebRequest r = (HttpWebRequest)HttpWebRequest.Create(uri);
HttpWebResponse response = (HttpWebResponse)r.GetResponse();
}
catch (OperationCanceledException)
{
// This may throw if the cancellation token is Cancelled.
}
catch (WebException)
{
// Something wrong with the web request (eg timeout)
}
}
}
// Implement IRegisteredObject
public void Stop(bool immediate)
{
queueWorkerCts.Cancel();
queueWorkerThread.Wait();
}
// Implement IDisposable
public void Dispose()
{
HostingEnvironment.UnregisterObject(this);
}
}
Instance of the class in HttpApplication (in Global.asax)
public class Global : System.Web.HttpApplication
{
public static SiteBackgroundCaller BackgroundCaller { get; private set; }
protected void Application_Start(object sender, EventArgs e)
{
BackgroundCaller = new SiteBackgroundCaller();
}
}
Queuing a web request from a page
public partial class MyPage: System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
Global.BackgroundCaller.QueueBackgroundRequest("http://www.example.com/service.aspx");
}
}
You can create the HttpWebRequest and not read the response:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("service.aspx");
using (var response = request.GetResponse())
{
// typically here you would call GetResponseStream and read the content
}
you can also use the async variation:
using (var response = await request.GetResponseAsync())
{
}

Call StaticFileHandler

I have an HttpHandler mapped to aspnet_isapi.dll to perform a custom authentication check on static files (.pdf files) using IIS 7.5 in Classic mode:
void IHttpHandler.ProcessRequest(HttpContext context)
{
if(!User.IsMember) {
Response.Redirect("~/Login.aspx?m=1");
}
else {
//serve static content
}
}
The above code works fine, except for the else statement logic. In the else statement, I simply want to allow the StaticFileHandler to process the request, but I haven't been able to sort this out. Any suggestions on how to simply "hand off" the file back to IIS to serve the request as a normal StaticFile request, would be appreciated.
To answer your question directly, you can create a StaticFileHandler and have it process the request:
// Serve static content:
Type type = typeof(HttpApplication).Assembly.GetType("System.Web.StaticFileHandler", true);
IHttpHandler handler = (IHttpHandler)Activator.CreateInstance(type, true);
handler.ProcessRequest(context);
But a better idea might be to create an HTTP module instead of an HTTP handler:
public class AuthenticationModule : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.AuthorizeRequest += this.Application_AuthorizeRequest;
}
private void Application_AuthorizeRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
if (!User.IsMember)
context.Response.Redirect("~/Login.aspx?m=1");
}
}

Categories

Resources