{ "id": "159346", "key": "TIMOB-23265", "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": "11", "description": "Is not a bug in our product", "name": "Not Our Bug" }, "resolutiondate": "2016-04-22T07:59:40.000+0000", "created": "2016-04-06T15:36:34.000+0000", "priority": { "name": "Critical", "id": "1" }, "labels": [], "versions": [ { "id": "16997", "name": "Release 5.2.0", "archived": false, "released": true, "releaseDate": "2016-02-23" } ], "issuelinks": [], "assignee": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "updated": "2017-03-24T18:54:01.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [], "description": "h6. Issue Description\r\n\r\nMark files as private (s3_acl:private) in the appcelerator cloud storage is returning 403:forbidden response from AWS in Android when user try to download the file. \r\n\r\nh6. Steps to replicate\r\n1. App queries cloud to get a list of files \r\n2. User selects a file from generated list \r\n3. App queries cloud to get temporary URL for selected file \r\n4. returned url is used in httpclient to download the file (A pdf in this case) \r\n5. file is displayed \r\n\r\nOn iOS this works fine, however on Android is returning a 403:forbidden response from AWS when I try to download the pdf. Before setting the files to private, this solution worked fine and I was able to download and display the files on both OS's \r\n\r\nh6. Additional notes\r\nAll of the calls to the cloud are done through an ArrowDB app hosted on node.js and I've tried using the sessionCookie from that login, but with no luck - though I'd assume that once the URL has been retrieved it is temporarily available to anyone and doesn't require further authentication - can't find much in the way of documentation around the differences of setting s3_acl to private. ", "attachment": [], "flagged": false, "summary": "Cannot access Appcelerator AWS storage from Android httpclient request", "creator": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "environment": null, "closedSprints": [ { "id": 618, "state": "closed", "name": "2016 Sprint 08 SDK", "startDate": "2016-04-09T00:30:18.262Z", "endDate": "2016-04-23T00:30:00.000Z", "completeDate": "2016-04-25T02:38:28.511Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "382011", "author": { "name": "cng", "key": "cng", "displayName": "Chee Kiat Ng", "active": false, "timeZone": "America/Los_Angeles" }, "body": "So a curl command works perfectly fine? Then this ticket should be in the TIMOB project if it's specific to httpclient request.\r\nAnd i think we are going to need more description, as this seems like a specific use case.\r\n\r\ncan you give sample code? including the titanium app code you to request, and how arrowdb is setup on the backend?", "updateAuthor": { "name": "cng", "key": "cng", "displayName": "Chee Kiat Ng", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2016-04-07T01:35:39.000+0000", "updated": "2016-04-07T01:35:39.000+0000" }, { "id": "382017", "author": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Snippet of code showing how the url is retrieved and the httpclient request: \r\n{code}\r\nAlloy.Globals.Arrow.services_getUrl({ \r\n_id:id, \r\n_session:Alloy.Globals.defaultSession \r\n},function(error,args){ \r\nif(error){ \r\nconsole.log(error); \r\ncallback(\"failed\"); \r\n}else{ \r\nvar url = args.message; \r\nvar xhr = Ti.Network.createHTTPClient({ \r\nonload: function() { \r\ncallback(this.responseData); \r\n}, \r\ncache:false, \r\nonerror: function(e){ \r\ntry{ \r\nconsole.log(e); \r\n}catch(e){ \r\nconsole.log(\"exception thrown\"); \r\n//403 error seems to break android sometimes... \r\n} \r\ncallback(\"failed\"); \r\n}, \r\ntimeout:10000 /* in milliseconds */ \r\n}); \r\nxhr.open(\"GET\", url); \r\nxhr.send(); \r\n} \r\n}); \r\n{code}\r\nThe code on the service which is handling the URL request is: \r\n{code}\r\narrowLocal.filesShow({ \r\nsession_id:req.query._session, \r\nfile_id:req.query._id \r\n},function(error,result){ \r\nif(!error){ \r\nres.send({\"status\":\"success\",\"message\":result.body.response.files[0].url}); \r\n}else{ \r\nres.send({\"status\":\"failure\",\"message\":\"Connection error, please try again\"}); \r\n} \r\n}); \r\n{code}", "updateAuthor": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-04-07T01:50:03.000+0000", "updated": "2016-04-07T01:50:03.000+0000" }, { "id": "382605", "author": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~msamah] Here more details about this issue:\r\n\r\nThe app uses a front end for submitting files which I'm currently working on. The user submits a file and names it, then sends a post request to the service: \r\n{code}\r\narrowLocal.filesCreate({ \r\nsession_id:req.body.sessionToken, \r\nname:req.body.fileName, \r\nfile:fs.createReadStream(req.files.fileUploaded.path), \r\ncustom_fields:{ \r\ntype:req.body.type, \r\nsize:req.files.fileUploaded.size \r\n}, \r\ns3_acl:'private', \r\nacl_name:\"MaternityAppControl\" \r\n},function(error,args){ \r\nif(error){ \r\nres.writeHead(405, error.body.meta.message, {'Content-Type': 'text/html'}); \r\nres.end('405 - '+error.body.meta.message+'

'+error.body.meta.message+'

'); \r\n}else{ \r\nres.writeHead(200, \"OK\", {'Content-Type': 'text/html'}); \r\nres.end('File Submitted

Success - File Submitted

'); \r\n} \r\n}); \r\n{code}\r\n\r\nThe app contains a table which populates based on the results of a query to the files database: \r\n\r\n{code}\r\narrowLocal.filesQuery({ \r\nsession_id:req.query._session, \r\nlimit:1000, \r\n},function(error,result){ \r\nif(!error){ \r\nres.send({\"status\":\"success\",\"message\":result.body.response.files}); \r\n}else{ \r\nres.send({\"status\":\"failure\",\"message\":\"Connection error, please try again\"}); \r\n} \r\n}); \r\n{code}\r\n\r\nIf the user clicks on a file in the app, another request will be sent to the service to get the up-to-date URL: \r\n\r\n{code}\r\narrowLocal.filesShow({ \r\nsession_id:req.query._session, \r\nfile_id:req.query._id \r\n},function(error,result){ \r\nif(!error){ \r\nres.send({\"status\":\"success\",\"message\":result.body.response.files[0].url}); \r\n}else{ \r\nres.send({\"status\":\"failure\",\"message\":\"Connection error, please try again\"}); \r\n} \r\n}); \r\n{code}\r\n\r\nEverything up to this point works fine. The URL is returned, it can be opened in a browser and displays the correct file. \r\n\r\nThe next step is where Android falls over. The app attemps to use the URL in a HTTPClient in order to download and handle the file within the app: \r\n\r\n{code}\r\nvar xhr = Ti.Network.createHTTPClient({ \r\nonload: function() { \r\ncallback({status:\"success\",message:this.responseData}); \r\n}, \r\ncache:false, \r\nonerror: function(e){ \r\ntry{ \r\nconsole.log(e); \r\n}catch(e){ \r\nconsole.log(JSON.stringify(xhr.getAllResponseHeaders())); \r\nconsole.log(\"exception thrown\"); \r\n//403 error seems to break android sometimes... \r\n} \r\ncallback({status:\"failed\"}); \r\n}, \r\ntimeout:10000 /* in milliseconds */ \r\n}); \r\nxhr.open(\"GET\", url); \r\nxhr.send(); \r\n{code}\r\n\r\nThe responseData here should be the file, and on iOS this works fine. I can store the file to a temp directory and open it with the document viewer. However, on Android the http request fails with a \"403-forbidden\" response from amazon aws. \r\n\r\n\r\nWhen I was uploading files without the s3-acl property set to private, Android was able to access the files and open them through intents. With this property set the connection is rejected from Android devices. \r\n\r\nFor now I've implemented a workaround on Android to just open the file in the browser (Which triggers a download) but this has the drawback of not immediately opening the file, so isn't ideal. ", "updateAuthor": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-04-13T16:07:28.000+0000", "updated": "2016-04-13T16:07:28.000+0000" }, { "id": "383648", "author": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "body": "Here's how to overcome this issue.\r\n1) Please set autoEncodeUrl to false in the HTTPClient (in Android)\r\n2) Set the requestHeader as such, setRequestHeader(\"Content-Type\", \"\"); This has to be done between the xhr.open() and xhr.send().\r\n\r\nExample of the code to download the file:-\r\n{code}\r\nvar xhr = Ti.Network.createHTTPClient({ \r\nautoEncodeUrl: false,\r\nonload: function() { \r\nconsole.log(\"success\" + this.responseData); \r\n}, \r\ncache:false, \r\nonerror: function(e){ \r\ntry{ \r\nconsole.log(e); \r\n}catch(e){ \r\nconsole.log(JSON.stringify(xhr.getAllResponseHeaders())); \r\nconsole.log(\"exception thrown\"); \r\n//403 error seems to break android sometimes... \r\n} \r\nconsole.log(\"failed:\"+e); \r\n}, \r\ntimeout:10000 /* in milliseconds */ \r\n}); \r\n\r\n\r\nxhr.open(\"GET\", url); \r\nxhr.setRequestHeader(\"Content-Type\", \"\");\r\nxhr.send();\r\n});\r\n{code}\r\n\r\nThe content-type of the headers seem to be causing an issue with AWS. Clearing it out makes it work. Also ensuring that the passed Url is not encoded helps too.", "updateAuthor": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "created": "2016-04-22T07:57:59.000+0000", "updated": "2016-04-22T08:01:39.000+0000" }, { "id": "383649", "author": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "body": "[~rramirez] Resolving this ticket. Please see above for solution.", "updateAuthor": { "name": "msamah", "key": "msamah", "displayName": "Ashraf Abu", "active": false, "timeZone": "Asia/Singapore" }, "created": "2016-04-22T08:00:44.000+0000", "updated": "2016-04-22T08:00:44.000+0000" }, { "id": "415705", "author": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Closing ticket with reference to the previous comments.", "updateAuthor": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-03-24T18:54:01.000+0000", "updated": "2017-03-24T18:54:01.000+0000" } ], "maxResults": 35, "total": 35, "startAt": 0 } } }