title: How do I use a php authentication script as proxy to CouchDB and still maintain full REST API functionality?
body: <p>I've recently successfully installed CouchDB version 1.2.1 and as a test I can access it using the following Apache rewrite in CentOS 6:</p>
  
  <pre><code>RewriteRule couchdb/(.*)$ http://127.0.0.1:5984/$1 [QSA,P]
  </code></pre>
  
  <p>I have a PHP authentication class that I use across a home grown API to support my mobile apps.  My API accepts and authenticates each request using an HMAC signature that is included in the URL like so:</p>
  
  <pre><code>https://api.domain.com/endpoint/?timestamp=[timestamp]&amp;signature=[signature]&amp;id=[id]...etc
  </code></pre>
  
  <p>Each endpoint has a corresponding script that ensures that checks for a proper signature before processing.</p>
  
  <p>Ideally, I'd like to effectively replace the above reverse proxy rewrite rule with some sort of PHP script that will act as a gatekeeper/gateway to the CouchDB instance, leveraging my authentication class, while still preserving all native CouchDB REST API functionality, including but not limited to replication and cookie authentication for users (the above is just for API authentication).  Can this be done?  I've tried using <a href="https://github.com/aventurella/couchdb_php_proxy" rel="nofollow">this</a> solution as modified below, but while it does in fact kick back valid JSON responses, replication fails, and I suspect other aspects such as user authentication will as well:</p>
  
  <pre><code>&lt;?php
  require_once('CouchDBProxy.php');
  require_once("common.php");
  
  //set some vars
  $resource = $_GET['resource'];
  $id = $_GET['appid'];
  $timestamp = $_GET['timestamp'];
  $signature = $_GET['signature'];
  
  //use common class for validating sig
  if ( Access::validSignature( $id, $timestamp, $signature ) ) {
      $proxy = new CouchDBProxy('127.0.0.1', '5984');
      $proxy-&gt;proxy('/'.$resource);
  }
  ?&gt;
  
  
  &lt;?php
  //COUCHDB_PROXY.PHP
  
      class CouchDBProxy
      {
          public $host;
          public $port;
          public $timeout = 10;
  
          /**
           * Initialize the proxy service
           *
           * @param string $host the host where the requests should be forwarded
           * @param string $port the port on the host to use
           * @author Adam Venturella
           */
          public function __construct($host, $port)
          {
              $this-&gt;host            = $host;
              $this-&gt;port            = $port;
          }
  
          /**
           * Begin proxying
           *
           * @return void
           * @author Adam Venturella
           */
  
          public function proxy($resource)
          {
              $verb    = strtolower($_SERVER['REQUEST_METHOD']);
              $command = null;
  
              switch($verb)
              {
                  case 'get':
                      $command = $this-&gt;proxy_get($resource);
                      break;
  
                  case 'post':
                      $command = $this-&gt;proxy_post($resource);
                      break;
  
                  case 'put':
                      $command = $this-&gt;proxy_put($resource);
                      break;
  
                  case 'delete':
                      $command = $this-&gt;proxy_delete($resource);
                      break;
  
                  case 'head':
                      $command = $this-&gt;proxy_head($resource);
                      break;
              }
  
              if($command)
              {
                  curl_exec($command);
                  curl_close($command);
              }
          }
  
          /**
           * Handle GET requests
           *
           * @return void
           * @author Adam Venturella
           */
          private function proxy_get($resource)
          {
              return $this-&gt;request($resource);
          }
  
          /**
           * Handle HEAD requests
           *
           * @return void
           * @author Adam Venturella
           */
          private function proxy_head($resource)
          {
              $command = $this-&gt;request($resource);
              curl_setopt( $command, CURLOPT_NOBODY, true);
              return $command;
          }
  
          /**
           * Handle POST requests
           *
           * @return void
           * @author Adam Venturella
           */
          private function proxy_post($resource)
          {
              $command = $this-&gt;request($resource);
              $data    = file_get_contents("php://input");
              curl_setopt($command, CURLOPT_POST, true);
              curl_setopt($command, CURLOPT_POSTFIELDS, $data);
  
              return $command;
          }
  
          /**
           * Handle DELETE Requests
           *
           * @return void
           * @author Adam Venturella
           */
          private function proxy_delete($resource)
          {
              $command = $this-&gt;request($resource);
              curl_setopt($command, CURLOPT_CUSTOMREQUEST, 'DELETE');  
              return $command;
          }
  
          /**
           * Handle PUT requests
           *
           * @return void
           * @author Adam Venturella
           */
          private function proxy_put($resource)
          {
              $command = $this-&gt;request($resource);
  
              $data     = file_get_contents("php://input");
              curl_setopt($command, CURLOPT_CUSTOMREQUEST, 'PUT');  
              curl_setopt($command, CURLOPT_POSTFIELDS, $data);
  
              return $command;
          }
  
          /**
           * Build the basic request
           *
           * @return void
           * @author Adam Venturella
           */
          private function request($resource)
          {
              $action    = $_SERVER['REQUEST_METHOD'];
              $uri       = $resource;
      //      $uri       = $_SERVER['REQUEST_URI'];
  
              $params    = null;
  
              //added from http://stackoverflow.com/questions/2916232/call-to-undefined-function-apache-request-headers
              if( !function_exists('apache_request_headers') ) {
                  function apache_request_headers() {
                    $arh = array();
                    $rx_http = '/\AHTTP_/';
                    foreach($_SERVER as $key =&gt; $val) {
                      if( preg_match($rx_http, $key) ) {
                        $arh_key = preg_replace($rx_http, '', $key);
                        $rx_matches = array();
                        // do some nasty string manipulations to restore the original letter case
                        // this should work in most cases
                        $rx_matches = explode('_', $arh_key);
                        if( count($rx_matches) &gt; 0 and strlen($arh_key) &gt; 2 ) {
                          foreach($rx_matches as $ak_key =&gt; $ak_val) $rx_matches[$ak_key] = ucfirst($ak_val);
                          $arh_key = implode('-', $rx_matches);
                        }
                        $arh[$arh_key] = $val;
                      }
                    }
                    return( $arh );
                  }
              }
              $headers   = apache_request_headers();
              $context   = array();
  
              $context[] = 'Host: '.$this-&gt;host.':'.$this-&gt;port;
              $context[] = 'X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'];
              $context[] = 'X-Forwarded-Host: '.$_SERVER['HTTP_HOST'];
              $context[] = 'X-Forwarded-Server: '.$_SERVER['SERVER_NAME'];
  
              foreach($headers as $key=&gt;$value)
              {
                  if(strtolower($key) != 'host')
                  {
                      $context[] = $key.': '.$value;
                  }
              }
  
              $command = curl_init();
              curl_setopt( $command, CURLOPT_HTTPHEADER, $context);
              curl_setopt( $command, CURLOPT_URL, "http://".$this-&gt;host.':'.$this-&gt;port.$uri);
              curl_setopt( $command, CURLOPT_BINARYTRANSFER, true );
              curl_setopt( $command, CURLOPT_TIMEOUT, $this-&gt;timeout );
              curl_setopt( $command, CURLOPT_HEADERFUNCTION, array($this,'processResponseHeaders'));
              curl_setopt( $command, CURLOPT_WRITEFUNCTION, array($this,'processResponseBody'));
              return $command;
          }
  
          /**
           * Process the response body
           *
           * @param cURL $command reference to the curl command used to generate this response
           * @param string $data the response body
           * @return void
           * @author Adam Venturella
           */
          private function processResponseBody(&amp;$command, $data)
          {
              $bytes = strlen($data);
              echo $data;
              return $bytes;
          }
  
          /**
           * Process the response headers
           *
           * @param cURL $command reference to the curl command used to generate this response
           * @param string $header current header in the response
           * @return void
           * @author Adam Venturella
           */
          private function processResponseHeaders(&amp;$command, $header)
          {
              $bytes = strlen($header);
  
              // cURL handles chunked decoding for us, so a response from 
              // this proxy will never be chunked
  
              if ($header !== "\r\n" &amp;&amp; strpos($header, 'chunked') === false)
              {
                  header(rtrim($header));
              }
  
              return $bytes;
          }
      }
      ?&gt;
  </code></pre>
  
  <p>I've been at this for days and I can't seem to make it work so I'm tossing it out for help either getting the php proxy script right, or help identifying an alternative approach.</p>