        
        // Which HTTP method? GET and HEAD use query string, POST etc. use
        // body for passing parameters
-@@ -144,10 +146,7 @@
+@@ -144,27 +171,10 @@
        
        // Add request headers
        foreach ($this->headers as $k => $v) {

+     * @param   int port default 8080
+     */
+    public function __construct($host, $port= 8080) {
+      $this->socket= new Socket($host, $port);
+    }
+
+    /**

+      $this->excludes[]= $pattern;
+      return $this;
+    }
+
+    /**
+     * Check whether a given URL is excluded
+     *
+     * @param   peer.URL url
+     * @return  boolean
+     */
+    public function isExcluded(URL $url) {
+      foreach ($this->excludes as $pattern) {
+        if (stristr($url->getHost(), $pattern)) return TRUE;
+      }
+      return FALSE;
+    }
+  }
+?>

Property changes on: skeleton/peer/http/HttpProxy.class.php
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:keywords
   + Id

Index: skeleton/peer/http/CurlHttpTransport.class.php
===================================================================
--- skeleton/peer/http/CurlHttpTransport.class.php	(revision 0)
+++ skeleton/peer/http/CurlHttpTransport.class.php	(revision 0)
@@ -0,0 +1,62 @@
+<?php
+/* This class is part of the XP framework
+ *
+ * $Id$ 
+ */
+
+  uses('peer.http.HttpTransport', 'io.streams.MemoryInputStream');
+
+  /**
+   * Transport via curl functions
+   *
+   * @ext      curl
+   * @see      xp://peer.http.HttpConnection
+   * @purpose  Transport
+   */
+  class CurlHttpTransport extends HttpTransport {
+    protected
+      $handle = NULL;
+
+    /**
+     * Constructor
+     *
+     * @param   peer.URL url
+     */
+    public function __construct(URL $url) {
+      $this->handle= curl_init();
+      curl_setopt($this->handle, CURLOPT_HEADER, 1);
+      curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, 1); 
+      curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0);
+      curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0);
+    }
+  
+    /**
+     * Sends a request
+     *
+     * @param   peer.http.HttpRequest request
+     * @param   int timeout default 60
+     * @param   float connecttimeout default 2.0
+     * @return  peer.http.HttpResponse response object
+     */
+    public function send(HttpRequest $request, $timeout= 60, $connecttimeout= 2.0) {
+      $curl= curl_copy_handle($this->handle);
+      curl_setopt($curl, CURLOPT_URL, $request->url->getUrl());
+      curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $request->getRequestString());
+      curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
+      
+      if ($this->proxy && !$this->proxy->isExcluded($request->getUrl())) {
+        curl_setopt($curl, CURLOPT_PROXY, $this->proxy->host);
+        curl_setopt($curl, CURLOPT_PROXYPORT, $this->proxy->port);
+      }
+      
+      $response= curl_exec($curl);
+      curl_close($curl);
+
+      if (FALSE === $response) {
+        throw new IOException(sprintf('%d: %s', curl_errno($curl), curl_error($curl)));
+      }
+
+      return new HttpResponse(new MemoryInputStream($response));
+    }
+  }
+?>

Property changes on: skeleton/peer/http/CurlHttpTransport.class.php
___________________________________________________________________
Name: svn:keywords
   + Id

Index: skeleton/peer/SocketInputStream.class.php
===================================================================
--- skeleton/peer/SocketInputStream.class.php	(revision 0)
+++ skeleton/peer/SocketInputStream.class.php	(revision 0)
@@ -0,0 +1,72 @@
+<?php
+/* This socket is part of the XP framework's experiments
+ *
+ * $Id$
+ */
+
+  uses('io.streams.InputStream', 'peer.Socket');
+
+  /**
+   * InputStream that reads from a socket
+   *
+   * @purpose  InputStream implementation
+   */
+  class SocketInputStream extends Object implements InputStream {
+    protected
+      $socket= NULL;
+    
+    /**
+     * Constructor
+     *
+     * @param   peer.Socket socket
+     */
+    public function __construct(Socket $socket) {
+      $this->socket= $socket;
+      $this->socket->isConnected() || $this->socket->connect();
+    }
+
+    /**
+     * Read a string
+     *
+     * @param   int limit default 8192
+     * @return  string
+     */
+    public function read($limit= 8192) {
+      return $this->socket->read($limit);
+    }
+
+    /**
+     * Returns the number of bytes that can be read from this stream 
+     * without blocking.
+     *
+     */
+    public function available() {
+      return $this->socket->eof() ? -1 : 1;
+    }
+
+    /**
+     * Close this buffer
+     *
+     */
+    public function close() {
+      $this->socket->close();
+    }
+
+    /**
+     * Destructor. Ensures socket is closed.
+     *
+     */
+    public function __destruct() {
+      $this->socket->isConnected() && $this->close();
+    }
+
+    /**
+     * Creates a string representation of this socket
+     *
+     * @return  string
+     */
+    public function toString() {
+      return $this->getClassName().'<'.$this->socket->toString().'>';
+    }
+  }
+?>

Property changes on: skeleton/peer/SocketInputStream.class.php
___________________________________________________________________
Name: svn:keywords
   + Id

Index: ports/classes/net/xp_framework/unittest/peer/HttpResponseTest.class.php
===================================================================
--- ports/classes/net/xp_framework/unittest/peer/HttpResponseTest.class.php	(revision 0)
+++ ports/classes/net/xp_framework/unittest/peer/HttpResponseTest.class.php	(revision 0)
@@ -0,0 +1,79 @@
+<?php
+/* This class is part of the XP framework
+ *
+ * $Id$
+ */
+
+  uses(
+    'unittest.TestCase',
+    'peer.http.HttpResponse',
+    'io.streams.MemoryInputStream'
+  );
+
+  /**
+   * TestCase for HTTP responses
+   *
+   * @see      xp://peer.http.HttpResponse
+   * @purpose  Unittest
+   */
+  class HttpResponseTest extends TestCase {
+  
+    /**
+     * Get a response with the specified headers and body
+     *
+     * @param   string[] headers
+     * @param   string body default ''
+     * @return  peer.http.HttpResponse
+     */
+    protected function newResponse(array $headers, $body= '') {
+      return new HttpResponse(new MemoryInputStream(implode("\r\n", $headers)."\r\n\r\n".$body));
+    }
+  
+    /**
+     * Test empty response
+     *
+     */
+    #[@test]
+    public function getEmptyDocument() {
+      $response= $this->newResponse(array('HTTP/1.0 200 OK', 'Content-Length: 0'));
+      $this->assertEquals(200, $response->getStatusCode());
+      $this->assertEquals('0', $response->getHeader('Content-Length'));
+    }
+
+    /**
+     * Test non-empty response
+     *
+     */
+    #[@test]
+    public function getDocument() {
+      $body= '<h1>File not found</h1>';
+      $response= $this->newResponse(array('HTTP/1.0 404 OK', 'Content-Length: 23', 'Content-Type: text/html'), $body);
+      $this->assertEquals(404, $response->getStatusCode());
+      $this->assertEquals('23', $response->getHeader('Content-Length'));
+      $this->assertEquals('text/html', $response->getHeader('Content-Type'));
+      $this->assertEquals($body, $response->readData());
+    }
+
+    /**
+     * Test chunked transfer-encoding
+     *
+     */
+    #[@test]
+    public function getChunkedDocument() {
+      $body= '<h1>File not found</h1>';
+      $response= $this->newResponse(array('HTTP/1.0 404 OK', 'Transfer-Encoding: chunked'), "17\r\n".$body."\r\n0\r\n");
+      $this->assertEquals(404, $response->getStatusCode());
+      $this->assertEquals('chunked', $response->getHeader('Transfer-Encoding'));
+      $this->assertEquals($body, $response->readData());
+    }
+
+    /**
+     * Test what happens when the server responds with an incorrect protocol
+     *
+     */
+    #[@test, @expect('lang.FormatException')]
+    public function incorrectProtocol() {
+      $this->newResponse(array('* OK IMAP server ready H mimap20 68140'));
+    }
+  }
+?>

Property changes on: ports/classes/net/xp_framework/unittest/peer/HttpResponseTest.class.php
___________________________________________________________________
Name: svn:keywords
   + Id

Index: ports/unittest/http.ini
===================================================================
--- ports/unittest/http.ini	(revision 12288)
+++ ports/unittest/http.ini	(working copy)
@@ -8,3 +8,6 @@
 
 [request]
 class="net.xp_framework.unittest.peer.HttpRequestTest"
+
+[response]
+class="net.xp_framework.unittest.peer.HttpResponseTest"
