/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.flow.controller;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import org.geoserver.ows.Request;
import org.geotools.util.logging.Logging;
/**
* A flow controller that throttles concurrent requests made from the same ip (any ip)
*
* @author Juan Marin, OpenGeo
*/
public class IpFlowController extends QueueController {
/**
* Thread local holding the current request queue id TODO: consider having a user map in {@link Request} instead
*/
static ThreadLocal<String> QUEUE_ID = new ThreadLocal<String>();
/**
* A flow controller that throttles concurrent requests made from the same ip (any ip)
*
* @author Juan Marin, OpenGeo
*/
static final Logger LOGGER = Logging.getLogger(IpFlowController.class);
public IpFlowController(int queueSize) {
this.queueSize = queueSize;
}
@Override
public void requestComplete(Request request) {
String queueId = QUEUE_ID.get();
QUEUE_ID.remove();
if(queueId != null) {
BlockingQueue<Request> queue = queues.get(queueId);
if (queue != null)
queue.remove(request);
}
}
@Override
public boolean requestIncoming(Request request, long timeout) {
boolean retval = true;
// check if this client already made other connections
final String incomingIp;
{
String ip = getRemoteAddr(request.getHttpRequest());
if (null == ip || "".equals(ip)) {
// may this happen? hope not, but if someone is trying to trick us lets not let him
// and pool it on the "empty IP" queue
incomingIp = "";
} else {
incomingIp = ip;
}
}
// see if we have that queue already
TimedBlockingQueue queue = queues.get(incomingIp);
// generate a unique queue id for this client if none was found
if (queue == null) {
//beware of multiple concurrent requests...
synchronized (queues) {
queue = queues.get(incomingIp);
if (queue == null) {
queue = new TimedBlockingQueue(queueSize, true);
queues.put(incomingIp, queue);
}
}
}
QUEUE_ID.set(incomingIp);
// queue token handling
try {
if (timeout > 0) {
retval = queue.offer(request, timeout, TimeUnit.MILLISECONDS);
} else {
queue.put(request);
}
} catch (InterruptedException e) {
LOGGER.log(Level.WARNING, "Unexpected interruption while "
+ "blocking on the request queue");
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("IpFlowController(" + queueSize + "," + incomingIp + ") queue size "
+ queue.size());
LOGGER.fine("IpFlowController(" + queueSize + "," + incomingIp + ") total queues "
+ queues.size());
}
return retval;
}
static String getRemoteAddr(HttpServletRequest req) {
String forwardedFor = req.getHeader("X-Forwarded-For");
if (forwardedFor != null) {
if(-1 == forwardedFor.indexOf(',')){
return forwardedFor;
}
String[] ips = forwardedFor.split(", ");
return ips[0];
} else {
return req.getRemoteAddr();
}
}
}