mirror of
https://github.com/skogaby/butterfly.git
synced 2026-04-26 01:44:56 -05:00
More request validation and boilerplate code; now the request URI is validated against the request body's contents, and we are parsing the request into an XML document
This commit is contained in:
parent
4d504ce04e
commit
872b4d97c5
|
|
@ -17,6 +17,7 @@ dependencies {
|
|||
|
||||
compile group: 'com.sparkjava', name: 'spark-core', version: '2.7.2'
|
||||
compile group: 'com.google.guava', name: 'guava', version: '23.5-jre'
|
||||
compile group: 'org.slf4j', name: 'slf4j-simple', version:'1.7.21'
|
||||
}
|
||||
|
||||
jar {
|
||||
|
|
|
|||
|
|
@ -2,18 +2,22 @@ package com.buttongames.butterfly.http;
|
|||
|
||||
import com.buttongames.butterfly.compression.Lz77;
|
||||
import com.buttongames.butterfly.encryption.Rc4;
|
||||
import com.buttongames.butterfly.http.exception.InvalidRequestException;
|
||||
import com.buttongames.butterfly.http.exception.InvalidRequestMethodException;
|
||||
import com.buttongames.butterfly.http.exception.InvalidRequestModelException;
|
||||
import com.buttongames.butterfly.http.exception.InvalidRequestModuleException;
|
||||
import com.buttongames.butterfly.http.exception.MismatchedRequestUriException;
|
||||
import com.buttongames.butterfly.http.handlers.ServicesRequestHandler;
|
||||
import com.buttongames.butterfly.xml.BinaryXmlUtils;
|
||||
import com.buttongames.butterfly.xml.XmlUtils;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.SAXException;
|
||||
import spark.Request;
|
||||
import spark.utils.StringUtils;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import static spark.Spark.exception;
|
||||
|
|
@ -62,11 +66,16 @@ public class ButterflyHttpServer {
|
|||
SUPPORTED_MODULES = ImmutableSet.of("services");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for requests for the <code>services<code> module.
|
||||
*/
|
||||
private ServicesRequestHandler servicesRequestHandler;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public ButterflyHttpServer() {
|
||||
|
||||
this.servicesRequestHandler = new ServicesRequestHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -96,12 +105,11 @@ public class ButterflyHttpServer {
|
|||
// configure our root route; its handler will parse the request and go from there
|
||||
post("/", ((request, response) -> {
|
||||
// send the request to the right module handler
|
||||
final String requestBody = validateAndUnpackRequest(request);
|
||||
final Element requestBody = validateAndUnpackRequest(request);
|
||||
final String requestModule = request.queryParams("module");
|
||||
final String requestMethod = request.queryParams("method");
|
||||
|
||||
if (requestModule.equals("services")) {
|
||||
return ServicesRequestHandler.handleRequest(requestBody, requestMethod, response);
|
||||
return this.servicesRequestHandler.handleRequest(requestBody, request, response);
|
||||
} else {
|
||||
throw new InvalidRequestModuleException();
|
||||
}
|
||||
|
|
@ -119,13 +127,17 @@ public class ButterflyHttpServer {
|
|||
}
|
||||
|
||||
/**
|
||||
* Do some basic validation on the request before we handle it. Returns the request
|
||||
* body in plaintext form for handling, if it was a valid request.
|
||||
* TODO: Remove all the hardcoded stuff.
|
||||
* @param request The request to validate and unpack
|
||||
* @return A string representing the plaintext version of the packet, in XML format.
|
||||
* Validates incoming requests for basic sanity checks, and returns the request
|
||||
* as a plaintext XML document.
|
||||
* @param request The incoming request.
|
||||
* @return An <code>Element</code> representing the root of the request document
|
||||
* @throws GeneralSecurityException
|
||||
* @throws IOException
|
||||
* @throws ParserConfigurationException
|
||||
* @throws SAXException
|
||||
*/
|
||||
private String validateAndUnpackRequest(Request request) throws GeneralSecurityException, IOException {
|
||||
private Element validateAndUnpackRequest(Request request)
|
||||
throws GeneralSecurityException, IOException, ParserConfigurationException, SAXException {
|
||||
final String requestUriModel = request.queryParams("model");
|
||||
final String requestUriModule = request.queryParams("module");
|
||||
final String requestUriMethod = request.queryParams("method");
|
||||
|
|
@ -163,9 +175,30 @@ public class ButterflyHttpServer {
|
|||
|
||||
// read the request body into an XML document and check its properties
|
||||
// to verify it matches the request URI
|
||||
// TODO: Implement
|
||||
final Element rootNode = XmlUtils.byteArrayToXmlFile(reqBody);
|
||||
|
||||
if (rootNode == null ||
|
||||
!rootNode.getNodeName().equals("call")) {
|
||||
throw new InvalidRequestException();
|
||||
}
|
||||
|
||||
final Element moduleNode = (Element) rootNode.getFirstChild();
|
||||
final String requestBodyModel = rootNode.getAttribute("model");
|
||||
final String requestBodyModule = moduleNode.getNodeName();
|
||||
final String requestBodyMethod = moduleNode.getAttribute("method");
|
||||
|
||||
if (StringUtils.isBlank(requestBodyModel) ||
|
||||
StringUtils.isBlank(requestBodyModule) ||
|
||||
StringUtils.isBlank(requestBodyMethod) ||
|
||||
!requestBodyModel.equals(requestUriModel) ||
|
||||
!requestBodyModule.equals(requestUriModule) ||
|
||||
!requestBodyMethod.equals(requestUriMethod)) {
|
||||
throw new MismatchedRequestUriException();
|
||||
}
|
||||
|
||||
// TODO: Verify the PCBID
|
||||
|
||||
// 4) return the XML document
|
||||
return new String(reqBody, StandardCharsets.UTF_8);
|
||||
return rootNode;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package com.buttongames.butterfly.http.exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a request is made with some bad property (catch-all exception for
|
||||
* when we run out of specific ones but have an issue).
|
||||
* @author skogaby (skogabyskogaby@gmail.com)
|
||||
*/
|
||||
public class InvalidRequestException extends RuntimeException {
|
||||
}
|
||||
|
|
@ -1,8 +1,21 @@
|
|||
package com.buttongames.butterfly.http.handlers;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
/**
|
||||
* Base request handler that the others inherit from.
|
||||
* @author skogaby (skogabyskogaby@gmail.com)
|
||||
*/
|
||||
public class BaseRequestHandler {
|
||||
public abstract class BaseRequestHandler {
|
||||
|
||||
/**
|
||||
* Handles an incoming request for the given module.
|
||||
* @param requestBody The XML document of the incoming request.
|
||||
* @param request The Spark request
|
||||
* @param response The Spark response
|
||||
* @return A response object for Spark
|
||||
*/
|
||||
public abstract Object handleRequest(final Element requestBody, final Request request, final Response response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.buttongames.butterfly.http.handlers;
|
||||
|
||||
import com.buttongames.butterfly.http.exception.InvalidRequestMethodException;
|
||||
import org.w3c.dom.Element;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
/**
|
||||
|
|
@ -10,27 +12,30 @@ import spark.Response;
|
|||
public class ServicesRequestHandler extends BaseRequestHandler {
|
||||
|
||||
/**
|
||||
* Handles all requests to the <code>services/code> module.
|
||||
* @param request
|
||||
* @param requestMethod
|
||||
* @param response
|
||||
* @return
|
||||
* Handles an incoming request for the services module.
|
||||
* @param requestBody The XML document of the incoming request.
|
||||
* @param request The Spark request
|
||||
* @param response The Spark response
|
||||
* @return A response object for Spark
|
||||
*/
|
||||
public static Object handleRequest(String request, String requestMethod, Response response) {
|
||||
@Override
|
||||
public Object handleRequest(final Element requestBody, final Request request, final Response response) {
|
||||
final String requestMethod = request.queryParams("method");
|
||||
|
||||
if (requestMethod.equals("get")) {
|
||||
return handleGetRequest(request, response);
|
||||
return handleGetRequest(request, response);
|
||||
} else {
|
||||
throw new InvalidRequestMethodException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles <code>services.get</code> requests.
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
* Handles an incoming request for the given module.
|
||||
* @param request The Spark request
|
||||
* @param response The Spark response
|
||||
* @return A response object for Spark
|
||||
*/
|
||||
private static Object handleGetRequest(String request, Response response) {
|
||||
private static Object handleGetRequest(final Request request, final Response response) {
|
||||
// TODO: Implement
|
||||
return "Womp womp";
|
||||
}
|
||||
|
|
|
|||
64
src/main/java/com/buttongames/butterfly/xml/XmlUtils.java
Normal file
64
src/main/java/com/buttongames/butterfly/xml/XmlUtils.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package com.buttongames.butterfly.xml;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Class with helper methods for manipulating XML files.
|
||||
* @author skogaby (skogabyskogaby@gmail.com)
|
||||
*/
|
||||
public class XmlUtils {
|
||||
|
||||
/**
|
||||
* Scrubs empty nodes from a document so we don't accidentally read them.
|
||||
* @param node The root node of the document to clean.
|
||||
*/
|
||||
public static void clean(final Node node) {
|
||||
final NodeList childrem = node.getChildNodes();
|
||||
|
||||
for (int n = childrem.getLength() - 1; n >= 0; n--) {
|
||||
final Node child = childrem.item(n);
|
||||
final short nodeType = child.getNodeType();
|
||||
|
||||
if (nodeType == Node.ELEMENT_NODE) {
|
||||
clean(child);
|
||||
} else if (nodeType == Node.TEXT_NODE) {
|
||||
final String trimmedNodeVal = child.getNodeValue().trim();
|
||||
|
||||
if (trimmedNodeVal.length() == 0) {
|
||||
node.removeChild(child);
|
||||
} else {
|
||||
child.setNodeValue(trimmedNodeVal);
|
||||
}
|
||||
} else if (nodeType == Node.COMMENT_NODE) {
|
||||
node.removeChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Reads the given byte[] into an Element that represents the root node of the XML body.
|
||||
* @param body
|
||||
* @return
|
||||
* @throws ParserConfigurationException
|
||||
* @throws IOException
|
||||
* @throws SAXException
|
||||
*/
|
||||
public static Element byteArrayToXmlFile(final byte[] body)
|
||||
throws ParserConfigurationException, IOException, SAXException {
|
||||
final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
|
||||
final DocumentBuilder builder = builderFactory.newDocumentBuilder();
|
||||
final Document reqDocument = builder.parse(new ByteArrayInputStream(body));
|
||||
XmlUtils.clean(reqDocument);
|
||||
|
||||
return reqDocument.getDocumentElement();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user