2009-09-23 23:26:14 +02:00
// HTTPDemon.java
2005-04-07 21:19:42 +02:00
// -----------------------
2008-07-20 19:14:51 +02:00
// (C) by Michael Peter Christen; mc@yacy.net
2005-04-07 21:19:42 +02:00
// first published on http://www.anomic.de
// Frankfurt, Germany, 2004
2005-06-09 12:07:02 +02:00
//
2008-03-05 14:29:42 +01:00
// $LastChangedDate$
// $LastChangedRevision$
// $LastChangedBy$
2005-04-07 21:19:42 +02:00
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2009-07-19 22:37:44 +02:00
package de.anomic.http.server ;
2005-04-07 21:19:42 +02:00
2005-06-09 12:07:02 +02:00
import java.io.ByteArrayOutputStream ;
2006-12-16 22:40:59 +01:00
import java.io.CharArrayWriter ;
2005-06-09 12:07:02 +02:00
import java.io.File ;
import java.io.FileInputStream ;
2005-05-05 07:32:19 +02:00
import java.io.IOException ;
2005-09-22 12:30:55 +02:00
import java.io.InputStream ;
2005-06-09 12:07:02 +02:00
import java.io.OutputStream ;
import java.io.PrintStream ;
2007-02-02 22:20:53 +01:00
import java.io.UnsupportedEncodingException ;
2005-05-05 07:32:19 +02:00
import java.net.InetAddress ;
2005-06-09 12:07:02 +02:00
import java.net.MalformedURLException ;
2008-08-08 03:40:28 +02:00
import java.net.URLDecoder ;
2007-02-02 22:20:53 +01:00
import java.net.URLEncoder ;
2005-05-09 00:36:26 +02:00
import java.util.Arrays ;
2005-06-09 12:07:02 +02:00
import java.util.Date ;
2005-05-05 07:32:19 +02:00
import java.util.HashMap ;
2005-05-09 00:36:26 +02:00
import java.util.HashSet ;
2005-06-09 12:07:02 +02:00
import java.util.Iterator ;
2008-08-08 03:40:28 +02:00
import java.util.List ;
2008-08-25 20:11:47 +02:00
import java.util.Map ;
2005-05-05 07:32:19 +02:00
import java.util.Properties ;
import java.util.StringTokenizer ;
2008-08-17 12:16:32 +02:00
import java.util.Map.Entry ;
2010-06-01 15:02:11 +02:00
import java.util.concurrent.ConcurrentHashMap ;
2011-01-03 21:52:54 +01:00
import java.util.regex.Pattern ;
2008-08-29 11:42:39 +02:00
import java.util.zip.GZIPInputStream ;
2005-04-19 08:55:57 +02:00
2010-08-23 14:32:02 +02:00
import net.yacy.cora.protocol.Domains ;
import net.yacy.cora.protocol.HeaderFramework ;
import net.yacy.cora.protocol.RequestHeader ;
import net.yacy.cora.protocol.ResponseHeader ;
2009-10-18 02:53:43 +02:00
import net.yacy.document.parser.html.CharacterCoding ;
2009-10-11 02:12:19 +02:00
import net.yacy.kelondro.data.meta.DigestURI ;
2009-10-10 01:13:30 +02:00
import net.yacy.kelondro.logging.Log ;
2009-10-10 01:22:22 +02:00
import net.yacy.kelondro.order.Base64Order ;
import net.yacy.kelondro.order.Digest ;
2009-10-10 03:14:19 +02:00
import net.yacy.kelondro.util.ByteBuffer ;
import net.yacy.kelondro.util.FileUtils ;
import net.yacy.kelondro.util.MemoryControl ;
2009-10-11 02:24:42 +02:00
import net.yacy.kelondro.util.MapTools ;
2009-10-10 01:13:30 +02:00
2008-08-08 03:40:28 +02:00
import org.apache.commons.fileupload.FileItem ;
import org.apache.commons.fileupload.FileItemFactory ;
import org.apache.commons.fileupload.FileUpload ;
import org.apache.commons.fileupload.FileUploadBase ;
import org.apache.commons.fileupload.FileUploadException ;
import org.apache.commons.fileupload.RequestContext ;
import org.apache.commons.fileupload.disk.DiskFileItemFactory ;
2008-05-04 12:53:04 +02:00
2010-11-21 02:29:32 +01:00
import de.anomic.data.UserDB ;
2009-07-19 22:37:44 +02:00
import de.anomic.search.Switchboard ;
2005-05-05 07:32:19 +02:00
import de.anomic.server.serverCore ;
import de.anomic.server.serverHandler ;
import de.anomic.server.serverObjects ;
import de.anomic.server.serverSwitch ;
2010-05-11 13:14:05 +02:00
import de.anomic.server.serverCore.Session ;
2010-11-28 03:57:31 +01:00
import java.util.Set ;
import java.util.concurrent.ConcurrentMap ;
2005-08-30 23:10:39 +02:00
2005-06-09 12:07:02 +02:00
/ * *
* Instances of this class can be passed as argument to the serverCore .
2009-01-01 20:45:15 +01:00
* The generic server dispatches HTTP commands and calls the
2005-06-09 12:07:02 +02:00
* method GET , HEAD or POST in this class
* these methods parse the command line and decide wether to call
* a proxy servlet or a file server servlet
* /
2009-07-19 22:37:44 +02:00
public final class HTTPDemon implements serverHandler , Cloneable {
2005-06-09 12:07:02 +02:00
2007-02-05 20:46:50 +01:00
/ * *
* < p > < code > public static final String < strong > ADMIN_ACCOUNT_B64MD5 < / strong > = " adminAccountBase64MD5 " < / code > < / p >
2010-05-11 13:14:05 +02:00
* < p > Name of the setting holding the authentication hash for the static < code > admin < / code > - account . It is calculated
2009-10-11 02:24:42 +02:00
* by first encoding < code > username : password < / code > as Base64 and hashing it using { @link MapTools # encodeMD5Hex ( String ) } . < / p >
2007-02-05 20:46:50 +01:00
* /
public static final String ADMIN_ACCOUNT_B64MD5 = " adminAccountBase64MD5 " ;
2007-01-06 12:05:50 +01:00
public static final int ERRORCASE_MESSAGE = 4 ;
public static final int ERRORCASE_FILE = 5 ;
2011-02-12 00:37:13 +01:00
private static final File TMPDIR = new File ( System . getProperty ( " java.io.tmpdir " ) ) ;
private static final FileItemFactory DISK_FILE_ITEM_FACTORY = new DiskFileItemFactory ( 5 * 1024 * 1024 , TMPDIR ) ;
2009-11-18 16:05:51 +01:00
2009-07-19 22:37:44 +02:00
private static AlternativeDomainNames alternativeResolver = null ;
2007-01-06 12:05:50 +01:00
2005-06-09 12:07:02 +02:00
/ * *
2010-11-28 03:57:31 +01:00
* A Set containing extensions that indicate content that should not be transported
2005-06-09 12:07:02 +02:00
* using zipped content encoding
* @see # shallTransportZipped ( String )
* /
2006-03-23 21:12:23 +01:00
//TODO: Load this from a file
2010-11-28 03:57:31 +01:00
private static final Set < String > disallowZippedContentEncoding = new HashSet < String > ( Arrays . asList ( new String [ ] {
2009-02-06 16:06:19 +01:00
" .gz " , " .tgz " , " .jpg " , " .jpeg " , " .png " , " .mp3 " , " .mov " , " .avi " , " .gif " , " .zip " , " .rar " , " .bz2 " , " .lha " , " .jar " , " .rpm " , " .arc " , " .arj " , " .wmv " , " .ico " , " .bmp "
2005-06-09 12:07:02 +02:00
} ) ) ;
2005-04-07 21:19:42 +02:00
// static objects
public static final String vDATE = " <<REPL>> " ;
public static final String copyright = " [ HTTP SERVER: AnomicHTTPD v " + vDATE + " by Michael Christen / www.anomic.de ] " ;
public static final String hline = " ------------------------------------------------------------------------------- " ;
2005-06-09 12:07:02 +02:00
2010-11-28 03:57:31 +01:00
public static final ConcurrentMap < String , String > reverseMappingCache = new ConcurrentHashMap < String , String > ( ) ;
2011-02-12 00:37:13 +01:00
private static volatile Switchboard switchboard ;
private static String virtualHost ;
2005-06-09 12:07:02 +02:00
public static boolean keepAliveSupport = false ;
2010-11-28 03:57:31 +01:00
private static ConcurrentMap < String , Long > YaCyHopAccessRequester = new ConcurrentHashMap < String , Long > ( ) ;
private static ConcurrentMap < String , Long > YaCyHopAccessTargets = new ConcurrentHashMap < String , Long > ( ) ;
2005-06-09 12:07:02 +02:00
2005-04-07 21:19:42 +02:00
// for authentication
2005-10-07 15:49:07 +02:00
private boolean use_proxyAccounts = false ;
2009-09-23 23:26:14 +02:00
private boolean proxyAccounts_init = false ; // is use_proxyAccounts set?
2005-04-19 08:55:57 +02:00
2005-06-09 12:07:02 +02:00
private int emptyRequestCount = 0 ;
private int keepAliveRequestCount = 0 ;
// needed for logging
2009-01-31 00:33:47 +01:00
private final static Log log = new Log ( " HTTPD " ) ;
2005-10-07 15:49:07 +02:00
2005-04-07 21:19:42 +02:00
// class methods
2009-07-19 22:37:44 +02:00
public HTTPDemon ( final serverSwitch s ) {
2005-04-07 21:19:42 +02:00
// handler info
2009-07-19 22:37:44 +02:00
HTTPDemon . switchboard = ( Switchboard ) s ;
HTTPDemon . virtualHost = switchboard . getConfig ( " fileHost " , " localhost " ) ;
2005-04-07 21:19:42 +02:00
// authentication: by default none
2005-10-07 15:49:07 +02:00
this . proxyAccounts_init = false ;
2005-06-09 12:07:02 +02:00
// configuring keep alive support
2010-01-11 00:09:48 +01:00
keepAliveSupport = Boolean . parseBoolean ( switchboard . getConfig ( " connectionKeepAliveSupport " , " false " ) ) ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
/ * *
* Can be used to reset this { @link serverHandler } oject so that
* it can be reused for further connections
* @see de . anomic . server . serverHandler # reset ( )
* /
2005-04-19 08:55:57 +02:00
public void reset ( ) {
2005-10-07 15:49:07 +02:00
this . proxyAccounts_init = false ;
2005-06-09 12:07:02 +02:00
this . emptyRequestCount = 0 ;
this . keepAliveRequestCount = 0 ;
2010-05-11 13:14:05 +02:00
}
2011-02-12 00:37:13 +01:00
private static boolean allowProxy ( final Session session ) {
2008-03-05 14:41:54 +01:00
final String proxyClient = switchboard . getConfig ( " proxyClient " , " * " ) ;
2010-05-11 13:14:05 +02:00
return ( proxyClient . equals ( " * " ) ) ? true : match ( session . userAddress . getHostAddress ( ) , proxyClient ) ;
}
2011-02-12 00:37:13 +01:00
private static boolean allowServer ( final Session session ) {
2008-03-05 14:41:54 +01:00
final String serverClient = switchboard . getConfig ( " serverClient " , " * " ) ;
2010-05-11 13:14:05 +02:00
return ( serverClient . equals ( " * " ) ) ? true : match ( session . userAddress . getHostAddress ( ) , serverClient ) ;
2005-04-07 21:19:42 +02:00
}
2010-05-11 13:14:05 +02:00
2011-02-12 00:37:13 +01:00
private static boolean allowYaCyHop ( final Session session ) {
2010-05-11 13:14:05 +02:00
return switchboard . getConfigBool ( " YaCyHop " , false ) ;
}
2008-08-02 14:12:04 +02:00
private static boolean match ( final String key , final String latch ) {
2005-06-09 12:07:02 +02:00
// the latch is a comma-separated list of patterns
// each pattern may contain one wildcard-character '*' which matches anything
2008-08-02 14:12:04 +02:00
final StringTokenizer st = new StringTokenizer ( latch , " , " ) ;
2005-06-09 12:07:02 +02:00
String pattern ;
while ( st . hasMoreTokens ( ) ) {
pattern = st . nextToken ( ) ;
2005-07-04 01:33:25 +02:00
if ( key . matches ( pattern ) ) return true ;
/ *
2005-06-09 12:07:02 +02:00
pos = pattern . indexOf ( " * " ) ;
if ( pos < 0 ) {
// no wild card: exact match
if ( key . equals ( pattern ) ) return true ;
} else {
// wild card: match left and right side of pattern
if ( ( key . startsWith ( pattern . substring ( 0 , pos ) ) ) & &
( key . endsWith ( pattern . substring ( pos + 1 ) ) ) ) return true ;
}
2005-07-04 01:33:25 +02:00
* /
2005-06-09 12:07:02 +02:00
}
return false ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2005-04-07 21:19:42 +02:00
public String greeting ( ) { // OBLIGATORIC FUNCTION
2005-06-09 12:07:02 +02:00
// a response line upon connection is send to client
// if no response line is wanted, return "" or null
return null ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2008-08-02 14:12:04 +02:00
public String error ( final Throwable e ) { // OBLIGATORIC FUNCTION
2005-06-09 12:07:02 +02:00
// return string in case of any error that occurs during communication
// is always (but not only) called if an IO-dependent exception occurrs.
2008-08-13 12:37:53 +02:00
log . logSevere ( " Unexpected Error. " + e . getClass ( ) . getName ( ) , e ) ;
2008-08-02 14:12:04 +02:00
final String message = e . getMessage ( ) ;
2010-01-13 19:41:33 +01:00
if ( message ! = null & & message . indexOf ( " heap space " ) > 0 ) Log . logException ( e ) ;
2007-03-08 17:15:40 +01:00
return " 501 Exception occurred: " + message ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
/ * *
2008-05-20 00:17:53 +02:00
* This function is used to determine if a persistent connection was requested by the client .
2005-06-09 12:07:02 +02:00
* @param header the received http - headers
2011-02-12 00:37:13 +01:00
* @param prop
2005-06-09 12:07:02 +02:00
* @return < code > true < / code > if a persistent connection was requested or < code > false < / code > otherwise
* /
2011-02-12 00:37:13 +01:00
private boolean handlePersistentConnection ( final RequestHeader header , final Properties prop ) {
2005-06-09 12:07:02 +02:00
if ( ! keepAliveSupport ) {
2010-05-11 13:14:05 +02:00
prop . put ( HeaderFramework . CONNECTION_PROP_PERSISTENT , " close " ) ;
2005-06-09 12:07:02 +02:00
return false ;
}
// getting the http version that is used by the client
2010-05-11 13:14:05 +02:00
final String httpVersion = prop . getProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , " HTTP/0.9 " ) ;
2005-06-09 12:07:02 +02:00
// managing keep-alive: in HTTP/0.9 and HTTP/1.0 every connection is closed
// afterwards. In HTTP/1.1 (and above, in the future?) connections are
// persistent by default, but closed with the "Connection: close"
// property.
2009-07-19 22:37:44 +02:00
boolean persistent = ! ( httpVersion . equals ( HeaderFramework . HTTP_VERSION_0_9 ) | | httpVersion . equals ( HeaderFramework . HTTP_VERSION_1_0 ) ) ;
if ( ( header . get ( RequestHeader . CONNECTION , " keep-alive " ) ) . toLowerCase ( ) . indexOf ( " close " ) ! = - 1 | |
( header . get ( RequestHeader . PROXY_CONNECTION , " keep-alive " ) ) . toLowerCase ( ) . indexOf ( " close " ) ! = - 1 ) {
2005-06-09 12:07:02 +02:00
persistent = false ;
}
2009-07-19 22:37:44 +02:00
final String transferEncoding = header . get ( HeaderFramework . TRANSFER_ENCODING , " identity " ) ;
2010-05-11 13:14:05 +02:00
final boolean isPostRequest = prop . getProperty ( HeaderFramework . CONNECTION_PROP_METHOD ) . equals ( HeaderFramework . METHOD_POST ) ;
2009-07-19 22:37:44 +02:00
final boolean hasContentLength = header . containsKey ( HeaderFramework . CONTENT_LENGTH ) ;
final boolean hasTransferEncoding = header . containsKey ( HeaderFramework . TRANSFER_ENCODING ) & & ! transferEncoding . equalsIgnoreCase ( " identity " ) ;
2006-11-09 06:59:56 +01:00
2005-06-09 12:07:02 +02:00
// if the request does not contain a content-length we have to close the connection
// independently of the value of the connection header
2006-11-09 06:59:56 +01:00
if ( persistent & & isPostRequest & & ! ( hasContentLength | | hasTransferEncoding ) )
2010-05-11 13:14:05 +02:00
prop . put ( HeaderFramework . CONNECTION_PROP_PERSISTENT , " close " ) ;
else prop . put ( HeaderFramework . CONNECTION_PROP_PERSISTENT , persistent ? " keep-alive " : " close " ) ;
2005-06-09 12:07:02 +02:00
return persistent ;
}
2009-07-17 15:59:21 +02:00
public static int staticAdminAuthenticated ( final String authorization , final serverSwitch sw ) {
2008-05-15 13:26:43 +02:00
// the authorization string must be given with the truncated 6 bytes at the beginning
2008-08-02 14:12:04 +02:00
final String adminAccountBase64MD5 = sw . getConfig ( ADMIN_ACCOUNT_B64MD5 , " " ) ;
2008-05-20 00:17:53 +02:00
if ( adminAccountBase64MD5 . length ( ) = = 0 ) return 2 ; // no password stored
2010-09-22 22:50:02 +02:00
if ( authorization = = null | | authorization . length ( ) = = 0 ) return 1 ;
2009-01-30 16:33:00 +01:00
if ( adminAccountBase64MD5 . equals ( Digest . encodeMD5Hex ( authorization ) ) ) return 4 ; // hard-authenticated, all ok
2007-12-10 03:04:48 +01:00
return 1 ;
2007-02-05 20:46:50 +01:00
}
2010-05-11 13:14:05 +02:00
private boolean handleYaCyHopAuthentication ( final RequestHeader header , Properties prop , Session session ) {
2007-02-09 16:25:10 +01:00
// check if the user has allowed that his/her peer is used for hops
2010-05-11 13:14:05 +02:00
if ( ! allowYaCyHop ( session ) ) return false ;
2007-02-09 16:25:10 +01:00
// proxy hops must identify with 4 criteria:
// the accessed port must not be port 80
2010-05-11 13:14:05 +02:00
final String host = prop . getProperty ( HeaderFramework . CONNECTION_PROP_HOST ) ;
2007-02-09 16:25:10 +01:00
if ( host = = null ) return false ;
int pos ;
2010-01-13 01:23:07 +01:00
if ( ( pos = host . indexOf ( ':' ) ) < 0 ) {
2007-02-09 16:25:10 +01:00
// default port 80
return false ; // not allowed
}
2008-08-02 15:57:00 +02:00
if ( Integer . parseInt ( host . substring ( pos + 1 ) ) = = 80 ) return false ;
2007-02-05 20:46:50 +01:00
// the access path must be into the yacy protocol path; it must start with 'yacy'
2010-05-11 13:14:05 +02:00
if ( ! ( prop . getProperty ( HeaderFramework . CONNECTION_PROP_PATH , " " ) . startsWith ( " /yacy/ " ) ) ) return false ;
2007-02-05 20:46:50 +01:00
// the accessing client must identify with user:password, where
// user = addressed peer name
// pw = addressed peer hash (b64-hash)
2009-07-19 22:37:44 +02:00
final String auth = header . get ( RequestHeader . PROXY_AUTHORIZATION , " xxxxxx " ) ;
2008-08-17 12:16:32 +02:00
if ( getAlternativeResolver ( ) ! = null ) {
2009-01-30 16:33:00 +01:00
final String test = Base64Order . standardCoder . encodeString ( getAlternativeResolver ( ) . myName ( ) + " : " + getAlternativeResolver ( ) . myID ( ) ) ;
2008-05-06 01:13:47 +02:00
if ( ! test . equals ( auth . trim ( ) . substring ( 6 ) ) ) return false ;
}
2007-02-05 20:46:50 +01:00
// the accessing client must use a yacy user-agent
2009-07-19 22:37:44 +02:00
if ( ! ( ( header . get ( HeaderFramework . USER_AGENT , " " ) ) . startsWith ( " yacy " ) ) ) return false ;
2007-02-09 16:25:10 +01:00
// furthermore, YaCy hops must not exceed a specific access frequency
// check access requester frequency: protection against DoS against this peer
2010-05-11 13:14:05 +02:00
final String requester = prop . getProperty ( HeaderFramework . CONNECTION_PROP_CLIENTIP ) ;
2007-02-09 16:25:10 +01:00
if ( requester = = null ) return false ;
if ( lastAccessDelta ( YaCyHopAccessRequester , requester ) < 10000 ) return false ;
2008-08-06 21:43:12 +02:00
YaCyHopAccessRequester . put ( requester , Long . valueOf ( System . currentTimeMillis ( ) ) ) ;
2007-02-09 16:25:10 +01:00
// check access target frequecy: protection against DoS from a single peer by several different requesters
if ( lastAccessDelta ( YaCyHopAccessTargets , host ) < 3000 ) return false ;
2008-08-06 21:43:12 +02:00
YaCyHopAccessTargets . put ( host , Long . valueOf ( System . currentTimeMillis ( ) ) ) ;
2007-02-09 16:25:10 +01:00
// passed all tests
return true ;
2007-02-05 20:46:50 +01:00
}
2010-06-01 15:02:11 +02:00
private static long lastAccessDelta ( final Map < String , Long > accessTable , final String domain ) {
2008-08-02 14:12:04 +02:00
final Long lastAccess = accessTable . get ( domain ) ;
2011-02-12 00:37:13 +01:00
return ( lastAccess = = null ) ? Long . MAX_VALUE : System . currentTimeMillis ( ) - lastAccess . longValue ( ) ;
2007-02-09 16:25:10 +01:00
}
2011-02-12 00:37:13 +01:00
private boolean handleProxyAuthentication ( final RequestHeader header , final Properties prop , final Session session ) throws IOException {
2005-06-09 12:07:02 +02:00
// getting the http version that is used by the client
2010-05-11 13:14:05 +02:00
final String httpVersion = prop . getProperty ( " HTTP " , " HTTP/0.9 " ) ;
2005-06-09 12:07:02 +02:00
// reading the authentication settings from switchboard
2008-03-05 14:29:42 +01:00
if ( ! this . proxyAccounts_init ) {
2008-08-06 21:43:12 +02:00
this . use_proxyAccounts = switchboard . getConfigBool ( " use_proxyAccounts " , false ) ;
2011-02-12 00:37:13 +01:00
this . proxyAccounts_init = true ; // is initialised
}
2005-06-09 12:07:02 +02:00
2005-10-07 15:49:07 +02:00
if ( this . use_proxyAccounts ) {
2009-07-19 22:37:44 +02:00
final String auth = header . get ( RequestHeader . PROXY_AUTHORIZATION , " xxxxxx " ) ;
2011-02-12 00:37:13 +01:00
UserDB . Entry entry = switchboard . userDB . ipAuth ( session . userAddress . getHostAddress ( ) ) ;
if ( entry = = null ) {
entry = switchboard . userDB . proxyAuth ( auth , session . userAddress . getHostAddress ( ) ) ;
}
if ( entry ! = null ) {
2008-08-02 14:12:04 +02:00
final int returncode = entry . surfRight ( ) ;
2011-02-12 00:37:13 +01:00
if ( returncode = = UserDB . Entry . PROXY_ALLOK ) {
return true ;
}
final serverObjects tp = new serverObjects ( ) ;
if ( returncode = = UserDB . Entry . PROXY_TIMELIMIT_REACHED ) {
2006-05-11 15:12:35 +02:00
tp . put ( " limit " , " 1 " ) ; //time per day
tp . put ( " limit_timelimit " , entry . getTimeLimit ( ) ) ;
2010-05-11 13:14:05 +02:00
sendRespondError ( prop , session . out , 403 , " Internet-Timelimit reached " , new File ( " proxymsg/proxylimits.inc " ) , tp , null ) ;
2011-02-12 00:37:13 +01:00
} else if ( returncode = = UserDB . Entry . PROXY_NORIGHT ) {
2006-05-11 15:12:35 +02:00
tp . put ( " limit " , " 0 " ) ;
2010-05-11 13:14:05 +02:00
sendRespondError ( prop , session . out , 403 , " Proxy use forbidden " , new File ( " proxymsg/proxylimits.inc " ) , tp , null ) ;
2006-05-11 15:12:35 +02:00
}
2005-12-07 00:51:29 +01:00
return false ;
2011-02-12 00:37:13 +01:00
}
2005-10-07 15:49:07 +02:00
// ask for authenticate
2010-05-11 13:14:05 +02:00
session . out . write ( ( httpVersion + " 407 Proxy Authentication Required " + serverCore . CRLF_STRING +
2011-02-12 00:37:13 +01:00
RequestHeader . PROXY_AUTHENTICATE + " : Basic realm= \" log-in \" " + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2010-05-11 13:14:05 +02:00
session . out . write ( ( HeaderFramework . CONTENT_LENGTH + " : 0 \ r \ n " ) . getBytes ( ) ) ;
session . out . write ( " \ r \ n " . getBytes ( ) ) ;
session . out . flush ( ) ;
2005-10-07 15:49:07 +02:00
return false ;
2005-12-07 00:51:29 +01:00
}
return true ;
2005-06-09 12:07:02 +02:00
}
2011-02-12 00:37:13 +01:00
public Boolean EMPTY ( final String arg , final Session session ) throws IOException {
return ( + + this . emptyRequestCount > 10 ) ? serverCore . TERMINATE_CONNECTION : serverCore . RESUME_CONNECTION ;
2010-05-11 13:14:05 +02:00
}
2011-02-12 00:37:13 +01:00
public Boolean UNKNOWN ( final String requestLine , final Session session ) throws IOException {
2010-05-11 13:14:05 +02:00
Properties prop = parseRequestLine ( HeaderFramework . METHOD_GET , requestLine , session ) ;
2005-06-09 12:07:02 +02:00
int pos ;
String unknownCommand = null , args = null ;
2010-01-13 01:23:07 +01:00
if ( ( pos = requestLine . indexOf ( ' ' ) ) > 0 ) {
2005-06-09 12:07:02 +02:00
unknownCommand = requestLine . substring ( 0 , pos ) ;
args = requestLine . substring ( pos + 1 ) ;
} else {
unknownCommand = requestLine ;
args = " " ;
}
2010-05-11 13:14:05 +02:00
parseRequestLine ( unknownCommand , args , session ) ;
2005-06-09 12:07:02 +02:00
2010-05-11 13:14:05 +02:00
sendRespondError ( prop , session . out , 4 , 501 , null , unknownCommand + " method not implemented " , null ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
}
2010-05-11 13:14:05 +02:00
2011-02-12 00:37:13 +01:00
public Boolean GET ( final String arg , final Session session ) {
2005-06-09 12:07:02 +02:00
try {
// parsing the http request line
2011-02-12 00:37:13 +01:00
final Properties prop = parseRequestLine ( HeaderFramework . METHOD_GET , arg , session ) ;
2005-06-09 12:07:02 +02:00
// we now know the HTTP version. depending on that, we read the header
2010-05-11 13:14:05 +02:00
final String httpVersion = prop . getProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , HeaderFramework . HTTP_VERSION_0_9 ) ;
2011-02-12 00:37:13 +01:00
final RequestHeader header = ( httpVersion . equals ( HeaderFramework . HTTP_VERSION_0_9 ) )
? new RequestHeader ( reverseMappingCache )
: readHeader ( prop , session ) ;
2005-06-09 12:07:02 +02:00
// handling transparent proxy support
2011-01-03 21:52:54 +01:00
handleTransparentProxySupport ( header , prop , virtualHost , HTTPDProxyHandler . isTransparentProxy ) ;
2005-06-09 12:07:02 +02:00
// determines if the connection should be kept alive
2010-05-11 13:14:05 +02:00
handlePersistentConnection ( header , prop ) ;
2005-06-09 12:07:02 +02:00
2010-05-11 13:14:05 +02:00
if ( prop . getProperty ( HeaderFramework . CONNECTION_PROP_HOST ) . equals ( virtualHost ) ) {
2005-06-09 12:07:02 +02:00
// pass to server
2010-05-11 13:14:05 +02:00
if ( allowServer ( session ) ) {
HTTPDFileHandler . doGet ( prop , header , session . out ) ;
2005-04-07 21:19:42 +02:00
} else {
2005-06-09 12:07:02 +02:00
// not authorized through firewall blocking (ip does not match filter)
2010-10-11 13:38:36 +02:00
session . out . write ( ( httpVersion + " 403 refused (IP not granted, 1) " + serverCore . CRLF_STRING + serverCore . CRLF_STRING + " you are not allowed to connect to this server, because you are using a non-granted IP ( " + session . userAddress . getHostAddress ( ) + " ). allowed are only connections that match with the following filter: " + switchboard . getConfig ( " serverClient " , " * " ) + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
}
} else {
// pass to proxy
2010-05-11 13:14:05 +02:00
if ( ( ( allowYaCyHop ( session ) ) & & ( handleYaCyHopAuthentication ( header , prop , session ) ) ) | |
( ( allowProxy ( session ) ) & & ( handleProxyAuthentication ( header , prop , session ) ) ) ) {
HTTPDProxyHandler . doGet ( prop , header , session . out ) ;
2005-06-09 12:07:02 +02:00
} else {
// not authorized through firewall blocking (ip does not match filter)
2010-10-11 13:38:36 +02:00
session . out . write ( ( httpVersion + " 403 refused (IP not granted, 2) " + serverCore . CRLF_STRING + serverCore . CRLF_STRING + " you are not allowed to connect to this proxy, because you are using a non-granted IP ( " + session . userAddress . getHostAddress ( ) + " ). allowed are only connections that match with the following filter: " + switchboard . getConfig ( " proxyClient " , " * " ) + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
2005-04-07 21:19:42 +02:00
}
2005-05-17 10:25:04 +02:00
}
2005-06-09 12:07:02 +02:00
2010-05-11 13:14:05 +02:00
return prop . getProperty ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) . equals ( " keep-alive " ) ? serverCore . RESUME_CONNECTION : serverCore . TERMINATE_CONNECTION ;
2008-08-02 14:12:04 +02:00
} catch ( final Exception e ) {
2010-05-11 13:14:05 +02:00
logUnexpectedError ( e , session . userAddress . getHostAddress ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
}
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2011-02-12 00:37:13 +01:00
private void logUnexpectedError ( final Exception e , final String address ) {
2005-06-28 13:27:31 +02:00
if ( e instanceof InterruptedException ) {
2008-08-13 12:37:53 +02:00
log . logInfo ( " Interruption detected " ) ;
2005-06-28 13:27:31 +02:00
} else {
2008-08-02 14:12:04 +02:00
final String errorMsg = e . getMessage ( ) ;
2005-06-29 11:50:48 +02:00
if ( errorMsg ! = null ) {
if ( errorMsg . startsWith ( " Socket closed " ) ) {
2010-05-11 13:14:05 +02:00
log . logInfo ( " httpd shutdown detected ... ( " + e . getMessage ( ) + " ), client = " + address ) ;
2005-06-29 12:05:38 +02:00
} else if ( ( errorMsg . startsWith ( " Broken pipe " ) | | errorMsg . startsWith ( " Connection reset " ) ) ) {
2005-06-29 11:50:48 +02:00
// client closed the connection, so we just end silently
2010-05-11 13:14:05 +02:00
log . logInfo ( " Client unexpectedly closed connection... ( " + e . getMessage ( ) + " ), client = " + address ) ;
2006-10-19 13:10:56 +02:00
} else if ( errorMsg . equals ( " 400 Bad request " ) ) {
2010-05-11 13:14:05 +02:00
log . logInfo ( " Bad client request ... ( " + e . getMessage ( ) + " ), client = " + address ) ;
2005-06-29 12:05:38 +02:00
} else {
2010-05-11 13:14:05 +02:00
log . logSevere ( " Unexpected Error ... ( " + e . getMessage ( ) + " ), client = " + address , e ) ;
2005-06-29 11:50:48 +02:00
}
2005-06-28 13:27:31 +02:00
} else {
2010-05-11 13:14:05 +02:00
log . logSevere ( " Unexpected Error ... ( " + e . getMessage ( ) + " ), client = " + address , e ) ;
2005-06-28 13:27:31 +02:00
}
}
}
2011-02-12 00:37:13 +01:00
public Boolean HEAD ( final String arg , final Session session ) {
2005-06-09 12:07:02 +02:00
try {
2011-02-12 00:37:13 +01:00
final Properties prop = parseRequestLine ( HeaderFramework . METHOD_HEAD , arg , session ) ;
2005-06-09 12:07:02 +02:00
// we now know the HTTP version. depending on that, we read the header
2010-05-11 13:14:05 +02:00
final String httpVersion = prop . getProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , HeaderFramework . HTTP_VERSION_0_9 ) ;
2011-02-12 00:37:13 +01:00
final RequestHeader header = ( httpVersion . equals ( HeaderFramework . HTTP_VERSION_0_9 ) )
? new RequestHeader ( reverseMappingCache )
: readHeader ( prop , session ) ;
2005-06-09 12:07:02 +02:00
// handle transparent proxy support
2011-01-03 21:52:54 +01:00
handleTransparentProxySupport ( header , prop , virtualHost , HTTPDProxyHandler . isTransparentProxy ) ;
2005-06-09 12:07:02 +02:00
// determines if the connection should be kept alive
2010-05-11 13:14:05 +02:00
handlePersistentConnection ( header , prop ) ;
2005-06-09 12:07:02 +02:00
// return multi-line message
2010-05-11 13:14:05 +02:00
if ( prop . getProperty ( HeaderFramework . CONNECTION_PROP_HOST ) . equals ( virtualHost ) ) {
2005-06-09 12:07:02 +02:00
// pass to server
2010-05-11 13:14:05 +02:00
if ( allowServer ( session ) ) {
HTTPDFileHandler . doHead ( prop , header , session . out ) ;
2005-06-09 12:07:02 +02:00
} else {
// not authorized through firewall blocking (ip does not match filter)
session . out . write ( ( httpVersion + " 403 refused (IP not granted) " +
2007-12-14 20:17:54 +01:00
serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
}
} else {
// pass to proxy
2010-05-11 13:14:05 +02:00
if ( ( ( allowYaCyHop ( session ) ) & & ( handleYaCyHopAuthentication ( header , prop , session ) ) ) | |
( ( allowProxy ( session ) ) & & ( handleProxyAuthentication ( header , prop , session ) ) ) ) {
HTTPDProxyHandler . doHead ( prop , header , session . out ) ;
2005-06-09 12:07:02 +02:00
} else {
// not authorized through firewall blocking (ip does not match filter)
session . out . write ( ( httpVersion + " 403 refused (IP not granted) " +
2007-12-14 20:17:54 +01:00
serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
}
2010-05-11 13:14:05 +02:00
return prop . getProperty ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) . equals ( " keep-alive " ) ? serverCore . RESUME_CONNECTION : serverCore . TERMINATE_CONNECTION ;
2008-08-02 14:12:04 +02:00
} catch ( final Exception e ) {
2010-05-11 13:14:05 +02:00
logUnexpectedError ( e , session . userAddress . getHostAddress ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
2005-09-30 18:02:58 +02:00
}
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2011-02-12 00:37:13 +01:00
public Boolean POST ( final String arg , final Session session ) {
2005-06-09 12:07:02 +02:00
try {
2011-02-12 00:37:13 +01:00
final Properties prop = parseRequestLine ( HeaderFramework . METHOD_POST , arg , session ) ;
2005-06-09 12:07:02 +02:00
// we now know the HTTP version. depending on that, we read the header
2010-05-11 13:14:05 +02:00
final String httpVersion = prop . getProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , HeaderFramework . HTTP_VERSION_0_9 ) ;
2011-02-12 00:37:13 +01:00
final RequestHeader header = ( httpVersion . equals ( HeaderFramework . HTTP_VERSION_0_9 ) )
? new RequestHeader ( reverseMappingCache )
: readHeader ( prop , session ) ;
2005-06-09 12:07:02 +02:00
2008-05-04 12:53:04 +02:00
// handle transfer-coding
final InputStream sessionIn ;
2009-07-19 22:37:44 +02:00
final String transferEncoding = header . get ( HeaderFramework . TRANSFER_ENCODING ) ;
2008-05-04 12:53:04 +02:00
if ( transferEncoding ! = null ) {
2009-07-19 22:37:44 +02:00
if ( ! HeaderFramework . HTTP_VERSION_1_1 . equals ( httpVersion ) ) {
2008-08-13 12:37:53 +02:00
log . logWarning ( " client " + session . getName ( ) + " uses transfer-coding with HTTP version " + httpVersion + " ! " ) ;
2008-05-04 12:53:04 +02:00
}
2009-07-19 22:37:44 +02:00
if ( " chunked " . equalsIgnoreCase ( header . get ( HeaderFramework . TRANSFER_ENCODING ) ) ) {
2010-05-11 13:14:05 +02:00
sessionIn = new ChunkedInputStream ( session . in ) ;
2008-05-04 12:53:04 +02:00
} else {
// "A server which receives an entity-body with a transfer-coding it does
// not understand SHOULD return 501 (Unimplemented), and close the
// connection." [RFC 2616, section 3.6]
session . out . write ( ( httpVersion + " 501 transfer-encoding not implemented " + serverCore . CRLF_STRING + serverCore . CRLF_STRING + " you send a transfer-encoding to this server, which is not supported: " + transferEncoding + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
return serverCore . TERMINATE_CONNECTION ;
}
} else {
2010-05-11 13:14:05 +02:00
sessionIn = session . in ;
2008-05-04 12:53:04 +02:00
}
2005-06-09 12:07:02 +02:00
// handle transparent proxy support
2011-01-03 21:52:54 +01:00
handleTransparentProxySupport ( header , prop , virtualHost , HTTPDProxyHandler . isTransparentProxy ) ;
2005-06-09 12:07:02 +02:00
// determines if the connection should be kept alive
2010-05-11 13:14:05 +02:00
handlePersistentConnection ( header , prop ) ;
2005-06-09 12:07:02 +02:00
// return multi-line message
2009-07-19 22:37:44 +02:00
if ( prop . getProperty ( HeaderFramework . CONNECTION_PROP_HOST ) . equals ( virtualHost ) ) {
2005-06-09 12:07:02 +02:00
// pass to server
2010-05-11 13:14:05 +02:00
if ( allowServer ( session ) ) {
HTTPDFileHandler . doPost ( prop , header , session . out , sessionIn ) ;
2005-04-07 21:19:42 +02:00
} else {
2005-06-09 12:07:02 +02:00
// not authorized through firewall blocking (ip does not match filter)
2010-05-11 13:14:05 +02:00
session . out . write ( ( httpVersion + " 403 refused (IP not granted) " + serverCore . CRLF_STRING + serverCore . CRLF_STRING + " you are not allowed to connect to this server, because you are using the non-granted IP " + session . userAddress . getHostAddress ( ) + " . allowed are only connections that match with the following filter: " + switchboard . getConfig ( " serverClient " , " * " ) + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
}
} else {
// pass to proxy
2010-05-11 13:14:05 +02:00
if ( ( ( allowYaCyHop ( session ) ) & & ( handleYaCyHopAuthentication ( header , prop , session ) ) ) | |
( ( allowProxy ( session ) ) & & ( handleProxyAuthentication ( header , prop , session ) ) ) ) {
HTTPDProxyHandler . doPost ( prop , header , session . out , sessionIn ) ;
2005-06-09 12:07:02 +02:00
} else {
// not authorized through firewall blocking (ip does not match filter)
2010-05-11 13:14:05 +02:00
session . out . write ( ( httpVersion + " 403 refused (IP not granted) " + serverCore . CRLF_STRING + serverCore . CRLF_STRING + " you are not allowed to connect to this proxy, because you are using the non-granted IP " + session . userAddress . getHostAddress ( ) + " . allowed are only connections that match with the following filter: " + switchboard . getConfig ( " proxyClient " , " * " ) + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
2005-04-07 21:19:42 +02:00
}
2005-05-17 10:25:04 +02:00
}
2009-08-24 17:24:02 +02:00
if ( sessionIn instanceof ChunkedInputStream ) sessionIn . close ( ) ; // read to end, but do not close the stream (maybe HTTP/1.1 persistent)
2005-06-09 12:07:02 +02:00
//return serverCore.RESUME_CONNECTION;
2010-05-11 13:14:05 +02:00
return prop . getProperty ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) . equals ( " keep-alive " ) ? serverCore . RESUME_CONNECTION : serverCore . TERMINATE_CONNECTION ;
2008-08-02 14:12:04 +02:00
} catch ( final Exception e ) {
2010-05-11 13:14:05 +02:00
logUnexpectedError ( e , session . userAddress . getHostAddress ( ) ) ;
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
2005-09-30 18:02:58 +02:00
}
2005-06-09 12:07:02 +02:00
}
2011-01-03 21:52:54 +01:00
public static void handleTransparentProxySupport ( final RequestHeader header , final Properties prop , final String virtualHost , final boolean isTransparentProxy ) {
// transparent proxy support is only available for http 1.0 and above connections
if ( prop . getProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , " HTTP/0.9 " ) . equals ( " HTTP/0.9 " ) ) return ;
// if the transparent proxy support was disabled, we have nothing todo here ...
if ( ! ( isTransparentProxy & & header . containsKey ( HeaderFramework . HOST ) ) ) return ;
// we only need to do the transparent proxy support if the request URL didn't contain the hostname
// and therefor was set to virtualHost by function parseQuery()
if ( ! prop . getProperty ( HeaderFramework . CONNECTION_PROP_HOST ) . equals ( virtualHost ) ) return ;
// TODO: we could have problems with connections from extern here ...
final String dstHostSocket = header . get ( HeaderFramework . HOST ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HOST , ( HTTPDemon . isThisHostName ( dstHostSocket ) ? virtualHost : dstHostSocket ) ) ;
}
2005-06-09 12:07:02 +02:00
2011-02-12 00:37:13 +01:00
public Boolean CONNECT ( String arg , final Session session ) throws IOException {
2005-06-09 12:07:02 +02:00
// establish a ssh-tunneled http connection
2005-09-08 16:48:32 +02:00
// this is to support https
2005-06-09 12:07:02 +02:00
// parse HTTP version
2010-01-13 01:23:07 +01:00
int pos = arg . indexOf ( ' ' ) ;
2011-02-12 00:37:13 +01:00
final String httpVersion ;
2005-06-09 12:07:02 +02:00
if ( pos > = 0 ) {
httpVersion = arg . substring ( pos + 1 ) ;
arg = arg . substring ( 0 , pos ) ;
2011-02-12 00:37:13 +01:00
} else {
httpVersion = " HTTP/1.0 " ;
2005-06-09 12:07:02 +02:00
}
2010-05-11 13:14:05 +02:00
Properties prop = new Properties ( ) ;
2009-07-19 22:37:44 +02:00
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , httpVersion ) ;
2005-06-09 12:07:02 +02:00
2005-09-08 16:48:32 +02:00
// parse hostname and port
2009-07-19 22:37:44 +02:00
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HOST , arg ) ;
2010-01-13 01:23:07 +01:00
pos = arg . indexOf ( ':' ) ;
2005-06-09 12:07:02 +02:00
int port = 443 ;
if ( pos > = 0 ) {
port = Integer . parseInt ( arg . substring ( pos + 1 ) ) ;
2008-08-02 15:57:00 +02:00
//the offcut: arg = arg.substring(0, pos);
2005-09-08 16:48:32 +02:00
}
2005-06-09 12:07:02 +02:00
2005-09-08 16:48:32 +02:00
// setting other connection properties
2010-05-11 13:14:05 +02:00
prop . setProperty ( HeaderFramework . CONNECTION_PROP_CLIENTIP , session . userAddress . isAnyLocalAddress ( ) ? " localhost " : session . userAddress . getHostAddress ( ) ) ;
2009-07-19 22:37:44 +02:00
prop . setProperty ( HeaderFramework . CONNECTION_PROP_METHOD , HeaderFramework . METHOD_CONNECT ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_PATH , " / " ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_EXT , " " ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_URL , " " ) ;
2005-06-09 12:07:02 +02:00
// parse remaining lines
2010-08-23 14:32:02 +02:00
final RequestHeader header = readHeader ( prop , session ) ;
2005-09-08 16:48:32 +02:00
2010-05-11 13:14:05 +02:00
if ( ! ( allowProxy ( session ) ) ) {
2005-09-08 16:48:32 +02:00
// not authorized through firewall blocking (ip does not match filter)
2010-05-11 13:14:05 +02:00
session . out . write ( ( httpVersion + " 403 refused (IP not granted) " + serverCore . CRLF_STRING + serverCore . CRLF_STRING + " you are not allowed to connect to this proxy, because you are using the non-granted IP " + session . userAddress . getHostAddress ( ) + " . allowed are only connections that match with the following filter: " + switchboard . getConfig ( " proxyClient " , " * " ) + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-09-08 16:48:32 +02:00
return serverCore . TERMINATE_CONNECTION ;
}
2005-06-09 12:07:02 +02:00
2005-10-14 14:51:56 +02:00
if ( port ! = 443 & & switchboard . getConfig ( " secureHttps " , " true " ) . equals ( " true " ) ) {
2005-06-09 12:07:02 +02:00
// security: connection only to ssl port
// we send a 403 (forbidden) error back
session . out . write ( ( httpVersion + " 403 Connection to non-443 forbidden " +
2007-12-14 20:17:54 +01:00
serverCore . CRLF_STRING + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-05-17 10:25:04 +02:00
return serverCore . TERMINATE_CONNECTION ;
}
2005-06-09 12:07:02 +02:00
2005-05-17 10:25:04 +02:00
// pass to proxy
2010-05-11 13:14:05 +02:00
if ( ( ( allowYaCyHop ( session ) ) & & ( handleYaCyHopAuthentication ( header , prop , session ) ) ) | |
( ( allowProxy ( session ) ) & & ( this . handleProxyAuthentication ( header , prop , session ) ) ) ) {
HTTPDProxyHandler . doConnect ( prop , header , session . in , session . out ) ;
2005-05-17 10:25:04 +02:00
} else {
// not authorized through firewall blocking (ip does not match filter)
2010-05-11 13:14:05 +02:00
session . out . write ( ( httpVersion + " 403 refused (IP not granted) " + serverCore . CRLF_STRING + serverCore . CRLF_STRING + " you are not allowed to connect to this proxy, because you are using the non-granted IP " + session . userAddress . getHostAddress ( ) + " . allowed are only connections that match with the following filter: " + switchboard . getConfig ( " proxyClient " , " * " ) + serverCore . CRLF_STRING ) . getBytes ( ) ) ;
2005-05-17 10:25:04 +02:00
}
2005-06-09 12:07:02 +02:00
return serverCore . TERMINATE_CONNECTION ;
2005-05-17 10:25:04 +02:00
}
2005-04-07 21:19:42 +02:00
2011-02-12 00:37:13 +01:00
private final Properties parseRequestLine ( final String cmd , final String s , final Session session ) {
2005-04-19 08:55:57 +02:00
2005-09-20 23:49:47 +02:00
// parsing the header
2011-02-12 00:37:13 +01:00
final Properties p = parseRequestLine ( cmd , s , virtualHost ) ;
2005-09-05 10:01:54 +02:00
2007-06-11 16:05:20 +02:00
// track the request
2010-05-11 13:14:05 +02:00
final String path = p . getProperty ( HeaderFramework . CONNECTION_PROP_URL ) ;
final String args = p . getProperty ( HeaderFramework . CONNECTION_PROP_ARGS , " " ) ;
switchboard . track ( session . userAddress . getHostAddress ( ) , ( args . length ( ) > 0 ) ? path + " ? " + args : path ) ;
2009-09-23 23:26:14 +02:00
2005-09-20 23:49:47 +02:00
// reseting the empty request counter
2005-06-09 12:07:02 +02:00
this . emptyRequestCount = 0 ;
2008-05-17 01:33:59 +02:00
// counting the amount of received requests within this permanent connection
2010-05-11 13:14:05 +02:00
p . setProperty ( HeaderFramework . CONNECTION_PROP_KEEP_ALIVE_COUNT , Integer . toString ( + + this . keepAliveRequestCount ) ) ;
2005-06-09 12:07:02 +02:00
2005-09-20 23:49:47 +02:00
// setting the client-IP
2010-05-11 13:14:05 +02:00
p . setProperty ( HeaderFramework . CONNECTION_PROP_CLIENTIP , session . userAddress . getHostAddress ( ) ) ;
return p ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2005-04-07 21:19:42 +02:00
// some static methods that needs to be used from any CGI
// and also by the httpdFileHandler
// but this belongs to the protocol handler, this class.
2005-06-09 12:07:02 +02:00
2008-08-02 14:12:04 +02:00
public static int parseArgs ( final serverObjects args , final InputStream in , final int length ) throws IOException {
2005-06-09 12:07:02 +02:00
// this is a quick hack using a previously coded parseMultipart based on a buffer
// should be replaced sometime by a 'right' implementation
2005-09-22 12:30:55 +02:00
byte [ ] buffer = null ;
// parsing post request bodies with a given length
if ( length ! = - 1 ) {
buffer = new byte [ length ] ;
2008-08-06 21:43:12 +02:00
int bytesRead = in . read ( buffer ) ;
assert bytesRead = = buffer . length ;
2005-09-22 12:30:55 +02:00
// parsing post request bodies which are gzip content-encoded
} else {
2008-10-20 16:07:09 +02:00
ByteArrayOutputStream bout = new ByteArrayOutputStream ( 512 ) ;
2009-01-31 02:06:56 +01:00
FileUtils . copy ( in , bout ) ;
2005-09-22 12:30:55 +02:00
buffer = bout . toByteArray ( ) ;
bout . close ( ) ; bout = null ;
}
2008-08-02 14:12:04 +02:00
final int argc = parseArgs ( args , new String ( buffer , " UTF-8 " ) ) ;
2005-06-09 12:07:02 +02:00
buffer = null ;
return argc ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2008-08-02 14:12:04 +02:00
public static int parseArgs ( final serverObjects args , String argsString ) {
2005-06-09 12:07:02 +02:00
// this parses a arg string that can either be attached to a URL query
// or can be given as result of a post method
// the String argsString is supposed to be constructed as
// <key1>=<value1>'&'<key2>=<value2>'&'<key3>=<value3>
// the calling function must strip off a possible leading '?' char
if ( argsString . length ( ) = = 0 ) return 0 ;
argsString = argsString + " & " ; // for technical reasons
int sep ;
int eqp ;
int argc = 0 ;
// Textfield1=default+value+Textfield+1&Textfield2=default+value+Textfield+2&selection1=sel1&selection2=othervalue1&selection2=sel2&selection3=sel3&Menu1=SubEnry11&radio1=button1&check1=button2&check1=button3&hidden1=&sButton1=enter+%281%29
while ( argsString . length ( ) > 0 ) {
2010-01-13 01:23:07 +01:00
eqp = argsString . indexOf ( '=' ) ;
2010-08-13 15:06:06 +02:00
if ( eqp < = 0 ) break ;
sep = argsString . indexOf ( '&' , eqp + 1 ) ;
if ( sep < = 0 ) break ;
2005-06-09 12:07:02 +02:00
// resulting equations are inserted into the property args with leading '&'
2007-10-24 23:38:19 +02:00
args . put ( parseArg ( argsString . substring ( 0 , eqp ) ) , parseArg ( argsString . substring ( eqp + 1 , sep ) ) ) ;
2005-06-09 12:07:02 +02:00
argsString = argsString . substring ( sep + 1 ) ;
argc + + ;
}
// we return the number of parsed arguments
return argc ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2007-02-02 22:20:53 +01:00
/ * *
* < p > This method basically does the same as { @link URLDecoder # decode ( String , String ) URLDecoder . decode ( s , " UTF-8 " ) }
* would do with the exception of more lazyness in regard to current browser implementations as they do not
* always comply with the standards . < / p >
* < p > The following replacements are performed on the input - < code > String < / code > : < / p >
* < ul >
* < li > ' < code > + < / code > ' - characters are replaced by space
* < li > ( supbsequent ( in the case of encoded unicode - chars ) ) ' < code > % HH < / code > ' - entities are replaced by their
* respective < code > char < / code > - representation < / li >
* < li > ' < code > % uHHHH < / code > ' - entities ( sent by IE although rejected by the W3C ) are replaced by their respective
* < code > char < / code > - representation < / li >
* < li > < strong > TODO < / strong > : < code > chars < / code > already encoded in UTF - 8 are url - encoded and re - decoded due to internal restrictions ,
* which slows down this method unnecessarily < / li >
* < / ul >
*
* @param s the URL - encoded < code > String < / code > to decode , note that the encoding used to URL - encode the original
* < code > String < / code > has to be UTF - 8 ( i . e . the " <code>accept-charset</code> " - property of HTML
* < code > & lt ; form & gt ; < / code > - elements )
* @return the " normal " Java - < code > String < / code > ( UTF - 8 ) represented by the input or < code > null < / code >
* if the passed argument < code > encoding < / code > is not supported
* /
2005-04-07 21:19:42 +02:00
private static String parseArg ( String s ) {
2005-06-09 12:07:02 +02:00
int pos = 0 ;
2008-08-02 14:12:04 +02:00
final ByteArrayOutputStream baos = new ByteArrayOutputStream ( s . length ( ) ) ;
2007-02-02 22:20:53 +01:00
2005-06-09 12:07:02 +02:00
while ( pos < s . length ( ) ) {
if ( s . charAt ( pos ) = = '+' ) {
2006-01-12 22:14:39 +01:00
baos . write ( ' ' ) ;
2005-04-20 09:39:40 +02:00
pos + + ;
2005-06-09 12:07:02 +02:00
} else if ( s . charAt ( pos ) = = '%' ) {
2007-02-02 22:20:53 +01:00
try {
if ( s . length ( ) > = pos + 6 & & ( s . charAt ( pos + 1 ) = = 'u' | | s . charAt ( pos + 1 ) = = 'U' ) ) {
// non-standard encoding of IE for unicode-chars
2008-08-02 14:12:04 +02:00
final int bh = Integer . parseInt ( s . substring ( pos + 2 , pos + 4 ) , 16 ) ;
final int bl = Integer . parseInt ( s . substring ( pos + 4 , pos + 6 ) , 16 ) ;
2007-02-02 22:20:53 +01:00
// TODO: needs conversion from UTF-16 to UTF-8
baos . write ( bh ) ;
baos . write ( bl ) ;
pos + = 6 ;
} else if ( s . length ( ) > = pos + 3 ) {
baos . write ( Integer . parseInt ( s . substring ( pos + 1 , pos + 3 ) , 16 ) ) ;
pos + = 3 ;
} else {
baos . write ( s . charAt ( pos + + ) ) ;
}
2008-08-02 14:12:04 +02:00
} catch ( final NumberFormatException e ) {
2007-02-02 22:20:53 +01:00
baos . write ( s . charAt ( pos + + ) ) ;
}
} else if ( s . charAt ( pos ) > 127 ) {
// Unicode chars sent by client, see http://www.w3.org/International/O-URL-code.html
try {
// don't write anything but url-encode the unicode char
s = s . substring ( 0 , pos ) + URLEncoder . encode ( s . substring ( pos , pos + 1 ) , " UTF-8 " ) + s . substring ( pos + 1 ) ;
2008-08-02 14:12:04 +02:00
} catch ( final UnsupportedEncodingException e ) { return null ; }
2005-06-09 12:07:02 +02:00
} else {
2006-01-12 22:14:39 +01:00
baos . write ( s . charAt ( pos + + ) ) ;
2005-06-09 12:07:02 +02:00
}
}
2007-02-02 22:20:53 +01:00
try {
return new String ( baos . toByteArray ( ) , " UTF-8 " ) ;
2008-08-02 14:12:04 +02:00
} catch ( final UnsupportedEncodingException e ) { return null ; }
2007-02-02 22:20:53 +01:00
}
2007-01-06 12:05:50 +01:00
// 06.01.2007: decode HTML entities by [FB]
public static String decodeHtmlEntities ( String s ) {
// replace all entities defined in wikiCode.characters and htmlentities
2009-07-08 23:48:08 +02:00
s = CharacterCoding . html2unicode ( s ) ;
2007-01-06 12:05:50 +01:00
// replace all other
2008-08-02 14:12:04 +02:00
final CharArrayWriter b = new CharArrayWriter ( s . length ( ) ) ;
2007-01-06 12:05:50 +01:00
int end ;
2011-02-12 00:37:13 +01:00
for ( int i = 0 , len = s . length ( ) ; i < len ; i + + ) {
2007-01-06 12:05:50 +01:00
if ( s . charAt ( i ) = = '&' & & ( end = s . indexOf ( ';' , i + 1 ) ) > i ) {
if ( s . charAt ( i + 1 ) = = '#' ) { // Ӓ symbols
b . write ( Integer . parseInt ( s . substring ( i + 2 , end ) ) ) ;
i + = end - i ;
} else { // 'named' smybols
2008-09-03 02:30:21 +02:00
if ( log . isFine ( ) ) log . logFine ( " discovered yet unimplemented HTML entity ' " + s . substring ( i , end + 1 ) + " ' " ) ;
2007-01-06 12:05:50 +01:00
b . write ( s . charAt ( i ) ) ;
}
} else {
b . write ( s . charAt ( i ) ) ;
}
}
return b . toString ( ) ;
}
2005-06-09 12:07:02 +02:00
2008-08-08 03:40:28 +02:00
/ * *
* parses the message accordingly to RFC 1867 using " Commons FileUpload " ( http : //commons.apache.org/fileupload/)
*
* @author danielr
* @since 07 . 08 . 2008
2008-08-10 00:44:17 +02:00
* @param header
* hier muss ARGC gesetzt werden !
2008-08-08 03:40:28 +02:00
* @param args
2008-08-29 11:42:39 +02:00
* @param in the raw body
2008-08-08 03:40:28 +02:00
* @return
* @throws IOException
* /
@SuppressWarnings ( " unchecked " )
2010-09-14 23:03:50 +02:00
public static Map < String , byte [ ] > parseMultipart ( final RequestHeader header , final serverObjects args , final InputStream in ) throws IOException {
2011-02-12 00:37:13 +01:00
2008-08-29 11:42:39 +02:00
final InputStream body = prepareBody ( header , in ) ;
2008-08-28 10:37:51 +02:00
RequestContext request = new yacyContextRequest ( header , body ) ;
2008-08-10 00:44:17 +02:00
// check information
if ( ! FileUploadBase . isMultipartContent ( request ) ) {
throw new IOException ( " the request is not a multipart-message! " ) ;
}
2009-05-12 22:23:55 +02:00
// check if we have enough memory
if ( ! MemoryControl . request ( request . getContentLength ( ) * 3 , false ) ) {
throw new IOException ( " not enough memory available for request. request.getContentLength() = " + request . getContentLength ( ) + " , MemoryControl.available() = " + MemoryControl . available ( ) ) ;
}
2008-08-10 00:44:17 +02:00
2008-08-10 12:54:13 +02:00
// parse data in memory
2011-02-12 00:37:13 +01:00
final FileUpload upload = new FileUpload ( DISK_FILE_ITEM_FACTORY ) ;
final List < FileItem > items ;
2008-08-10 00:44:17 +02:00
try {
items = upload . parseRequest ( request ) ;
} catch ( FileUploadException e ) {
throw new IOException ( " FileUploadException " + e . getMessage ( ) ) ;
}
2011-02-12 00:37:13 +01:00
2008-08-10 12:54:13 +02:00
// format information for further usage
2011-02-12 00:37:13 +01:00
final Map < String , byte [ ] > files = new HashMap < String , byte [ ] > ( ) ;
for ( final FileItem item : items ) {
2008-08-10 00:44:17 +02:00
if ( item . isFormField ( ) ) {
// simple text
if ( item . getContentType ( ) = = null | | ! item . getContentType ( ) . contains ( " charset " ) ) {
// old yacy clients use their local default charset, on most systems UTF-8 (I hope ;)
args . put ( item . getFieldName ( ) , item . getString ( " UTF-8 " ) ) ;
} else {
// use default encoding (given as header or ISO-8859-1)
args . put ( item . getFieldName ( ) , item . getString ( ) ) ;
}
} else {
// file
args . put ( item . getFieldName ( ) , item . getName ( ) ) ;
2009-01-31 02:06:56 +01:00
final byte [ ] fileContent = FileUtils . read ( item . getInputStream ( ) ) ;
2008-08-10 00:44:17 +02:00
item . getInputStream ( ) . close ( ) ;
files . put ( item . getFieldName ( ) , fileContent ) ;
}
}
header . put ( " ARGC " , String . valueOf ( items . size ( ) ) ) ; // store argument count
return files ;
2005-04-07 21:19:42 +02:00
}
2008-08-29 11:42:39 +02:00
/ * *
* prepares the body so that it can be read as whole plain text
* ( uncompress if necessary and ensure correct ending )
*
* @param header
* @param in
* @return
* @throws IOException
* /
2009-07-19 22:37:44 +02:00
private static InputStream prepareBody ( final RequestHeader header , final InputStream in ) throws IOException {
2008-08-29 11:42:39 +02:00
InputStream body = in ;
// data may be compressed
2009-07-19 22:37:44 +02:00
final String bodyEncoding = header . get ( HeaderFramework . CONTENT_ENCODING ) ;
if ( HeaderFramework . CONTENT_ENCODING_GZIP . equalsIgnoreCase ( bodyEncoding ) & & ! ( body instanceof GZIPInputStream ) ) {
2008-08-29 11:42:39 +02:00
body = new GZIPInputStream ( body ) ;
// length of uncompressed data is unknown
2009-07-19 22:37:44 +02:00
header . remove ( HeaderFramework . CONTENT_LENGTH ) ;
2008-08-29 11:42:39 +02:00
} else {
// ensure the end of data (if client keeps alive the connection)
final long clength = header . getContentLength ( ) ;
if ( clength > 0 ) {
body = new ContentLengthInputStream ( body , clength ) ;
}
}
return body ;
}
2005-06-09 12:07:02 +02:00
2008-08-08 03:40:28 +02:00
/ * *
* wraps the request into a org . apache . commons . fileupload . RequestContext
*
2008-08-29 11:42:39 +02:00
* @author danielr
* @since 07 . 08 . 2008
* /
2009-07-19 22:37:44 +02:00
private static class yacyContextRequest extends RequestHeader implements RequestContext {
2008-08-29 11:42:39 +02:00
private static final long serialVersionUID = - 8936741958551376593L ;
2008-08-25 20:11:47 +02:00
private final InputStream inStream ;
2008-08-29 11:42:39 +02:00
/ * *
* creates a new yacyContextRequest
*
* @param header
* @param in
* /
2011-02-12 00:37:13 +01:00
public yacyContextRequest ( final Map < String , String > requestHeader , final InputStream in ) {
2008-08-29 11:42:39 +02:00
super ( null , requestHeader ) ;
this . inStream = in ;
}
/ *
* ( non - Javadoc )
*
* @see org . apache . commons . fileupload . RequestContext # getInputStream ( )
* /
// @Override
public InputStream getInputStream ( ) throws IOException {
return inStream ;
}
}
2008-08-08 03:40:28 +02:00
2008-08-02 14:12:04 +02:00
public static int indexOf ( final int start , final byte [ ] array , final byte [ ] pattern ) {
2005-06-09 12:07:02 +02:00
// return a position of a pattern in an array
if ( start > array . length - pattern . length ) return - 1 ;
if ( pattern . length = = 0 ) return start ;
2011-02-12 00:37:13 +01:00
for ( int pos = start , lens = array . length - pattern . length ; pos < = lens ; pos + + ) {
if ( ( array [ pos ] = = pattern [ 0 ] ) & & ( equals ( array , pos , pattern , 0 , pattern . length ) ) ) {
2005-06-09 12:07:02 +02:00
return pos ;
2011-02-12 00:37:13 +01:00
}
}
2005-06-09 12:07:02 +02:00
return - 1 ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2008-08-02 14:12:04 +02:00
public static boolean equals ( final byte [ ] a , final int aoff , final byte [ ] b , final int boff , final int len ) {
2005-06-09 12:07:02 +02:00
if ( ( aoff + len > a . length ) | | ( boff + len > b . length ) ) return false ;
2011-02-12 00:37:13 +01:00
for ( int i = 0 ; i < len ; i + + ) {
if ( a [ aoff + i ] ! = b [ boff + i ] ) {
return false ;
}
}
2005-06-09 12:07:02 +02:00
return true ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
2009-01-01 20:40:06 +01:00
@Override
2009-07-19 22:37:44 +02:00
public HTTPDemon clone ( ) {
return new HTTPDemon ( switchboard ) ;
2005-04-07 21:19:42 +02:00
}
2005-06-09 12:07:02 +02:00
public static final void sendRespondBody (
2008-08-02 14:12:04 +02:00
final OutputStream respond ,
final byte [ ] body
2005-06-09 12:07:02 +02:00
) throws IOException {
respond . write ( body ) ;
respond . flush ( ) ;
}
public static final void sendRespondError (
2008-08-02 14:12:04 +02:00
final Properties conProp ,
final OutputStream respond ,
final int errorcase ,
final int httpStatusCode ,
final String httpStatusText ,
final String detailedErrorMsg ,
final Throwable stackTrace
2005-06-09 12:07:02 +02:00
) throws IOException {
2005-10-10 12:33:09 +02:00
sendRespondError (
conProp ,
respond ,
errorcase ,
httpStatusCode ,
2005-10-13 09:29:14 +02:00
httpStatusText ,
2005-10-10 12:33:09 +02:00
detailedErrorMsg ,
null ,
2005-10-13 09:29:14 +02:00
null ,
2006-06-12 14:12:21 +02:00
stackTrace ,
null
2005-10-10 12:33:09 +02:00
) ;
}
public static final void sendRespondError (
2008-08-02 14:12:04 +02:00
final Properties conProp ,
final OutputStream respond ,
final int httpStatusCode ,
final String httpStatusText ,
final File detailedErrorMsgFile ,
final serverObjects detailedErrorMsgValues ,
final Throwable stackTrace
2005-10-10 12:33:09 +02:00
) throws IOException {
sendRespondError (
conProp ,
respond ,
2005-10-13 09:29:14 +02:00
5 ,
2005-10-10 12:33:09 +02:00
httpStatusCode ,
httpStatusText ,
2005-10-13 09:29:14 +02:00
null ,
2005-10-10 12:33:09 +02:00
detailedErrorMsgFile ,
detailedErrorMsgValues ,
2006-06-12 14:12:21 +02:00
stackTrace ,
null
2005-10-10 12:33:09 +02:00
) ;
}
2006-06-12 14:12:21 +02:00
public static final void sendRespondError (
2008-08-02 14:12:04 +02:00
final Properties conProp ,
final OutputStream respond ,
final int errorcase ,
final int httpStatusCode ,
2005-10-10 12:33:09 +02:00
String httpStatusText ,
2008-08-02 14:12:04 +02:00
final String detailedErrorMsgText ,
final Object detailedErrorMsgFile ,
final serverObjects detailedErrorMsgValues ,
final Throwable stackTrace ,
2009-07-19 22:37:44 +02:00
ResponseHeader header
2005-10-10 12:33:09 +02:00
) throws IOException {
2005-06-09 12:07:02 +02:00
FileInputStream fis = null ;
2005-07-07 15:58:54 +02:00
ByteArrayOutputStream o = null ;
2005-06-09 12:07:02 +02:00
try {
// setting the proper http status message
2009-07-19 22:37:44 +02:00
final String httpVersion = conProp . getProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , " HTTP/1.1 " ) ;
2005-06-09 12:07:02 +02:00
if ( ( httpStatusText = = null ) | | ( httpStatusText . length ( ) = = 0 ) ) {
2009-07-19 22:37:44 +02:00
if ( httpVersion . equals ( " HTTP/1.0 " ) & & HeaderFramework . http1_0 . containsKey ( Integer . toString ( httpStatusCode ) ) )
httpStatusText = HeaderFramework . http1_0 . get ( Integer . toString ( httpStatusCode ) ) ;
else if ( httpVersion . equals ( " HTTP/1.1 " ) & & HeaderFramework . http1_1 . containsKey ( Integer . toString ( httpStatusCode ) ) )
httpStatusText = HeaderFramework . http1_1 . get ( Integer . toString ( httpStatusCode ) ) ;
2005-06-09 12:07:02 +02:00
else httpStatusText = " Unknown " ;
}
// generating the desired request url
2009-07-19 22:37:44 +02:00
String host = conProp . getProperty ( HeaderFramework . CONNECTION_PROP_HOST ) ;
final String path = conProp . getProperty ( HeaderFramework . CONNECTION_PROP_PATH , " / " ) ;
final String args = conProp . getProperty ( HeaderFramework . CONNECTION_PROP_ARGS ) ;
final String method = conProp . getProperty ( HeaderFramework . CONNECTION_PROP_METHOD ) ;
2005-06-09 12:07:02 +02:00
2011-02-12 00:37:13 +01:00
final int port ;
2010-01-13 01:23:07 +01:00
final int pos = host . indexOf ( ':' ) ;
2005-06-09 12:07:02 +02:00
if ( pos ! = - 1 ) {
port = Integer . parseInt ( host . substring ( pos + 1 ) ) ;
host = host . substring ( 0 , pos ) ;
2011-02-12 00:37:13 +01:00
} else {
port = 80 ;
2005-06-09 12:07:02 +02:00
}
String urlString ;
try {
2009-10-11 02:12:19 +02:00
urlString = ( new DigestURI ( ( method . equals ( HeaderFramework . METHOD_CONNECT ) ? " https " : " http " ) , host , port , ( args = = null ) ? path : path + " ? " + args ) ) . toString ( ) ;
2008-08-02 14:12:04 +02:00
} catch ( final MalformedURLException e ) {
2007-11-14 20:14:53 +01:00
urlString = " invalid URL " ;
}
2005-06-09 12:07:02 +02:00
// set rewrite values
2008-08-02 14:12:04 +02:00
final serverObjects tp = new serverObjects ( ) ;
2007-11-14 20:14:53 +01:00
2009-07-19 22:37:44 +02:00
final String clientIP = conProp . getProperty ( HeaderFramework . CONNECTION_PROP_CLIENTIP , " 127.0.0.1 " ) ;
2007-11-14 20:14:53 +01:00
2005-09-30 08:15:22 +02:00
// check if ip is local ip address
2009-10-11 02:12:19 +02:00
final InetAddress hostAddress = Domains . dnsResolve ( clientIP ) ;
2005-11-07 11:57:54 +01:00
if ( hostAddress = = null ) {
2009-11-17 00:00:54 +01:00
tp . put ( " host " , Domains . myPublicLocalIP ( ) . getHostAddress ( ) ) ;
2011-01-28 11:54:13 +01:00
tp . put ( " port " , serverCore . getPortNr ( switchboard . getConfig ( " port " , " 8090 " ) ) ) ;
2005-11-07 11:57:54 +01:00
} else if ( hostAddress . isSiteLocalAddress ( ) | | hostAddress . isLoopbackAddress ( ) ) {
2009-11-17 00:00:54 +01:00
tp . put ( " host " , Domains . myPublicLocalIP ( ) . getHostAddress ( ) ) ;
2011-01-28 11:54:13 +01:00
tp . put ( " port " , serverCore . getPortNr ( switchboard . getConfig ( " port " , " 8090 " ) ) ) ;
2005-11-07 11:57:54 +01:00
} else {
2009-10-11 02:12:19 +02:00
tp . put ( " host " , switchboard . myPublicIP ( ) ) ;
2011-01-28 11:54:13 +01:00
tp . put ( " port " , Integer . toString ( serverCore . getPortNr ( switchboard . getConfig ( " port " , " 8090 " ) ) ) ) ;
2007-11-14 20:14:53 +01:00
}
2008-08-17 12:16:32 +02:00
tp . put ( " peerName " , ( getAlternativeResolver ( ) = = null ) ? " " : getAlternativeResolver ( ) . myName ( ) ) ;
2007-11-14 20:14:53 +01:00
tp . put ( " errorMessageType " , errorcase ) ;
2005-06-09 12:07:02 +02:00
tp . put ( " httpStatus " , Integer . toString ( httpStatusCode ) + " " + httpStatusText ) ;
2009-07-19 22:37:44 +02:00
tp . put ( " requestMethod " , conProp . getProperty ( HeaderFramework . CONNECTION_PROP_METHOD ) ) ;
2005-06-09 12:07:02 +02:00
tp . put ( " requestURL " , urlString ) ;
2007-11-14 20:14:53 +01:00
2005-10-13 09:29:14 +02:00
switch ( errorcase ) {
2007-01-06 12:05:50 +01:00
case ERRORCASE_FILE :
2008-01-06 20:23:38 +01:00
tp . put ( " errorMessageType_file " , ( detailedErrorMsgFile = = null ) ? " " : detailedErrorMsgFile . toString ( ) ) ;
2009-12-02 01:37:59 +01:00
if ( ( detailedErrorMsgValues ! = null ) & & ! detailedErrorMsgValues . isEmpty ( ) ) {
2005-10-10 12:33:09 +02:00
// rewriting the value-names and add the proper name prefix:
2011-02-12 00:37:13 +01:00
for ( final Entry < String , String > entry : detailedErrorMsgValues . entrySet ( ) ) {
2008-08-17 12:16:32 +02:00
tp . put ( " errorMessageType_ " + entry . getKey ( ) , entry . getValue ( ) ) ;
2005-10-10 12:33:09 +02:00
}
2005-10-13 09:29:14 +02:00
}
break ;
2008-05-04 12:53:04 +02:00
case ERRORCASE_MESSAGE :
2005-10-13 09:29:14 +02:00
default :
2008-05-04 12:53:04 +02:00
tp . put ( " errorMessageType_detailedErrorMsg " , ( detailedErrorMsgText = = null ) ? " " : detailedErrorMsgText . replaceAll ( " \ n " , " <br /> " ) ) ;
2005-10-13 09:29:14 +02:00
break ;
2005-10-10 12:33:09 +02:00
}
2005-06-09 12:07:02 +02:00
// building the stacktrace
2005-07-07 15:58:54 +02:00
if ( stackTrace ! = null ) {
tp . put ( " printStackTrace " , 1 ) ;
2009-01-30 16:33:00 +01:00
final ByteBuffer errorMsg = new ByteBuffer ( 100 ) ;
2005-06-09 12:07:02 +02:00
stackTrace . printStackTrace ( new PrintStream ( errorMsg ) ) ;
2007-01-17 05:20:19 +01:00
tp . put ( " printStackTrace_exception " , stackTrace . toString ( ) ) ;
tp . put ( " printStackTrace_stacktrace " , new String ( errorMsg . getBytes ( ) , " UTF-8 " ) ) ;
2005-06-09 12:07:02 +02:00
} else {
2007-01-17 05:20:19 +01:00
tp . put ( " printStackTrace " , 0 ) ;
2005-06-09 12:07:02 +02:00
}
2005-08-23 13:32:36 +02:00
// Generated Tue, 23 Aug 2005 11:19:14 GMT by brain.wg (squid/2.5.STABLE3)
// adding some system information
2010-08-23 14:32:02 +02:00
final String systemDate = HeaderFramework . formatRFC1123 ( new Date ( ) ) ;
2007-01-17 05:20:19 +01:00
tp . put ( " date " , systemDate ) ;
2005-08-23 13:32:36 +02:00
2005-06-09 12:07:02 +02:00
// rewrite the file
2010-09-02 21:24:22 +02:00
final File htRootPath = new File ( switchboard . getAppPath ( ) , switchboard . getConfig ( " htRootPath " , " htroot " ) ) ;
2005-07-07 15:58:54 +02:00
2009-07-19 22:37:44 +02:00
TemplateEngine . writeTemplate (
2005-07-07 15:58:54 +02:00
fis = new FileInputStream ( new File ( htRootPath , " /proxymsg/error.html " ) ) ,
2008-10-20 16:07:09 +02:00
o = new ByteArrayOutputStream ( 512 ) ,
2005-07-07 15:58:54 +02:00
tp ,
" -UNRESOLVED_PATTERN- " . getBytes ( )
) ;
2008-08-02 14:12:04 +02:00
final byte [ ] result = o . toByteArray ( ) ;
2005-06-16 10:34:52 +02:00
o . close ( ) ; o = null ;
2005-06-09 12:07:02 +02:00
2011-02-12 00:37:13 +01:00
if ( header = = null ) {
2009-07-19 22:37:44 +02:00
header = new ResponseHeader ( ) ;
2011-02-12 00:37:13 +01:00
}
2009-07-19 22:37:44 +02:00
header . put ( HeaderFramework . CONNECTION_PROP_PROXY_RESPOND_STATUS , Integer . toString ( httpStatusCode ) ) ;
header . put ( HeaderFramework . DATE , systemDate ) ;
header . put ( HeaderFramework . CONTENT_TYPE , " text/html " ) ;
header . put ( HeaderFramework . CONTENT_LENGTH , Integer . toString ( result . length ) ) ;
header . put ( HeaderFramework . PRAGMA , " no-cache " ) ;
2005-06-09 12:07:02 +02:00
sendRespondHeader ( conProp , respond , httpVersion , httpStatusCode , httpStatusText , header ) ;
2006-02-16 12:45:32 +01:00
2009-07-19 22:37:44 +02:00
if ( ! method . equals ( HeaderFramework . METHOD_HEAD ) ) {
2006-02-16 12:45:32 +01:00
// write the array to the client
2009-01-31 02:06:56 +01:00
FileUtils . copy ( result , respond ) ;
2006-02-16 12:45:32 +01:00
}
2005-06-09 12:07:02 +02:00
respond . flush ( ) ;
} finally {
2009-11-05 21:28:37 +01:00
if ( fis ! = null ) try { fis . close ( ) ; } catch ( final Exception e ) { Log . logException ( e ) ; }
if ( o ! = null ) try { o . close ( ) ; } catch ( final Exception e ) { Log . logException ( e ) ; }
2005-06-09 12:07:02 +02:00
}
}
2005-12-07 14:26:27 +01:00
public static final void sendRespondHeader (
2008-08-02 14:12:04 +02:00
final Properties conProp ,
final OutputStream respond ,
final String httpVersion ,
final int httpStatusCode ,
final String httpStatusText ,
2005-12-07 14:26:27 +01:00
String contentType ,
2008-08-02 14:12:04 +02:00
final long contentLength ,
2005-12-07 14:26:27 +01:00
Date moddate ,
2008-08-02 14:12:04 +02:00
final Date expires ,
2009-07-19 22:37:44 +02:00
ResponseHeader headers ,
2008-08-02 14:12:04 +02:00
final String contentEnc ,
final String transferEnc ,
final boolean nocache
2005-12-07 14:26:27 +01:00
) throws IOException {
2005-06-09 12:07:02 +02:00
2009-07-19 22:37:44 +02:00
final String reqMethod = conProp . getProperty ( HeaderFramework . CONNECTION_PROP_METHOD ) ;
2006-02-15 13:31:52 +01:00
2009-07-19 22:37:44 +02:00
if ( ( transferEnc ! = null ) & & ! httpVersion . equals ( HeaderFramework . HTTP_VERSION_1_1 ) ) {
2007-06-26 17:06:23 +02:00
throw new IllegalArgumentException ( " Transfer encoding is only supported for http/1.1 connections. The current connection version is " + httpVersion ) ;
2006-02-15 13:31:52 +01:00
}
2006-02-16 11:54:47 +01:00
2009-07-19 22:37:44 +02:00
if ( ! reqMethod . equals ( HeaderFramework . METHOD_HEAD ) ) {
2011-02-12 00:37:13 +01:00
if ( ! conProp . getProperty ( HeaderFramework . CONNECTION_PROP_PERSISTENT , " close " ) . equals ( " close " ) & &
transferEnc = = null & & contentLength < 0 ) {
throw new IllegalArgumentException ( " Message MUST contain a Content-Length or a non-identity transfer-coding header field. " ) ;
2006-02-15 13:31:52 +01:00
}
if ( transferEnc ! = null & & contentLength > = 0 ) {
throw new IllegalArgumentException ( " Messages MUST NOT include both a Content-Length header field and a non-identity transfer-coding. " ) ;
}
}
2011-02-12 00:37:13 +01:00
if ( headers = = null ) {
headers = new ResponseHeader ( ) ;
}
2008-08-02 14:12:04 +02:00
final Date now = new Date ( System . currentTimeMillis ( ) ) ;
2005-06-09 12:07:02 +02:00
2009-07-19 22:37:44 +02:00
headers . put ( HeaderFramework . SERVER , " AnomicHTTPD (www.anomic.de) " ) ;
2010-08-23 14:32:02 +02:00
headers . put ( HeaderFramework . DATE , HeaderFramework . formatRFC1123 ( now ) ) ;
2009-02-05 16:15:13 +01:00
if ( moddate . after ( now ) ) {
moddate = now ;
}
2010-08-23 14:32:02 +02:00
headers . put ( HeaderFramework . LAST_MODIFIED , HeaderFramework . formatRFC1123 ( moddate ) ) ;
2005-06-09 12:07:02 +02:00
2005-12-09 18:35:45 +01:00
if ( nocache ) {
2010-09-29 21:56:42 +02:00
headers . put ( HeaderFramework . CACHE_CONTROL , " no-cache " ) ;
headers . put ( HeaderFramework . CACHE_CONTROL , " no-store " ) ;
headers . put ( HeaderFramework . PRAGMA , " no-cache " ) ;
2005-12-09 18:35:45 +01:00
}
2006-03-07 16:58:50 +01:00
2010-02-17 10:08:16 +01:00
if ( contentType = = null ) contentType = " text/html; charset=UTF-8 " ;
2010-11-27 10:16:16 +01:00
if ( headers . get ( HeaderFramework . CONTENT_TYPE ) = = null ) headers . put ( HeaderFramework . CONTENT_TYPE , contentType ) ;
2009-07-19 22:37:44 +02:00
if ( contentLength > 0 ) headers . put ( HeaderFramework . CONTENT_LENGTH , Long . toString ( contentLength ) ) ;
2009-06-30 15:01:35 +02:00
//if (cookie != null) headers.put(httpHeader.SET_COOKIE, cookie);
2010-08-23 14:32:02 +02:00
if ( expires ! = null ) headers . put ( HeaderFramework . EXPIRES , HeaderFramework . formatRFC1123 ( expires ) ) ;
2009-07-19 22:37:44 +02:00
if ( contentEnc ! = null ) headers . put ( HeaderFramework . CONTENT_ENCODING , contentEnc ) ;
if ( transferEnc ! = null ) headers . put ( HeaderFramework . TRANSFER_ENCODING , transferEnc ) ;
2005-06-09 12:07:02 +02:00
2006-01-13 23:50:04 +01:00
sendRespondHeader ( conProp , respond , httpVersion , httpStatusCode , httpStatusText , headers ) ;
2005-06-09 12:07:02 +02:00
}
public static final void sendRespondHeader (
2008-08-02 14:12:04 +02:00
final Properties conProp ,
final OutputStream respond ,
final String httpVersion ,
final int httpStatusCode ,
2009-07-19 22:37:44 +02:00
final ResponseHeader header
2005-06-09 12:07:02 +02:00
) throws IOException {
sendRespondHeader ( conProp , respond , httpVersion , httpStatusCode , null , header ) ;
}
2006-01-13 23:50:04 +01:00
2006-01-13 22:29:04 +01:00
public static final void sendRespondHeader (
2008-08-02 14:12:04 +02:00
final Properties conProp ,
final OutputStream respond ,
2006-01-13 22:29:04 +01:00
String httpVersion ,
2008-08-02 14:12:04 +02:00
final int httpStatusCode ,
2006-01-13 22:29:04 +01:00
String httpStatusText ,
2009-07-19 22:37:44 +02:00
ResponseHeader responseHeader
2006-01-13 22:29:04 +01:00
) throws IOException {
2005-06-09 12:07:02 +02:00
if ( respond = = null ) throw new NullPointerException ( " The outputstream must not be null. " ) ;
if ( conProp = = null ) throw new NullPointerException ( " The connection property structure must not be null. " ) ;
2009-07-19 22:37:44 +02:00
if ( httpVersion = = null ) httpVersion = conProp . getProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , HeaderFramework . HTTP_VERSION_1_1 ) ;
if ( responseHeader = = null ) responseHeader = new ResponseHeader ( ) ;
2005-06-09 12:07:02 +02:00
try {
2006-02-11 16:21:04 +01:00
if ( ( httpStatusText = = null ) | | ( httpStatusText . length ( ) = = 0 ) ) {
2009-07-19 22:37:44 +02:00
if ( httpVersion . equals ( HeaderFramework . HTTP_VERSION_1_0 ) & & HeaderFramework . http1_0 . containsKey ( Integer . toString ( httpStatusCode ) ) )
httpStatusText = HeaderFramework . http1_0 . get ( Integer . toString ( httpStatusCode ) ) ;
else if ( httpVersion . equals ( HeaderFramework . HTTP_VERSION_1_1 ) & & HeaderFramework . http1_1 . containsKey ( Integer . toString ( httpStatusCode ) ) )
httpStatusText = HeaderFramework . http1_1 . get ( Integer . toString ( httpStatusCode ) ) ;
2006-02-11 16:21:04 +01:00
else httpStatusText = " Unknown " ;
}
2008-12-04 13:54:16 +01:00
final StringBuilder header = new StringBuilder ( 560 ) ;
2006-02-11 16:21:04 +01:00
2006-02-16 11:27:21 +01:00
// "HTTP/0.9" does not have a status line or header in the response
2009-07-19 22:37:44 +02:00
if ( ! httpVersion . toUpperCase ( ) . equals ( HeaderFramework . HTTP_VERSION_0_9 ) ) {
2006-02-16 11:27:21 +01:00
// write status line
2008-12-04 13:54:16 +01:00
header . append ( httpVersion ) . append ( " " )
2006-02-16 11:27:21 +01:00
. append ( Integer . toString ( httpStatusCode ) ) . append ( " " )
. append ( httpStatusText ) . append ( " \ r \ n " ) ;
2005-12-09 18:35:45 +01:00
// prepare header
2009-07-19 22:37:44 +02:00
if ( ! responseHeader . containsKey ( HeaderFramework . DATE ) )
2010-08-23 14:32:02 +02:00
responseHeader . put ( HeaderFramework . DATE , HeaderFramework . formatRFC1123 ( new Date ( ) ) ) ;
2009-07-19 22:37:44 +02:00
if ( ! responseHeader . containsKey ( HeaderFramework . CONTENT_TYPE ) )
responseHeader . put ( HeaderFramework . CONTENT_TYPE , " text/html; charset=UTF-8 " ) ; // fix this
if ( ! responseHeader . containsKey ( RequestHeader . CONNECTION ) & & conProp . containsKey ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) )
responseHeader . put ( RequestHeader . CONNECTION , conProp . getProperty ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) ) ;
if ( ! responseHeader . containsKey ( RequestHeader . PROXY_CONNECTION ) & & conProp . containsKey ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) )
responseHeader . put ( RequestHeader . PROXY_CONNECTION , conProp . getProperty ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) ) ;
2005-12-09 18:35:45 +01:00
2009-07-19 22:37:44 +02:00
if ( conProp . containsKey ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) & &
conProp . getProperty ( HeaderFramework . CONNECTION_PROP_PERSISTENT ) . equals ( " keep-alive " ) & &
! responseHeader . containsKey ( HeaderFramework . TRANSFER_ENCODING ) & &
! responseHeader . containsKey ( HeaderFramework . CONTENT_LENGTH ) )
responseHeader . put ( HeaderFramework . CONTENT_LENGTH , " 0 " ) ;
2005-12-09 18:35:45 +01:00
// adding some yacy specific headers
2009-07-19 22:37:44 +02:00
responseHeader . put ( HeaderFramework . X_YACY_KEEP_ALIVE_REQUEST_COUNT , conProp . getProperty ( HeaderFramework . CONNECTION_PROP_KEEP_ALIVE_COUNT ) ) ;
responseHeader . put ( HeaderFramework . X_YACY_ORIGINAL_REQUEST_LINE , conProp . getProperty ( HeaderFramework . CONNECTION_PROP_REQUESTLINE ) ) ;
2010-05-11 13:14:05 +02:00
//responseHeader.put(HeaderFramework.X_YACY_PREVIOUS_REQUEST_LINE,conProp.getProperty(HeaderFramework.CONNECTION_PROP_PREV_REQUESTLINE));
2006-02-11 16:21:04 +01:00
2006-01-13 22:29:04 +01:00
//read custom headers
2011-02-12 00:37:13 +01:00
final Iterator < ResponseHeader . Entry > it = responseHeader . getAdditionalHeaderProperties ( ) . iterator ( ) ;
while ( it . hasNext ( ) ) {
//Append user properties to the main String
//TODO: Should we check for user properites. What if they intersect properties that are already in header?
final ResponseHeader . Entry e = it . next ( ) ;
header . append ( e . getKey ( ) ) . append ( " : " ) . append ( e . getValue ( ) ) . append ( " \ r \ n " ) ;
}
2005-12-09 18:35:45 +01:00
// write header
2008-08-25 20:11:47 +02:00
final Iterator < String > i = responseHeader . keySet ( ) . iterator ( ) ;
2005-12-09 18:35:45 +01:00
String key ;
char tag ;
int count ;
while ( i . hasNext ( ) ) {
2008-01-28 19:21:08 +01:00
key = i . next ( ) ;
2005-12-09 18:35:45 +01:00
tag = key . charAt ( 0 ) ;
if ( ( tag ! = '*' ) & & ( tag ! = '#' ) ) { // '#' in key is reserved for proxy attributes as artificial header values
2008-08-25 20:11:47 +02:00
count = responseHeader . keyCount ( key ) ;
2005-12-09 18:35:45 +01:00
for ( int j = 0 ; j < count ; j + + ) {
2009-07-11 23:01:27 +02:00
header . append ( key ) . append ( " : " ) . append ( responseHeader . getSingle ( key , j ) ) . append ( " \ r \ n " ) ;
2005-12-09 18:35:45 +01:00
}
}
}
2006-02-16 12:07:17 +01:00
// end header
2008-12-04 13:54:16 +01:00
header . append ( " \ r \ n " ) ;
2006-02-16 12:07:17 +01:00
// sending headers to the client
2008-12-04 13:54:16 +01:00
respond . write ( header . toString ( ) . getBytes ( ) ) ;
2006-02-16 12:07:17 +01:00
// flush stream
respond . flush ( ) ;
2005-06-09 12:07:02 +02:00
}
2009-07-19 22:37:44 +02:00
conProp . put ( HeaderFramework . CONNECTION_PROP_PROXY_RESPOND_HEADER , responseHeader ) ;
conProp . put ( HeaderFramework . CONNECTION_PROP_PROXY_RESPOND_STATUS , Integer . toString ( httpStatusCode ) ) ;
2008-08-02 14:12:04 +02:00
} catch ( final Exception e ) {
2005-06-09 12:07:02 +02:00
// any interruption may be caused be network error or because the user has closed
// the windows during transmission. We simply pass it as IOException
throw new IOException ( e . getMessage ( ) ) ;
}
}
2008-08-02 14:12:04 +02:00
public static boolean shallTransportZipped ( final String path ) {
2005-06-09 12:07:02 +02:00
if ( ( path = = null ) | | ( path . length ( ) = = 0 ) ) return true ;
int pos ;
2010-01-13 01:23:07 +01:00
if ( ( pos = path . lastIndexOf ( '.' ) ) ! = - 1 ) {
2005-06-09 12:07:02 +02:00
return ! disallowZippedContentEncoding . contains ( path . substring ( pos ) . toLowerCase ( ) ) ;
}
return true ;
2005-09-30 18:02:58 +02:00
}
2005-06-09 12:07:02 +02:00
2008-08-02 14:12:04 +02:00
public static boolean isThisSeedIP ( final String hostName ) {
2006-04-25 07:29:20 +02:00
if ( ( hostName = = null ) | | ( hostName . length ( ) = = 0 ) ) return false ;
// getting ip address and port of this seed
2008-08-17 12:16:32 +02:00
if ( getAlternativeResolver ( ) = = null ) return false ;
2006-04-25 07:29:20 +02:00
// resolve ip addresses
2009-10-11 02:12:19 +02:00
final InetAddress seedInetAddress = Domains . dnsResolve ( getAlternativeResolver ( ) . myIP ( ) ) ;
final InetAddress hostInetAddress = Domains . dnsResolve ( hostName ) ;
2006-05-15 12:50:10 +02:00
if ( seedInetAddress = = null | | hostInetAddress = = null ) return false ;
2006-04-25 07:29:20 +02:00
// if it's equal, the hostname points to this seed
return ( seedInetAddress . equals ( hostInetAddress ) ) ;
}
2008-08-02 14:12:04 +02:00
public static boolean isThisHostName ( final String hostName ) {
2005-10-12 10:17:43 +02:00
if ( ( hostName = = null ) | | ( hostName . length ( ) = = 0 ) ) return false ;
try {
2010-01-13 01:23:07 +01:00
final int idx = hostName . indexOf ( ':' ) ;
2005-10-12 10:17:43 +02:00
final String dstHost = ( idx ! = - 1 ) ? hostName . substring ( 0 , idx ) . trim ( ) : hostName . trim ( ) ;
2008-08-06 21:43:12 +02:00
final Integer dstPort = ( idx ! = - 1 ) ? Integer . valueOf ( hostName . substring ( idx + 1 ) . trim ( ) ) : Integer . valueOf ( 80 ) ;
2005-10-12 10:17:43 +02:00
// if the hostname endswith thisPeerName.yacy ...
2008-08-17 12:16:32 +02:00
final String alternativeAddress = ( getAlternativeResolver ( ) = = null ) ? null : getAlternativeResolver ( ) . myAlternativeAddress ( ) ;
2008-05-06 01:13:47 +02:00
if ( ( alternativeAddress ! = null ) & & ( dstHost . endsWith ( alternativeAddress ) ) ) {
2005-10-12 10:17:43 +02:00
return true ;
/ *
* If the port number is equal to the yacy port and the IP address is an address of this host . . .
* Please note that yacy is listening to all interfaces of this host
* /
2006-04-25 07:29:20 +02:00
} else if (
// check if the destination port is equal to the port yacy is listening to
2011-01-28 11:54:13 +01:00
dstPort . equals ( Integer . valueOf ( serverCore . getPortNr ( switchboard . getConfig ( " port " , " 8090 " ) ) ) ) & &
2006-04-25 07:29:20 +02:00
(
2011-02-12 00:37:13 +01:00
// check if the destination host is our local IP address
Domains . isThisHostIP ( dstHost ) | |
// check if the destination host is our seed ip address
isThisSeedIP ( dstHost )
2006-04-25 07:29:20 +02:00
)
) {
2005-10-12 10:17:43 +02:00
return true ;
}
2008-08-02 14:12:04 +02:00
} catch ( final Exception e ) { }
2005-10-12 10:17:43 +02:00
return false ;
2008-08-17 12:16:32 +02:00
}
/ * *
* @param alternativeResolver the alternativeResolver to set
* /
2011-02-12 00:37:13 +01:00
public static void setAlternativeResolver ( final AlternativeDomainNames alternativeResolver ) {
2009-07-19 22:37:44 +02:00
HTTPDemon . alternativeResolver = alternativeResolver ;
2008-08-17 12:16:32 +02:00
}
/ * *
* @return the alternativeResolver
* /
2010-11-22 20:12:51 +01:00
public static AlternativeDomainNames getAlternativeResolver ( ) {
2008-08-17 12:16:32 +02:00
return alternativeResolver ;
2010-08-23 14:32:02 +02:00
}
public static RequestHeader readHeader ( final Properties prop , final serverCore . Session theSession ) throws IOException {
// reading all headers
final RequestHeader header = new RequestHeader ( HTTPDemon . reverseMappingCache ) ;
int p ;
String line ;
while ( ( line = theSession . readLineAsString ( ) ) ! = null ) {
if ( line . length ( ) = = 0 ) break ; // this separates the header of the HTTP request from the body
// parse the header line: a property separated with the ':' sign
if ( ( p = line . indexOf ( ':' ) ) > = 0 ) {
// store a property
header . add ( line . substring ( 0 , p ) . trim ( ) , line . substring ( p + 1 ) . trim ( ) ) ;
}
}
/ *
* doing some header validation here . . .
* /
final String httpVersion = prop . getProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , " HTTP/0.9 " ) ;
if ( httpVersion . equals ( " HTTP/1.1 " ) & & ! header . containsKey ( HeaderFramework . HOST ) ) {
// the HTTP/1.1 specification requires that an HTTP/1.1 server must reject any
// HTTP/1.1 message that does not contain a Host header.
HTTPDemon . sendRespondError ( prop , theSession . out , 0 , 400 , null , null , null ) ;
throw new IOException ( " 400 Bad request " ) ;
}
return header ;
}
2011-01-03 21:52:54 +01:00
private static final Pattern P_20 = Pattern . compile ( " " , Pattern . LITERAL ) ;
private static final Pattern P_7B = Pattern . compile ( " { " , Pattern . LITERAL ) ;
private static final Pattern P_7D = Pattern . compile ( " } " , Pattern . LITERAL ) ;
private static final Pattern P_7C = Pattern . compile ( " | " , Pattern . LITERAL ) ;
private static final Pattern P_5C = Pattern . compile ( " \\ " , Pattern . LITERAL ) ;
private static final Pattern P_5E = Pattern . compile ( " ^ " , Pattern . LITERAL ) ;
private static final Pattern P_5B = Pattern . compile ( " [ " , Pattern . LITERAL ) ;
private static final Pattern P_5D = Pattern . compile ( " ] " , Pattern . LITERAL ) ;
private static final Pattern P_60 = Pattern . compile ( " ` " , Pattern . LITERAL ) ;
2010-08-23 14:32:02 +02:00
2011-01-03 21:52:54 +01:00
public static Properties parseRequestLine ( final String cmd , String args , final String virtualHost ) {
final Properties prop = new Properties ( ) ;
// storing informations about the request
prop . setProperty ( HeaderFramework . CONNECTION_PROP_METHOD , cmd ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_REQUESTLINE , cmd + " " + args ) ;
// this parses a whole URL
2011-02-12 00:37:13 +01:00
if ( args . isEmpty ( ) ) {
2011-01-03 21:52:54 +01:00
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HOST , virtualHost ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_PATH , " / " ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , HeaderFramework . HTTP_VERSION_0_9 ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_EXT , " " ) ;
return prop ;
}
// store the version propery "HTTP" and cut the query at both ends
int sep = args . lastIndexOf ( ' ' ) ;
2011-02-12 00:37:13 +01:00
if ( ( sep > = 0 ) & & ( args . substring ( sep + 1 ) . toLowerCase ( ) . startsWith ( " http/ " ) ) ) {
2011-01-03 21:52:54 +01:00
// HTTP version is given
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , args . substring ( sep + 1 ) . trim ( ) ) ;
args = args . substring ( 0 , sep ) . trim ( ) ; // cut off HTTP version mark
} else {
// HTTP version is not given, it will be treated as ver 0.9
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HTTP_VER , HeaderFramework . HTTP_VERSION_0_9 ) ;
}
// replacing spaces in the url string correctly
args = P_20 . matcher ( args ) . replaceAll ( " %20 " ) ;
// replace unwise characters (see RFC 2396, 2.4.3), which may not be escaped
args = P_7B . matcher ( args ) . replaceAll ( " %7B " ) ;
args = P_7D . matcher ( args ) . replaceAll ( " %7D " ) ;
args = P_7C . matcher ( args ) . replaceAll ( " %7C " ) ;
args = P_5C . matcher ( args ) . replaceAll ( " %5C " ) ;
args = P_5E . matcher ( args ) . replaceAll ( " %5E " ) ;
args = P_5B . matcher ( args ) . replaceAll ( " %5B " ) ;
args = P_5D . matcher ( args ) . replaceAll ( " %5D " ) ;
args = P_60 . matcher ( args ) . replaceAll ( " %60 " ) ;
// properties of the query are stored with the prefix "&"
// additionally, the values URL and ARGC are computed
2011-02-12 00:37:13 +01:00
final String argsString ;
2011-01-03 21:52:54 +01:00
sep = args . indexOf ( '?' ) ;
if ( sep > = 0 ) {
// there are values attached to the query string
argsString = args . substring ( sep + 1 ) ; // cut head from tail of query
args = args . substring ( 0 , sep ) ;
2011-02-12 00:37:13 +01:00
} else {
argsString = " " ;
2011-01-03 21:52:54 +01:00
}
prop . setProperty ( HeaderFramework . CONNECTION_PROP_URL , args ) ; // store URL
2011-02-12 00:37:13 +01:00
if ( ! argsString . isEmpty ( ) ) {
prop . setProperty ( HeaderFramework . CONNECTION_PROP_ARGS , argsString ) ;
} // store arguments in original form
2011-01-03 21:52:54 +01:00
// finally find host string
2011-02-12 00:37:13 +01:00
final String path ;
2011-01-03 21:52:54 +01:00
if ( args . toUpperCase ( ) . startsWith ( " HTTP:// " ) ) {
// a host was given. extract it and set path
args = args . substring ( 7 ) ;
sep = args . indexOf ( '/' ) ;
if ( sep < 0 ) {
// this is a malformed url, something like
// http://index.html
// we are lazy and guess that it means
// /index.html
// which is a localhost access to the file servlet
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HOST , args ) ;
path = " / " ;
} else {
// THIS IS THE "GOOD" CASE
// a perfect formulated url
final String dstHostSocket = args . substring ( 0 , sep ) ;
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HOST , ( HTTPDemon . isThisHostName ( dstHostSocket ) ? virtualHost : dstHostSocket ) ) ;
path = args . substring ( sep ) ; // yes, including beginning "/"
}
} else {
// no host in url. set path
if ( args . length ( ) > 0 & & args . charAt ( 0 ) = = '/' ) {
// thats also fine, its a perfect localhost access
// in this case, we simulate a
// http://localhost/s
// access by setting a virtual host
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HOST , virtualHost ) ;
path = args ;
} else {
// the client 'forgot' to set a leading '/'
// this is the same case as above, with some lazyness
prop . setProperty ( HeaderFramework . CONNECTION_PROP_HOST , virtualHost ) ;
path = " / " + args ;
}
}
prop . setProperty ( HeaderFramework . CONNECTION_PROP_PATH , path ) ;
// find out file extension (we already stripped ?-parameters from args)
2011-02-12 00:37:13 +01:00
final String ext ;
2011-01-03 21:52:54 +01:00
sep = path . lastIndexOf ( '.' ) ;
if ( sep > = 0 ) {
final int ancpos = path . indexOf ( " # " , sep + 1 ) ;
if ( ancpos > = sep ) {
// ex: /foo/bar.html#xy => html
ext = path . substring ( sep + 1 , ancpos ) . toLowerCase ( ) ;
} else {
// ex: /foo/bar.php => php
ext = path . substring ( sep + 1 ) . toLowerCase ( ) ;
}
2011-02-12 00:37:13 +01:00
} else {
ext = " " ; // default when no file extension
2011-01-03 21:52:54 +01:00
}
prop . setProperty ( HeaderFramework . CONNECTION_PROP_EXT , ext ) ;
return prop ;
}
2005-04-07 21:19:42 +02:00
}