Completed
Push — master ( f091a0...4929b8 )
by Miguel
10s
created

Abstract::_vcl_sub_synth_https_fix()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 10
nc 2
nop 0
1
<?php
2
3
/**
4
 * Nexcess.net Turpentine Extension for Magento
5
 * Copyright (C) 2012  Nexcess.net L.L.C.
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License along
18
 * with this program; if not, write to the Free Software Foundation, Inc.,
19
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
 */
21
22
abstract class Nexcessnet_Turpentine_Model_Varnish_Configurator_Abstract {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
23
24
    const VCL_CUSTOM_C_CODE_FILE = 'uuid.c';
25
26
    /**
27
     * Get the correct version of a configurator from a socket
28
     *
29
     * @param  Nexcessnet_Turpentine_Model_Varnish_Admin_Socket $socket
30
     * @return Nexcessnet_Turpentine_Model_Varnish_Configurator_Abstract
31
     */
32
    static public function getFromSocket($socket) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
33
        try {
34
            $version = $socket->getVersion();
35
        } catch (Mage_Core_Exception $e) {
36
            Mage::getSingleton('core/session')
37
                ->addError('Error determining Varnish version: '.
38
                    $e->getMessage());
39
            return null;
40
        }
41
        switch ($version) {
42
            case '4.0':
43
                return Mage::getModel(
44
                    'turpentine/varnish_configurator_version4',
45
                    array('socket' => $socket) );
46
47
            case '3.0':
48
                return Mage::getModel(
49
                    'turpentine/varnish_configurator_version3',
50
                        array('socket' => $socket) );
51
            case '2.1':
52
                return Mage::getModel(
53
                    'turpentine/varnish_configurator_version2',
54
                        array('socket' => $socket) );
55
            default:
56
                Mage::throwException('Unsupported Varnish version');
57
        }
58
    }
59
60
    /**
61
     * The socket this configurator is based on
62
     *
63
     * @var Nexcessnet_Turpentine_Model_Varnish_Admin_Socket
64
     */
65
    protected $_socket = null;
66
    /**
67
     * options array
68
     *
69
     * @var array
70
     */
71
    protected $_options = array(
72
        'vcl_template'  => null,
73
    );
74
75
    public function __construct($options = array()) {
76
        $this->_options = array_merge($this->_options, $options);
77
    }
78
79
    abstract public function generate($doClean = true);
80
    // abstract protected function _getTemplateVars();
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
81
82
    /**
83
     * Save the generated config to the file specified in Magento config
84
     *
85
     * @param  string $generatedConfig config generated by @generate
86
     * @return null
87
     */
88
    public function save($generatedConfig) {
89
        $filename = $this->_getVclFilename();
90
        $dir = dirname($filename);
91
        if ( ! is_dir($dir)) {
92
            // this umask is probably redundant, but just in case...
93
            if ( ! mkdir($dir, 0777 & ~umask(), true)) {
94
                $err = error_get_last();
95
                return array(false, $err);
96
            }
97
        }
98
        if (strlen($generatedConfig) !==
99
                file_put_contents($filename, $generatedConfig)) {
100
            $err = error_get_last();
101
            return array(false, $err);
102
        }
103
        return array(true, null);
104
    }
105
106
    /**
107
     * Get the full path for a given template filename
108
     *
109
     * @param  string $baseFilename
110
     * @return string
111
     */
112
    protected function _getVclTemplateFilename($baseFilename) {
113
           $extensionDir = Mage::getModuleDir('', 'Nexcessnet_Turpentine');
114
           return sprintf('%s/misc/%s', $extensionDir, $baseFilename);
115
    }
116
117
    /**
118
     * Get the name of the file to save the VCL to
119
     *
120
     * @return string
121
     */
122
    protected function _getVclFilename() {
123
        return $this->_formatTemplate(
124
            Mage::getStoreConfig('turpentine_varnish/servers/config_file'),
125
            array('root_dir' => Mage::getBaseDir()) );
126
    }
127
128
    /**
129
     * Get the name of the custom include VCL file
130
     *
131
     * @return string
132
     */
133
    protected function _getCustomIncludeFilename($position='') {
134
        $key = 'custom_include_file';
135
        $key .= ($position) ? '_'.$position : '';
136
        return $this->_formatTemplate(
137
            Mage::getStoreConfig('turpentine_varnish/servers/'.$key),
138
            array('root_dir' => Mage::getBaseDir()) );
139
    }
140
141
142
    /**
143
     * Get the custom VCL template, if it exists
144
     * Returns 'null' if the file doesn't exist
145
     *
146
     * @return string
147
     */
148
    protected function _getCustomTemplateFilename() {
149
        $filePath = $this->_formatTemplate(
150
            Mage::getStoreConfig('turpentine_varnish/servers/custom_vcl_template'),
151
            array('root_dir' => Mage::getBaseDir())
152
        );
153
        if (is_file($filePath)) { return $filePath; }
154
        else { return null; }
155
    }
156
157
158
    /**
159
     * Format a template string, replacing {{keys}} with the appropriate values
160
     * and remove unspecified keys
161
     *
162
     * @param  string $template template string to operate on
163
     * @param  array  $vars     array of key => value replacements
164
     * @return string
165
     */
166
    protected function _formatTemplate($template, array $vars) {
167
        $needles = array_map(create_function('$k', 'return "{{".$k."}}";'),
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
168
            array_keys($vars));
169
        $replacements = array_values($vars);
170
        // do replacements, then delete unused template vars
171
        return preg_replace('~{{[^}]+}}~', '',
172
            str_replace($needles, $replacements, $template));
173
    }
174
175
    /**
176
     * Format a VCL subroutine call
177
     *
178
     * @param  string $subroutine subroutine name
179
     * @return string
180
     */
181
    protected function _vcl_call($subroutine) {
182
        return sprintf('call %s;', $subroutine);
183
    }
184
185
    /**
186
     * Get the Magento admin frontname
187
     *
188
     * This is just the plain string, not in URL format. ex:
189
     * http://example.com/magento/admin -> admin
190
     *
191
     * @return string
192
     */
193
    protected function _getAdminFrontname() {
194
        if (Mage::getStoreConfig('admin/url/use_custom_path')) {
195
            if(Mage::getStoreConfig('web/url/use_store')) {
196
                return Mage::getModel('core/store')->load(0)->getCode() . "/" . Mage::getStoreConfig('admin/url/custom_path');
197
            } else {
198
                return Mage::getStoreConfig('admin/url/custom_path');
199
            }
200
        } else {
201
            return (string) Mage::getConfig()->getNode(
202
                'admin/routers/adminhtml/args/frontName' );
203
        }
204
    }
205
206
    /**
207
     * Get the hostname for host normalization from Magento's base URL
208
     *
209
     * @return string
210
     */
211
    protected function _getNormalizeHostTarget() {
212
        $configHost = trim(Mage::getStoreConfig(
213
            'turpentine_vcl/normalization/host_target' ));
214
        if ($configHost) {
215
            return $configHost;
216
        } else {
217
            $baseUrl = parse_url(Mage::getBaseUrl());
218
            if (isset($baseUrl['port'])) {
219
                return sprintf('%s:%d', $baseUrl['host'], $baseUrl['port']);
220
            } else {
221
                return $baseUrl['host'];
222
            }
223
        }
224
    }
225
226
    /**
227
     * Get hosts as regex
228
     *
229
     * ex: base_url: example.com
230
     *     path_regex: (example.com|example.net)
231
     *
232
     * @return string
233
     */
234
    public function getAllowedHostsRegex() {
235
        $hosts = array();
236
        foreach (Mage::app()->getStores() as $store) {
237
            $hosts[] = parse_url($store->getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB, false), PHP_URL_HOST);
238
        }
239
240
        $hosts = array_values(array_unique($hosts));
241
242
        $pattern = '('.implode('|', array_map("preg_quote", $hosts)).')';
243
        return $pattern;
244
    }
245
246
    /**
247
     * Get the Host normalization sub routine
248
     *
249
     * @return string
250
     */
251
    protected function _vcl_sub_allowed_hosts_regex() {
252
        $tpl = <<<EOS
253
# if host is not allowed in magento pass to backend
254
        if (req.http.host !~ "{{allowed_hosts_regex}}") {
255
            return (pass);
256
        }
257
EOS;
258
        return $this->_formatTemplate($tpl, array(
259
            'allowed_hosts_regex' => $this->getAllowedHostsRegex() ));
260
    }
261
262
    /**
263
     * Get the base url path regex
264
     *
265
     * ex: base_url: http://example.com/magento/
266
     *     path_regex: /magento/(?:(?:index|litespeed)\.php/)?
267
     *
268
     * @return string
269
     */
270
    public function getBaseUrlPathRegex() {
271
        $pattern = '^(%s)(?:(?:index|litespeed)\\.php/)?';
272
        return sprintf($pattern, implode('|',
273
            array_map(create_function('$x', 'return preg_quote($x,"|");'),
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
274
                $this->_getBaseUrlPaths())));
275
    }
276
277
    /**
278
     * Get the path part of each store's base URL and static file URLs
279
     *
280
     * @return array
281
     */
282
    protected function _getBaseUrlPaths() {
283
        $paths = array();
284
        $linkTypes = array(Mage_Core_Model_Store::URL_TYPE_LINK,
285
                            Mage_Core_Model_Store::URL_TYPE_JS,
286
                            Mage_Core_Model_Store::URL_TYPE_SKIN,
287
                            Mage_Core_Model_Store::URL_TYPE_MEDIA);
288
        foreach (Mage::app()->getStores() as $store) {
289
            foreach ($linkTypes as $linkType) {
290
                $paths[] = parse_url($store->getBaseUrl($linkType, false),
291
                    PHP_URL_PATH);
292
                $paths[] = parse_url($store->getBaseUrl($linkType, true),
293
                    PHP_URL_PATH);
294
            }
295
        }
296
        $paths = array_unique($paths);
297
        usort($paths, create_function('$a, $b',
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
298
            'return strlen( $b ) - strlen( $a );'));
299
        return array_values($paths);
300
    }
301
302
    /**
303
     * Format the URL exclusions for insertion in a regex. Admin frontname and
304
     * API are automatically added.
305
     *
306
     * @return string
307
     */
308
    protected function _getUrlExcludes() {
309
        $urls = Mage::getStoreConfig('turpentine_vcl/urls/url_blacklist');
310
        return implode('|', array_merge(array($this->_getAdminFrontname(), 'api'),
311
            Mage::helper('turpentine/data')->cleanExplode(PHP_EOL, $urls)));
312
    }
313
314
    /**
315
     * Get the default cache TTL from Magento config
316
     *
317
     * @return string
318
     */
319
    protected function _getDefaultTtl() {
320
        return Mage::helper('turpentine/varnish')->getDefaultTtl();
321
    }
322
323
    /**
324
     * Get the default backend configuration string
325
     *
326
     * @return string
327
     */
328 View Code Duplication
    protected function _getDefaultBackend() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
329
        $timeout = Mage::getStoreConfig('turpentine_vcl/backend/frontend_timeout');
330
        $default_options = array(
331
            'first_byte_timeout'    => $timeout.'s',
332
            'between_bytes_timeout' => $timeout.'s',
333
        );
334
        if (Mage::getStoreConfig('turpentine_vcl/backend/load_balancing') != 'no') {
335
            return $this->_vcl_director('default', $default_options);
336
        } else {
337
            return $this->_vcl_backend('default',
338
                Mage::getStoreConfig('turpentine_vcl/backend/backend_host'),
339
                Mage::getStoreConfig('turpentine_vcl/backend/backend_port'),
340
                $default_options);
341
        }
342
    }
343
344
    /**
345
     * Get the admin backend configuration string
346
     *
347
     * @return string
348
     */
349 View Code Duplication
    protected function _getAdminBackend() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
350
        $timeout = Mage::getStoreConfig('turpentine_vcl/backend/admin_timeout');
351
        $admin_options = array(
352
            'first_byte_timeout'    => $timeout.'s',
353
            'between_bytes_timeout' => $timeout.'s',
354
        );
355
        if (Mage::getStoreConfig('turpentine_vcl/backend/load_balancing') != 'no') {
356
            return $this->_vcl_director('admin', $admin_options);
357
        } else {
358
            return $this->_vcl_backend('admin',
359
                Mage::getStoreConfig('turpentine_vcl/backend/backend_host'),
360
                Mage::getStoreConfig('turpentine_vcl/backend/backend_port'),
361
                $admin_options);
362
        }
363
    }
364
365
    /**
366
     * Get the grace period for vcl_fetch
367
     *
368
     * This is curently hardcoded to 15 seconds, will be configurable at some
369
     * point
370
     *
371
     * @return string
372
     */
373
    protected function _getGracePeriod() {
374
        return Mage::getStoreConfig('turpentine_vcl/ttls/grace_period');
375
    }
376
377
    /**
378
     * Get whether debug headers should be enabled or not
379
     *
380
     * @return string
381
     */
382
    protected function _getEnableDebugHeaders() {
383
        return Mage::getStoreConfig('turpentine_varnish/general/varnish_debug')
384
            ? 'true' : 'false';
385
    }
386
387
    /**
388
     * Format the GET variable excludes for insertion in a regex
389
     *
390
     * @return string
391
     */
392
    protected function _getGetParamExcludes() {
393
        return implode('|', Mage::helper('turpentine/data')->cleanExplode(',',
394
            Mage::getStoreConfig('turpentine_vcl/params/get_params')));
395
    }
396
397
    protected function _getIgnoreGetParameters()
398
    {
399
        /** @var Nexcessnet_Turpentine_Helper_Data $helper */
400
        $helper = Mage::helper('turpentine');
401
        $ignoredParameters = $helper->cleanExplode(',', Mage::getStoreConfig('turpentine_vcl/params/ignore_get_params'));
402
        return implode('|', $ignoredParameters);
403
    }
404
405
    /**
406
     * @return boolean
407
     */
408
    protected function _sendUnModifiedUrlToBackend()
409
    {
410
        return Mage::getStoreConfigFlag('turpentine_vcl/params/transfer_unmodified_url');
411
    }
412
413
    /**
414
     * Get the Generate Session
415
     *
416
     * @return string
417
     */
418
    protected function _getGenerateSessionStart() {
419
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
420
            ? '/* -- REMOVED' : '';
421
    }
422
423
    /**
424
     * Get the Generate Session
425
     *
426
     * @return string
427
     */
428
    protected function _getGenerateSessionEnd() {
429
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
430
            ? '-- */' : '';
431
    }
432
433
434
    /**
435
     * Get the Generate Session
436
     *
437
     * @return string
438
     */
439
    protected function _getGenerateSession() {
440
        return Mage::getStoreConfigFlag('turpentine_varnish/general/vcl_fix')
441
            ? 'return (pipe);' : 'call generate_session;';
442
    }
443
444
445
    /**
446
     * Get the Generate Session Expires
447
     *
448
     * @return string
449
     */
450
    protected function _getGenerateSessionExpires() {
451
        return Mage::getStoreConfig('turpentine_varnish/general/vcl_fix')
452
            ? '# call generate_session_expires' : 'call generate_session_expires;';
453
    }
454
455
    /**
456
     * Get the Force Static Caching option
457
     *
458
     * @return string
459
     */
460
    protected function _getForceCacheStatic() {
461
        return Mage::getStoreConfig('turpentine_vcl/static/force_static')
462
            ? 'true' : 'false';
463
    }
464
465
    /**
466
     * Get the Force Static Caching option
467
     *
468
     * @return string
469
     */
470
    protected function _getSimpleHashStatic() {
471
        return Mage::getStoreConfig('turpentine_vcl/static/simple_hash')
472
            ? 'true' : 'false';
473
    }
474
475
    /**
476
     * Format the list of static cache extensions
477
     *
478
     * @return string
479
     */
480
    protected function _getStaticExtensions() {
481
        return implode('|', Mage::helper('turpentine/data')->cleanExplode(',',
482
            Mage::getStoreConfig('turpentine_vcl/static/exts')));
483
    }
484
485
    /**
486
     * Get the static caching TTL
487
     *
488
     * @return string
489
     */
490
    protected function _getStaticTtl() {
491
        return Mage::getStoreConfig('turpentine_vcl/ttls/static_ttl');
492
    }
493
494
    /**
495
     * Format the by-url TTL value list
496
     *
497
     * @return string
498
     */
499
    protected function _getUrlTtls() {
500
        $str = array();
501
        $configTtls = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
502
            Mage::getStoreConfig('turpentine_vcl/ttls/url_ttls'));
503
        $ttls = array();
504
        foreach ($configTtls as $line) {
505
            $ttls[] = explode(',', trim($line));
506
        }
507
        foreach ($ttls as $ttl) {
508
            $str[] = sprintf('if (bereq.url ~ "%s%s") { set beresp.ttl = %ds; }',
509
                $this->getBaseUrlPathRegex(), $ttl[0], $ttl[1]);
510
        }
511
        $str = implode(' else ', $str);
512
        if ($str) {
513
            $str .= sprintf(' else { set beresp.ttl = %ds; }',
514
                $this->_getDefaultTtl());
515
        } else {
516
            $str = sprintf('set beresp.ttl = %ds;', $this->_getDefaultTtl());
517
        }
518
        return $str;
519
    }
520
521
    /**
522
     * Get the Enable Caching value
523
     *
524
     * @return string
525
     */
526
    protected function _getEnableCaching() {
527
        return Mage::helper('turpentine/varnish')->getVarnishEnabled() ?
528
            'true' : 'false';
529
    }
530
531
    /**
532
     * Get the list of allowed debug IPs
533
     *
534
     * @return array
535
     */
536
    protected function _getDebugIps() {
537
        return Mage::helper('turpentine/data')->cleanExplode(',',
538
            Mage::getStoreConfig('dev/restrict/allow_ips'));
539
    }
540
541
    /**
542
     * Get the list of crawler IPs
543
     *
544
     * @return array
545
     */
546
    protected function _getCrawlerIps() {
547
        return Mage::helper('turpentine/data')->cleanExplode(',',
548
            Mage::getStoreConfig('turpentine_vcl/backend/crawlers'));
549
    }
550
551
    /**
552
     * Get the regex formatted list of crawler user agents
553
     *
554
     * @return string
555
     */
556
    protected function _getCrawlerUserAgents() {
557
        return implode('|', Mage::helper('turpentine/data')
558
            ->cleanExplode(',',
559
                Mage::getStoreConfig(
560
                    'turpentine_vcl/backend/crawler_user_agents' )));
561
    }
562
563
    /**
564
     * Get the time to increase a cached objects TTL on cache hit (in seconds).
565
     *
566
     * This should be set very low since it gets added to every hit.
567
     *
568
     * @return string
569
     */
570
    protected function _getLruFactor() {
571
        return Mage::getStoreConfig('turpentine_vcl/ttls/lru_factor');
572
    }
573
574
    /**
575
     * Get the advanced session validation restrictions
576
     *
577
     * Note that if User-Agent Normalization is on then the normalized user-agent
578
     * is used for user-agent validation instead of the full user-agent
579
     *
580
     * @return string
581
     */
582
    protected function _getAdvancedSessionValidationTargets() {
583
        $validation = array();
584
        if (Mage::getStoreConfig('web/session/use_remote_addr')) {
585
            $validation[] = 'client.ip';
586
        }
587
        if (Mage::getStoreConfig('web/session/use_http_via')) {
588
            $validation[] = 'req.http.Via';
589
        }
590
        if (Mage::getStoreConfig('web/session/use_http_x_forwarded_for')) {
591
            $validation[] = 'req.http.X-Forwarded-For';
592
        }
593
        if (Mage::getStoreConfig(
594
                    'web/session/use_http_user_agent' ) &&
595
                ! Mage::getStoreConfig(
596
                    'turpentine_vcl/normalization/user_agent' )) {
597
            $validation[] = 'req.http.User-Agent';
598
        }
599
        return $validation;
600
    }
601
602
    /**
603
     * Remove empty and commented out lines from the generated VCL
604
     *
605
     * @param  string $dirtyVcl generated vcl
606
     * @return string
607
     */
608
    protected function _cleanVcl($dirtyVcl) {
609
        return implode(PHP_EOL,
610
            array_filter(
611
                Mage::helper('turpentine/data')
612
                    ->cleanExplode(PHP_EOL, $dirtyVcl),
613
                array($this, '_cleanVclHelper')
614
            )
615
        );
616
    }
617
618
    /**
619
     * Helper to filter out blank/commented lines for VCL cleaning
620
     *
621
     * @param  string $line
622
     * @return bool
623
     */
624
    protected function _cleanVclHelper($line) {
625
        return $line &&
626
            ((substr($line, 0, 1) != '#' &&
627
            substr($line, 0, 2) != '//') ||
628
            substr($line, 0, 8) == '#include');
629
    }
630
631
    /**
632
     * Format a VCL backend declaration
633
     *
634
     * @param  string $name    name of the backend
635
     * @param  string $host    backend host
636
     * @param  string $port    backend port
637
     * @param  array  $options options
638
     * @return string
639
     */
640
    protected function _vcl_backend($name, $host, $port, $options = array()) {
641
        $tpl = <<<EOS
642
backend {{name}} {
643
    .host = "{{host}}";
644
    .port = "{{port}}";
645
646
EOS;
647
        $vars = array(
648
            'host'  => $host,
649
            'port'  => $port,
650
            'name'  => $name,
651
        );
652
        $str = $this->_formatTemplate($tpl, $vars);
653
        foreach ($options as $key => $value) {
654
            $str .= sprintf('   .%s = %s;', $key, $value).PHP_EOL;
655
        }
656
        $str .= '}'.PHP_EOL;
657
        return $str;
658
    }
659
660
    /**
661
     * Format a VCL director declaration, for load balancing
662
     *
663
     * @param string $name           name of the director, also used to select config settings
664
     * @param array  $backendOptions options for each backend
665
     * @return string
666
     */
667
    protected function _vcl_director($name, $backendOptions) {
668
        $tpl = <<<EOS
669
director {{name}} round-robin {
670
{{backends}}
671
}
672
EOS;
673
        if ('admin' == $name && 'yes_admin' == Mage::getStoreConfig('turpentine_vcl/backend/load_balancing')) {
674
            $backendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
675
                Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes_admin'));
676
            $probeUrl = Mage::getStoreConfig('turpentine_vcl/backend/backend_probe_url_admin');
677
        } else {
678
            $backendNodes = Mage::helper('turpentine/data')->cleanExplode(PHP_EOL,
679
                Mage::getStoreConfig('turpentine_vcl/backend/backend_nodes'));
680
            $probeUrl = Mage::getStoreConfig('turpentine_vcl/backend/backend_probe_url');
681
        }
682
        $backends = '';
683
        foreach ($backendNodes as $backendNode) {
684
            $parts = explode(':', $backendNode, 2);
685
            $host = (empty($parts[0])) ? '127.0.0.1' : $parts[0];
686
            $port = (empty($parts[1])) ? '80' : $parts[1];
687
            $backends .= $this->_vcl_director_backend($host, $port, $probeUrl, $backendOptions);
688
        }
689
        $vars = array(
690
            'name' => $name,
691
            'backends' => $backends
692
        );
693
        return $this->_formatTemplate($tpl, $vars);
694
    }
695
696
    /**
697
     * Format a VCL backend declaration to put inside director
698
     *
699
     * @param string $host     backend host
700
     * @param string $port     backend port
701
     * @param string $probeUrl URL to check if backend is up
702
     * @param array  $options  extra options for backend
703
     * @return string
704
     */
705 View Code Duplication
    protected function _vcl_director_backend($host, $port, $probeUrl = '', $options = array()) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
706
        $tpl = <<<EOS
707
    {
708
        .backend = {
709
            .host = "{{host}}";
710
            .port = "{{port}}";
711
{{probe}}
712
713
EOS;
714
        $vars = array(
715
            'host'  => $host,
716
            'port'  => $port,
717
            'probe' => ''
718
        );
719
        if ( ! empty($probeUrl)) {
720
            $vars['probe'] = $this->_vcl_get_probe($probeUrl);
721
        }
722
        $str = $this->_formatTemplate($tpl, $vars);
723
        foreach ($options as $key => $value) {
724
            $str .= sprintf('            .%s = %s;', $key, $value).PHP_EOL;
725
        }
726
        $str .= <<<EOS
727
        }
728
    }
729
EOS;
730
        return $str;
731
    }
732
733
    /**
734
     * Format a VCL probe declaration to put in backend which is in director
735
     *
736
     * @param string $probeUrl URL to check if backend is up
737
     * @return string
738
     */
739
    protected function _vcl_get_probe($probeUrl) {
740
        $urlParts = parse_url($probeUrl);
741
        if (empty($urlParts)) {
742
            // Malformed URL
743
            return '';
744
        } else {
745
            $tpl = <<<EOS
746
            .probe = {
747
                .request =
748
                    "GET {{probe_path}} HTTP/1.1"
749
                    "Host: {{probe_host}}"
750
                    "Connection: close";
751
            }
752
EOS;
753
            $vars = array(
754
                'probe_host' => $urlParts['host'],
755
                'probe_path' => $urlParts['path']
756
            );
757
            return $this->_formatTemplate($tpl, $vars);
758
        }
759
    }
760
761
    /**
762
     * Format a VCL ACL declaration
763
     *
764
     * @param  string $name  ACL name
765
     * @param  array  $hosts list of hosts to add to the ACL
766
     * @return string
767
     */
768
    protected function _vcl_acl($name, array $hosts) {
769
        $tpl = <<<EOS
770
acl {{name}} {
771
    {{hosts}}
772
}
773
EOS;
774
        $fmtHost = create_function('$h', 'return sprintf(\'"%s";\',$h);');
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
775
        $vars = array(
776
            'name'  => $name,
777
            'hosts' => implode("\n    ", array_map($fmtHost, $hosts)),
778
        );
779
        return $this->_formatTemplate($tpl, $vars);
780
    }
781
782
    /**
783
     * Get the User-Agent normalization sub routine
784
     *
785
     * @return string
786
     */
787
    protected function _vcl_sub_normalize_user_agent() {
788
        /**
789
         * Mobile regex from
790
         * @link http://magebase.com/magento-tutorials/magento-design-exceptions-explained/
791
         */
792
        $tpl = <<<EOS
793
if (req.http.User-Agent ~ "iP(?:hone|ad|od)|BlackBerry|Palm|Googlebot-Mobile|Mobile|mobile|mobi|Windows Mobile|Safari Mobile|Android|Opera (?:Mini|Mobi)") {
794
        set req.http.X-Normalized-User-Agent = "mobile";
795
    } else {
796
        set req.http.X-Normalized-User-Agent = "other";
797
    }
798
799
EOS;
800
        return $tpl;
801
    }
802
803
    /**
804
     * Get the Accept-Encoding normalization sub routine
805
     *
806
     * @return string
807
     */
808
    protected function _vcl_sub_normalize_encoding() {
809
        $tpl = <<<EOS
810
if (req.http.Accept-Encoding) {
811
        if (req.http.Accept-Encoding ~ "\*|gzip") {
812
            set req.http.Accept-Encoding = "gzip";
813
        } else if (req.http.Accept-Encoding ~ "deflate") {
814
            set req.http.Accept-Encoding = "deflate";
815
        } else {
816
            # unknown algorithm
817
            unset req.http.Accept-Encoding;
818
        }
819
    }
820
821
EOS;
822
        return $tpl;
823
    }
824
825
    /**
826
     * Get the Host normalization sub routine
827
     *
828
     * @return string
829
     */
830
    protected function _vcl_sub_normalize_host() {
831
        $tpl = <<<EOS
832
set req.http.Host = "{{normalize_host_target}}";
833
834
EOS;
835
        return $this->_formatTemplate($tpl, array(
836
            'normalize_host_target' => $this->_getNormalizeHostTarget() ));
837
    }
838
839
    /**
840
     * Get the hostname for cookie normalization
841
     *
842
     * @return string
843
     */
844
    protected function _getNormalizeCookieTarget() {
845
        return trim(Mage::getStoreConfig(
846
            'turpentine_vcl/normalization/cookie_target' ));
847
    }
848
849
    /**
850
     * Get the regex for cookie normalization
851
     *
852
     * @return string
853
     */
854
    protected function _getNormalizeCookieRegex() {
855
        return trim(Mage::getStoreConfig(
856
            'turpentine_vcl/normalization/cookie_regex' ));
857
    }
858
859
    /**
860
     * Get the allowed IPs when in maintenance mode
861
     *
862
     * @return string
863
     */
864 View Code Duplication
    protected function _vcl_sub_maintenance_allowed_ips() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
865
        if (( ! $this->_getDebugIps()) || ! Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')) {
866
            return false;
867
        }
868
869
        switch (Mage::getStoreConfig('turpentine_varnish/servers/version')) {
870
            case 4.0:
871
                $tpl = <<<EOS
872
if (req.http.X-Forwarded-For) {
873
    if (req.http.X-Forwarded-For !~ "{{debug_ips}}") {
874
        return (synth(999, "Maintenance mode"));
875
    }
876
}
877
else {
878
    if (client.ip !~ debug_acl) {
879
        return (synth(999, "Maintenance mode"));
880
    }
881
}
882
883
EOS;
884
                break;
885
            default:
886
                $tpl = <<<EOS
887
if (req.http.X-Forwarded-For) {
888
    if(req.http.X-Forwarded-For !~ "{{debug_ips}}") {
889
        error 503;
890
    }
891
} else {
892
    if (client.ip !~ debug_acl) {
893
        error 503;
894
    }
895
}
896
EOS;
897
        }
898
899
        return $this->_formatTemplate($tpl, array(
900
            'debug_ips' => Mage::getStoreConfig('dev/restrict/allow_ips') ));
901
    }
902
903
    /**
904
     * When using Varnish as front door listen on port 80 and Nginx/Apache listen on port 443 for HTTPS, the fix will keep the url parameters when redirect from HTTP to HTTPS.
905
     *
906
     * @return string
907
     */
908
    protected function _vcl_sub_https_redirect_fix() {
909
        $baseUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_WEB);
910
        $baseUrl = str_replace(array('http://','https://'), '', $baseUrl);
911
        $baseUrl = rtrim($baseUrl,'/');
912
        
913
        switch (Mage::getStoreConfig('turpentine_varnish/servers/version')) {
914
            case 4.0:
915
                $tpl = <<<EOS
916
if ( (req.http.host ~ "^(?i)www.$baseUrl" || req.http.host ~ "^(?i)$baseUrl") && req.http.X-Forwarded-Proto !~ "(?i)https") {
917
        return (synth(750, ""));
918
    }
919
EOS;
920
                break;
921
            default:
922
                $tpl = <<<EOS
923
if ( (req.http.host ~ "^(?i)www.$baseUrl" || req.http.host ~ "^(?i)$baseUrl") && req.http.X-Forwarded-Proto !~ "(?i)https") {
924
        error 750 "https://" + req.http.host + req.url;
925
    }
926
EOS;
927
        }
928
929
        return $tpl;
930
    }
931
932
    /**
933
     * Get the allowed IPs when in maintenance mode
934
     *
935
     * @return string
936
     */
937 View Code Duplication
    protected function _vcl_sub_synth()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
938
    {
939
        if (( ! $this->_getDebugIps()) || ! Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')) {
940
            return false;
941
        }
942
943
        switch (Mage::getStoreConfig('turpentine_varnish/servers/version')) {
944
            case 4.0:
945
                $tpl = <<<EOS
946
sub vcl_synth {
947
    if (resp.status == 999) {
948
        set resp.status = 404;
949
        set resp.http.Content-Type = "text/html; charset=utf-8";
950
        synthetic({"{{vcl_synth_content}}"});
951
        return (deliver);
952
    }
953
    return (deliver);
954
}
955
956
EOS;
957
                break;
958
            default:
959
                $tpl = <<<EOS
960
sub vcl_error {
961
    set obj.http.Content-Type = "text/html; charset=utf-8";
962
    synthetic {"{{vcl_synth_content}}"};
963
    return (deliver);
964
}
965
EOS;
966
        }
967
968
        return $this->_formatTemplate($tpl, array(
969
            'vcl_synth_content' => Mage::getStoreConfig('turpentine_vcl/maintenance/custom_vcl_synth')));
970
    }
971
972
    /**
973
     * vcl_synth for fixing https
974
     *
975
     * @return string
976
     */
977
    protected function _vcl_sub_synth_https_fix()
978
    {
979
        $tpl = $this->_vcl_sub_synth();
980
981
        if(!$tpl){
0 ignored issues
show
Bug Best Practice introduced by
The expression $tpl of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
982
            $tpl = <<<EOS
983
sub vcl_synth {
984
    if (resp.status == 750) {
985
        set resp.status = 301;
986
        set resp.http.Location = "https://" + req.http.host + req.url;
987
        return(deliver);
988
    }
989
}
990
EOS;
991
        }else{
992
            $tpl_750 = '
993
sub vcl_synth {
994
    if (resp.status == 750) {
995
        set resp.status = 301;
996
        set resp.http.Location = "https://" + req.http.host + req.url;
997
        return(deliver);
998
    }';
999
1000
        $tpl = str_ireplace('sub vcl_synth {', $tpl_750, $tpl);
1001
        }
1002
1003
        return $tpl;
1004
    }
1005
1006
1007
1008
    /**
1009
     * Build the list of template variables to apply to the VCL template
1010
     *
1011
     * @return array
1012
     */
1013
    protected function _getTemplateVars() {
1014
        $vars = array(
1015
            'default_backend'   => $this->_getDefaultBackend(),
1016
            'admin_backend'     => $this->_getAdminBackend(),
1017
            'admin_frontname'   => $this->_getAdminFrontname(),
1018
            'normalize_host_target' => $this->_getNormalizeHostTarget(),
1019
            'url_base_regex'    => $this->getBaseUrlPathRegex(),
1020
            'allowed_hosts_regex'   => $this->getAllowedHostsRegex(),
1021
            'url_excludes'  => $this->_getUrlExcludes(),
1022
            'get_param_excludes'    => $this->_getGetParamExcludes(),
1023
            'get_param_ignored' => $this->_getIgnoreGetParameters(),
1024
            'default_ttl'   => $this->_getDefaultTtl(),
1025
            'enable_get_excludes'   => ($this->_getGetParamExcludes() ? 'true' : 'false'),
1026
            'enable_get_ignored' => ($this->_getIgnoreGetParameters() ? 'true' : 'false'),
1027
            'send_unmodified_url' => ($this->_sendUnModifiedUrlToBackend() ? 'true' : 'false'),
1028
            'debug_headers' => $this->_getEnableDebugHeaders(),
1029
            'grace_period'  => $this->_getGracePeriod(),
1030
            'force_cache_static'    => $this->_getForceCacheStatic(),
1031
            'simple_hash_static'    => $this->_getSimpleHashStatic(),
1032
            'generate_session_expires'    => $this->_getGenerateSessionExpires(),
1033
            'generate_session'    => $this->_getGenerateSession(),
1034
            'generate_session_start'    => $this->_getGenerateSessionStart(),
1035
            'generate_session_end'    => $this->_getGenerateSessionEnd(),
1036
            'static_extensions' => $this->_getStaticExtensions(),
1037
            'static_ttl'    => $this->_getStaticTtl(),
1038
            'url_ttls'      => $this->_getUrlTtls(),
1039
            'enable_caching'    => $this->_getEnableCaching(),
1040
            'crawler_acl'   => $this->_vcl_acl('crawler_acl',
1041
                $this->_getCrawlerIps()),
1042
            'esi_cache_type_param'  =>
1043
                Mage::helper('turpentine/esi')->getEsiCacheTypeParam(),
1044
            'esi_method_param'  =>
1045
                Mage::helper('turpentine/esi')->getEsiMethodParam(),
1046
            'esi_ttl_param' => Mage::helper('turpentine/esi')->getEsiTtlParam(),
1047
            'secret_handshake'  => Mage::helper('turpentine/varnish')
1048
                ->getSecretHandshake(),
1049
            'crawler_user_agent_regex'  => $this->_getCrawlerUserAgents(),
1050
            // 'lru_factor'    => $this->_getLruFactor(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1051
            'debug_acl'     => $this->_vcl_acl('debug_acl',
1052
                $this->_getDebugIps()),
1053
            'custom_c_code' => file_get_contents(
1054
                $this->_getVclTemplateFilename(self::VCL_CUSTOM_C_CODE_FILE) ),
1055
            'esi_private_ttl'   => Mage::helper('turpentine/esi')
1056
                ->getDefaultEsiTtl(),
1057
        );
1058
1059
        if ((bool) Mage::getStoreConfig('turpentine_vcl/urls/bypass_cache_store_url')) {
1060
            $vars['allowed_hosts'] = $this->_vcl_sub_allowed_hosts_regex();
1061
        }
1062
1063
        if (Mage::getStoreConfig('turpentine_vcl/normalization/encoding')) {
1064
            $vars['normalize_encoding'] = $this->_vcl_sub_normalize_encoding();
1065
        }
1066
        if (Mage::getStoreConfig('turpentine_vcl/normalization/user_agent')) {
1067
            $vars['normalize_user_agent'] = $this->_vcl_sub_normalize_user_agent();
1068
        }
1069
        if (Mage::getStoreConfig('turpentine_vcl/normalization/host')) {
1070
            $vars['normalize_host'] = $this->_vcl_sub_normalize_host();
1071
        }
1072
        if (Mage::getStoreConfig('turpentine_vcl/normalization/cookie_regex')) {
1073
            $vars['normalize_cookie_regex'] = $this->_getNormalizeCookieRegex();
1074
        }
1075
        if (Mage::getStoreConfig('turpentine_vcl/normalization/cookie_target')) {
1076
            $vars['normalize_cookie_target'] = $this->_getNormalizeCookieTarget();
1077
        }
1078
1079
        if (Mage::getStoreConfig('turpentine_vcl/maintenance/enable')) {
1080
            // in vcl_recv set the allowed IPs otherwise load the vcl_error (v3)/vcl_synth (v4)
1081
            $vars['maintenance_allowed_ips'] = $this->_vcl_sub_maintenance_allowed_ips();
1082
            // set the vcl_error from Magento database
1083
            $vars['vcl_synth'] = $this->_vcl_sub_synth();
1084
        }
1085
        
1086
        if (Mage::getStoreConfig('turpentine_varnish/general/https_redirect_fix')) {
1087
            $vars['https_redirect'] = $this->_vcl_sub_https_redirect_fix();
1088
            if(Mage::getStoreConfig('turpentine_varnish/servers/version') == '4.0'){
1089
                $vars['vcl_synth'] = $this->_vcl_sub_synth_https_fix();
1090
            }
1091
        }
1092
1093
        foreach (array('','top') as $position) {
1094
            $customIncludeFile = $this->_getCustomIncludeFilename($position);
1095
            if (is_readable($customIncludeFile)) {
1096
                $key = 'custom_vcl_include';
1097
                $key .= ($position) ? '_'.$position : '';
1098
                $vars[$key] = file_get_contents($customIncludeFile);
1099
            }
1100
        }
1101
1102
        return $vars;
1103
    }
1104
}
1105