Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-7718] iOS: Network.Socket - app crashes with exception when listening for http requests

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionFixed
Resolution Date2012-03-16T12:39:08.000+0000
Affected Version/sRelease 1.8.1
Fix Version/sRelease 2.0.0, Sprint 2012-06
ComponentsiOS
Labelsmodule_network, qe-testadded
Reporterme@gmail.com
AssigneeStephen Tramer
Created2012-02-18T10:57:31.000+0000
Updated2014-04-01T21:33:31.000+0000

Description

Problem

App crashes after several calls to the socket running on the app. Sometimes it crashes after 2-3 requests, but I have seen it take up to 30.

Test case

var info = Ti.API.info;

///=====================
var socket = undefined;
var connectedSockets = [];

function connect()
{
    if(socket === undefined || socket.state === Ti.Network.Socket.CLOSED)
    {
        socket = Titanium.Network.Socket.createTCP({
            host:Ti.Platform.address,
            port:8080,
            listenQueueSize:100,
            accepted: accepted,
            closed: function(e)
            {
                info("Closed listener");
            },
            error: function(e)
            {
                Ti.API.error("Socket encountered error when listening");
                Ti.API.error(" error code '" + e.errorCode + "'");
                Ti.API.error(" error description '" + e.error + "'");
            }
        });
        try
        {
            socket.listen();
            info("###Listening on " + socket.host + ":" + socket.port);
            socket.accept(acceptedCallbacks);
        } catch (e)
        {
            info('Exception: ' + e);
        }
    }

}

function accepted(e)
{
    var sock = e.inbound;
    info('ACCEPTED: ' + sock.host + ':' + sock.port);
    Ti.Stream.pump(sock, pumpCallback, 1024, true);
    socket.accept(acceptedCallbacks);
}


function pumpCallback(e)
{
    var sock = e.source;

    if(e.bytesProcessed == -1)
    { // EOF
        info("<EOF> - Closing the remote socket!");

        removeSocket(sock);
        sock.close();
    }
    else
    {
        if(e.errorDescription === undefined || e.errorDescription === "")
        {
            info("DATA: " + e.buffer.toString());
            //--
            try
            {
                var s = "Sup world!";

                var msg = "HTTP/1.1 200 OK\n";
                msg += "Date: Fri, 31 Dec 1999 23:59:59 GMT\n";
                msg += "Connection: close\n";
                msg += "Content-Type: text/html\n";
                msg += "Content-Length: " + s.length + "\n";
                msg += "\n";
                msg += s;

                sock.write(Ti.createBuffer({value: msg}));
                sock.close();

            } catch (e)
            {
                info('Exception: ' + e);
            }

            //--
        }
        else
        {
            info("READ ERROR: " + e.errorDescription);
        }
    }
}

var acceptedCallbacks = {
    error : function(e)
    {
        Ti.API.info(e.error);
        Ti.UI.createAlertDialog({
            title:"Socket error: " + e.socket.host,
            message:e.error
        }).show();
    }
};


exports.start = function() {
    connect();
}

Logs

2012-02-18 13:51:20.345 Kiosk[69478:1be07] -[KrollObject getCFRunLoop]: unrecognized selector sent to instance 0x9d7f790

[ERROR] The application has crashed with an unhandled exception. Stack trace:



0   CoreFoundation                      0x030b1052 __exceptionPreprocess + 178

1   libobjc.A.dylib                     0x03242d0a objc_exception_throw + 44

2   CoreFoundation                      0x030b2ced -[NSObject doesNotRecognizeSelector:] + 253

3   CoreFoundation                      0x03017f00 ___forwarding___ + 432

4   CoreFoundation                      0x03017ce2 _CF_forwarding_prep_0 + 50

5   Kiosk                               0x001c32b8 -[AsyncSocket attachStreamsToRunLoop:error:] + 104

6   Kiosk                               0x001c2b8f -[AsyncSocket doAcceptFromSocket:withNewNativeSocket:] + 607

7   Kiosk                               0x001cb2b8 -[AsyncSocket doCFSocketCallback:forSocket:withAddress:withData:] + 488

8   Kiosk                               0x001c1fd4 MyCFSocketCallback + 196

9   CoreFoundation                      0x0301fe44 __CFSocketPerformV0 + 948

10  CoreFoundation                      0x0308597f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15

11  CoreFoundation                      0x02fe8b73 __CFRunLoopDoSources0 + 243

12  CoreFoundation                      0x02fe8454 __CFRunLoopRun + 1012

13  CoreFoundation                      0x02fe7db4 CFRunLoopRunSpecific + 212

14  CoreFoundation                      0x02fe7ccb CFRunLoopRunInMode + 123

15  Foundation                          0x0103041f -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 300

16  Kiosk                               0x0020bac5 -[TiNetworkSocketTCPProxy socketRunLoop] + 325

17  Kiosk                               0x0020c27b -[TiNetworkSocketTCPProxy startListeningSocket] + 699

18  Foundation                          0x00ffc4e6 -[NSThread main] + 76

19  Foundation                          0x00ffc457 __NSThread__main__ + 1258

20  libsystem_c.dylib                   0x931e9ed9 _pthread_start + 335

21  libsystem_c.dylib                   0x931ed6de thread_start + 34

2012-02-18 13:51:20.349 Kiosk[69478:1be07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[KrollObject getCFRunLoop]: unrecognized selector sent to instance 0x9d7f790'

*** First throw call stack:

(0x30b1052 0x3242d0a 0x30b2ced 0x3017f00 0x3017ce2 0x1c32b8 0x1c2b8f 0x1cb2b8 0x1c1fd4 0x301fe44 0x308597f 0x2fe8b73 0x2fe8454 0x2fe7db4 0x2fe7ccb 0x103041f 0x20bac5 0x20c27b 0xffc4e6 0xffc457 0x931e9ed9 0x931ed6de)

terminate called throwing an exception

Comments

  1. Blain Hamon 2012-03-02

    Unfortunately, with the given test, app.js needed fixing (removing the exports.start) and we're not finding any prematurely-released sockets. Instead, they're being held about. Can you refine the test case and ensure it works?
  2. me@gmail.com 2012-03-02

       var info = Ti.API.info;
       
       ///=====================
       var socket = undefined;
       var connectedSockets = [];
       
       function connect()
       {
           if(socket === undefined || socket.state === Ti.Network.Socket.CLOSED)
           {
               socket = Titanium.Network.Socket.createTCP({
                   host:Ti.Platform.address,
                   port:8080,
                   listenQueueSize:100,
                   accepted: accepted,
                   closed: function(e)
                   {
                       info("Closed listener");
                   },
                   error: function(e)
                   {
                       Ti.API.error("Socket encountered error when listening");
                       Ti.API.error(" error code '" + e.errorCode + "'");
                       Ti.API.error(" error description '" + e.error + "'");
                   }
               });
               try
               {
                   socket.listen();
                   info("###Listening on " + socket.host + ":" + socket.port);
                   socket.accept(acceptedCallbacks);
               } catch (e)
               {
                   info('Exception: ' + e);
               }
           }
       
       }
       
       function accepted(e)
       {
           var sock = e.inbound;
           info('ACCEPTED: ' + sock.host + ':' + sock.port);
           Ti.Stream.pump(sock, pumpCallback, 1024, true);
           socket.accept(acceptedCallbacks);
       }
       
       
       function pumpCallback(e)
       {
           var sock = e.source;
       
           if(e.bytesProcessed == -1)
           { // EOF
               info("<EOF> - Closing the remote socket!");
       
               removeSocket(sock);
               sock.close();
           }
           else
           {
               if(e.errorDescription === undefined || e.errorDescription === "")
               {
                   info("DATA: " + e.buffer.toString());
                   //--
                   try
                   {
                       var s = "Sup world!";
       
                       var msg = "HTTP/1.1 200 OK\n";
                       msg += "Date: Fri, 31 Dec 1999 23:59:59 GMT\n";
                       msg += "Connection: close\n";
                       msg += "Content-Type: text/html\n";
                       msg += "Content-Length: " + s.length + "\n";
                       msg += "\n";
                       msg += s;
       
                       sock.write(Ti.createBuffer({value: msg}));
                       sock.close();
       
                   } catch (e)
                   {
                       info('Exception: ' + e);
                   }
       
                   //--
               }
               else
               {
                   info("READ ERROR: " + e.errorDescription);
               }
           }
       }
       
       var acceptedCallbacks = {
           error : function(e)
           {
               Ti.API.info(e.error);
               Ti.UI.createAlertDialog({
                   title:"Socket error: " + e.socket.host,
                   message:e.error
               }).show();
           }
       };
       
       
       connect();
       
    just run this code in an iOS app, and paste the IP address found in the console into a browser and refresh the browser a few times. Also, while normally I have found the above code will crash after 2-3 requests, it can at times take about 10-20
  3. Blain Hamon 2012-03-05

    Still can't recreate this and the test JS has a bug: {quote} line = 57; message = "Can't find variable: removeSocket"; {quote} In other words, the removeSocket function referenced isn't in the test suite. Can you recreate the crash using the latest build? It might have already been fixed in the last two weeks since the most recently reported build it was tested.
  4. Blain Hamon 2012-03-05

    Please reopen when reproduced on most recent builds.
  5. me@gmail.com 2012-03-08

    Sorry, that method was no longer used...
       var info = Ti.API.info;
       
       ///=====================
       var socket = undefined;
       var connectedSockets = [];
       
       function connect()
       {
           if(socket === undefined || socket.state === Ti.Network.Socket.CLOSED)
           {
               socket = Titanium.Network.Socket.createTCP({
                   host:Ti.Platform.address,
                   port:8080,
                   listenQueueSize:100,
                   accepted: accepted,
                   closed: function(e)
                   {
                       info("Closed listener");
                   },
                   error: function(e)
                   {
                       Ti.API.error("Socket encountered error when listening");
                       Ti.API.error(" error code '" + e.errorCode + "'");
                       Ti.API.error(" error description '" + e.error + "'");
                   }
               });
               try
               {
                   socket.listen();
                   info("###Listening on " + socket.host + ":" + socket.port);
                   socket.accept(acceptedCallbacks);
               } catch (e)
               {
                   info('Exception: ' + e);
               }
           }
       
       }
       
       function accepted(e)
       {
           var sock = e.inbound;
           info('ACCEPTED: ' + sock.host + ':' + sock.port);
           Ti.Stream.pump(sock, pumpCallback, 1024, true);
           socket.accept(acceptedCallbacks);
       }
       
       
       function pumpCallback(e)
       {
           var sock = e.source;
       
           if(e.bytesProcessed == -1)
           { // EOF
               info("<EOF> - Closing the remote socket!");
       
               sock.close();
           }
           else
           {
               if(e.errorDescription === undefined || e.errorDescription === "")
               {
                   info("DATA: " + e.buffer.toString());
                   //--
                   try
                   {
                       var s = "Sup world!";
       
                       var msg = "HTTP/1.1 200 OK\n";
                       msg += "Date: Fri, 31 Dec 1999 23:59:59 GMT\n";
                       msg += "Connection: close\n";
                       msg += "Content-Type: text/html\n";
                       msg += "Content-Length: " + s.length + "\n";
                       msg += "\n";
                       msg += s;
       
                       sock.write(Ti.createBuffer({value: msg}));
                       sock.close();
       
                   } catch (e)
                   {
                       info('Exception: ' + e);
                   }
       
                   //--
               }
               else
               {
                   info("READ ERROR: " + e.errorDescription);
               }
           }
       }
       
       var acceptedCallbacks = {
           error : function(e)
           {
               Ti.API.info(e.error);
               Ti.UI.createAlertDialog({
                   title:"Socket error: " + e.socket.host,
                   message:e.error
               }).show();
           }
       };
       
       connect();
       
       
    And I just tried with the latest build - Titanium SDK version: 2.0.0 (03/08/12 12:47 4bc31b0) The issue still exists
  6. Stephen Tramer 2012-03-13

    This sample code includes information only about one endpoint of the socket system (the listener). To resolve and test the issue we will also need information about the data being pushed over the wire and a method to do so.
  7. me@gmail.com 2012-03-13

    I am just visiting the ip address / port from a browser... No data is being posted to the app beyond browser headers
  8. Stephen Tramer 2012-03-13

    OK, thanks for the information. That's what I'll use for testing.
  9. Stephen Tramer 2012-03-13

    Note that this bug only appears on non-local (no loopback, no simulator) testing. This appears to be a timing issue related to bad runloop management, which can be resolved by switching sockets to another mechanism (GCD!).
  10. Stephen Tramer 2012-03-16

    TESTING

    ---- This ticket should be tested with the code in the comment above, by doing the following:

    The test *MUST* be run on a device, which is connected to the same LAN (wifi access point) as the machine(s) which will connect to it.

    Using a browser (any) on the testing machine(s), connect to the endpoint the device is listening on (IP address/port are printed in the console log)

    *PASS 1*: A plain HTML page with "Sup world!" on it.

    NOW: Slam the reload button in the browser as hard and fast as you can, repeatedly, for as long as you want.

    *PASS 2*: The page continuously loads, the listening socket continues to listen, and there are no crashes on the device.

    Testing of this ticket should also run the drillbit Socket.TCP test.
  11. Vishal Duggal 2012-03-16

    Resolved by PR 1703
  12. me@gmail.com 2012-03-16

    Thanks guys!
  13. Dustin Hyde 2012-03-19

  14. jithinpv 2013-11-25

    Anvil test case added. PR link: https://github.com/appcelerator/titanium_mobile/pull/5011

JSON Source