Skip to content

Commit

Permalink
Refactoring of #237 and #240
Browse files Browse the repository at this point in the history
  • Loading branch information
perwendel committed May 14, 2015
1 parent 63736a9 commit b71eb61
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 9 deletions.
70 changes: 70 additions & 0 deletions src/main/java/spark/utils/GzipUtils.java
@@ -0,0 +1,70 @@
/*
* Copyright 2015 - Per Wendel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package spark.utils;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.zip.GZIPOutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* GZIP utility class.
*
* @author Edward Raff
* @author Per Wendel
*/
public class GzipUtils {

private static final String ACCEPT_ENCODING = "Accept-Encoding";
private static final String CONTENT_ENCODING = "Content-Encoding";

private static final String GZIP = "gzip";

// Hide constructor
private GzipUtils() {

}

/**
* Checks if the HTTP request/response accepts and wants GZIP and i that case wraps the response output stream in a
* {@link java.util.zip.GZIPOutputStream}.
*
* @param httpRequest the HTTP servlet request.
* @param httpResponse the HTTP servlet response.
* @return if accepted and wanted a {@link java.util.zip.GZIPOutputStream} otherwise the unchanged response
* output stream.
* @throws IOException in case of IO error.
*/
public static OutputStream checkAndWrap(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws
IOException {
OutputStream outputStream = httpResponse.getOutputStream();

// GZIP Support handled here. First we must ensure that we want to use gzip, and that the client supports gzip
boolean acceptsGzip = Collections.list(httpRequest.getHeaders(ACCEPT_ENCODING)).stream().anyMatch(s -> s.contains(GZIP));
boolean wantGzip = httpResponse.getHeaders(CONTENT_ENCODING).contains(GZIP);

if (acceptsGzip && wantGzip) {
outputStream = new GZIPOutputStream(outputStream, true);
}

return outputStream;
}

}
7 changes: 6 additions & 1 deletion src/main/java/spark/webserver/BytesSerializer.java
Expand Up @@ -20,10 +20,15 @@
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;


/**
* Bytes serializer.
*
* @author alex
*/
public class BytesSerializer extends Serializer { public class BytesSerializer extends Serializer {


@Override @Override
public boolean canHandle(Object element) { public boolean canProcess(Object element) {
return element instanceof byte[] || element instanceof ByteBuffer; return element instanceof byte[] || element instanceof ByteBuffer;
} }


Expand Down
6 changes: 3 additions & 3 deletions src/main/java/spark/webserver/DefaultSerializer.java
Expand Up @@ -21,14 +21,14 @@
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;


/** /**
* Serilizer that writes the result of toString to output in UTF-8 encoding * Serializer that writes the result of toString to output in UTF-8 encoding
* *
* @author alsoto * @author alex
*/ */
public class DefaultSerializer extends Serializer { public class DefaultSerializer extends Serializer {


@Override @Override
public boolean canHandle(Object element) { public boolean canProcess(Object element) {
return true; return true;
} }


Expand Down
7 changes: 6 additions & 1 deletion src/main/java/spark/webserver/InputStreamSerializer.java
Expand Up @@ -22,10 +22,15 @@


import spark.utils.IOUtils; import spark.utils.IOUtils;


/**
* Input stream serializer.
*
* @author alex
*/
public class InputStreamSerializer extends Serializer { public class InputStreamSerializer extends Serializer {


@Override @Override
public boolean canHandle(Object element) { public boolean canProcess(Object element) {
return element instanceof InputStream; return element instanceof InputStream;
} }


Expand Down
13 changes: 11 additions & 2 deletions src/main/java/spark/webserver/MatcherFilter.java
Expand Up @@ -17,6 +17,7 @@
package spark.webserver; package spark.webserver;


import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.List; import java.util.List;


import javax.servlet.Filter; import javax.servlet.Filter;
Expand All @@ -40,6 +41,7 @@
import spark.route.HttpMethod; import spark.route.HttpMethod;
import spark.route.RouteMatch; import spark.route.RouteMatch;
import spark.route.SimpleRouteMatcher; import spark.route.SimpleRouteMatcher;
import spark.utils.GzipUtils;


/** /**
* Filter for matching of filters and routes. * Filter for matching of filters and routes.
Expand Down Expand Up @@ -240,7 +242,14 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
if (httpResponse.getContentType() == null) { if (httpResponse.getContentType() == null) {
httpResponse.setContentType("text/html; charset=utf-8"); httpResponse.setContentType("text/html; charset=utf-8");
} }
serializerChain.process(httpResponse.getOutputStream(), bodyContent);
// Check if gzip is wanted/accepted and in that case handle that
OutputStream outputStream = GzipUtils.checkAndWrap(httpRequest, httpResponse);

// serialize the body to output stream
serializerChain.process(outputStream, bodyContent);

outputStream.flush();//needed for GZIP stream. NOt sure where the HTTP response actually gets cleaned up
} }
} else if (chain != null) { } else if (chain != null) {
chain.doFilter(httpRequest, httpResponse); chain.doFilter(httpRequest, httpResponse);
Expand All @@ -253,4 +262,4 @@ public void destroy() {


private static final String NOT_FOUND = "<html><body><h2>404 Not found</h2></body></html>"; private static final String NOT_FOUND = "<html><body><h2>404 Not found</h2></body></html>";
private static final String INTERNAL_ERROR = "<html><body><h2>500 Internal Error</h2></body></html>"; private static final String INTERNAL_ERROR = "<html><body><h2>500 Internal Error</h2></body></html>";
} }
29 changes: 27 additions & 2 deletions src/main/java/spark/webserver/Serializer.java
Expand Up @@ -28,12 +28,24 @@ public abstract class Serializer {


private Serializer next; private Serializer next;


/**
* Sets the next serializer in the chain.
*
* @param serializer the next serializer.
*/
public void setNext(Serializer serializer) { public void setNext(Serializer serializer) {
this.next = serializer; this.next = serializer;
} }


/**
* Wraps {@link spark.webserver.Serializer#process(java.io.OutputStream, Object)} and calls next serializer in chain.
*
* @param outputStream the output stream.
* @param element the element to process.
* @throws IOException IOException in case of IO error.
*/
public void processElement(OutputStream outputStream, Object element) throws IOException { public void processElement(OutputStream outputStream, Object element) throws IOException {
if (canHandle(element)) { if (canProcess(element)) {
process(outputStream, element); process(outputStream, element);
} else { } else {
if (next != null) { if (next != null) {
Expand All @@ -42,7 +54,20 @@ public void processElement(OutputStream outputStream, Object element) throws IOE
} }
} }


public abstract boolean canHandle(Object element); /**
* Checks if the serializer implementation can process the element type.
*
* @param element the element to process.
* @return true if the serializer can process the provided element.
*/
public abstract boolean canProcess(Object element);


/**
* Processes the provided element and serializes to output stream.
*
* @param outputStream the output stream.
* @param element the element.
* @throws IOException In the case of IO error.
*/
public abstract void process(OutputStream outputStream, Object element) throws IOException; public abstract void process(OutputStream outputStream, Object element) throws IOException;
} }
13 changes: 13 additions & 0 deletions src/main/java/spark/webserver/SerializerChain.java
Expand Up @@ -19,10 +19,16 @@
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;


/**
* Chain of serializers for the output.
*/
public class SerializerChain { public class SerializerChain {


private Serializer root; private Serializer root;


/**
* Constructs a serializer chain.
*/
public SerializerChain() { public SerializerChain() {


DefaultSerializer defaultSerializer = new DefaultSerializer(); DefaultSerializer defaultSerializer = new DefaultSerializer();
Expand All @@ -36,6 +42,13 @@ public SerializerChain() {
this.root = bytesSerializer; this.root = bytesSerializer;
} }


/**
* Process the output.
*
* @param outputStream the output stream to write to.
* @param element the element to serialize.
* @throws IOException in the case of IO error.
*/
public void process(OutputStream outputStream, Object element) throws IOException { public void process(OutputStream outputStream, Object element) throws IOException {
this.root.processElement(outputStream, element); this.root.processElement(outputStream, element);
} }
Expand Down

0 comments on commit b71eb61

Please sign in to comment.