001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.proxy.handlers.http;
021
022import java.net.InetSocketAddress;
023import java.net.MalformedURLException;
024import java.net.URL;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.mina.proxy.ProxyAuthException;
029import org.apache.mina.proxy.handlers.ProxyRequest;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * HttpProxyRequest.java - Wrapper class for HTTP requests.
035 * 
036 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
037 * @since MINA 2.0.0-M3
038 */
039public class HttpProxyRequest extends ProxyRequest {
040    private final static Logger logger = LoggerFactory.getLogger(HttpProxyRequest.class);
041
042    /**
043     * The HTTP verb.
044     */
045    private final String httpVerb;
046
047    /**
048     * The HTTP URI.
049     */
050    private final String httpURI;
051
052    /**
053     * The HTTP protocol version.
054     */
055    private String httpVersion;
056
057    /**
058     * The target hostname.
059     */
060    private String host;
061
062    /**
063     * The request headers.
064     */
065    private Map<String, List<String>> headers;
066
067    /**
068     * The additionnal properties supplied to use with the proxy for 
069     * authentication for example. 
070     */
071    private transient Map<String, String> properties;
072
073    /**
074     * Constructor which creates a HTTP/1.0 CONNECT request to the specified 
075     * endpoint.
076     *  
077     * @param endpointAddress the endpoint to connect to
078     */
079    public HttpProxyRequest(final InetSocketAddress endpointAddress) {
080        this(endpointAddress, HttpProxyConstants.HTTP_1_0, null);
081    }
082
083    /**
084     * Constructor which creates a CONNECT request to the specified endpoint
085     * using the provided protocol version.
086     *  
087     * @param endpointAddress the endpoint to connect to
088     * @param httpVersion the HTTP protocol version
089     */
090    public HttpProxyRequest(final InetSocketAddress endpointAddress, final String httpVersion) {
091        this(endpointAddress, httpVersion, null);
092    }
093
094    /**
095     * Constructor which creates a CONNECT request to the specified endpoint
096     * using the provided protocol version and setting the requested headers.
097     *  
098     * @param endpointAddress the endpoint to connect to
099     * @param httpVersion the HTTP protocol version
100     * @param headers the additionnal http headers
101     */
102    public HttpProxyRequest(final InetSocketAddress endpointAddress, final String httpVersion,
103            final Map<String, List<String>> headers) {
104        this.httpVerb = HttpProxyConstants.CONNECT;
105        if (!endpointAddress.isUnresolved()) {
106            this.httpURI = endpointAddress.getHostName() + ":" + endpointAddress.getPort();
107        } else {
108            this.httpURI = endpointAddress.getAddress().getHostAddress() + ":" + endpointAddress.getPort();
109        }
110
111        this.httpVersion = httpVersion;
112        this.headers = headers;
113    }
114
115    /**
116     * Constructor which creates a HTTP/1.0 GET request to the specified 
117     * http URI.
118     *  
119     * @param httpURI the target URI
120     */
121    public HttpProxyRequest(final String httpURI) {
122        this(HttpProxyConstants.GET, httpURI, HttpProxyConstants.HTTP_1_0, null);
123    }
124
125    /**
126     * Constructor which creates a GET request to the specified http URI
127     * using the provided protocol version
128     *  
129     * @param httpURI the target URI
130     * @param httpVersion the HTTP protocol version
131     */
132    public HttpProxyRequest(final String httpURI, final String httpVersion) {
133        this(HttpProxyConstants.GET, httpURI, httpVersion, null);
134    }
135
136    /**
137     * Constructor which creates a request using the provided HTTP verb targeted at
138     * the specified http URI using the provided protocol version.
139     * 
140     * @param httpVerb the HTTP verb to use 
141     * @param httpURI the target URI
142     * @param httpVersion the HTTP protocol version
143     */
144    public HttpProxyRequest(final String httpVerb, final String httpURI, final String httpVersion) {
145        this(httpVerb, httpURI, httpVersion, null);
146    }
147
148    /**
149     * Constructor which creates a request using the provided HTTP verb targeted at
150     * the specified http URI using the provided protocol version and setting the 
151     * requested headers.
152     * 
153     * @param httpVerb the HTTP verb to use 
154     * @param httpURI the target URI
155     * @param httpVersion the HTTP protocol version
156     * @param headers the additional http headers
157     */
158    public HttpProxyRequest(final String httpVerb, final String httpURI, final String httpVersion,
159            final Map<String, List<String>> headers) {
160        this.httpVerb = httpVerb;
161        this.httpURI = httpURI;
162        this.httpVersion = httpVersion;
163        this.headers = headers;
164    }
165
166    /**
167     * @return the HTTP request verb.
168     */
169    public final String getHttpVerb() {
170        return httpVerb;
171    }
172
173    /**
174     * @return the HTTP version.
175     */
176    public String getHttpVersion() {
177        return httpVersion;
178    }
179
180    /**
181     * Sets the HTTP version.
182     * 
183     * @param httpVersion the HTTP protocol version
184     */
185    public void setHttpVersion(String httpVersion) {
186        this.httpVersion = httpVersion;
187    }
188
189    /**
190     * @return the host to which we are connecting.
191     */
192    public synchronized final String getHost() {
193        if (host == null) {
194            if (getEndpointAddress() != null && !getEndpointAddress().isUnresolved()) {
195                host = getEndpointAddress().getHostName();
196            }
197
198            if (host == null && httpURI != null) {
199                try {
200                    host = (new URL(httpURI)).getHost();
201                } catch (MalformedURLException e) {
202                    logger.debug("Malformed URL", e);
203                }
204            }
205        }
206
207        return host;
208    }
209
210    /**
211     * @return the request HTTP URI.
212     */
213    public final String getHttpURI() {
214        return httpURI;
215    }
216
217    /**
218     * @return the HTTP headers.
219     */
220    public final Map<String, List<String>> getHeaders() {
221        return headers;
222    }
223
224    /**
225     * Set the HTTP headers.
226     * 
227     * @param headers The HTTP headers to set
228     */
229    public final void setHeaders(Map<String, List<String>> headers) {
230        this.headers = headers;
231    }
232
233    /**
234     * @return additional properties for the request.
235     */
236    public Map<String, String> getProperties() {
237        return properties;
238    }
239
240    /**
241     * Set additional properties for the request.
242     * 
243     * @param properties The properties to add to the reqyest
244     */
245    public void setProperties(Map<String, String> properties) {
246        this.properties = properties;
247    }
248
249    /**
250     * Check if the given property(ies) is(are) set. Otherwise throws a 
251     * {@link ProxyAuthException}.
252     * 
253     * @param propNames The list of property name to check
254     * @throws ProxyAuthException If we get an error during the proxy authentication
255     */
256    public void checkRequiredProperties(String... propNames) throws ProxyAuthException {
257        StringBuilder sb = new StringBuilder();
258        for (String propertyName : propNames) {
259            if (properties.get(propertyName) == null) {
260                sb.append(propertyName).append(' ');
261            }
262        }
263        if (sb.length() > 0) {
264            sb.append("property(ies) missing in request");
265            throw new ProxyAuthException(sb.toString());
266        }
267    }
268
269    /**
270     * @return the string representation of the HTTP request .
271     */
272    public String toHttpString() {
273        StringBuilder sb = new StringBuilder();
274
275        sb.append(getHttpVerb()).append(' ').append(getHttpURI()).append(' ').append(getHttpVersion())
276                .append(HttpProxyConstants.CRLF);
277
278        boolean hostHeaderFound = false;
279
280        if (getHeaders() != null) {
281            for (Map.Entry<String, List<String>> header : getHeaders().entrySet()) {
282                if (!hostHeaderFound) {
283                    hostHeaderFound = header.getKey().equalsIgnoreCase("host");
284                }
285
286                for (String value : header.getValue()) {
287                    sb.append(header.getKey()).append(": ").append(value).append(HttpProxyConstants.CRLF);
288                }
289            }
290
291            if (!hostHeaderFound && getHttpVersion() == HttpProxyConstants.HTTP_1_1) {
292                sb.append("Host: ").append(getHost()).append(HttpProxyConstants.CRLF);
293            }
294        }
295
296        sb.append(HttpProxyConstants.CRLF);
297
298        return sb.toString();
299    }
300}