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 package org.apache.wiki.util;
020
021 import java.text.DateFormat;
022 import java.text.ParseException;
023 import java.text.SimpleDateFormat;
024 import java.util.Date;
025
026 import javax.servlet.http.Cookie;
027 import javax.servlet.http.HttpServletRequest;
028
029 import org.apache.commons.lang.StringUtils;
030 import org.apache.log4j.Logger;
031
032
033 /**
034 * Contains useful utilities for some common HTTP tasks.
035 *
036 * @since 2.1.61.
037 */
038 public final class HttpUtil {
039
040 static Logger log = Logger.getLogger( HttpUtil.class );
041
042 /** Private constructor to prevent direct instantiation. */
043 private HttpUtil() {
044 }
045
046 /**
047 * returns the remote address by looking into {@code x-forwarded-for} header or, if unavailable,
048 * into {@link HttpServletRequest#getRemoteAddr()}.
049 *
050 * @param req http request
051 * @return remote address associated to the request.
052 */
053 public static String getRemoteAddress( HttpServletRequest req ) {
054 return StringUtils.isNotEmpty ( req.getHeader( "X-Forwarded-For" ) ) ? req.getHeader( "X-Forwarded-For" ) :
055 req.getRemoteAddr();
056 }
057
058 /**
059 * Attempts to retrieve the given cookie value from the request.
060 * Returns the string value (which may or may not be decoded
061 * correctly, depending on browser!), or null if the cookie is
062 * not found. The algorithm will automatically trim leading
063 * and trailing double quotes, if found.
064 *
065 * @param request The current request
066 * @param cookieName The name of the cookie to fetch.
067 * @return Value of the cookie, or null, if there is no such cookie.
068 */
069
070 public static String retrieveCookieValue( HttpServletRequest request, String cookieName ) {
071 Cookie[] cookies = request.getCookies();
072
073 if( cookies != null ) {
074 for( int i = 0; i < cookies.length; i++ ) {
075 if( cookies[i].getName().equals( cookieName ) ) {
076 String value = cookies[i].getValue();
077 if( value.length() == 0 ) {
078 return null;
079 }
080 if( value.charAt( 0 ) == '"' && value.charAt( value.length() - 1 ) == '"' ) {
081 value = value.substring( 1, value.length() - 1 );
082 }
083 return value;
084 }
085 }
086 }
087
088 return null;
089 }
090
091 /**
092 * Creates an ETag based on page information. An ETag is unique to each page
093 * and version, so it can be used to check if the page has changed. Do not
094 * assume that the ETag is in any particular format.
095 *
096 * @param pageName The page name for which the ETag should be created.
097 * @param lastModified The page last modified date for which the ETag should be created.
098 * @return A String depiction of an ETag.
099 */
100 public static String createETag( String pageName, Date lastModified ) {
101 return Long.toString( pageName.hashCode() ^ lastModified.getTime() );
102 }
103
104 /**
105 * If returns true, then should return a 304 (HTTP_NOT_MODIFIED)
106 * @param req the HTTP request
107 * @param pageName the wiki page name to check for
108 * @param lastModified the last modified date of the wiki page to check for
109 * @return the result of the check
110 */
111 public static boolean checkFor304( HttpServletRequest req, String pageName, Date lastModified ) {
112 //
113 // We'll do some handling for CONDITIONAL GET (and return a 304)
114 // If the client has set the following headers, do not try for a 304.
115 //
116 // pragma: no-cache
117 // cache-control: no-cache
118 //
119
120 if( "no-cache".equalsIgnoreCase( req.getHeader( "Pragma" ) )
121 || "no-cache".equalsIgnoreCase( req.getHeader( "cache-control" ) ) ) {
122 // Wants specifically a fresh copy
123 } else {
124 //
125 // HTTP 1.1 ETags go first
126 //
127 String thisTag = createETag( pageName, lastModified );
128
129 String eTag = req.getHeader( "If-None-Match" );
130
131 if( eTag != null && eTag.equals(thisTag) ) {
132 return true;
133 }
134
135 //
136 // Next, try if-modified-since
137 //
138 DateFormat rfcDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
139
140 try {
141 long ifModifiedSince = req.getDateHeader( "If-Modified-Since" );
142
143 //log.info("ifModifiedSince:"+ifModifiedSince);
144 if( ifModifiedSince != -1 ) {
145 long lastModifiedTime = lastModified.getTime();
146
147 //log.info("lastModifiedTime:" + lastModifiedTime);
148 if( lastModifiedTime <= ifModifiedSince ) {
149 return true;
150 }
151 } else {
152 try {
153 String s = req.getHeader("If-Modified-Since");
154
155 if( s != null ) {
156 Date ifModifiedSinceDate = rfcDateFormat.parse(s);
157 //log.info("ifModifiedSinceDate:" + ifModifiedSinceDate);
158 if( lastModified.before(ifModifiedSinceDate) ) {
159 return true;
160 }
161 }
162 } catch (ParseException e) {
163 log.warn(e.getLocalizedMessage(), e);
164 }
165 }
166 } catch( IllegalArgumentException e ) {
167 // Illegal date/time header format.
168 // We fail quietly, and return false.
169 // FIXME: Should really move to ETags.
170 }
171 }
172
173 return false;
174 }
175
176 /**
177 * Attempts to form a valid URI based on the string given. Currently
178 * it can guess email addresses (mailto:). If nothing else is given,
179 * it assumes it to be an http:// url.
180 *
181 * @param uri URI to take a poke at
182 * @return Possibly a valid URI
183 * @since 2.2.8
184 */
185 public static String guessValidURI( String uri ) {
186 if( uri.indexOf( '@' ) != -1 ) {
187 if( !uri.startsWith( "mailto:" ) ) {
188 // Assume this is an email address
189 uri = "mailto:" + uri;
190 }
191 } else if( notBeginningWithHttpOrHttps( uri ) ) {
192 uri = "http://" + uri;
193 }
194
195 return uri;
196 }
197
198 static boolean notBeginningWithHttpOrHttps( String uri ) {
199 return uri.length() > 0 && !( ( uri.startsWith("http://" ) || uri.startsWith( "https://" ) ) );
200 }
201
202 }