public void run() {
		handler.addClient(this);
		connected = true;
		int numberOfRetries = 0;
		int seconds = 0;
		InputStream inputStream;
   		try {
   	    	while(connected && !clientSocket.isClosed()) {
    			inputStream = clientSocket.getInputStream();
    			// give the target a chance to send something
    			try {
    				Thread.sleep(500);
    			}
    			catch(InterruptedException e) {
    				// nah
    			}
    			if(inputStream.available() > 0) {
    				byte[] request = new byte[inputStream.available()];
    				inputStream.read(request);
    				String requestString = new String(request);
    				Fluid.log("Request:\n" + requestString, 1);
    				// authorization needed
    				if(username != null && password != null) {
    					int index = 0;
    					boolean authorized = false;
    					if((index = requestString.indexOf("Authorization: Basic")) >= 0) {
    						String base64 = requestString.substring(index + 21).trim();
    						if((index = base64.indexOf(" ")) > 0)
    							base64 = base64.substring(0, index);
    						if((index = base64.indexOf("\r")) > 0)
    							base64 = base64.substring(0, index);
    						if((index = base64.indexOf("\n")) > 0)
    							base64 = base64.substring(0, index);
    						if(base64.equals(Base64Encoder.encode(username + ":" + password)))
    							authorized = true;
    					}
    					if(!authorized) {
    						for(int i = 0; i < requestAuthorization.length; i++) {
    							outputStream.write(requestAuthorization[i].getBytes());
    							outputStream.write("\r\n".getBytes());
    						}
							outputStream.write("\r\n\r\n".getBytes());
    						connected = false;
    						break;
    					}
    				}
    				// pulling stats
    				if(requestString.indexOf("stats.xml") > 0) {
    					String maxListeners = "";
    					if(maxNumberOfClients > 0)
    						maxListeners = "<limit>" + maxNumberOfClients + "</limit>";
    					String response = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n" +
    					"<icestats>" +
    					"<client_connections>" + Client.numberOfConnections + "</client_connections>" +
    					"<server>" + Fluid.version + "</server>" + 
    					"<server_start>" + Fluid.startingTime + "</server_start>" +
    					"<sources>1</sources>" +
    					"<source mount=\"/live\">" +
    					"<listeners>" + Client.numberOfClients + "</listeners>" +
    					maxListeners +
    					"<public>0</public>" +
    					"</source>" +
    					"</icestats>\r\n\r\n";
    					String responseHeader = "HTTP/1.0 200 OK\r\n" + 
    					"Content-Type: text/xml\r\n" +
    					"Content-Length: " + response.length() + "\r\n\r\n";
						outputStream.write(responseHeader.getBytes());
						outputStream.write(response.getBytes("ISO-8859-1"));
    					connected = false;
    					break;
    				}
    				if(maxNumberOfClients > 0 && numberOfClients >= maxNumberOfClients) {
						for(int i = 0; i < rejectFull.length; i++) {
							outputStream.write(rejectFull[i].getBytes());
							outputStream.write("\r\n".getBytes());
						}
						outputStream.write("\r\n\r\n".getBytes());
						connected = false;
						break;
    				}
    				// everything is ok, let the handler do its stuff
    				handler.handleRequest(this, request);
    				// then welcome the client
    				if(!greeted) {
    					String agent = "Unknown client";
    					int index = 0;
    					if((index = requestString.indexOf("User-Agent:")) >= 0) {
    						agent = requestString.substring(index + 11).trim();
    						if((index = agent.indexOf("\r")) > 0)
    							agent = agent.substring(0, index);
    						if((index = agent.indexOf("\n")) > 0)
    							agent = agent.substring(0, index);    						
    					}
    					Fluid.log(
    							"Target: " + this + 
    							" (" + agent + ") connected"
    							);
    					String[] greeting;
    					if(requestString.indexOf("Icy-MetaData:1") >= 0)
    						greeting = greetingICY;
    					else
    						greeting = greetingHTTP;
    					for(int i = 0; i < greeting.length; i++) {
        					Fluid.log("Greeting: " + greeting[i], 2);
    						outputStream.write(greeting[i].getBytes());
    						outputStream.write("\r\n".getBytes());
    					}
    					outputStream.write(("Content-Length: " + contentLength).getBytes());
    					outputStream.write("\r\n\r\n".getBytes());
    					Fluid.log("Setting greeted to true (real greeting)", 2);
						Client.numberOfClients++;
						greeted = true;
    				}
    			}
    			// assume client does not need greeting if it stays silent after two retries
    			if(!greeted) {
    				if(++numberOfRetries > 1) {
        				Client.numberOfClients++;
        				greeted = true;
    				}
    			}
    			// check if idle
    			seconds = (seconds + 1) % (2 * idleTimeout);
    			if(seconds == 0) {
    				if(idle) {
    					connected = false;
    					Fluid.log(
    							"Target: " + this + 
    							" idle timeout (" + idleTimeout + " seconds)"
    							);
    					break;
    				}
    				else {
    					idle = true;
    				}    				
    			}
    		}
			clientSocket.close();
   		}
   	    catch(IOException e) {
    		connected = false;
    	}
   	    if(greeted) {
   	   	    Client.numberOfClients--;
   	    	// the 1 avoids division by zero
   	    	long millis = 1 + System.currentTimeMillis() - connectionTime; 
   	    	Fluid.log(
   					"Target: " + this 
   					+ " (" + bytesSent + " B over "
   					+ (long) (millis / 1000 / 3600) + "."
   					+ (long) (millis / 1000 / 60) % 60 + ":"
   					+ (long) (millis / 1000) % 60 % 60 + " = "
   					+ (long) (bytesSent * 8 * 1000 / millis) + " bps)"
   					+ " disconnected"
				);
   	    }
   	    handler.removeClient(this);
    }