diff --git a/src/main/php/web/Application.class.php b/src/main/php/web/Application.class.php index 4719dab2..01dd21b2 100755 --- a/src/main/php/web/Application.class.php +++ b/src/main/php/web/Application.class.php @@ -43,6 +43,16 @@ public final function routing() { */ protected abstract function routes(); + /** + * Installs global filters + * + * @param web.Filter[] $filters + * @return void + */ + public function install($filters) { + $this->routing= Routing::cast(new Filters($filters, $this->routing())); + } + /** * Service delegates to the routing, calling its `service()` method. * diff --git a/src/main/php/xp/web/SAPI.class.php b/src/main/php/xp/web/SAPI.class.php index 8625cab5..0db80cbd 100755 --- a/src/main/php/xp/web/SAPI.class.php +++ b/src/main/php/xp/web/SAPI.class.php @@ -35,7 +35,12 @@ public function scheme() { return 'http'; } public function uri() { return $_SERVER['REQUEST_URI']; } /** @return [:string] */ - public function headers() { return getallheaders(); } + public function headers() { + yield 'Remote-Addr' => $_SERVER['REMOTE_ADDR']; + foreach (getallheaders() as $name => $value) { + yield $name => $value; + } + } /** @return string */ public function readLine() { diff --git a/src/main/php/xp/web/Source.class.php b/src/main/php/xp/web/Source.class.php index 809c70b2..1ca5d96c 100755 --- a/src/main/php/xp/web/Source.class.php +++ b/src/main/php/xp/web/Source.class.php @@ -8,14 +8,23 @@ class Source { /** * Creates a new application from a given name and environment * - * @param string $name + * @param string $name `application[+filter[,filter[,...]]]` * @param web.Environment $environment */ public function __construct($name, $environment) { - if ('-' === $name) { + sscanf($name, '%[^+]+%s', $application, $filters); + + if ('-' === $application) { $this->application= new ServeDocumentRootStatically($environment); } else { - $this->application= XPClass::forName($name)->newInstance($environment); + $this->application= XPClass::forName($application)->newInstance($environment); + } + + if ($filters) { + $this->application->install(array_map( + function($filter) { return XPClass::forName($filter)->newInstance(); }, + explode(',', $filters) + )); } } diff --git a/src/main/php/xp/web/dev/Buffer.class.php b/src/main/php/xp/web/dev/Buffer.class.php new file mode 100755 index 00000000..e975650b --- /dev/null +++ b/src/main/php/xp/web/dev/Buffer.class.php @@ -0,0 +1,31 @@ +status= $status; + $this->message= $message; + $this->headers= $headers; + } + + public function write($bytes) { + $this->bytes.= $bytes; + } + + /** + * Drain this buffered output to a given output instance, closing it + * once finished. + * + * @param web.io.Output $out + * @return void + */ + public function drain(Output $out) { + $out->begin($this->status, $this->message, $this->headers); + $out->write($this->bytes); + $out->close(); + } +} \ No newline at end of file diff --git a/src/main/php/xp/web/dev/Console.class.php b/src/main/php/xp/web/dev/Console.class.php new file mode 100755 index 00000000..4efdff9c --- /dev/null +++ b/src/main/php/xp/web/dev/Console.class.php @@ -0,0 +1,74 @@ +template= $name.'.html'; + } + + /** + * Creates HTML table rows + * + * @param [:var] $headers + * @return string + */ + private function rows($headers) { + $r= ''; + foreach ($headers as $name => $value) { + $r.= ' + '.htmlspecialchars($name).' + '.htmlspecialchars(implode(', ', $value)).' + '; + } + return $r; + } + + /** + * Filters the request + * + * @param web.Request $req + * @param web.Response $res + * @param web.filters.Invocation $invocation + * @return var + */ + public function filter($req, $res, $invocation) { + $buffer= new Buffer(); + + try { + ob_start(); + $result= $invocation->proceed($req, new Response($buffer)); + } finally { + $debug= ob_get_clean(); + ob_end_clean(); + } + + if (empty($debug)) { + $buffer->drain($res->output()); + } else { + $res->status(200, 'Debug'); + $res->send(sprintf( + typeof($this)->getClassLoader()->getResource($this->template), + htmlspecialchars($debug), + $buffer->status, + htmlspecialchars($buffer->message), + $this->rows($buffer->headers), + htmlspecialchars($buffer->bytes) + )); + } + + return $result; + } +} \ No newline at end of file diff --git a/src/main/php/xp/web/dev/console.html b/src/main/php/xp/web/dev/console.html new file mode 100755 index 00000000..7ff8e308 --- /dev/null +++ b/src/main/php/xp/web/dev/console.html @@ -0,0 +1,68 @@ + + + + Debug + + + +
+

+ Debug + + DEBUG +

+
+ +
%1$s
+
+ + +

HTTP/1.1 %2$d %3$s

+ + %4$s +
+
%5$s
+
+ + + diff --git a/src/main/php/xp/web/srv/Develop.class.php b/src/main/php/xp/web/srv/Develop.class.php index 29561a38..b60ae18c 100755 --- a/src/main/php/xp/web/srv/Develop.class.php +++ b/src/main/php/xp/web/srv/Develop.class.php @@ -10,7 +10,7 @@ use peer\Socket; use io\IOException; -class Develop { +class Develop implements Server { private $host, $port; /** @@ -61,7 +61,7 @@ public function serve($source, $profile, $webroot, $docroot, $config) { // Export environment putenv('DOCUMENT_ROOT='.$docroot); putenv('SERVER_PROFILE='.$profile); - putenv('WEB_SOURCE='.$source); + putenv('WEB_SOURCE='.$source.'+xp.web.dev.Console'); putenv('WEB_CONFIG='.implode('PATH_SEPARATOR', $config)); putenv('WEB_ROOT='.$webroot); diff --git a/src/main/php/xp/web/srv/Server.class.php b/src/main/php/xp/web/srv/Server.class.php new file mode 100755 index 00000000..de2b3762 --- /dev/null +++ b/src/main/php/xp/web/srv/Server.class.php @@ -0,0 +1,15 @@ +