Uploaded image for project: 'Titanium SDK/CLI'
  1. Titanium SDK/CLI
  2. TIMOB-18679

iOS: HttpClient timeout on POST request to a HTTPS server

    Details

    • Type: Bug
    • Status: Closed
    • Priority: High
    • Resolution: Not Our Bug
    • Affects Version/s: Release 3.5.0
    • Fix Version/s: None
    • Component/s: iOS
    • Environment:

      Mac OS X 10.10.1
      Titanium CLI 3.4.0
      Titanium SDK 3.2.3 and 3.5.0
      iOS sdk 7.1 and 8.1

    • Story Points:
      8
    • Sprint:
      2015 Sprint 11 SDK

      Description

      I'm working on a Titanium Classic Mobile project that needs to get data from SOAP services that are hosted in a https server. I'm using this suds2 library as a helper to construct the xml request.

      The project was working fine with sdk 3.2.3 but I need to update it because of this Apple restriction so I basically need to compile with sdk 3.5.0.

      After sdk 3.3.0 the httpClient responds with a timeout error, so it looks like the request never leaves the client. This problem was pointed in this issue before, I added a comment there too. I'm aware of Vishal Duggal comment in that post, mentioning all the stuff that changed in the httpClient, and I think that was the reason that ticket was closed, but I'm not sending a GET request so that comment does not apply in this case.

      In order to reproduce this problem I will recommend to use sdk 3.2.3 to show that the code was working fine and then switch to 3.5.0 to show the issue.

      Follow these steps to reproduce the problem:

      1. Create a new Titanium Classic project targeting iOS with 3.2.3 sdk.
      2. Create a new file in Resources directory and name it suds2.js and paste the following code taken from the source mentioned above:

      /Resources/suds2.js

      /*jslint maxerr:1000 */
      /**
      * *
      * Sud2 is forked from Kevin Whinnery's suds.js project
      * Updates to Suds2 can be found at https://github.com/benbahrenburg/Suds2
      * 
      * Suds: A Lightweight JavaScript SOAP Client
      * Copyright: 2009 Kevin Whinnery (http://www.kevinwhinnery.com)
      * License: http://www.apache.org/licenses/LICENSE-2.0.html
      * Source: http://github.com/kwhinnery/Suds
      */
      var SudsClient = function(_options) {
        
        //A generic extend function - thanks MooTools
        function extend(original, extended) {
          for (var key in (extended || {})) {
            if (original.hasOwnProperty(key)) {
              original[key] = extended[key];
            }
          }
          return original;
        }
       
        function endWith(str,suffix){
      		str = str +'';
      		var lastIndex = str.lastIndexOf(suffix);
          	return (lastIndex != -1) && (lastIndex + suffix.length == str.length);
        };
          
        //Check if an object is an array
        function isArray(obj) {
          return Object.prototype.toString.call(obj) == '[object Array]';
        }
        
        //Grab an XMLHTTPRequest Object
        function getXHR() {
           return Titanium.Network.createHTTPClient({
              validatesSecureCertificate : false,
              timeout: 5000
           });
        }
        
        //Parse a string and create an XML DOM object
        function xmlDomFromString(_xml) {
          var xmlDoc = Titanium.XML.parseString(_xml);
          return xmlDoc;
        };
        
        // Convert a JavaScript object to an XML string - takes either an
        function convertToXml(_obj, namespacePrefix) {
          var xml = '';
          if (isArray(_obj)) {
            for (var i = 0; i < _obj.length; i++) {
              xml += convertToXml(_obj[i], namespacePrefix);
            }
          } else {
            //For now assuming we either have an array or an object graph
            for (var key in _obj) {
              if (namespacePrefix && namespacePrefix.length) {
                xml += '<' + namespacePrefix + ':' + key + '>';
              } else {
                xml += '<'+key+'>';
              }
              if (isArray(_obj[key]) || (typeof _obj[key] == 'object' && _obj[key] != null)) {
                xml += convertToXml(_obj[key]);
              }
              else {
                xml += _obj[key];
              }
              if (namespacePrefix && namespacePrefix.length) {
                xml += '</' + namespacePrefix + ':' + key + '>';
              } else {
                xml += '</'+key+'>';
              }
            }
          }
          return xml;
        }
        
        // Client Configuration
        var config = extend({
          endpoint:'http://localhost',
          targetNamespace: 'http://localhost',
          envelopeBegin: '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:ns0="PLACEHOLDER" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>',
          envelopeEnd: '</soap:Body></soap:Envelope>',
          headerBegin: '<soap:Header>',
          headerNode:'head',
          headerEnd: '</soap:Header>',
          timeout : 5000,
          includeNS : true,
          addTargetSchema : false,
          ns:'ns0'
        },_options);
        
        function wrapNS(nsOnly){
        	if(config.includeNS){
        		return ((nsOnly) ? config.ns :  (config.ns + ':'));
        	}else{
        		return '';
        	}
        };
        
        function addTargetSchema(){
         	if(config.addTargetSchema){
         		var temp = config.targetNamespace;
         		if(endWith(config.targetNamespace,'/')){
         			temp = temp.substr(0,(temp.length -1)); 
         		}
         		return ' xmlns="' + temp + '"';  		
      	}else{
      		return '';
      	}
        };
        
        // Invoke a web service
        this.invoke = function(_soapAction,_body,_callback,_header,_callbackError) {   
       
          //Build request body 
          var body = _body;
          var header = _header;
          
          //Allow straight string input for XML body - if not, build from object
       
          if (typeof body !== 'string') {
            body = '<' + wrapNS(false) +_soapAction + addTargetSchema() + '>';
            body += convertToXml(_body, wrapNS(true));
            body += '</' + wrapNS(false) +_soapAction+'>';
          }
              
          var ebegin = config.envelopeBegin;
          config.envelopeBegin = ebegin.replace('PLACEHOLDER', config.targetNamespace);
          
          //Build Soapaction header - if no trailing slash in namespace, need to splice one in for soap action
          var soapAction = '';
          if (config.targetNamespace.lastIndexOf('/') != config.targetNamespace.length - 1) {
            soapAction = config.targetNamespace+'/'+_soapAction;
          }
          else {
            soapAction = config.targetNamespace+_soapAction;
          }
          
       
          //POST XML document to service endpoint
          var xhr = getXHR();
          xhr.onload = function() {
            _callback.call(this, this.responseText);
          };
       
          xhr.onerror = function(e) {
            //_callbackError.call(this, e);
            Ti.API.info("error" + JSON.stringify(e));
          };
       
          //xhr.setTimeout(config.timeout);
       
          var sendXML = '';
          if(!header) {
              sendXML = config.envelopeBegin+config.bodyBegin+body+config.envelopeEnd;
          } else {
              //Allow straight string input for XML body - if not, build from object
              if (typeof header !== 'string') {
                header = '<'+_soapAction+' xmlns="'+config.targetNamespace+'">';
                header += convertToXml(_header);
                header += '</'+_soapAction+'>';
              }
       
              sendXML = config.envelopeBegin+config.headerBegin+header+config.headerEnd+config.bodyBegin+body+config.envelopeEnd;
          }   
       
       
       
          xhr.open('POST', config.endpoint);
       
      		xhr.setRequestHeader('Content-Type', 'text/xml');
       
      		xhr.setRequestHeader('SOAPAction', soapAction);
      	  if (config.authorization !== undefined) {
      		  xhr.setRequestHeader('Authorization', 'Basic ' + config.authorization);
      		}		
       
          
          Ti.API.info("endpoit " + config.endpoint);
       
       
       
      		xhr.send(config.envelopeBegin+body+config.envelopeEnd); 
       
        };
      };
      module.exports = SudsClient;
      

      3. Add this code to the app.js file:

      /Resources/app.js

      var win = Ti.UI.createWindow({
      	backgroundColor: 'white'
      });
       
      var label = Ti.UI.createLabel({
      });
       
      var config = {
      	endpoint:"https://cajanet.com:9443/CajaAhorro/services/WebServiceImpl?wsdl",
          targetNamespace: 'http://services.data.cajaAhorro.isi.com.mx',	
         	includeNS : false,
          addTargetSchema : true 
      };
       
      var sudsModule = require('suds2');
       
      var sudsClient = new sudsModule(config);
      sudsClient.invoke('logOff', {strClaveUsu: 19844},
      	function(xmlDoc) {
          	
          	Ti.API.info("Response " + xmlDoc);
          	label.text = xmlDoc;
       
          	return;
      	}
      );
       
      win.add(label);
      win.open();
      

      5. Run the project.

      You should be able to see the response XML in the label added to the window showing that the code is working.

      6. To reproduce the timeout error just change the sdk to a newer one, let's say 3.5.0 and run the project again.

      You will see this error on the console:

      {"type":"error","source":{"url":"https://cajanet.com:9443/CajaAhorro/services/WebServiceImpl?wsdl","method":"POST","timeout":5000,"validatesSecureCertificate":false},"code":-1001,"error":"The request timed out.","success":false}
      

      I created a question about this problem in the forum too because I finished all my resources looking for an answer.

      I attached a project with all the resources described above.

      Please consider to take a look at this, I know that SOAP and XML sucks but it's not my fault that my client's infrastructure is built on this.

      Thank you for your time.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                penrique Pedro Enrique (Inactive)
                Reporter:
                luiscript Luis F. Garcia
              • Watchers:
                0 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:

                  Backbone Issue Sync

                  • Titanium SDK/CLI <> Titanium Mobile
                    Synced with:
                    TIMOB-19783
                    Sync status:
                    ERROR
                    Last received:
                    Last sent:

                    Git Source Code