Permalink
Browse files

Once upon a time ...

  • Loading branch information...
0 parents commit 510e6b3142f80abe078b2b7e729e4f16aada633a @GromNaN committed Apr 2, 2012
@@ -0,0 +1 @@
+vendor
@@ -0,0 +1,66 @@
+Buzy is a an HTTP client for PHP built on top of Symfony2 components
+====================================================================
+
+
+Requirements
+------------
+
+* PHP 5.3 +
+* Symfony HttpFoundation
+* Symfony Event Dispatcher
+* Curl Extension
+
+Usage
+-----
+
+```php
+
+$browser = new Buzy\Browser();
+$response = $browser->get('http://www.google.com');
+
+echo $browser->getLastRequest()."\n";
+echo $response;
+```
+
+You can also use the low-level HTTP classes directly.
+
+```php
+
+$request = new Symfony\Component\HttpFoundation\Request::create('http://google.com', 'GET');
+$response = new Symfony\Component\HttpFoundation\Response();
+
+$client = new Buzz\Client\FileGetContents();
+$client->send($request, $response);
+
+echo $request;
+echo $response;
+```
+
+Simple reverse proxy
+--------------------
+
+With this 4 lines of code, you can re-send a request and transfert response.
+
+```php
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Suzy\Browser;
+
+$request = Request::createFromGlobals();
+$request->server->set('HTTP_HOST', 'internal-server');
+
+$browser = new Browser();
+
+$response = $browser->send($request);
+
+$response->send();
+
+// The response is sent back to the client
+```
+
+Potential usages
+----------------
+
+* Resolve external ESI into a Symfony application without any cache server like Varnish
+*
@@ -0,0 +1,15 @@
+{
+ "name": "grom/suzy",
+ "description": "Web client on top of Symfony2 HTTP foundation",
+ "require": {
+ "symfony/http-foundation": "*",
+ "symfony/event-dispatcher": "*",
+ "doctrine/common": "*"
+ },
+ "authors": [
+ {
+ "name": "Jerome Tamarelle",
+ "email": "jerome@tamarelle.net"
+ }
+ ]
+}
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit backupGlobals="false"
+ backupStaticAttributes="false"
+ colors="true"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ processIsolation="false"
+ stopOnFailure="false"
+ syntaxCheck="false"
+ bootstrap="tests/bootstrap.php"
+ >
+
+ <testsuites>
+ <testsuite name="Buzy Test Suite">
+ <directory>./tests/</directory>
+ </testsuite>
+ </testsuites>
+
+ <filter>
+ <whitelist>
+ <directory>./src/Buzy/</directory>
+ </whitelist>
+ </filter>
+
+</phpunit>
@@ -0,0 +1,137 @@
+<?php
+
+namespace Buzy;
+
+use Buzy\Client\ClientInterface;
+use Buzy\Client\FileGetContents;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class Browser
+{
+ private $client;
+ private $dispatcher;
+
+ public function __construct(ClientInterface $client = null, EventDispatcherInterface $dispatcher = null)
+ {
+ $this->client = $client ?: new FileGetContents();
+ $this->dispatcher = $dispatcher;
+ }
+
+ public function get($url, $headers = array())
+ {
+ return $this->call($url, 'GET', $headers);
+ }
+
+ public function post($url, $headers = array(), $content = '')
+ {
+ return $this->call($url, 'POST', $headers, $content);
+ }
+
+ public function head($url, $headers = array())
+ {
+ return $this->call($url, 'HEAD', $headers);
+ }
+
+ public function put($url, $headers = array(), $content = '')
+ {
+ return $this->call($url, 'PUT', $headers, $content);
+ }
+
+ public function delete($url, $headers = array(), $content = '')
+ {
+ return $this->call($url, 'DELETE', $headers, $content);
+ }
+
+ /**
+ * Sends a request.
+ *
+ * @param string $uri The URL to call
+ * @param string $method The request method to use
+ * @param array $headers An array of request headers
+ * @param string $content The request content
+ *
+ * @return Response The response object
+ */
+ public function call($uri, $method, $headers = array(), $content = '')
+ {
+ $request = Request::create($uri, $method, array(), array(), array(), array(), $content);
+
+ foreach ($headers as $key => $value) {
+ if (is_numeric($key)) {
+ list($key, $value) = explode(':', $value, 2);
+ }
+ $request->headers->set($name, $value);
+ }
+
+ return $this->send($request);
+ }
+
+ /**
+ * Sends a form request.
+ *
+ * @param string $url The URL to submit to
+ * @param array $fields An array of fields
+ * @param string $method The request method to use
+ * @param array $headers An array of request headers
+ *
+ * @return Response The response object
+ */
+ public function submit($url, array $fields, $method = 'POST', $headers = array())
+ {
+ $request = Request::create($uri, $method, $fields, array(), array(), array(), null);
+
+ foreach ($headers as $key => $value) {
+ if (is_numeric($key)) {
+ list($key, $value) = explode(':', $value, 2);
+ }
+ $request->headers->set($name, $value);
+ }
+
+ return $this->send($request);
+ }
+
+ /**
+ * Sends a request.
+ *
+ * @param Request $request A request object
+ * @param Response $response A response object
+ *
+ * @return Response A response object
+ */
+ public function send(Request $request, Response $response = null)
+ {
+ if (null === $response) {
+ $response = new Response();
+ }
+
+ if (null !== $this->dispatcher) {
+ $event = new BrowserEvent($request, $response);
+ $this->dispatcher->dispatch(BrowserEvent::REQUEST, $event);
+
+ if ($event->isPropagationStopped()) {
+ return $response;
+ }
+ }
+
+ $this->client->send($request, $response);
+
+ if (null !== $this->dispatcher) {
+ $this->dispatcher->dispatch(BrowserEvent::RESPONSE, $event);
+ }
+
+ return $response;
+ }
+
+ public function setClient(ClientInterface $client)
+ {
+ $this->client = $client;
+ }
+
+ public function getClient()
+ {
+ return $this->client;
+ }
+}
@@ -0,0 +1,32 @@
+<?php
+
+namespace Buzy;
+
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class BrowserEvent extends Event
+{
+ const REQUEST = 'buzy.request';
+ const RESPONSE = 'buzy.response';
+
+ private $request;
+ private $response;
+
+ public function __construct(Request $request, Response $response)
+ {
+ $this->request = $request;
+ $this->response = $response;
+ }
+
+ public function getRequest()
+ {
+ return $this->request;
+ }
+
+ public function getResponse()
+ {
+ return $this->response;
+ }
+}
@@ -0,0 +1,93 @@
+<?php
+
+namespace Buzy\Cache;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Doctrine\Common\Cache\Cache;
+use Buzy\BrowserEvent;
+
+/**
+ * HTTP Cache listener provide standard cache features.
+ *
+ * @author Jérôme Tamarelle <jerome@tamarelle.net>
+ */
+class CacheListener implements EventSubscriberInterface
+{
+ /**
+ * @var \Doctrine\Common\Cache\Cache
+ */
+ private $cache;
+
+ /**
+ * Constructor.
+ *
+ * @param Doctrine\Common\Cache\Cache $cache
+ */
+ public function __construct(Cache $cache)
+ {
+ $this->cache = $cache;
+ }
+
+ /**
+ * Try to find a cached response before the resquest is sent
+ *
+ * @todo Implement HTTP cache rules
+ *
+ * @param \Buzy\BrowserEvent $event
+ */
+ public function onRequest(BrowserEvent $event)
+ {
+ $id = $this->generateRequestIdentifier($event->getRequest());
+
+ if ($this->cache->contains($id)) {
+ $cachedResponse = unserialize($this->cache->fetch($id));
+
+ $event->getResponse()->headers = clone $cachedResponse->headers;
+
+ $event->getResponse()
+ ->setContent($cachedResponse->getContent())
+ ->setContentType($cachedResponse->getContentType())
+ ->setProtocolVersion($cachedResponse->getProtocolVersion())
+ ->setStatusCode($cachedResponse->getStatusCode())
+ ->setEtag($cachedResponse->getEtag())
+ ->headers
+ ->set('Age', $cachedResponse->headers->get('Age') + time() - $cachedResponse->getDate()->format('U'))
+ ;
+
+ $event->stopPropagation();
+ }
+ }
+
+ /**
+ * Store the response if it is cachable.
+ *
+ * @param \Buzy\BrowserEvent $event
+ */
+ public function onResponse(BrowserEvent $event)
+ {
+ $response = $event->getResponse();
+ if ($response->isCacheable()) {
+ $id = $this->generateRequestIdentifier($event->getRequest());
+ $ttl = max($response->getTtl() - $response->getAge(), 0);
+ $this->cache->save($id, serialize($response));
+ }
+ }
+
+ protected function generateRequestIdentifier(Request $request)
+ {
+ return sha1($request->headers->all());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ static public function getSubscribedEvents()
+ {
+ return array(
+ BrowserEvent::REQUEST => 'onRequest',
+ BrowserEvent::RESPONSE => 'onResponse',
+ );
+ }
+}
@@ -0,0 +1,17 @@
+<?php
+
+namespace Buzy\Client;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+interface ClientInterface
+{
+ /**
+ * Populates the supplied response with the response for the supplied request.
+ *
+ * @param Request $request A request object
+ * @param Response $response A response object
+ */
+ function send(Request $request, Response $response);
+}
Oops, something went wrong.

0 comments on commit 510e6b3

Please sign in to comment.