blob: 3cd7f5d13b1fc1b70bae5b0437005100b1427fbf [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.wiki.util;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Contains useful utilities for some common HTTP tasks.
*
* @since 2.1.61.
*/
public final class HttpUtil {
static Logger log = Logger.getLogger( HttpUtil.class );
/** Private constructor to prevent direct instantiation. */
private HttpUtil() {
}
/**
* returns the remote address by looking into {@code x-forwarded-for} header or, if unavailable,
* into {@link HttpServletRequest#getRemoteAddr()}.
*
* @param req http request
* @return remote address associated to the request.
*/
public static String getRemoteAddress( HttpServletRequest req ) {
return StringUtils.isNotEmpty ( req.getHeader( "X-Forwarded-For" ) ) ? req.getHeader( "X-Forwarded-For" ) :
req.getRemoteAddr();
}
/**
* Attempts to retrieve the given cookie value from the request.
* Returns the string value (which may or may not be decoded
* correctly, depending on browser!), or null if the cookie is
* not found. The algorithm will automatically trim leading
* and trailing double quotes, if found.
*
* @param request The current request
* @param cookieName The name of the cookie to fetch.
* @return Value of the cookie, or null, if there is no such cookie.
*/
public static String retrieveCookieValue( HttpServletRequest request, String cookieName ) {
Cookie[] cookies = request.getCookies();
if( cookies != null ) {
for( int i = 0; i < cookies.length; i++ ) {
if( cookies[i].getName().equals( cookieName ) ) {
String value = cookies[i].getValue();
if( value.length() == 0 ) {
return null;
}
if( value.charAt( 0 ) == '"' && value.charAt( value.length() - 1 ) == '"' ) {
value = value.substring( 1, value.length() - 1 );
}
return value;
}
}
}
return null;
}
/**
* Creates an ETag based on page information. An ETag is unique to each page
* and version, so it can be used to check if the page has changed. Do not
* assume that the ETag is in any particular format.
*
* @param pageName The page name for which the ETag should be created.
* @param lastModified The page last modified date for which the ETag should be created.
* @return A String depiction of an ETag.
*/
public static String createETag( String pageName, Date lastModified ) {
return Long.toString( pageName.hashCode() ^ lastModified.getTime() );
}
/**
* If returns true, then should return a 304 (HTTP_NOT_MODIFIED)
* @param req the HTTP request
* @param pageName the wiki page name to check for
* @param lastModified the last modified date of the wiki page to check for
* @return the result of the check
*/
public static boolean checkFor304( HttpServletRequest req, String pageName, Date lastModified ) {
//
// We'll do some handling for CONDITIONAL GET (and return a 304)
// If the client has set the following headers, do not try for a 304.
//
// pragma: no-cache
// cache-control: no-cache
//
if( "no-cache".equalsIgnoreCase( req.getHeader( "Pragma" ) )
|| "no-cache".equalsIgnoreCase( req.getHeader( "cache-control" ) ) ) {
// Wants specifically a fresh copy
} else {
//
// HTTP 1.1 ETags go first
//
String thisTag = createETag( pageName, lastModified );
String eTag = req.getHeader( "If-None-Match" );
if( eTag != null && eTag.equals(thisTag) ) {
return true;
}
//
// Next, try if-modified-since
//
DateFormat rfcDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
try {
long ifModifiedSince = req.getDateHeader( "If-Modified-Since" );
//log.info("ifModifiedSince:"+ifModifiedSince);
if( ifModifiedSince != -1 ) {
long lastModifiedTime = lastModified.getTime();
//log.info("lastModifiedTime:" + lastModifiedTime);
if( lastModifiedTime <= ifModifiedSince ) {
return true;
}
} else {
try {
String s = req.getHeader("If-Modified-Since");
if( s != null ) {
Date ifModifiedSinceDate = rfcDateFormat.parse(s);
//log.info("ifModifiedSinceDate:" + ifModifiedSinceDate);
if( lastModified.before(ifModifiedSinceDate) ) {
return true;
}
}
} catch (ParseException e) {
log.warn(e.getLocalizedMessage(), e);
}
}
} catch( IllegalArgumentException e ) {
// Illegal date/time header format.
// We fail quietly, and return false.
// FIXME: Should really move to ETags.
}
}
return false;
}
/**
* Attempts to form a valid URI based on the string given. Currently
* it can guess email addresses (mailto:). If nothing else is given,
* it assumes it to be an http:// url.
*
* @param uri URI to take a poke at
* @return Possibly a valid URI
* @since 2.2.8
*/
public static String guessValidURI( String uri ) {
if( uri.indexOf( '@' ) != -1 ) {
if( !uri.startsWith( "mailto:" ) ) {
// Assume this is an email address
uri = "mailto:" + uri;
}
} else if( notBeginningWithHttpOrHttps( uri ) ) {
uri = "http://" + uri;
}
return uri;
}
static boolean notBeginningWithHttpOrHttps( final String uri ) {
return uri.length() > 0 && !( ( uri.startsWith("http://" ) || uri.startsWith( "https://" ) ) );
}
/**
* Returns the query string (the portion after the question mark).
*
* @param request The HTTP request to parse.
* @return The query string. If the query string is null, returns an empty string.
*
* @since 2.1.3 (method moved from WikiEngine on 2.11.0.M6)
*/
public static String safeGetQueryString( final HttpServletRequest request, final Charset contentEncoding ) {
if( request == null ) {
return "";
}
String res = request.getQueryString();
if( res != null ) {
res = new String( res.getBytes( StandardCharsets.ISO_8859_1 ), contentEncoding );
//
// Ensure that the 'page=xyz' attribute is removed
// FIXME: Is it really the mandate of this routine to do that?
//
final int pos1 = res.indexOf("page=");
if( pos1 >= 0 ) {
String tmpRes = res.substring( 0, pos1 );
final int pos2 = res.indexOf( "&",pos1 ) + 1;
if ( ( pos2 > 0 ) && ( pos2 < res.length() ) ) {
tmpRes = tmpRes + res.substring(pos2);
}
res = tmpRes;
}
}
return res;
}
}