private function procPacket($method, $url, $data = null, $headers = array()) {
    /*
     * For now we only data data as strings. Streams and other formats will be
     * permitted later.
     */
    if($data && !is_string($data)) {
      throw new SagException('Unexpected data format. Please report this bug.');
    }

    if($this->pathPrefix && is_string($this->pathPrefix)) {
      $url = $this->pathPrefix . $url;
    }

    // Filter the use of the Expect header since we don't support Continue headers.
    $headers['Expect'] = isset($headers['Expect']) ? $headers['Expect'] : null;
    if(!$headers['Expect']){
        /*
       * PHP cURL will set the Expect header to 100-continue if we don't set it
       * ourselves. See https://github.com/sbisbee/sag/pull/51
       */
        $headers['Expect'] = (isset($headers['expect']) && $headers['expect']) ? $headers['expect'] : ' '; //1 char string, so it's == to true
    }

    if(strtolower($headers['Expect']) === '100-continue') {
      throw new SagException('Sag does not support HTTP/1.1\'s Continue.');
    }

    // Build the request packet.
    $headers["Host"] = "{$this->host}:{$this->port}";
    $headers["User-Agent"] = "Sag/%VERSION%";

    /*
     * This prevents some unRESTful requests, such as inline attachments in
     * CouchDB 1.1.0, from sending multipart responses that would break our
     * parser.
     */
    $headers['Accept'] = 'application/json';

    //usernames and passwords can be blank
    if($this->authType == Sag::$AUTH_BASIC && (isset($this->user) || isset($this->pass))) {
      $headers["Authorization"] = 'Basic '.base64_encode("{$this->user}:{$this->pass}");
    }
    elseif($this->authType == Sag::$AUTH_COOKIE && isset($this->authSession)) {
      $headers['Cookie'] = array( 'AuthSession' => $this->authSession );
      $headers['X-CouchDB-WWW-Authenticate'] = 'Cookie';
    }

    if(is_array($this->globalCookies) && sizeof($this->globalCookies)) {
      //might have been set before by auth handling
      if($headers['Cookie']) {
        $headers['Cookie'] = array_merge($headers['Cookie'], $this->globalCookies);
      }
      else {
        $headers['Cookie'] = $this->globalCookies;
      }
    }

    /*
     * Checking this again because $headers['Cookie'] could be set in two
     * different logic paths above.
     */
    if(!empty($headers['Cookie'])) {
      $buff = '';

      foreach($headers['Cookie'] as $k => $v) {
        $buff = (($buff) ? ' ' : '') . "$k=$v;";
      }

      $headers['Cookie'] = $buff;
      unset($buff);
    }

    /*
     * JSON is our default and most used Content-Type, but others need to be
     * specified to allow attachments.
     */
    if(!isset($headers['Content-Type'])) {
      $headers['Content-Type'] = 'application/json';
    }

    if($data) {
      $headers['Content-Length'] = strlen($data);
    }

    return $this->httpAdapter->procPacket($method, $url, $data, $headers);
  }