浏览代码

Merge remote-tracking branch 'radnan/issue-1318'

Jordi Boggiano 12 年之前
父节点
当前提交
2492dea4a8
共有 3 个文件被更改,包括 168 次插入1 次删除
  1. 11 1
      doc/03-cli.md
  2. 144 0
      src/Composer/Util/NoProxyPattern.php
  3. 13 0
      src/Composer/Util/StreamContextFactory.php

+ 11 - 1
doc/03-cli.md

@@ -417,6 +417,16 @@ some tools like git or curl will only use the lower-cased `http_proxy` version.
 Alternatively you can also define the git proxy using
 Alternatively you can also define the git proxy using
 `git config --global http.proxy <proxy url>`.
 `git config --global http.proxy <proxy url>`.
 
 
+### no_proxy
+
+If you are behind a proxy and would like to disable it for certain domains, you
+can use the `no_proxy` env var. Simply set it to a comma separated list of
+domains the proxy should *not* be used for.
+
+The env var accepts domains, IP addresses, and IP address blocks in CIDR
+notation. You can restrict the filter to a particular port (e.g. `:80`). You
+can also set it to `*` to ignore the proxy for all HTTP requests.
+
 ### HTTP_PROXY_REQUEST_FULLURI
 ### HTTP_PROXY_REQUEST_FULLURI
 
 
 If you use a proxy but it does not support the request_fulluri flag, then you
 If you use a proxy but it does not support the request_fulluri flag, then you
@@ -435,7 +445,7 @@ The `COMPOSER_HOME` var allows you to change the composer home directory. This
 is a hidden, global (per-user on the machine) directory that is shared between
 is a hidden, global (per-user on the machine) directory that is shared between
 all projects.
 all projects.
 
 
-By default it points to `/home/<user>/.composer` on *nix,
+By default it points to `/home/<user>/.composer` on \*nix,
 `/Users/<user>/.composer` on OSX and
 `/Users/<user>/.composer` on OSX and
 `C:\Users\<user>\AppData\Roaming\Composer` on Windows.
 `C:\Users\<user>\AppData\Roaming\Composer` on Windows.
 
 

+ 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) {
+            if ($rule == '*') {
+                return true;
+            }
+
+            $match = false;
+
+            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;
             $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
             // enabled request_fulluri unless it is explicitly disabled
             switch (parse_url($url, PHP_URL_SCHEME)) {
             switch (parse_url($url, PHP_URL_SCHEME)) {