docs: function _submitRequest(params, callback) {
      var self = this;
      var TaskId = params.TaskId;
      if (TaskId && !self._isRunningTask(TaskId)) return;
  
      var bucket = params.Bucket;
      var region = params.Region;
      var object = params.Key;
      var method = params.method || 'GET';
      var url = params.url;
      var body = params.body;
      var json = params.json;
      var rawBody = params.rawBody;
  
      // 处理 readStream and body
      var readStream;
      if (body && typeof body.pipe === 'function') {
          readStream = body;
          body = null;
      }
  
      // url
      url = url || getUrl({
          ForcePathStyle: self.options.ForcePathStyle,
          protocol: self.options.Protocol,
          domain: self.options.Domain,
          bucket: bucket,
          region: region,
          object: object,
      });
      if (params.action) {
          url = url + '?' + params.action;
      }
  
      var opt = {
          method: method,
          url: url,
          headers: params.headers,
          qs: params.qs,
          body: body,
          json: json,
      };
  
      // 获取签名
      opt.headers.Authorization = params.AuthData.Authorization;
      params.AuthData.Token && (opt.headers['token'] = params.AuthData.Token);
      params.AuthData.ClientIP && (opt.headers['clientIP'] = params.AuthData.ClientIP);
      params.AuthData.ClientUA && (opt.headers['clientUA'] = params.AuthData.ClientUA);
      params.AuthData.XCosSecurityToken && (opt.headers['x-cos-security-token'] = params.AuthData.XCosSecurityToken);
  
      // 清理 undefined 和 null 字段
      opt.headers && (opt.headers = util.clearKey(opt.headers));
      opt = util.clearKey(opt);
  
      var Ip = this.options.Ip;
      if (Ip) {
          opt.url = opt.url.replace(/^(https?:\/\/)([^\/]+)/, function (str, pre, Host) {
              opt.headers.Host = Host;
              return pre + Ip;
          });
      }
      if (this.options.StrictSsl !== true) {
          opt.strictSSL = this.options.StrictSsl;
      }
      if (this.options.Proxy) {
          opt.proxy = this.options.Proxy;
      }
      if (this.options.Timeout) {
          opt.timeout = this.options.Timeout;
      }
      if (this.options.KeepAlive) {
          opt.forever = true;
      }
  
      self.emit('before-send', opt);
      var sender = REQUEST(opt);
      var retResponse;
      var hasReturned;
      var cb = function (err, data) {
          TaskId && self.off('inner-kill-task', killTask);
          if (hasReturned) return;
          hasReturned = true;
          var attrs = {};
          retResponse && retResponse.statusCode && (attrs.statusCode = retResponse.statusCode);
          retResponse && retResponse.headers && (attrs.headers = retResponse.headers);
          if (err) {
              err = util.extend(err || {}, attrs);
              callback(err, null);
          } else {
              data = util.extend(data || {}, attrs);
              callback(null, data);
          }
          if (sender) {
              sender.removeAllListeners && sender.removeAllListeners();
              sender = null;
          }
      };
      var xml2json = function (body) {
          try {
              json = util.xml2json(body) || {};
          } catch (e) {
              json = body || {};
          }
          return json;
      };
      sender.on('error', function (err) {
          cb({error: err});
      });
      sender.on('response', function (response) {
          retResponse = response;
          var responseContentLength = response.headers['content-length'] || 0;
          var chunkList = [];
          var statusCode = response.statusCode;
          var statusSuccess = Math.floor(statusCode / 100) === 2; // 200 202 204 206
          if (statusSuccess && params.outputStream) {
              sender.on('end', function () {
                  cb(null, {});
              });
          } else if (responseContentLength >= process.binding('buffer').kMaxLength && opt.method !== 'HEAD') {
              cb({error: 'file size large than ' + process.binding('buffer').kMaxLength + ', please use "Output" Stream to getObject.'});
          } else {
              var dataHandler = function (chunk) {
                  chunkList.push(chunk);
              };
              var endHandler = function () {
                  var json;
                  try {
                      var body = Buffer.concat(chunkList);
                  } catch (e) {
                      cb({error: e});
                      return;
                  }
                  var bodyStr = body.toString();
                  if (statusSuccess) {
                      if (rawBody) { // 不对 body 进行转换，body 直接挂载返回
                          cb(null, {body: body});
                      } else if (body.length) {
                          json = xml2json(body.toString());
                          if (json && json.Error) {
                              cb({error: json.Error});
                          } else {
                              cb(null, json);
                          }
                      } else {
                          cb(null, {});
                      }
                  } else {
                      bodyStr && (json = xml2json(bodyStr));
                      cb({error: json && json.Error || response.statusMessage || 'statusCode error'});
                  }
                  chunkList = null;
              };
              sender.on('data', dataHandler);
              sender.on('end', endHandler);
          }
      });
  
      // kill task
      var killTask = function (data) {
          if (data.TaskId === TaskId) {
              sender && sender.abort && sender.abort();
              self.off('inner-kill-task', killTask);
          }
      };
      TaskId && self.on('inner-kill-task', killTask);
  
      // upload progress
      if (params.onProgress && typeof params.onProgress === 'function') {
          var contentLength = opt.headers['Content-Length'];
          var time0 = Date.now();
          var size0 = 0;
          sender.on('drain', function () {
              var time1 = Date.now();
              var loaded = 0;
              try {
                  loaded = sender.req.connection.bytesWritten - sender.req._header.length;
              } catch (e) {
              }
              var total = contentLength;
              var speed = parseInt((loaded - size0) / ((time1 - time0) / 1000) * 100) / 100;
              var percent = total ? (parseInt(loaded / total * 100) / 100) : 0;
              time0 = time1;
              size0 = loaded;
              params.onProgress({
                  loaded: loaded,
                  total: total,
                  speed: speed,
                  percent: percent,
              });
          });
      }
      // download progress
      if (params.onDownloadProgress && typeof params.onDownloadProgress === 'function') {
          var time0 = Date.now();
          var size0 = 0;
          var loaded = 0;
          var total = 0;
          sender.on('response', function (res) {
              total = res.headers['content-length'];
              sender.on('data', function (chunk) {
                  loaded += chunk.length;
                  var time1 = Date.now();
                  var speed = parseInt((loaded - size0) / ((time1 - time0) / 1000) * 100) / 100;
                  var percent = total ? (parseInt(loaded / total * 100) / 100) : 0;
                  time0 = time1;
                  size0 = loaded;
                  params.onDownloadProgress({
                      loaded: loaded,
                      total: total,
                      speed: speed,
                      percent: percent,
                  });
              });
          });
      }
  
      // pipe 输入
      if (readStream) {
          readStream.on('error', function (err) {
              sender && sender.abort && sender.abort();
              cb(err);
          });
          readStream.pipe(sender);
      }
      // pipe 输出
      if (params.outputStream) {
          params.outputStream.on('error', function (err) {
              sender && sender.abort && sender.abort();
              cb(err)
          });
          sender.pipe(params.outputStream);
      }
  
      return sender;
  
  }