package com.yahoo.dtf.actions.http.server; import java.util.ArrayList; import java.util.HashMap; import com.yahoo.dtf.actions.Action; import com.yahoo.dtf.exception.DTFException; import com.yahoo.dtf.exception.ParseException; /** * @dtf.tag http_server * * @dtf.since 1.0 * @dtf.author Rodney Gomes * * @dtf.tag.desc This tag will startup an HTTP server and bind it to the * host and port you specify. It will then create HTTP listeners * for the path and methods you specify and allow you to receive * each of the requests and do something with it. Now currently * each listener has as many threads as requests can come in, so * there is no throttling or pooling of request handling threads. * We will add this in the near future when it becomes a problem. * <br/> * The other thing to note bout the http_server tag is that it * doe snot currently allow you to define any HTTP config values * but can easily be adapted to do so in the near future. * * <p> * Events available within your http_listener include the same * event attributes that would be visible on the client side when * using any of the DTF HTTP tags and also include the following: * </p> * * <dl> * <b><dt>http.[METHOD].path<dt></b> * <dd> * This attribute contains the exact path that was hit with * your HTTP request. * </dd> * </dl> * * @dtf.tag.example * <http_server port="8080"> * <http_listener path="/oddtest" method="PUT"> * <mod op1="${http.put.headers.number}" op2="2" result="result"/> * <if> * <eq op1="${result}" op2="0"/> * <then> * <http_response status="200" * message="${http.put.headers.number} is even"/> * </then> * <else> * <http_response status="200" * message="${http.put.headers.number} is odd"/> * </else> * </if> * </http_listener> * </http_server> * * @dtf.tag.example * <http_server port="8082"> * <http_listener path="/echo-data" method="PUT"> * <log>received [${http.put.body}] in the HTTP body</log> * <log>received headers [${http.headers}]</log> * </http_listener> * </http_server> * * @dtf.tag.example * <http_server port="8082" command="stop"/> * */ public class Http_server extends Action { public final static String HTTP_SERVER_CTX = "dtf.http.server.ctx"; /** * @dtf.attr host * @dtf.attr.desc The hostname to bind to, incase your machine has multiple * interfaces or you would rather bind to localhost to not * allow external systems to use/attack your HTTP server. */ private String host = null; /** * @dtf.attr port * @dtf.attr.desc The port number to bind to, by default set to 80. */ private String port = null; /** * @dtf.attr command * @dtf.attr.desc What operation to do on the HTTP server identified by the * key hostname,port. The two existing options are 'start' * and 'stop' being that by default command is set to try * and start up the HTTP server described. */ private String command = null; /** * @dtf.attr threads * @dtf.attr.desc this property specifies the number of maximum threads to * use at any given time to handle incoming requests to this * HTTP server. */ private String threads = null; private static Object _lock = new Object(); protected static HashMap<String, RequestListener> getHttpServers() { synchronized (_lock) { HashMap<String, RequestListener> listeners = (HashMap<String, RequestListener>) getGlobalContext(HTTP_SERVER_CTX); if ( listeners == null ) { listeners = new HashMap<String, RequestListener>(); registerGlobalContext(HTTP_SERVER_CTX, listeners); } return listeners; } } @Override public void execute() throws DTFException { HashMap<String, RequestListener> listeners = getHttpServers(); String key = getHost() + ":" + getPort(); RequestListener rl = listeners.get(key); if ( command.equals("stop") ) { if ( rl == null ) throw new DTFException("No server started at [" + key + "]"); getLogger().info("Stopping [" + key + "]"); rl.shutdown(); listeners.remove(key); } else if ( command.equals("start") ){ if ( rl != null ) throw new DTFException("Already have a server at [" + key + "]"); rl = new RequestListener(getPort(),getThreads()); ArrayList<Http_listener> httplisteners = findActions(Http_listener.class); for (int i = 0; i < httplisteners.size(); i++) { Http_listener listener = httplisteners.get(i); DTFHttpHandler handler = new DTFHttpHandler(listener.getMethod(), listener); rl.addHttpListener(listener.getPath(), handler); } rl.start(); try { listeners.put(key,rl); getLogger().info("Starting [" + key + "]"); rl.join(); } catch (InterruptedException e) { throw new DTFException("Server thread interrupted.",e); } } else { throw new DTFException("Uknown HTTP server command [" + getCommand() + "]"); } } public String getHost() throws ParseException { return replaceProperties(host); } public void setHost(String host) { this.host = host; } public int getPort() throws ParseException { return toInt("port", port); } public void setPort(String port) { this.port = port; } public String getCommand() throws ParseException { return replaceProperties(command); } public void setCommand(String command) { this.command = command; } public int getThreads() throws ParseException { return toInt("threads",threads); } public void setThreads(String threads) { this.threads = threads; } }