001/*
002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.platform.web.common.vh;
023
024import static org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants.REQUESTED_URL;
025
026import javax.servlet.ServletRequest;
027import javax.servlet.http.HttpServletRequest;
028
029import org.apache.commons.lang3.StringUtils;
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter;
033import org.nuxeo.runtime.api.Framework;
034
035public class VirtualHostHelper {
036
037    private static final int HTTP_PORT_NUMBER = 80;
038
039    private static final int HTTPS_PORT_NUMBER = 443;
040
041    private static final Log log = LogFactory.getLog(VirtualHostHelper.class);
042
043    private static final String X_FORWARDED_HOST = "x-forwarded-host";
044
045    private static final String X_FORWARDED_PROTO = "x-forwarded-proto";
046
047    private static final String X_FORWARDED_PORT = "x-forwarded-port";
048
049    private static final String VH_HEADER = "nuxeo-virtual-host";
050
051    private static final String VH_PARAM = "nuxeo.virtual.host";
052
053    // Utility class.
054    private VirtualHostHelper() {
055    }
056
057    private static HttpServletRequest getHttpServletRequest(ServletRequest request) {
058        if (request instanceof HttpServletRequest) {
059            return (HttpServletRequest) request;
060        }
061        return null;
062    }
063
064    /**
065     * @return WebApp name : ie : nuxeo
066     */
067    public static String getWebAppName(ServletRequest request) {
068        return getContextPath(request).replace("/", "");
069    }
070
071    /**
072     * @return Server URL as : protocol://serverName:port/
073     */
074    public static String getServerURL(ServletRequest request) {
075        return getServerURL(request, false);
076    }
077
078    private static String getServerUrl(String scheme, String serverName, int serverPort) {
079        StringBuilder sbaseURL = new StringBuilder();
080        sbaseURL.append(scheme);
081        sbaseURL.append("://");
082        sbaseURL.append(serverName);
083        if (serverPort != 0) {
084            if ("http".equals(scheme) && serverPort != HTTP_PORT_NUMBER || "https".equals(scheme)
085                    && serverPort != HTTPS_PORT_NUMBER) {
086                sbaseURL.append(':');
087                sbaseURL.append(serverPort);
088            }
089        }
090        sbaseURL.append('/');
091        return sbaseURL.toString();
092    }
093
094    /**
095     * @return Server URL as : protocol://serverName:port/
096     */
097    public static String getServerURL(ServletRequest request, boolean local) {
098        String baseURL = null;
099        HttpServletRequest httpRequest = getHttpServletRequest(request);
100        if (httpRequest != null) {
101            // Detect Nuxeo specific header for VH
102            String nuxeoVH = httpRequest.getHeader(VH_HEADER);
103            if (nuxeoVH == null) {
104                nuxeoVH = Framework.getProperty(VH_PARAM);
105            }
106            if (!local && nuxeoVH != null && nuxeoVH.contains("http")) {
107                baseURL = nuxeoVH;
108            } else {
109                // default values
110                String serverName = httpRequest.getServerName();
111                int serverPort = httpRequest.getServerPort();
112                String scheme = httpRequest.getScheme();
113
114                if (!local) {
115                    String forwardedPort = httpRequest.getHeader(X_FORWARDED_PORT);
116
117                    if (forwardedPort != null) {
118                        try {
119                            serverPort = Integer.parseInt(forwardedPort);
120                        } catch (NumberFormatException e) {
121                            log.error("Unable to get forwarded port from header", e);
122                        }
123                    }
124
125                    String forwardedProto = httpRequest.getHeader(X_FORWARDED_PROTO);
126                    if (forwardedProto != null) {
127                        scheme = forwardedProto;
128                    }
129
130                    // Detect virtual hosting based in standard header
131                    String forwardedHost = httpRequest.getHeader(X_FORWARDED_HOST);
132                    if (forwardedHost != null) {
133                        if (forwardedHost.contains(":")) {
134                            serverName = forwardedHost.split(":")[0];
135                            serverPort = Integer.valueOf(forwardedHost.split(":")[1]);
136                        } else {
137                            serverName = forwardedHost;
138                            serverPort = "https".equals(scheme) ? HTTPS_PORT_NUMBER : HTTP_PORT_NUMBER;
139                        }
140                    }
141                }
142
143                baseURL = getServerUrl(scheme, serverName, serverPort);
144            }
145        }
146        if (baseURL == null) {
147            log.error("Could not retrieve base url correctly");
148            log.debug("Could not retrieve base url correctly", new Throwable());
149        }
150        return baseURL;
151    }
152
153    /**
154     * @return base URL as protocol://serverName:port/webappName/
155     */
156    public static String getBaseURL(ServletRequest request) {
157        String baseURL = null;
158        String serverUrl = getServerURL(request, false);
159        if (serverUrl != null) {
160            String webAppName = getWebAppName(request);
161
162            baseURL = StringUtils.isNotBlank(webAppName) ? serverUrl + webAppName + '/' : serverUrl;
163
164        }
165        return baseURL;
166    }
167
168    /**
169     * Returns the context path of the application. Try to get it from the {@code ServletRequest} and then from the
170     * {@code org.nuxeo.ecm.contextPath} system property. Fallback on default context path {@code /nuxeo}.
171     */
172    public static String getContextPath(ServletRequest request) {
173        HttpServletRequest httpRequest = getHttpServletRequest(request);
174        String contextPath = null;
175        if (httpRequest != null) {
176            contextPath = httpRequest.getContextPath();
177        }
178        return contextPath != null ? contextPath : getContextPathProperty();
179    }
180
181    public static String getContextPathProperty() {
182        return Framework.getProperty("org.nuxeo.ecm.contextPath", "/nuxeo");
183    }
184
185    /**
186     * Computes the url to be redirected when logging out
187     *
188     * @return redirect URL as protocol://serverName:port/webappName/...
189     * @since 9.1
190     */
191    public static String getRedirectUrl(HttpServletRequest request) {
192        String redirectURL = getBaseURL(request);
193        if (request.getAttribute(REQUESTED_URL) != null) {
194            redirectURL += request.getAttribute(REQUESTED_URL);
195        } else if (request.getParameter(REQUESTED_URL) != null) {
196            redirectURL += request.getParameter(REQUESTED_URL);
197        } else if (NuxeoAuthenticationFilter.getRequestedUrl(request) != null) {
198            redirectURL += NuxeoAuthenticationFilter.getRequestedUrl(request);
199        } else {
200            redirectURL = request.getRequestURL().toString();
201            String queryString = request.getQueryString();
202            if (queryString != null) {
203                redirectURL += '?' + queryString;
204            }
205        }
206        return redirectURL;
207    }
208
209}