{ "id": "169741", "key": "TIMOB-25260", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [], "resolution": { "id": "5", "description": "All attempts at reproducing this issue failed, or not enough information was available to reproduce the issue. Reading the code produces no clues as to why this behavior would occur. If more information appears later, please reopen the issue.", "name": "Cannot Reproduce" }, "resolutiondate": "2017-09-12T12:55:45.000+0000", "created": "2017-09-10T07:53:15.000+0000", "priority": { "name": "Critical", "id": "1" }, "labels": [ "httpclient" ], "versions": [], "issuelinks": [], "assignee": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "updated": "2018-10-04T08:54:02.000+0000", "status": { "description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.", "name": "Resolved", "id": "5", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "description": "When making a POST request with httpClient and the resource returns a 401 error, a second POST request is being made automatically. Similar issues have previously been reported (and resolved) with Android, but not iOS.\r\n\r\n{code:js}\r\nvar httpClient = Ti.Network.createHTTPClient({\t\r\n onload: function(e) {\r\n // This function is not triggered with the 401 response code\r\n },\t\t\r\n onerror: function(e) {\t\t\r\n // This function is triggered with the 401 response code, but a second POST is also made\r\n },\r\n onreadystatechange: function(e) {\r\n Ti.API.warn(this.readyState);\r\n // 1\r\n // 3\r\n // 3\r\n // 4\r\n // 4\r\n },\r\n timeout: 2000\t\t\r\n});\r\n\r\nhttpClient.open('POST', 'https://httpstat.us/401');\r\n{code}", "attachment": [], "flagged": false, "summary": "iOS: HTTPclient sends additional post request on receipt of 401 error", "creator": { "name": "Ian.taylor@fraigneux.com", "key": "ian.taylor@fraigneux.com", "displayName": "Ian Taylor", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "Ian.taylor@fraigneux.com", "key": "ian.taylor@fraigneux.com", "displayName": "Ian Taylor", "active": true, "timeZone": "America/Los_Angeles" }, "environment": "\r\n", "closedSprints": [ { "id": 126, "state": "closed", "name": "2018 Sprint 05 SDK", "startDate": "2018-02-25T19:38:08.926Z", "endDate": "2018-03-11T18:38:00.000Z", "completeDate": "2018-03-11T22:06:01.520Z", "originBoardId": 100 } ], "comment": { "comments": [ { "id": "427738", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Hey there! I will move the ticket to TIMOB for further investigation, but we need some more infos from you:\r\n- Titanium SDK version?\r\n- Example endpoint?\r\n\r\nThank you!", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-09-10T11:20:13.000+0000", "updated": "2017-09-10T11:20:13.000+0000" }, { "id": "427739", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Ok, so after a quick investigation, here are the results:\r\n- The {{readyState}} = 4 is {{DONE}}\r\n- The request is not sent twice, because:\r\n - The {{readyState}} 3 ({{LOADING}}) is skipped in the event, instead {{DONE}} is sent\r\n - This does not happen when I log the values before they getting sent to the client, which leads to a race-condition while sending the ready-states in situations where the request finished immediately after it returns the \"loading\" state.\r\n- We should still fix this, but the issue would be a different then the one we investigated\r\n\r\nI would rename the ticket and description to the appropriate issue and schedule it for the next releases, but before I would be interested in your feedback to ensure we do not miss something from the initial ticket description. Thank you! \r\n\r\n*EDIT 1*: Workaround: Use {{e.readyState}} instead of {{this.readyState}}!\r\n*EDIT 2*: Proposed fix: Use {{dispatch_barrier_async}} within the APSHTTPClient library to ensure the {{readyState}} property is set before the events are triggered.", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-09-10T11:37:44.000+0000", "updated": "2017-09-10T11:48:09.000+0000" }, { "id": "427743", "author": { "name": "Ian.taylor@fraigneux.com", "key": "ian.taylor@fraigneux.com", "displayName": "Ian Taylor", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Thanks for the reply. I'm on the road right now so can't give you many more details right now. To elaborate though:\n\nI included the onreadystatechange only because I used it for debugging. I don't use it in production. The endpoint I'm using is a login for a web service. When I get a 401 response the onerror callback handles this. However, I can see from the web service logs that an additional request is made. \n\nDoes that help?", "updateAuthor": { "name": "Ian.taylor@fraigneux.com", "key": "ian.taylor@fraigneux.com", "displayName": "Ian Taylor", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-09-10T12:54:58.000+0000", "updated": "2017-09-10T12:54:58.000+0000" }, { "id": "427744", "author": { "name": "Ian.taylor@fraigneux.com", "key": "ian.taylor@fraigneux.com", "displayName": "Ian Taylor", "active": true, "timeZone": "America/Los_Angeles" }, "body": "In the onload and onerror callbacks I use this.responseText. I will try using e.responseText to see if that is a workaround. ", "updateAuthor": { "name": "Ian.taylor@fraigneux.com", "key": "ian.taylor@fraigneux.com", "displayName": "Ian Taylor", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-09-10T12:57:54.000+0000", "updated": "2017-09-10T12:57:54.000+0000" }, { "id": "427793", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "I have tested this will multiple servers and even have set-up a local node server that received a POST request and logged it to a monitoring-console every time a new POST request was seen. That's why I came to the conclusion that either your app-logic will trigger it twice without noticing (for example with global Ti.App events not being handled correctly or recursive code in special if-statements), or your server is working not correctly and gives multiple responses. Both actually happened to me in my 7 years of Titanium / web-interaction as well, so this might be a direction to check.\r\n\r\nIn any case, I will have to resolve this ticket as {{Cannot Reproduce}} unless we receive a full test-case that includes both the client code and and a server-URL that we can trigger.", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-09-12T12:55:32.000+0000", "updated": "2017-09-12T12:55:32.000+0000" }, { "id": "427812", "author": { "name": "Ian.taylor@fraigneux.com", "key": "ian.taylor@fraigneux.com", "displayName": "Ian Taylor", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Here is the client code. I can send you the endpoint via email.\r\n\r\nvar win = Ti.UI.createWindow({backgroundColor: \"#fff\"});\r\nvar pw = Ti.UI.createTextField({top:100,width:100,value:'adminman'});\r\nvar un = Ti.UI.createTextField({top:200,width:100,value:'adminman'});\r\nvar btn = Ti.UI.createButton({top:300,width:100,backgroundColor:'#26d045'});\r\n\r\nbtn.addEventListener('click', function(e) {\r\n\tif (Titanium.Network.online) {\t\t\r\n\t\tvar client = Ti.Network.createHTTPClient({\r\n\t\t\tonload:function(event) {\t\t\t\t\t\r\n\t\t\t\tTi.API.info('onload '+this.responseText+ ' code: '+this.status);\t\t\t\r\n\t\t\t},\t\t\r\n\t\t\tonerror:function(event) {\t\t\t\t\r\n\t\t\t\tTi.API.info('onerror '+this.responseText+ ' code: '+this.status);\r\n\t\t\t},\r\n\t\t\ttimeout:2000\t\t\r\n\t\t});\t\t\t\r\n\t\tclient.open('POST','endpoint');\r\n\t\tclient.setRequestHeader('Content-Type','application/json');\t\r\n\t\tclient.send(JSON.stringify({password:pw.value,username:un.value}));\r\n\t}\r\n});\r\n\r\nwin.add(btn);\r\nwin.add(pw);\r\nwin.add(un);\r\nwin.open();", "updateAuthor": { "name": "Ian.taylor@fraigneux.com", "key": "ian.taylor@fraigneux.com", "displayName": "Ian Taylor", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-09-12T17:01:04.000+0000", "updated": "2017-09-12T17:01:04.000+0000" }, { "id": "427813", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Thanks for sharing. I updated it with a test URL and formatted the source:\r\n{code:js}\r\nvar win = Ti.UI.createWindow({\r\n backgroundColor: \"#fff\"\r\n});\r\nvar pw = Ti.UI.createTextField({\r\n top: 100,\r\n width: 100,\r\n value: 'adminman'\r\n});\r\nvar un = Ti.UI.createTextField({\r\n top: 200,\r\n width: 100,\r\n value: 'adminman'\r\n});\r\nvar btn = Ti.UI.createButton({\r\n top: 300,\r\n width: 100,\r\n backgroundColor: '#26d045'\r\n});\r\nbtn.addEventListener('click', function(e) {\r\n if (Titanium.Network.online) {\r\n var client = Ti.Network.createHTTPClient({\r\n onload: function(event) {\r\n Ti.API.info('onload ' + this.responseText + ' code: ' + this.status);\r\n },\r\n onerror: function(event) {\r\n Ti.API.info('onerror ' + this.responseText + ' code: ' + this.status);\r\n },\r\n timeout: 2000\r\n });\r\n client.open('POST', 'https://httpstat.us/401');\r\n client.setRequestHeader('Content-Type', 'application/json');\r\n client.send(JSON.stringify({\r\n password: pw.value,\r\n username: un.value\r\n }));\r\n }\r\n});\r\nwin.add(btn);\r\nwin.add(pw);\r\nwin.add(un);\r\nwin.open();\r\n{code}\r\nWhich still fires the events properly and the server is only triggered once.", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-09-12T17:28:01.000+0000", "updated": "2017-09-12T17:28:01.000+0000" }, { "id": "441388", "author": { "name": "tcornett", "key": "tcornett", "displayName": "Tommy Cornett", "active": true, "timeZone": "America/New_York" }, "body": "I am having this exact same issue in 7.1.1.GA and have narrowed down the scenario.\r\n\r\nWhen the server sends a 401 Unauthorized response, AND that response contains a \"WWW-Authenticate\" header, Appcelerator will send the request again. If the same 401 response comes back the second time, the error \"cancelled\" is returned.\r\n\r\nWhen removing the \"WWW-Authenticate\" header from the server response in my test app, Appcelerator behaved as expected and did not repeat the request. However, in our production instance, we are unable to remove the header from the response, so this is causing us quite a bit of grief.\r\n\r\nI have a sample Appc app, and also a small service (written in asp.net/C#) that readily produces this issue. Please reach out to me if you would like me to send this to you.\r\n\r\nI hope this allows you to reproduce and resolve this issue, and look forward to a fix.\r\n\r\nThanks!\r\n::Tommy", "updateAuthor": { "name": "tcornett", "key": "tcornett", "displayName": "Tommy Cornett", "active": true, "timeZone": "America/New_York" }, "created": "2018-09-06T18:28:55.000+0000", "updated": "2018-09-06T18:28:55.000+0000" }, { "id": "441411", "author": { "name": "tcornett", "key": "tcornett", "displayName": "Tommy Cornett", "active": true, "timeZone": "America/New_York" }, "body": "This also happens for both POST and GET... I haven't tried PUT or DELETE.", "updateAuthor": { "name": "tcornett", "key": "tcornett", "displayName": "Tommy Cornett", "active": true, "timeZone": "America/New_York" }, "created": "2018-09-07T13:44:37.000+0000", "updated": "2018-09-07T13:44:37.000+0000" }, { "id": "442277", "author": { "name": "dbankier", "key": "dbankier", "displayName": "David Bankier", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Also getting the `cancelled` response.", "updateAuthor": { "name": "dbankier", "key": "dbankier", "displayName": "David Bankier", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2018-10-04T08:54:02.000+0000", "updated": "2018-10-04T08:54:02.000+0000" } ], "maxResults": 10, "total": 10, "startAt": 0 } } }