package org.toilelibre.libe.outside.monitor; import java.io.IOException; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Random; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.Banner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.batch.JobExecutionExitCodeGenerator; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; @SpringBootApplication @EnableWebMvc @EnableWebSecurity public class RequestMonitor { @Controller @RequestMapping ("/**") static class MonitorController { @RequestMapping (value = "/public/noContent", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE, method = RequestMethod.GET) @ResponseStatus (code = HttpStatus.NO_CONTENT) @ResponseBody public String emptyResponse () { return null; } @RequestMapping (value = "/public/data", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE, method = RequestMethod.POST) @ResponseStatus (code = HttpStatus.OK) @ResponseBody public byte[] data (final HttpServletRequest request) throws IOException { return this.logRequest (request, IOUtils.toString (request.getInputStream ())).getBytes (); } @RequestMapping (value = "/public/form", produces = MediaType.TEXT_PLAIN_VALUE, method = RequestMethod.POST) @ResponseStatus (code = HttpStatus.OK) @ResponseBody public String form (final HttpServletRequest request) throws ServletException, IOException { final Collection<Part> parts = request.getParts (); RequestMonitor.LOGGER.info (parts.toString ()); return this.logRequest (request, ""); } @RequestMapping (value = "/public/json", produces = MediaType.TEXT_PLAIN_VALUE) @ResponseStatus (code = HttpStatus.OK) @ResponseBody public String json (final HttpServletRequest request, @RequestBody (required = true) final String body) throws IOException { @SuppressWarnings ("unchecked") final Map<String, Object> map = new ObjectMapper ().readValue (body, Map.class); RequestMonitor.LOGGER.info (map.toString ()); return this.logRequest (request, body); } @RequestMapping (value = "/public/tooLong", produces = MediaType.TEXT_PLAIN_VALUE) @ResponseStatus (code = HttpStatus.OK) @ResponseBody public String tooLong () throws InterruptedException { Thread.sleep (1000); RequestMonitor.LOGGER.info ("Finally !"); return "...Finally."; } @RequestMapping (value = "/private/login", produces = MediaType.TEXT_PLAIN_VALUE) @ResponseStatus (code = HttpStatus.FOUND) @ResponseBody public String login (final HttpServletRequest request, final HttpServletResponse response, @RequestBody (required = false) final String body, final Authentication auth) { response.setHeader ("Location", this.serverLocation (request) + "/private/logged"); this.logRequest (request, body); return ""; } private String logRequest (final HttpServletRequest request, final String body) { final StringBuffer curlLog = new StringBuffer ("curl"); curlLog.append (" -k "); curlLog.append ("-E src/test/resources/clients/libe/libe.pem"); curlLog.append (" -X "); curlLog.append (request.getMethod ()); for (final Enumeration<String> headerNameEnumeration = request.getHeaderNames (); headerNameEnumeration.hasMoreElements ();) { final String headerName = headerNameEnumeration.nextElement (); final String headerValue = request.getHeader (headerName); curlLog.append (" -H '"); curlLog.append (headerName); curlLog.append (": "); curlLog.append (headerValue); curlLog.append ("'"); } if (body != null) { curlLog.append (" -d '"); curlLog.append (body.replace ("'", "''")); curlLog.append ("'"); } curlLog.append (" "); curlLog.append (" '"); curlLog.append (this.serverLocation (request) + request.getServletPath () + (request.getQueryString () == null ? "" : "?" + request.getQueryString ())); curlLog.append ("'"); RequestMonitor.LOGGER.info (curlLog.toString ()); return curlLog.toString (); } @RequestMapping (produces = "text/plain;charset=utf-8") @ResponseStatus (code = HttpStatus.OK) @ResponseBody public String receiveRequest (final HttpServletRequest request, @RequestBody (required = false) final String body) { return this.logRequest (request, body); } @RequestMapping (value = "/public/redirection", produces = MediaType.TEXT_PLAIN_VALUE) @ResponseStatus (code = HttpStatus.FOUND) @ResponseBody public String redirection (final HttpServletRequest request, final HttpServletResponse response, @RequestBody (required = false) final String body) { response.setHeader ("Location", this.serverLocation (request) + "/public/redirectedThere"); this.logRequest (request, body); return ""; } private String serverLocation (final HttpServletRequest request) { return request.getScheme () + "://" + request.getServerName () + ":" + RequestMonitor.port (); } @RequestMapping (value = "/public/unauthorized", produces = MediaType.TEXT_PLAIN_VALUE) @ResponseStatus (code = HttpStatus.UNAUTHORIZED) @ResponseBody public String unauthorized (final HttpServletRequest request, final HttpServletResponse response, @RequestBody (required = false) final String body) { response.setHeader ("Location", this.serverLocation (request) + "/public/tryagain"); this.logRequest (request, body); return ""; } } @Configuration @EnableWebSecurity static class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure (final HttpSecurity http) throws Exception { http.authorizeRequests ().antMatchers ("/private").permitAll ().anyRequest ().authenticated ().and ().httpBasic ().realmName ("basic").and ().logout ().permitAll (); } @Override public void configure (final WebSecurity http) { http.ignoring ().antMatchers ("/public/**"); } @Autowired public void configureGlobal (final AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication ().withUser ("user").password ("{noop}password").roles ("USER"); } } private static ConfigurableApplicationContext context; private static final Logger LOGGER = LoggerFactory.getLogger (RequestMonitor.class); private static int managementPort; private static int port; public static void main (final String [] args) { RequestMonitor.start (args); } public static int port () { return RequestMonitor.port; } public static int [] start () { return RequestMonitor.start (new String [0]); } public static int [] start (final boolean withSsl, final String [] args) { final Random random = new Random (); RequestMonitor.port = random.nextInt (32767) + 32768; RequestMonitor.managementPort = random.nextInt (32767) + 32768; RequestMonitor.start (RequestMonitor.port, RequestMonitor.managementPort, withSsl, args); return new int [] { RequestMonitor.port, RequestMonitor.managementPort }; } public static void start (final int port, final int managementPort, final boolean withSsl, final String [] args) { Map<String, Object> properties = new HashMap<String, Object>(); properties.put ("server.port", port); properties.put ("management.port", managementPort); if (withSsl) { properties.put ("server.ssl.key-store", "classpath:server/libe/libe.jks"); properties.put ("server.ssl.key-store-password", "myserverpass"); properties.put ("server.ssl.trust-store", "classpath:server/libe/libe.jks"); properties.put ("server.ssl.trust-store-password", "myserverpass"); properties.put ("server.ssl.client-auth", "need"); properties.put ("server.ssl.enabled-protocols","SSLv2,SSLv3,TLSv1.0,TLSv1.1,TLSv1.2"); } RequestMonitor.context = new SpringApplicationBuilder () .sources (RequestMonitor.class) .bannerMode (Banner.Mode.OFF) .addCommandLineProperties (true) .properties (properties) .run (args); } public static int [] start (final String [] args) { return RequestMonitor.start (true, args); } public static void stop () { SpringApplication.exit (RequestMonitor.context, new JobExecutionExitCodeGenerator ()); } }