+ + + DEBUG +
+%1$s+
HTTP/1.1 %2$d %3$s
+%5$s+
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.= '
%1$s+
%5$s+