Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blocking requests #17

Open
jgauffin opened this issue Apr 2, 2015 · 0 comments
Open

Blocking requests #17

jgauffin opened this issue Apr 2, 2015 · 0 comments

Comments

@jgauffin
Copy link
Owner

jgauffin commented Apr 2, 2015

Source: http://stackoverflow.com/questions/27139443/how-do-i-close-a-connection-on-a-request-to-the-griffin-webserver

We're using Griffin.WebServer as a replacement for the .Net HttpClient because the httpclient requires elevated privileges. After exhausting our options on that subject, we decided on Griffin.WebServer. Sorry this is not tagged with Griffin.WebServer - I do not have rep to create that tag.

This simple webserver responds to requests, and works great - once. Then the connection stays open, and a subsequent request will not process until I have closed the connection forcibly (using TCPView and right-click - Close Connection), or it times out.

Here is the client I am testing with:

    static void Main(string[] args)
    {
        try
        {
            var request = new
            {
                JsonData1 = 50538,
                JsonData2 = 2,
                JsonData3 = 1
            };

            var handler = new HttpClientHandler();
            handler.UseDefaultCredentials = true;
            using (var client = new HttpClient(handler))
            {
                client.BaseAddress = new Uri("http://ALocalMachine:8800/listener/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client.DefaultRequestHeaders.ConnectionClose = true;
                Task<HttpResponseMessage> postTask = client.PostAsJsonAsync("RunTest/", request);

                postTask.Wait();

                if (!postTask.Result.IsSuccessStatusCode)
                {
                    throw new Exception("Unable to start test run - " + postTask.Result.ReasonPhrase);
                }

                var responseTask = postTask.Result.Content.ReadAsStringAsync();
                responseTask.Wait();
                dynamic response = JsonConvert.DeserializeObject(responseTask.Result);

                var results = new
                {
                    Error = "",
                    Success = true
                };
            }

            // this request fails with connection forcebly closed
            var request = new
            {
                JsonData1 = 50539,
                JsonData2 = 3,
                JsonData3 = 4
            };

            var handler = new HttpClientHandler();
            handler.UseDefaultCredentials = true;
            using (var client = new HttpClient(handler))
            {
                client.BaseAddress = new Uri("http://ALocalMachine:8800/listener/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                client.DefaultRequestHeaders.ConnectionClose = true;
                Task<HttpResponseMessage> postTask = client.PostAsJsonAsync("RunTest/", request);

                postTask.Wait();

                if (!postTask.Result.IsSuccessStatusCode)
                {
                    throw new Exception("Unsuccessful - " + postTask.Result.ReasonPhrase);
                }

                var responseTask = postTask.Result.Content.ReadAsStringAsync();
                responseTask.Wait();
                dynamic response = JsonConvert.DeserializeObject(responseTask.Result);

                var results = new
                {
                    Error = "",
                    Success = true
                };
            }

            Console.WriteLine("successful");
        }
        catch (Exception ex)
        {
            Console.WriteLine("failed with error: " + ex.ToString());
        }

        Console.ReadLine();
    }

Below is the Griffin IWorkerModule.

public class MyModule : IWorkerModule
{
    public class ListenerNotifyEventArgs : EventArgs
    {
        public IHttpContext Context { get; set; }
    }

    public delegate void ListenerNotifyEventHandler(object sender, ListenerNotifyEventArgs e);

    public event ListenerNotifyEventHandler OnListenerNotify;

    public void BeginRequest(IHttpContext context)
    {
    }

    public void EndRequest(IHttpContext context)
    {
    }

    public void HandleRequestAsync(IHttpContext context, Action<IAsyncModuleResult> callback)
    {
        // Since this module only supports sync
        callback(new AsyncModuleResult(context, HandleRequest(context)));
    }

    public ModuleResult HandleRequest(IHttpContext context)
    {
        if (OnListenerNotify != null)
            OnListenerNotify(null, new ListenerNotifyEventArgs(){Context = context});

        return ModuleResult.Stop;
    }
}

Here is my listener callback. Adding the:

    context.Response.KeepAlive = false;

didn't make any difference...

    private void ListenerCallback(object sender, MyModule.ListenerNotifyEventArgs eventArgs)
    {
        var context = eventArgs.Context;

        try
        {
            var requestDetails = context.Request.Uri.AbsolutePath.Substring("/TestifyAgent/".Length);

            if (requestDetails.ToLower().StartsWith("runtest"))
            {
                var data_text = new StreamReader(context.Request.Body, context.Request.ContentEncoding).ReadToEnd();

                dynamic requestVals = JsonConvert.DeserializeObject(data_text);
                int jsonData1 = Convert.ToInt32(requestVals.JsonData1);
                int jsonData2 = Convert.ToInt32(requestVals.JsonData2);
                int jsonData3 = Convert.ToInt32(requestVals.JsonData3);

                StartProcess(jsonData1, jsonData2, jsonData3);

                var returnData = new
                {
                    Result = "Success",
                    Environment.MachineName,
                    runRequest.TestCaseRunId
                };
                var returnJsonData = JsonConvert.SerializeObject(returnData);
                var returnUtfData = Encoding.UTF8.GetBytes(returnJsonData);

                context.Response.StatusCode = (int) HttpStatusCode.OK;
                context.Response.StatusDescription = "OK";
                context.Response.ContentType = "text/html";
                context.Response.Body = new MemoryStream();
                context.Response.Body.Write(returnUtfData, 0, returnUtfData.Length);
                context.Response.Body.Flush();
                context.Response.Body.Position = 0;
                context.Response.KeepAlive = false;
                return;
            }
            else
            {
                var buffer = Encoding.UTF8.GetBytes("<html><head></head><body><h1>500 - Server Error</h1></body></html>");
                context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
                context.Response.StatusDescription = "Bad Request";
                context.Response.ContentType = "text/html";
                context.Response.Body = new MemoryStream();
                context.Response.Body.Write(buffer, 0, buffer.Length);
                context.Response.Body.Flush();
                context.Response.Body.Position = 0;
                context.Response.KeepAlive = false;

                Notify("Invalid Request method:" + context.Request.Method + " Url: " + context.Request.Uri.AbsoluteUri);
                return;
            }
        }
        catch (Exception ex)
        {
            TrySendErrorOrWriteEventLog(ex);

            try
            {
                var buffer =
                    Encoding.UTF8.GetBytes("<html><head></head><body><h1>500 - Server Error</h1>Error processing request:" +
                    context.Request.Method + " Error: " + ex.Message + "</body></html>");
                context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
                context.Response.StatusDescription = "Internal Server Error";
                context.Response.ContentType = "text/html";
                context.Response.Body.Write(buffer, 0, buffer.Length);
                context.Response.Body.Flush();
                context.Response.Body.Position = 0;
            }
            catch { }
            return;
        }
    }

EDIT:
I will leave this open in case someone needs it (and it gets answered - jgauffin?), however, I found a simple solution for creating a listener that did not require elevated privileges at this CodeProject page. It is a very straightforward solution and with one change it works very well. The one issue I had with it is that it won't shut down. It needs this added to the listener loop:

public abstract class SimpleHttpServer {

    protected int port;
    private TcpListener _listener;

    // this lets us call in with Listener.Server.Close(); when we want to shutdown
    public TcpListener Listener { get { return _listener; } }
    public bool IsActive { get; set; }

    public SimpleHttpServer(int port) 
    {
        this.port = port;
    }

    public void listen()
    {
        IsActive = true;
        _listener = new TcpListener(port);
        _listener.Start();
        while (IsActive) 
        {
            try
            {
                TcpClient s = _listener.AcceptTcpClient();
                SimpleHttpProcessor processor = new SimpleHttpProcessor(s, this);
                Thread thread = new Thread(new ThreadStart(processor.process));
                thread.Start();
                Thread.Sleep(1);
            }
            catch (Exception ex)
            {   // shutdown requested?
                if (ex.Message.Contains("A blocking operation was interrupted by a call to WSACancelBlockingCall"))
                    return;
            }
        }
    }

    public abstract void handleGETRequest(SimpleHttpProcessor p);
    public abstract void handlePOSTRequest(SimpleHttpProcessor p, StreamReader inputData);
}

Then in your shutdown include something like this:

        simpleHttpServer.Listener.Server.Close();
        if (!thread.Join(10000)) // try to wait for it...
            thread.Abort();
        simpleHttpServer = null;

Hope this is helpful to another in the same search as I was. This took way longer than it should have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant