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 static java.util.Objects.requireNonNull;
028import java.util.StringTokenizer;
029import java.util.regex.Pattern;
030
031import org.eclipse.aether.repository.Proxy;
032import org.eclipse.aether.repository.ProxySelector;
033import org.eclipse.aether.repository.RemoteRepository;
034
035/**
036 * A simple proxy selector that selects the first matching proxy from a list of configured proxies.
037 */
038public final class DefaultProxySelector
039    implements ProxySelector
040{
041
042    private List<ProxyDef> proxies = new ArrayList<ProxyDef>();
043
044    /**
045     * Adds the specified proxy definition to the selector. Proxy definitions are ordered, the first matching proxy for
046     * a given repository will be used.
047     * 
048     * @param proxy The proxy definition to add, must not be {@code null}.
049     * @param nonProxyHosts The list of (case-insensitive) host names to exclude from proxying, may be {@code null}.
050     * @return This proxy selector for chaining, never {@code null}.
051     */
052    public DefaultProxySelector add( Proxy proxy, String nonProxyHosts )
053    {
054        requireNonNull( proxy, "proxy cannot be null" );
055        proxies.add( new ProxyDef( proxy, nonProxyHosts ) );
056
057        return this;
058    }
059
060    public Proxy getProxy( RemoteRepository repository )
061    {
062        Map<String, ProxyDef> candidates = new HashMap<String, ProxyDef>();
063
064        String host = repository.getHost();
065        for ( ProxyDef proxy : proxies )
066        {
067            if ( !proxy.nonProxyHosts.isNonProxyHost( host ) )
068            {
069                String key = proxy.proxy.getType().toLowerCase( Locale.ENGLISH );
070                if ( !candidates.containsKey( key ) )
071                {
072                    candidates.put( key, proxy );
073                }
074            }
075        }
076
077        String protocol = repository.getProtocol().toLowerCase( Locale.ENGLISH );
078
079        if ( "davs".equals( protocol ) )
080        {
081            protocol = "https";
082        }
083        else if ( "dav".equals( protocol ) )
084        {
085            protocol = "http";
086        }
087        else if ( protocol.startsWith( "dav:" ) )
088        {
089            protocol = protocol.substring( "dav:".length() );
090        }
091
092        ProxyDef proxy = candidates.get( protocol );
093        if ( proxy == null && "https".equals( protocol ) )
094        {
095            proxy = candidates.get( "http" );
096        }
097
098        return ( proxy != null ) ? proxy.proxy : null;
099    }
100
101    static class NonProxyHosts
102    {
103
104        private final Pattern[] patterns;
105
106        NonProxyHosts( String nonProxyHosts )
107        {
108            List<Pattern> patterns = new ArrayList<Pattern>();
109            if ( nonProxyHosts != null )
110            {
111                for ( StringTokenizer tokenizer = new StringTokenizer( nonProxyHosts, "|" ); tokenizer.hasMoreTokens(); )
112                {
113                    String pattern = tokenizer.nextToken();
114                    pattern = pattern.replace( ".", "\\." ).replace( "*", ".*" );
115                    patterns.add( Pattern.compile( pattern, Pattern.CASE_INSENSITIVE ) );
116                }
117            }
118            this.patterns = patterns.toArray( new Pattern[patterns.size()] );
119        }
120
121        boolean isNonProxyHost( String host )
122        {
123            if ( host != null )
124            {
125                for ( Pattern pattern : patterns )
126                {
127                    if ( pattern.matcher( host ).matches() )
128                    {
129                        return true;
130                    }
131                }
132            }
133            return false;
134        }
135
136    }
137
138    static class ProxyDef
139    {
140
141        final Proxy proxy;
142
143        final NonProxyHosts nonProxyHosts;
144
145        ProxyDef( Proxy proxy, String nonProxyHosts )
146        {
147            this.proxy = proxy;
148            this.nonProxyHosts = new NonProxyHosts( nonProxyHosts );
149        }
150
151    }
152
153}