001package org.eclipse.aether.util.repository;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 * 
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Locale;
026import java.util.Map;
027import java.util.StringTokenizer;
028import java.util.regex.Pattern;
029
030import org.eclipse.aether.repository.Proxy;
031import org.eclipse.aether.repository.ProxySelector;
032import org.eclipse.aether.repository.RemoteRepository;
033
034/**
035 * A simple proxy selector that selects the first matching proxy from a list of configured proxies.
036 */
037public final class DefaultProxySelector
038    implements ProxySelector
039{
040
041    private List<ProxyDef> proxies = new ArrayList<ProxyDef>();
042
043    /**
044     * Adds the specified proxy definition to the selector. Proxy definitions are ordered, the first matching proxy for
045     * a given repository will be used.
046     * 
047     * @param proxy The proxy definition to add, must not be {@code null}.
048     * @param nonProxyHosts The list of (case-insensitive) host names to exclude from proxying, may be {@code null}.
049     * @return This proxy selector for chaining, never {@code null}.
050     */
051    public DefaultProxySelector add( Proxy proxy, String nonProxyHosts )
052    {
053        if ( proxy == null )
054        {
055            throw new IllegalArgumentException( "proxy not specified" );
056        }
057        proxies.add( new ProxyDef( proxy, nonProxyHosts ) );
058
059        return this;
060    }
061
062    public Proxy getProxy( RemoteRepository repository )
063    {
064        Map<String, ProxyDef> candidates = new HashMap<String, ProxyDef>();
065
066        String host = repository.getHost();
067        for ( ProxyDef proxy : proxies )
068        {
069            if ( !proxy.nonProxyHosts.isNonProxyHost( host ) )
070            {
071                String key = proxy.proxy.getType().toLowerCase( Locale.ENGLISH );
072                if ( !candidates.containsKey( key ) )
073                {
074                    candidates.put( key, proxy );
075                }
076            }
077        }
078
079        String protocol = repository.getProtocol().toLowerCase( Locale.ENGLISH );
080
081        if ( "davs".equals( protocol ) )
082        {
083            protocol = "https";
084        }
085        else if ( "dav".equals( protocol ) )
086        {
087            protocol = "http";
088        }
089        else if ( protocol.startsWith( "dav:" ) )
090        {
091            protocol = protocol.substring( "dav:".length() );
092        }
093
094        ProxyDef proxy = candidates.get( protocol );
095        if ( proxy == null && "https".equals( protocol ) )
096        {
097            proxy = candidates.get( "http" );
098        }
099
100        return ( proxy != null ) ? proxy.proxy : null;
101    }
102
103    static class NonProxyHosts
104    {
105
106        private final Pattern[] patterns;
107
108        public NonProxyHosts( String nonProxyHosts )
109        {
110            List<Pattern> patterns = new ArrayList<Pattern>();
111            if ( nonProxyHosts != null )
112            {
113                for ( StringTokenizer tokenizer = new StringTokenizer( nonProxyHosts, "|" ); tokenizer.hasMoreTokens(); )
114                {
115                    String pattern = tokenizer.nextToken();
116                    pattern = pattern.replace( ".", "\\." ).replace( "*", ".*" );
117                    patterns.add( Pattern.compile( pattern, Pattern.CASE_INSENSITIVE ) );
118                }
119            }
120            this.patterns = patterns.toArray( new Pattern[patterns.size()] );
121        }
122
123        boolean isNonProxyHost( String host )
124        {
125            if ( host != null )
126            {
127                for ( Pattern pattern : patterns )
128                {
129                    if ( pattern.matcher( host ).matches() )
130                    {
131                        return true;
132                    }
133                }
134            }
135            return false;
136        }
137
138    }
139
140    static class ProxyDef
141    {
142
143        final Proxy proxy;
144
145        final NonProxyHosts nonProxyHosts;
146
147        public ProxyDef( Proxy proxy, String nonProxyHosts )
148        {
149            this.proxy = proxy;
150            this.nonProxyHosts = new NonProxyHosts( nonProxyHosts );
151        }
152
153    }
154
155}