package com.philemonworks.critter.ui; import com.philemonworks.critter.TrafficManager; import com.philemonworks.critter.action.Delay; import com.philemonworks.critter.action.Forward; import com.philemonworks.critter.action.Respond; import com.philemonworks.critter.condition.Host; import com.philemonworks.critter.rule.Rule; import com.philemonworks.critter.rule.RuleConverter; import com.philemonworks.critter.ui.fixed.EditFixedResponsePage; import com.philemonworks.critter.ui.fixed.FixedResponseBuilder; import org.apache.commons.lang3.StringUtils; import org.rendershark.http.HttpServer; import org.rendersnake.HtmlCanvas; import org.rendersnake.StringResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.*; import javax.ws.rs.core.Response; import java.io.*; import java.net.URI; import java.net.URL; import java.net.URLDecoder; import java.util.Properties; @Path("/ui") public class AdminUIResource { private static final Logger LOG = LoggerFactory.getLogger(AdminUIResource.class); @Inject TrafficManager trafficManager; @Inject @Named("Proxy") HttpServer proxyServer; @GET @Path("/newrule") @Produces("text/html") public Response newRule() throws IOException { HtmlCanvas html = new HtmlCanvas(); html.getPageContext() .withString("rulexml", StringResource.get(("/newrule.xml"))) .withBoolean("proxy.started", this.proxyServer.isStarted()); html.render(new SiteLayout(new NewRulePage())); return Response.ok().entity(html.toHtml()).build(); } @GET @Path("/newdelay") @Produces("text/html") public Response newDelay() throws IOException { HtmlCanvas html = new HtmlCanvas(); html.getPageContext() .withObject("rule", new Rule()) .withBoolean("proxy.started", this.proxyServer.isStarted()); html.render(new SiteLayout(new EditDelayPage())); return Response.ok().entity(html.toHtml()).build(); } @GET @Path("/newresponse") @Produces("text/html") public Response newFixedResponse() throws IOException { HtmlCanvas html = new HtmlCanvas(); html.getPageContext() .withObject("rule", new Rule()) .withBoolean("proxy.started", this.proxyServer.isStarted()); html.render(new SiteLayout(new EditFixedResponsePage())); return Response.ok().entity(html.toHtml()).build(); } @POST @Path("/toggleproxy") public Response toggleProxyActivation() throws Exception { if (this.proxyServer.isStarted()) { this.proxyServer.shutDown(); } else { this.proxyServer.startUp(); } return Response.seeOther(new URI("/")).build(); } @POST @Path("/newrule") @Produces("text/html") public Response saveRule(InputStream input) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String decoded = URLDecoder.decode(reader.readLine(),"utf8"); // Despite the name, this utility class is for HTML form decoding int eq = decoded.indexOf('='); String rulexml = null; try { rulexml = decoded.substring(eq+1); Rule rule = (Rule)RuleConverter.fromXml(rulexml, true); saveRuleIfItDoesNotExist(rule); } catch (Exception ex) { LOG.error("new rule contains errors:", ex); HtmlCanvas html = new HtmlCanvas(); html.getPageContext().withString("alert","This definition is not valid, please correct:<br>" + ex.getMessage()); html.getPageContext().withString("rulexml",rulexml); html.render(new SiteLayout(new NewRulePage())); return Response.ok().entity(html.toHtml()).build(); } return Response.seeOther(new URI("/")).build(); } private void saveRuleIfItDoesNotExist(Rule rule) { rule.ensureId(); if (this.trafficManager.getRule(rule.id) == null) { this.trafficManager.addOrReplaceRule(rule); } else { throw new IllegalArgumentException(String.format("A rule with ID %s already exists.", rule.id)); } } @POST @Path("/newresponse") @Produces("text/html") public Response saveFixedResponse(InputStream input) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String decoded = URLDecoder.decode(reader.readLine(),"utf8"); // Despite the name, this utility class is for HTML form decoding try { FixedResponseBuilder formDecoder = new FixedResponseBuilder(); Rule rule = formDecoder.buildRuleFrom(EditFixedResponsePage.toInput(EditFixedResponsePage.decode(decoded))); saveRuleIfItDoesNotExist(rule); } catch (Exception ex) { LOG.error("save fixed response failed", ex); HtmlCanvas html = new HtmlCanvas(); html.getPageContext().withString("alert", "This definition is not valid, please correct. " + ex.getMessage()); html.render(new SiteLayout(new EditFixedResponsePage())); return Response.ok().entity(html.toHtml()).build(); } return Response.seeOther(new URI("/")).build(); } @POST @Path("/newdelay") @Produces("text/html") public Response saveDelay(InputStream input) throws Exception { try { // TODO put this in util BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String decoded = URLDecoder.decode(reader.readLine(),"utf8"); // Despite the name, this utility class is for HTML form decoding Properties props = new Properties(); for (String keyvalue : decoded.split("&")) { String[] pair = keyvalue.split("="); props.put(pair[0], pair[1]); } Rule rule = new Rule(); rule.id = props.getProperty("critter_id"); URL url = new URL(props.getProperty("critter_url")); Host host = new Host(); host.matches = url.getHost(); rule.getConditions().add(host); if (!StringUtils.isEmpty(url.getPath()) && !"/".matches(url.getPath())) { com.philemonworks.critter.condition.Path path = new com.philemonworks.critter.condition.Path(); path.matches = url.getPath(); rule.getConditions().add(path); } Delay delay = new Delay(); delay.milliSeconds = Long.parseLong(props.getProperty("critter_delay")); rule.getActions().add(delay); rule.getActions().add(new Forward()); rule.getActions().add(new Respond()); saveRuleIfItDoesNotExist(rule); } catch (Exception ex) { LOG.error("save new delay failed", ex); HtmlCanvas html = new HtmlCanvas(); html.getPageContext().withString("alert","This definition is not valid, please correct."); html.render(new SiteLayout(new EditDelayPage())); return Response.ok().entity(html.toHtml()).build(); } return Response.seeOther(new URI("/")).build(); } @GET @Path("/traffic.css") @Produces("text/css") public Response trafficCss() { return Response.ok().entity(this.getClass().getResourceAsStream("/traffic.css")).build(); } @GET @Path("/traffic.js") @Produces("text/plain") public Response trafficJs() { return Response.ok().entity(this.getClass().getResourceAsStream("/traffic.js")).build(); } @GET @Path("/rules/{id}") @Produces("text/html") public Response showRule(@PathParam("id") String id) throws IOException { HtmlCanvas html = new HtmlCanvas(); html.getPageContext() .withObject("rule", trafficManager.getRule(id)) .withBoolean("proxy.started", this.proxyServer.isStarted()); html.render(new SiteLayout(new RulePage())); return Response.ok().entity(html.toHtml()).build(); } @GET @Path("/rules/{id}/edit") @Produces("text/html") public Response editRule(@PathParam("id") String id) throws IOException { HtmlCanvas html = new HtmlCanvas(); Rule rule = trafficManager.getRule(id); html.getPageContext() .withObject("rule", rule) .withObject("id", id) .withString("rulexml", RuleConverter.toXml(rule)) .withBoolean("proxy.started", this.proxyServer.isStarted()); html.render(new SiteLayout(new EditRulePage())); return Response.ok().entity(html.toHtml()).build(); } @POST @Path("/rules/{id}/edit") @Produces("text/html") public Response saveRuleAfterEdit(@PathParam("id") String id, InputStream input) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String decoded = URLDecoder.decode(reader.readLine(), "utf8"); // Despite the name, this utility class is for HTML form decoding int eq = decoded.indexOf('='); String rulexml = null; try { rulexml = decoded.substring(eq+1); Rule rule = (Rule)RuleConverter.fromXml(new ByteArrayInputStream(rulexml.getBytes())); this.trafficManager.addOrReplaceRule(rule); } catch (Exception ex) { HtmlCanvas html = new HtmlCanvas(); html.getPageContext().withString("alert","This definition is not valid, please correct."); html.getPageContext().withString("rulexml",rulexml); html.getPageContext().withString("id", id); html.render(new SiteLayout(new EditRulePage())); return Response.ok().entity(html.toHtml()).build(); } return Response.seeOther(new URI("/")).build(); } }