diff --git a/lib/browser/client.js b/lib/browser/client.js index 7c656e011..df478e64d 100644 --- a/lib/browser/client.js +++ b/lib/browser/client.js @@ -62,7 +62,6 @@ Client.initOptions = function initOptions(options) { region: 'oss-cn-hangzhou', internal: false, secure: false, - timeout: 60000, // 60s bucket: null, endpoint: null, cname: false, @@ -75,7 +74,9 @@ Client.initOptions = function initOptions(options) { opts.accessKeyId = opts.accessKeyId.trim(); opts.accessKeySecret = opts.accessKeySecret.trim(); - opts.timeout = ms(opts.timeout); + if (opts.timeout) { + opts.timeout = ms(opts.timeout); + } if (opts.endpoint) { opts.endpoint = setEndpoint(opts.endpoint); @@ -307,17 +308,30 @@ proto.createRequest = function createRequest(params) { proto.request = function* request(params) { var reqParams = this.createRequest(params); - var result = yield this.urllib.request(reqParams.url, reqParams.params); - debug('response %s %s, got %s, headers: %j', params.method, reqParams.url, result.status, result.headers); - if (params.successStatuses && params.successStatuses.indexOf(result.status) === -1) { - var err = yield* this.requestError(result); + var result; + var reqErr; + try { + result = yield this.urllib.request(reqParams.url, reqParams.params); + debug('response %s %s, got %s, headers: %j', params.method, reqParams.url, result.status, result.headers); + } catch (err) { + reqErr = err; + } + var err + if (result && params.successStatuses && params.successStatuses.indexOf(result.status) === -1) { + err = yield this.requestError(result); if (err.code === 'RequestTimeTooSkewed') { - this.amendTimeSkewed = +new Date(err.serverTime) - new Date() - return yield* this.request(params); + this.amendTimeSkewed = +new Date(err.serverTime) - new Date() + return yield this.request(params); } err.params = params; + } else if (reqErr) { + err = yield this.requestError(reqErr); + } + + if (err) { throw err; } + if (params.xmlResponse) { result.data = yield this.parseXML(result.data); } @@ -446,24 +460,31 @@ proto.parseXML = function parseXMLThunk(str) { proto.requestError = function* requestError(result) { var err; if (!result.data || !result.data.length) { - // HEAD not exists resource - if (result.status === 404) { - err = new Error('Object not exists'); - err.name = 'NoSuchKeyError'; - err.status = 404; - err.code = 'NoSuchKey'; - } else if (result.status === 412) { - err = new Error('Pre condition failed'); - err.name = 'PreconditionFailedError'; - err.status = 412; - err.code = 'PreconditionFailed'; - } else { - err = new Error('Unknow error, status: ' + result.status); - err.name = 'UnknowError'; + if (result.status === -1 || result.status === -2) { //-1 is net error , -2 is timeout + err = new Error(result.message); + err.name = result.name; err.status = result.status; + err.code = result.name; + } else { + // HEAD not exists resource + if (result.status === 404) { + err = new Error('Object not exists'); + err.name = 'NoSuchKeyError'; + err.status = 404; + err.code = 'NoSuchKey'; + } else if (result.status === 412) { + err = new Error('Pre condition failed'); + err.name = 'PreconditionFailedError'; + err.status = 412; + err.code = 'PreconditionFailed'; + } else { + err = new Error('Unknow error, status: ' + result.status); + err.name = 'UnknowError'; + err.status = result.status; + } + err.requestId = result.headers['x-oss-request-id']; + err.host = ''; } - err.requestId = result.headers['x-oss-request-id']; - err.host = ''; } else { var message = String(result.data); debug('request response error data: %s', message); diff --git a/lib/browser/multipart.js b/lib/browser/multipart.js index e5ed3724f..34a8d1e0b 100644 --- a/lib/browser/multipart.js +++ b/lib/browser/multipart.js @@ -138,8 +138,10 @@ proto._resumeMultipart = function* _resumeMultipart(checkpoint, options) { // check errors after all jobs are completed for (var i = 0; i < results.length; i++) { if (results[i].isError) { - throw new Error( - 'Failed to upload some parts with error: ' + results[i].error.toString()); + var error = results[i].error; + error.partNum = i; + error.message = 'Failed to upload some parts with error: ' + results[i].error.toString() + " part_num: "+ i; + throw error; } } } diff --git a/lib/client.js b/lib/client.js index 679922b6e..9ca4db12a 100644 --- a/lib/client.js +++ b/lib/client.js @@ -305,13 +305,26 @@ proto.createRequest = function createRequest(params) { proto.request = function* request(params) { var reqParams = this.createRequest(params); - var result = yield this.urllib.request(reqParams.url, reqParams.params); - debug('response %s %s, got %s, headers: %j', params.method, reqParams.url, result.status, result.headers); - if (params.successStatuses && params.successStatuses.indexOf(result.status) === -1) { - var err = yield* this.requestError(result); + var result; + var reqErr; + try { + result = yield this.urllib.request(reqParams.url, reqParams.params); + debug('response %s %s, got %s, headers: %j', params.method, reqParams.url, result.status, result.headers); + } catch (err) { + reqErr = err; + } + var err; + if (result && params.successStatuses && params.successStatuses.indexOf(result.status) === -1) { + err = yield this.requestError(result); err.params = params; + } else if(reqErr) { + err = yield this.requestError(reqErr); + } + + if (err) { throw err; } + if (params.xmlResponse) { result.data = yield this.parseXML(result.data); } @@ -440,24 +453,31 @@ proto.parseXML = function parseXMLThunk(str) { proto.requestError = function* requestError(result) { var err; if (!result.data || !result.data.length) { - // HEAD not exists resource - if (result.status === 404) { - err = new Error('Object not exists'); - err.name = 'NoSuchKeyError'; - err.status = 404; - err.code = 'NoSuchKey'; - } else if (result.status === 412) { - err = new Error('Pre condition failed'); - err.name = 'PreconditionFailedError'; - err.status = 412; - err.code = 'PreconditionFailed'; - } else { - err = new Error('Unknow error, status: ' + result.status); - err.name = 'UnknowError'; + if (result.status === -1 || result.status === -2) { //-1 is net error , -2 is timeout + err = new Error(result.message); + err.name = result.name; err.status = result.status; + err.code = result.name; + } else { + // HEAD not exists resource + if (result.status === 404) { + err = new Error('Object not exists'); + err.name = 'NoSuchKeyError'; + err.status = 404; + err.code = 'NoSuchKey'; + } else if (result.status === 412) { + err = new Error('Pre condition failed'); + err.name = 'PreconditionFailedError'; + err.status = 412; + err.code = 'PreconditionFailed'; + }else { + err = new Error('Unknow error, status: ' + result.status); + err.name = 'UnknowError'; + err.status = result.status; + } + err.requestId = result.headers['x-oss-request-id']; + err.host = ''; } - err.requestId = result.headers['x-oss-request-id']; - err.host = ''; } else { var message = String(result.data); debug('request response error data: %s', message); diff --git a/lib/multipart.js b/lib/multipart.js index 927a8c152..91af58681 100644 --- a/lib/multipart.js +++ b/lib/multipart.js @@ -137,8 +137,10 @@ proto._resumeMultipart = function* _resumeMultipart(checkpoint, options) { // check errors after all jobs are completed for (var i = 0; i < results.length; i++) { if (results[i].isError) { - throw new Error( - 'Failed to upload some parts with error: ' + results[i].error.toString()); + var error = results[i].error; + error.partNum = i; + error.message = 'Failed to upload some parts with error: ' + results[i].error.toString() + " part_num: "+ i; + throw error; } } } diff --git a/shims/xhr.js b/shims/xhr.js index f2163ab88..07bc30bfc 100644 --- a/shims/xhr.js +++ b/shims/xhr.js @@ -138,6 +138,10 @@ exports.requestWithCallback = function requestWithCallback(url, args, callback) lookup: args.lookup, }; + if (args.timeout) { + options.timeout = args.timeout; + } + var sslNames = [ 'pfx', 'key', @@ -644,6 +648,18 @@ exports.requestWithCallback = function requestWithCallback(url, args, callback) if (typeof(window) === 'undefined') { // start connect timer just after `request` return, and just in nodejs environment startConnectTimer(); + } else { + req.on('timeout', function () { + if (statusCode === -1) { + statusCode = -2; + } + var msg = 'Connect timeout for ' + connectTimeout + 'ms'; + var errorName = 'ConnectionTimeoutError'; + __err = new Error(msg); + __err.name = errorName; + __err.requestId = reqId; + abortRequest(); + }); } function abortRequest() { @@ -721,7 +737,8 @@ exports.requestWithCallback = function requestWithCallback(url, args, callback) }); req.on('error', function (err) { - if (err.name === 'Error') { + //TypeError for browser fetch api, Error for browser xmlhttprequest api + if (err.name === 'Error' || err.name === 'TypeError') { err.name = connected ? 'ResponseError' : 'RequestError'; } err.message += ' (req "error")'; diff --git a/test/browser.tests.js b/test/browser.tests.js index adb901964..8b3e5b666 100644 --- a/test/browser.tests.js +++ b/test/browser.tests.js @@ -767,7 +767,7 @@ describe('browser', function () { return function (done) { assert.equal(true, res && Object.keys(res).length !== 0); done(); - } + }; } } ); @@ -775,33 +775,35 @@ describe('browser', function () { assert.equal(result.res.status, 200); }); - // it('should upload file using multipart upload with exception', function* () { - // // create a file with 1M random data - // var fileContent = Array(1024*1024).fill('a').join('') - // var file = new File([fileContent], 'multipart-upload-file'); - // - // var name = prefix + 'multipart/upload-file-exception'; - // - // var stubUploadPart = sinon.stub(this.store, '_uploadPart'); - // stubUploadPart.throws("TestUploadPartException"); - // - // - // var error_msg = ""; - // try { - // yield this.store.multipartUpload(name, file, { - // progress: function () { - // return function (done) { - // done(); - // }; - // } - // }); - // } catch (err) { - // error_msg = err.toString(); - // } - // assert.equal(error_msg, - // "Error: Failed to upload some parts with error: TestUploadPartException"); - // this.store._uploadPart().restore(); - // }); + it('should upload file using multipart upload with exception', function* () { + // create a file with 1M random data + var fileContent = Array(1024*1024).fill('a').join('') + var file = new File([fileContent], 'multipart-upload-file'); + + var name = prefix + 'multipart/upload-file-exception'; + + var stubUploadPart = sinon.stub(this.store, '_uploadPart'); + stubUploadPart.throws("TestUploadPartException"); + + var error_msg = ""; + var partNum; + try { + yield this.store.multipartUpload(name, file, { + progress: function () { + return function (done) { + done(); + }; + } + }); + } catch (err) { + error_msg = err.message; + partNum = err.partNum; + } + assert.equal(error_msg, + "Failed to upload some parts with error: TestUploadPartException part_num: 0"); + assert.equal(partNum, 0); + this.store._uploadPart.restore(); + }); }); }); @@ -835,4 +837,56 @@ describe('browser', function () { timemachine.reset(); }) }); + + describe('request err', function() { + before(function* () { + var ossConfig = { + region: stsConfig.region, + accessKeyId: stsConfig.Credentials.AccessKeyId, + accessKeySecret: stsConfig.Credentials.AccessKeySecret, + stsToken: stsConfig.Credentials.SecurityToken, + bucket: stsConfig.bucket, + timeout: 1 + }; + this.store = oss(ossConfig); + }); + it('request timeout exception', function* () { + var fileContent = Array(1024*1024).fill('a').join('') + var file = new File([fileContent], 'multipart-upload-file'); + + var name = prefix + 'multipart/upload-file-timeout'; + + var timeout_err = ""; + try { + yield this.store.multipartUpload(name, file); + } catch (err) { + timeout_err = err; + } + assert.equal(true, timeout_err && Object.keys(timeout_err).length !== 0); + assert.equal(timeout_err.status, -2); + }); + + it('request net exception', function* () { + var fileContent = Array(1024*1024).fill('a').join('') + var file = new File([fileContent], 'multipart-upload-file'); + + var name = prefix + 'multipart/upload-file-timeout'; + var stubNetError = sinon.stub(this.store.urllib, 'request'); + var netErr = new Error('TestNetErrorException'); + netErr.status = -1; + netErr.code = 'RequestError'; + netErr.name = 'RequestError'; + stubNetError.throws(netErr); + var net_err = ""; + try { + yield this.store.multipartUpload(name, file); + } catch (err) { + net_err = err; + } + assert.equal(true, net_err && Object.keys(net_err).length !== 0); + assert.equal(net_err.status, -1); + + this.store.urllib.request.restore(); + }); + }); }); diff --git a/test/multipart.test.js b/test/multipart.test.js index f3914ca13..fe055518d 100644 --- a/test/multipart.test.js +++ b/test/multipart.test.js @@ -235,7 +235,6 @@ describe('test/multipart.test.js', function () { var fileName = yield utils.createTempFile('multipart-upload-file', 1024 * 1024); var name = prefix + 'multipart/upload-file-exception'; - var progress = 0; var clientTmp = oss(config); clientTmp.useBucket(this.bucket, this.region); @@ -244,13 +243,17 @@ describe('test/multipart.test.js', function () { var error_msg = ""; + var partNum; try { yield clientTmp.multipartUpload(name, fileName); } catch (err) { - error_msg = err.toString(); + error_msg = err.message; + partNum = err.partNum; } assert.equal(error_msg, - "Error: Failed to upload some parts with error: TestUploadPartException"); + "Failed to upload some parts with error: TestUploadPartException part_num: 0"); + assert.equal(partNum, 0); + clientTmp._uploadPart.restore(); }); it('should upload web file using multipart upload', function* () { @@ -415,4 +418,54 @@ describe('test/multipart.test.js', function () { }); }); + + describe('request error', function() { + + it('request timeout exception', function* () { + var fileName = yield utils.createTempFile('multipart-upload-file', 1024 * 1024);// 1m + var name = prefix + 'multipart/upload-file'; + + var stubNetError = sinon.stub(this.store.urllib, 'request'); + var netErr = new Error('TestTimeoutErrorException'); + netErr.status = -2; + netErr.code = 'ConnectionTimeoutError'; + netErr.name = 'ConnectionTimeoutError'; + stubNetError.throws(netErr); + var timeout_err; + try { + yield this.store.multipartUpload(name, fileName); + } catch (err) { + timeout_err = err; + } + + assert.equal(true, timeout_err && Object.keys(timeout_err).length !== 0); + assert.equal(timeout_err.status, -2); + this.store.urllib.request.restore(); + }); + + it('request net exception', function* () { + var fileName = yield utils.createTempFile('multipart-upload-file', 1024 * 1024);// 1m + var name = prefix + 'multipart/upload-file'; + + var stubNetError = sinon.stub(this.store.urllib, 'request'); + var netErr = new Error('TestNetErrorException'); + netErr.status = -1; + netErr.code = 'RequestError'; + netErr.name = 'RequestError'; + stubNetError.throws(netErr); + + var net_err; + try { + yield this.store.multipartUpload(name, fileName); + } catch (err) { + net_err = err; + } + + assert.equal(true, net_err && Object.keys(net_err).length !== 0); + assert.equal(net_err.status, -1); + + this.store.urllib.request.restore(); + }); + }); + });