1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Bridges\HttpDI;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class HttpExtension extends Nette\DI\CompilerExtension
17: {
18: public $defaults = [
19: 'proxy' => [],
20: 'headers' => [
21: 'X-Powered-By' => 'Nette Framework',
22: 'Content-Type' => 'text/html; charset=utf-8',
23: ],
24: 'frames' => 'SAMEORIGIN',
25: 'csp' => [],
26: ];
27:
28:
29: private $cliMode;
30:
31:
32: public function __construct($cliMode = false)
33: {
34: $this->cliMode = $cliMode;
35: }
36:
37:
38: public function loadConfiguration()
39: {
40: $builder = $this->getContainerBuilder();
41: $config = $this->validateConfig($this->defaults);
42:
43: $builder->addDefinition($this->prefix('requestFactory'))
44: ->setClass(Nette\Http\RequestFactory::class)
45: ->addSetup('setProxy', [$config['proxy']]);
46:
47: $builder->addDefinition($this->prefix('request'))
48: ->setClass(Nette\Http\Request::class)
49: ->setFactory('@Nette\Http\RequestFactory::createHttpRequest');
50:
51: $builder->addDefinition($this->prefix('response'))
52: ->setClass(Nette\Http\Response::class);
53:
54: $builder->addDefinition($this->prefix('context'))
55: ->setClass(Nette\Http\Context::class)
56: ->addSetup('::trigger_error', ['Service http.context is deprecated.', E_USER_DEPRECATED]);
57:
58: if ($this->name === 'http') {
59: $builder->addAlias('nette.httpRequestFactory', $this->prefix('requestFactory'));
60: $builder->addAlias('nette.httpContext', $this->prefix('context'));
61: $builder->addAlias('httpRequest', $this->prefix('request'));
62: $builder->addAlias('httpResponse', $this->prefix('response'));
63: }
64: }
65:
66:
67: public function afterCompile(Nette\PhpGenerator\ClassType $class)
68: {
69: if ($this->cliMode) {
70: return;
71: }
72:
73: $initialize = $class->getMethod('initialize');
74: $config = $this->getConfig();
75: $headers = $config['headers'];
76:
77: if (isset($config['frames']) && $config['frames'] !== true) {
78: $frames = $config['frames'];
79: if ($frames === false) {
80: $frames = 'DENY';
81: } elseif (preg_match('#^https?:#', $frames)) {
82: $frames = "ALLOW-FROM $frames";
83: }
84: $headers['X-Frame-Options'] = $frames;
85: }
86:
87: if (!empty($config['csp'])) {
88: $value = '';
89: foreach ($config['csp'] as $type => $policy) {
90: $value .= $type;
91: foreach ((array) $policy as $item) {
92: $value .= preg_match('#^[a-z-]+\z#', $item) ? " '$item'" : " $item";
93: }
94: $value .= '; ';
95: }
96: if (strpos($value, "'nonce'")) {
97: $value = Nette\DI\ContainerBuilder::literal(
98: 'str_replace(?, ? . base64_encode(Nette\Utils\Random::generate(16, "\x00-\xFF")), ?)',
99: ["'nonce", "'nonce-", $value]
100: );
101: }
102: $headers['Content-Security-Policy'] = $value;
103: }
104:
105: foreach ($headers as $key => $value) {
106: if ($value != null) {
107: $initialize->addBody('$this->getService(?)->setHeader(?, ?);', [$this->prefix('response'), $key, $value]);
108: }
109: }
110: }
111: }
112: