none
HttpListener in Worker Role...

    Question

  • Hi there!

    I really struggeling a couple of days getting a worker role running with occasionally should process incoming http (POST) requests with XML documents.

    It's running fine in local compute emulation - but doesn't accept any http requests when published into the cloud (the other worker role tasks are doing fine).

    With a quite similiar worker role it's running fine (only tcp Endpoint is used). But after googling hours and days I don't have any mor ideas how to get this thing run with an http endpoint.

    The main code parts are:

        public class PiepsWorkSMS : RoleEntryPoint
        {
            public override void Run()
            {
                string port=RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["MY_EP"].IPEndpoint.Port.ToString();
                HttpListener listener = new HttpListener();
                try
                {
                    listener.Prefixes.Add("http://*:" + port + "/");
                    listener.Start();
                }
                catch (Exception ex)
                {
                    Trace.Write("Worker Role COULD NOT START! Error:" + ex.Message, "Error");
                    return;
                }

                IAsyncResult listenerResult = null;
                while (true)
                {

                    if(listenerResult==null || listenerResult.IsCompleted) listenerResult = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);

                    // processing worker role tasks...

                   // ...

                }

            }

            public static void ListenerCallback(IAsyncResult result)
            {
                HttpListener listener = (HttpListener)result.AsyncState;
                HttpListenerContext context = listener.EndGetContext(result);
                HttpListenerRequest request = context.Request;

                HttpListenerResponse response = context.Response;

                StreamReader sr = new StreamReader(request.InputStream);

                XmlDocument doc = new XmlDocument();
                

                try
                {
                    doc.LoadXml(sr.ReadToEnd());
                    // processing XML document here....
                }
                catch {}

                XmlDocument responseDoc = new XmlDocument();
                XmlNode root;
                root=responseDoc.CreateElement("Response");
                root.AppendChild(responseDoc.CreateElement("Code")).InnerText="2000";
                root.AppendChild(responseDoc.CreateElement("CodeDescription")).InnerText="OK";
                responseDoc.AppendChild(root);
                responseDoc.Save(response.OutputStream);
                response.OutputStream.Close();
            }

    The ServiceDefinition.csdef is:

    <ServiceDefinition name="WindowsAzureProject1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
      <WorkerRole name="PiepsWorkSMS" vmsize="ExtraSmall">
        <Imports>
          <Import moduleName="RemoteAccess" />
          <Import moduleName="RemoteForwarder" />
          <Import moduleName="Diagnostics" />
        </Imports>
        <Endpoints>
          <InputEndpoint name="SMS_EP" protocol="http" port="8404"  />
        </Endpoints>
        <ConfigurationSettings>
          <Setting name="DiagnosticsConnectionString" />
        </ConfigurationSettings>
        <Runtime executionContext="elevated" />
      </WorkerRole>
    </ServiceDefinition>

    Tuesday, April 10, 2012 7:57 PM

Answers

  • Hi,

    I guess this problem may ocurred by permission. In local environment, you run the Visual Studio by Administrattor and runs OK, but on the cloud environment, you need add executionContext to elevated before start the WorkRole Service, please try move these code snippets to OnStart method and have a try:

                 string port=RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["MY_EP"].IPEndpoint.Port.ToString();
                 HttpListener listener = new HttpListener();
                 try
                 {
                     listener.Prefixes.Add("http://*:" + port + "/");
                     listener.Start();
                 }
                 catch (Exception ex)
                 {
                     Trace.Write("Worker Role COULD NOT START! Error:" + ex.Message, "Error");
                     return;
                 }
     

    Hope this helps.


    Please mark the replies as answers if they help or unmark if not. If you have any feedback about my replies, please contact msdnmg@microsoft.com Microsoft One Code Framework

    Wednesday, April 11, 2012 5:00 AM
  • Hi,

    I wonder why the real local port happens to be 8404. Could you add a localport 8404? Then set Firewall Rule for 8084 port to see whether it works.

    http://msdn.microsoft.com/en-us/library/windowsazure/gg433020.aspx

    Also just in case, try to explictly add "http://xxx.cloudapp.net:8404" as the prefix of your listener. And when you test, access http://xxx.cloudapp.net:8404....

    BTW, why not using Web Role?


    Allen Chen [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.





    Thursday, April 12, 2012 8:42 AM

All replies

  • Hi,

    I guess this problem may ocurred by permission. In local environment, you run the Visual Studio by Administrattor and runs OK, but on the cloud environment, you need add executionContext to elevated before start the WorkRole Service, please try move these code snippets to OnStart method and have a try:

                 string port=RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["MY_EP"].IPEndpoint.Port.ToString();
                 HttpListener listener = new HttpListener();
                 try
                 {
                     listener.Prefixes.Add("http://*:" + port + "/");
                     listener.Start();
                 }
                 catch (Exception ex)
                 {
                     Trace.Write("Worker Role COULD NOT START! Error:" + ex.Message, "Error");
                     return;
                 }
     

    Hope this helps.


    Please mark the replies as answers if they help or unmark if not. If you have any feedback about my replies, please contact msdnmg@microsoft.com Microsoft One Code Framework

    Wednesday, April 11, 2012 5:00 AM
  • Hi! I guess this was a step forward - but still not working...

    I moved the parts to the OnStart method - it seems that only the OnStart is acting in elevated executionContext...

            HttpListener MyHttpListener = null;
            public override bool OnStart()
            {
                // Set the maximum number of concurrent connections 
                ServicePointManager.DefaultConnectionLimit = 12;
                DiagnosticMonitor.Start("DiagnosticsConnectionString");
                string port = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["SMS_EP"].IPEndpoint.Port.ToString();
                Trace.WriteLine("Initializing SMS Worker Role using port " + port + "...", "Information");
                MyHttpListener = new HttpListener();
                try
                {
                    MyHttpListener.Prefixes.Add("http://*:" + port + "/");
                    MyHttpListener.Start();
                    Trace.WriteLine("Initializing using port " + port + " done", "Information");
                }
                catch (Exception ex)
                {
                    Trace.Write("SMS Worker Role COULD NOT INITIALIZE! Error:" + ex.Message, "Error");
                    return false;
                }
                RoleEnvironment.Changing += RoleEnvironmentChanging;
                return base.OnStart();
            }

    Still working in local enviroment ... and now also working (at lieast initializing correctly) in the cloud.

    With RDP connection I did also check using netsh...

    Now I see an entry like this (8404 is my used port)

        Reserved URL            : http://10.119.148.5:8404/
            User: CIS\f9ff2917-12d5-4e50-9ec2-59dd603e0076
                Listen: Yes
                Delegate: No
                SDDL: D:(A;;GX;;;S-1-5-85-1709629745-3114573072-253485099-2827043508-603539684)

    I also used a frre tool CurrPorts which shows:

    This looks good, at least for me...

    I've also wrote a small console application which just POSTs a XML document to the server. It's working when I post it to my local running instance.

    But when I try to post it to http://myrole.cloudapp.net:8404  I'm just getting The remote server returned an error (503) Server Unavailiable.

    But at least it's answering this request... posts to other ports times out...

    Right after that netstat shows me a still pending connection: TCP    10.119.148.5:8404      83-65-96-194:37232     TIME_WAIT

    The 83-65-96-194 is my correct WAN-IP... and CurrPorts also shows:

    For me it seems that there is still a conflict with some parts of the server side http handling...

    One of my biggest issue for AZURE developing is, that I still didn't find way so see my Trace.Logs... (like I do in local compute emulator)

    Any more ideas how to get closed to the problem?

    Best Regards


    Michael

    "If we knew what it was we were doing - it would not be called research, would it?" -- A. Einstein


    Wednesday, April 11, 2012 7:22 AM
  • Hi,

    According to your description, i'd like to suggest you use Remote desktop to Azure VM and send a request to local machine to see if works, it seems that port 8404 is available but we cant make sure it's works fine.

    About Trace log in Azure environment, do you ever store azure trace message in Blob storage? The usual method is store trace message in Blob storage and view it by application or other tools (such as Azure Storage Explorer). More details about how to enable Azure Trace and store trace message in Storage, please refer to the following link:

    http://msdn.microsoft.com/en-us/magazine/ff714589.aspx

    Hope this helps.


    Please mark the replies as answers if they help or unmark if not. If you have any feedback about my replies, please contact msdnmg@microsoft.com Microsoft One Code Framework

    Wednesday, April 11, 2012 9:38 AM
  • I doubt this would be the issue in your case, but nevertheless, make sure to check your firewall rules to be sure.

    Be nice to nerds ... Chances are you'll end up working for one!

    Wednesday, April 11, 2012 10:35 AM
  • Arwind,

    I tried to execute my small demo programm (just doing a http-POST).

    via RDP on the VM:

    <small>C:\Pieps>ipconfig</small>

    Windows IP Configuration
    Ethernet adapter Local Area Connection 2:

       Connection-specific DNS Suffix  . : reddog.microsoft.com
       Link-local IPv6 Address . . . . . : fe80::5482:4c7a:cb4e:2c%15
       IPv4 Address. . . . . . . . . . . : 10.119.148.5
       Subnet Mask . . . . . . . . . . . : 255.255.254.0
       Default Gateway . . . . . . . . . : 10.119.148.1

    Tunnel adapter Local Area Connection* 8:

       Media State . . . . . . . . . . . : Media disconnected
       Connection-specific DNS Suffix  . : reddog.microsoft.com

    Tunnel adapter Local Area Connection* 9:

       Connection-specific DNS Suffix  . :
       IPv6 Address. . . . . . . . . . . : 2001:0:5ef5:79fd:1c3d:2392:f588:6bfa
       Link-local IPv6 Address . . . . . : fe80::1c3d:2392:f588:6bfa%17
       Default Gateway . . . . . . . . . : ::

    C:\Pieps>TestSMS.exe MOSMS http://10.119.148.5 8404
    PiepsOnlineTestTool:TestSMS
    Information  ->  Test Forward XML (MOSMS) http-POST to http://10.119.148.5:8404
    Error  ->  SendSMS -> httpResponse(): The remote server returned an error: (503) Server Unavailable.
    Send MOSMS returns E3

    C:\Pieps>TestSMS.exe MOSMS http://127.0.0.1 8404
    PiepsOnlineTestTool:TestSMS
    Information  ->  Test Forward XML (MOSMS) http-POST to http://127.0.0.1:8404
    Send MOSMS returns OK

    C:\Pieps>

    Using the localhost it's working, but not using the designated IP.

    Why this ?

    BR


    Michael
    "If we knew what it was we were doing - it would not be called research, would it?" -- A. Einstein


    Wednesday, April 11, 2012 11:00 AM
  • Hi Michael,

    Do you mean you the Instance IP address is unavailable for the application, but localhost is OK?

    Please check your firewall settings of that port (8404), is it was blocked?

    The specifical VM instances IP address is not recommendeded way as usual, would you like to change it to Load Balancer's IP? The azure application allow only 1 external address for outside, and load balance will help to throw these request to actually instances.

    ping your hosted service and get that IP (ping http://xxxx.cloudapp.net/).

    Hope this helps.


    Please mark the replies as answers if they help or unmark if not. If you have any feedback about my replies, please contact msdnmg@microsoft.com Microsoft One Code Framework

    Wednesday, April 11, 2012 12:46 PM
  • Arwind,

    I did copy and execute this XML-POST-tool directly on the VM...

    And the application is only accessible via 127.0.0.1:8404

    It's not working using the Instanc's IP nor the Ballancer's IP... (I got this with the ping @ xxx.cloudapp.net)

    Could this be an Firewall issue on the VM itself ?

    BR


    Michael
    "If we knew what it was we were doing - it would not be called research, would it?" -- A. Einstein

    Wednesday, April 11, 2012 1:43 PM
  • Hi,

    In general, you do not need to configure firewall settings of VM with extra works, but in this situation, i suggest you check the outbound port 8404 if it was blocked. And as i mentioned on the last reply, try to change the IP address to the Load Balancer's IP, and see if it works.

    Thank you. 


    Please mark the replies as answers if they help or unmark if not. If you have any feedback about my replies, please contact msdnmg@microsoft.com Microsoft One Code Framework

    Wednesday, April 11, 2012 2:23 PM
  • Arwind,

    now I tried really everything...

    What I know for now is:

    The Instanc's IP is 10.119.148.5 (I get this from ipconfig on the VM)

    The Balancer's IP is 168.63.4.157 ( I get this from a ping on xxx.cloudapp.net and from the Azure Management Portal)

    My role instance is running, no error messages on startup and it's working (at least the other working part...)

    Im working directly on the VM (via RDP).

    I'm executing my testprogram - which send a XML document via http-post - directly on the VM.

    It's working when I use http://127.0.0.1:8404

    I always get the error The remote server returned an error: (503) Server Unavailable when I use http://10.119.148.5:8404 or http://xxx.cloudapp.net:8404 or even http://168.63.4.157:8404

    Manipulating the firewall didn't change anything. I added an Inbound and Outbound rule for 8404. Finally I did disable the firewall at all.

    It  seems we still have a problem with the http url reservation !?!?!

    I really don't know what else to try...

    Best Regards


    Michael
    "If we knew what it was we were doing - it would not be called research, would it?" -- A. Einstein


    Wednesday, April 11, 2012 7:13 PM
  • Hi,

    I wonder why the real local port happens to be 8404. Could you add a localport 8404? Then set Firewall Rule for 8084 port to see whether it works.

    http://msdn.microsoft.com/en-us/library/windowsazure/gg433020.aspx

    Also just in case, try to explictly add "http://xxx.cloudapp.net:8404" as the prefix of your listener. And when you test, access http://xxx.cloudapp.net:8404....

    BTW, why not using Web Role?


    Allen Chen [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.





    Thursday, April 12, 2012 8:42 AM
  • Hi Allen!

    Yes, this DID solve the problem... I just had to add the additional prefix...
    Now I also switched back to port 80 - also working...

    My final working OnStart-method looks like this:

    public override bool OnStart()
            {
                // Set the maximum number of concurrent connections 
                ServicePointManager.DefaultConnectionLimit = 12;
                TimeSpan tsOneMinute = TimeSpan.FromMinutes(1);
                DiagnosticMonitorConfiguration dmc =
                DiagnosticMonitor.GetDefaultInitialConfiguration();
                // from Microsoft ...
                // Transfer logs to storage every minute
                dmc.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
                // Transfer verbose, critical, etc. logs
                dmc.Logs.ScheduledTransferLogLevelFilter = LogLevel.Verbose;
                var storage = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"));
                // Start up the diagnostic manager with the given configuration
                DiagnosticMonitor.Start(storage, dmc);
                string port = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["SMS_EP"].IPEndpoint.Port.ToString();
                Trace.WriteLine("Initializing SMS Worker Role using port " + port + "...", "Information");
                MyHttpListener = new HttpListener();
                try
                {
                    
                    MyHttpListener.Prefixes.Add("http://*:" + port + "/");
                    MyHttpListener.Prefixes.Add("http://xxxx.cloudapp.net:" + port + "/");
                    MyHttpListener.Start();
                    Trace.WriteLine("Initializing using port " + port + " done", "Information");
                }
                catch (Exception ex)
                {
                    Trace.Write("SMS Worker Role COULD NOT INITIALIZE! Error:" + ex.Message, "Error");
                    return false;
                }
                // For information on handling configuration changes
                // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
                RoleEnvironment.Changing += RoleEnvironmentChanging;
                return base.OnStart();
            }

    Also the Diagnostic trace is working now...

    BTW, what's the easiest way to get the DNS-name (xxx.cloudapp.net) of the worker role? Because now I've hard coded it, which isn't a nice solution...

    The reason I decided to go for a worker role is, that the main job is to periodically scan the database (or wait for an entry in a message queue) and do the XML transport to a SMS gateway. Once the SMS is delivered we get back a confirmation XML -> that's the point for the http-listener.....

    THX for your support


    Michael
    "If we knew what it was we were doing - it would not be called research, would it?" -- A. Einstein


    Thursday, April 12, 2012 9:52 AM
  • Hi,

    Use "Get Deployment" RESTful API to get that url, the DNS-name (xxx.cloudapp.net) is in <Url> section of the Response Body.

    Get Deployment RESTful service:

    http://msdn.microsoft.com/en-us/library/windowsazure/ee460804.aspx

    Code snippets:

                string subscriptionID = "<Your subscription ID>";
                string thrumbnail = "<Your certificate thumbprint>";
    
                string hostedServiceName = "<Your hosted service name>";
                string deploySplot="<Production or Stagging>";
                Uri requestUri = new Uri("https://management.core.windows.net/" + subscriptionID + "/services/hostedservices/" + hostedServiceName + "/deploymentslots/" + deploySplot);
                X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
                store.Open(OpenFlags.OpenExistingOnly);
                X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindByThumbprint, thrumbnail, false);
                store.Close();
                if (collection.Count != 0)
                {
                    X509Certificate2 certificate = collection[0];
                    HttpWebRequest httpRequest = (HttpWebRequest)HttpWebRequest.Create(requestUri);
                    httpRequest.ClientCertificates.Add(certificate);
                    httpRequest.Headers.Add("x-ms-version", "2011-10-01");
                    httpRequest.Method = "GET";
                    HttpWebResponse httpResponse = httpRequest.GetResponse() as HttpWebResponse;
    
                    Stream stream = httpResponse.GetResponseStream();
                    using (StreamReader reader = new StreamReader(stream))
                    {
                        string s = reader.ReadToEnd();
                        //Handle the response string to get <Url> section...
                    }

    Hope this helps.


    Please mark the replies as answers if they help or unmark if not. If you have any feedback about my replies, please contact msdnmg@microsoft.com Microsoft One Code Framework

    Thursday, April 12, 2012 10:23 AM
  • >BTW, what's the easiest way to get the DNS-name (xxx.cloudapp.net) of the worker role? Because now I've hard coded it, which isn't a nice solution

    I would set it in config file.


    Allen Chen [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, April 13, 2012 1:24 AM