docs: function responseDispatcher(response) {
    /*jshint validthis:true */
    var operation = this;
  
    if (!isResponseStatusOkay.call(operation, response)) {
      return;
    }
  
    var outputMode = operation.outputMode;
    if (outputMode === 'none') {
      return;
    }
  
    var responseType     = response.headers['content-type'];
    var responseTypeLen  = (responseType === undefined) ? 0 : responseType.length;
    var responseBoundary = null;
    if (15 <= responseTypeLen && responseType.substr(0, 15) === 'multipart/mixed') {
      responseBoundary = responseType.replace(
          /^multipart.mixed\s*;\s*boundary\s*=\s*([^\s;]+)([\s;].*)?$/, '$1'
          );
      if (responseBoundary.length === responseTypeLen) {
        operation.errorListener('multipart/mixed response without boundary');
        return;
      }
      if (responseBoundary !== mlutil.multipartBoundary) {
        operation.logger.debug(
            'expected '+mlutil.multipartBoundary+
            ' but received '+responseBoundary+' multipart/mixed boundary'
            );
      }
    }
    var isMultipart = (responseBoundary != null);
  
    // inputHeader may be boundary (for multipart) or content type (for body)
    // Allows dispatch function signatures to remain consistent
    var inputHeader = isMultipart ? responseBoundary : responseType;
  
    var responseLength = response.headers['content-length'];
    var isEmpty = ((responseLength != null) && responseLength === '0');
  
    var expectedType = operation.responseType;
  
    // point-in-time operations: if timestamp unset, set with header value
    if (operation.timestamp !== undefined && operation.timestamp !== null) {
      if (operation.timestamp.value === null
        && response.headers['ml-effective-timestamp']) {
        operation.timestamp.value = response.headers['ml-effective-timestamp'];
      }
    }
  
    var dispatcher = null;
    if (isMultipart) {
      if (expectedType !== 'multipart') {
        operation.logger.debug('expected body but received multipart');
      }
  
      dispatcher = new MultipartDispatcher(operation);
    } else if (20 <= responseTypeLen && responseType.substr(0, 20) === 'application/json-seq') {
      dispatcher = new JSONSeqDispatcher(operation);
    } else if (8 <= responseTypeLen && responseType.substr(0, 8) === 'text/csv') {
      dispatcher = new CSVDispatcher(operation);
    } else {
      if (expectedType === 'multipart') {
        operation.logger.debug('expected multipart but received body');
      }
  
      dispatcher = new BodyDispatcher(operation);
    }
  
    response.on('error', operation.errorListener);
  
    if (isEmpty) {
      if (expectedType !== 'empty') {
        operation.logger.debug('expected body or multipart but received empty response');
      }
  
      if (outputMode === 'promise') {
        dispatcher.emptyPromise(response);
      } else {
        dispatcher.emptyStream(response);
      }
    } else {
      if (expectedType === 'empty') {
        operation.logger.debug('expected empty response but received body or multipart');
      }
  
      switch(outputMode) {
      case 'promise':
        dispatcher.promise(inputHeader, response);
        break;
      case 'chunkedStream':
        dispatcher.chunkedStream(inputHeader, response);
        break;
      case 'objectStream':
        dispatcher.objectStream(inputHeader, response);
        break;
      case 'sequenceStream':
        dispatcher.sequenceStream(inputHeader, response);
        break;
      default:
        operation.errorListener('unknown output mode '+outputMode);
        break;
      }
    }
  }