Преглед изворни кода

added no_proxy handler - fixes #1318

- handle no_proxy directive when building stream context
- using CIDR matching from Zend library
- uses parts of code provided courtesy of @hoffman
radnan пре 11 година
родитељ
комит
a55c9b6a88
2 измењених фајлова са 157 додато и 0 уклоњено
  1. 144 0
      src/Composer/Util/NoProxyPattern.php
  2. 13 0
      src/Composer/Util/StreamContextFactory.php

+ 144 - 0
src/Composer/Util/NoProxyPattern.php

@@ -0,0 +1,144 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Util;
+
+/**
+ * Tests URLs against no_proxy patterns.
+ */
+class NoProxyPattern
+{
+    /**
+     * @var string[]
+     */
+    protected $rules = array();
+
+    /**
+     * @param string $pattern no_proxy pattern
+     */
+    public function __construct($pattern)
+    {
+        $this->rules = preg_split("/[\s,]+/", $pattern);
+    }
+
+    /**
+     * Test a URL against the stored pattern.
+     *
+     * @param string $url
+     *
+     * @return true if the URL matches one of the rules.
+     */
+    public function test($url)
+    {
+        $host = parse_url($url, PHP_URL_HOST);
+        $port = parse_url($url, PHP_URL_PORT);
+
+        if (empty($port)) {
+            switch (parse_url($url, PHP_URL_SCHEME)) {
+                case 'http':
+                    $port = 80;
+                    break;
+                case 'https':
+                    $port = 443;
+                    break;
+            }
+        }
+
+        foreach ($this->rules as $rule) {
+            $match = false;
+
+            if ($rule == '*') {
+                $match - true;
+            } else {
+                list($ruleHost) = explode(':', $rule);
+                list($base) = explode('/', $ruleHost);
+
+                if (filter_var($base, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+                    // ip or cidr match
+
+                    if (!isset($ip)) {
+                        $ip = gethostbyname($host);
+                    }
+
+                    if (strpos($ruleHost, '/') === false) {
+                        $match = $ip === $ruleHost;
+                    } else {
+                        $match = self::inCIDRBlock($ruleHost, $ip);
+                    }
+                } else {
+                    // match end of domain
+
+                    $haystack = '.' . trim($host, '.') . '.';
+                    $needle = '.'. trim($ruleHost, '.') .'.';
+                    $match = stripos(strrev($haystack), strrev($needle)) === 0;
+                }
+
+                // final port check
+                if ($match && strpos($rule, ':') !== false) {
+                    list(, $rulePort) = explode(':', $rule);
+                    if (!empty($rulePort) && $port != $rulePort) {
+                        $match = false;
+                    }
+                }
+            }
+
+            if ($match) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Check an IP adress against a CIDR
+     *
+     * http://framework.zend.com/svn/framework/extras/incubator/library/ZendX/Whois/Adapter/Cidr.php
+     *
+     * @param string $cidr IPv4 block in CIDR notation
+     * @param string $ip IPv4 address
+     *
+     * @return boolean
+     */
+    private static function inCIDRBlock($cidr, $ip)
+    {
+        // Get the base and the bits from the CIDR
+        list($base, $bits) = explode('/', $cidr);
+
+        // Now split it up into it's classes
+        list($a, $b, $c, $d) = explode('.', $base);
+
+        // Now do some bit shifting/switching to convert to ints
+        $i    = ($a << 24) + ($b << 16) + ($c << 8) + $d;
+        $mask = $bits == 0 ? 0: (~0 << (32 - $bits));
+
+        // Here's our lowest int
+        $low = $i & $mask;
+
+        // Here's our highest int
+        $high = $i | (~$mask & 0xFFFFFFFF);
+
+        // Now split the ip we're checking against up into classes
+        list($a, $b, $c, $d) = explode('.', $ip);
+
+        // Now convert the ip we're checking against to an int
+        $check = ($a << 24) + ($b << 16) + ($c << 8) + $d;
+
+        // If the ip is within the range, including highest/lowest values,
+        // then it's witin the CIDR range
+        if ($check >= $low && $check <= $high) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 13 - 0
src/Composer/Util/StreamContextFactory.php

@@ -63,6 +63,19 @@ final class StreamContextFactory
             }
 
             $options['http']['proxy'] = $proxyURL;
+            
+            // Handle no_proxy directive
+            if (!empty($_SERVER['no_proxy'])) {
+                $host = parse_url($url, PHP_URL_HOST);
+                
+                if (!empty($host)) {
+                    $pattern = new NoProxyPattern($_SERVER['no_proxy']);
+                    
+                    if ($pattern->test($url)) {
+                        $options['http']['proxy'] = '';
+                    }
+                }
+            }
 
             // enabled request_fulluri unless it is explicitly disabled
             switch (parse_url($url, PHP_URL_SCHEME)) {