package org.jrack; import org.jrack.context.MapContext; import org.jrack.utils.StreamHelper; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.io.*; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; public class RackResponse extends MapContext<String> { public static final String DEFAULT_ENCODING = "utf-8"; public RackResponse(Context<String> env) { for (Map.Entry<String, Object> entry : env) { with(entry.getKey(), entry.getValue()); } } public RackResponse(int status) { with(Rack.MESSAGE_STATUS, status); } public RackResponse withHeader(String key, String value) { with(Rack.HTTP_ + key, value); return this; } public RackResponse withHeaders(Context<String> headers) { for (Map.Entry<String, Object> entry : headers) { withHeader(entry.getKey(), (String) entry.getValue()); } return this; } public RackResponse withBody(RackBody body) { with(Rack.MESSAGE_BODY, body); return this; } public RackResponse withBody(String body) { with(Rack.MESSAGE_BODY, new StringRackBody(body)); return this; } public RackResponse withBody(byte[] bytes) { with(Rack.MESSAGE_BODY, new BytesRackBody(bytes)); return this; } public RackResponse withBody(File file) throws FileNotFoundException { with(Rack.MESSAGE_BODY, new FileRackBody(file)); return this; } public RackResponse withContentLength(long length) { return withHeader("Content-Length", Long.toString(length)); } public RackResponse withContentType(String type) { return withHeader("Content-Type", type); } public int getStatus() { return (Integer) getObject(Rack.MESSAGE_STATUS); } public RackBody getBody() { return getBody(this); } public Context<String> getHeaders() { return getHeaders(this); } public static Context<String> getHeaders(Context<String> env) { Context<String> ret = new MapContext<String>(); for (Map.Entry<String, Object> entry : env) { if (entry.getKey().startsWith(Rack.HTTP_)) { ret.with(entry.getKey().substring(Rack.HTTP_.length()), entry.getValue()); } } return ret; } public static RackBody getBody(Context<String> response) { return (RackBody) response.getObject(Rack.MESSAGE_BODY); } public static String getBodyAsString(Context<String> response, Charset charset) { StringBuilder ret = new StringBuilder(); for (byte[] chunk : getBody(response).getBodyAsBytes()) { ret.append(new String(chunk, charset)); } return ret.toString(); } public static String getBodyAsString(Context<String> response) { StringBuilder ret = new StringBuilder(); for (byte[] chunk : getBody(response).getBodyAsBytes()) { ret.append(new String(chunk)); } return ret.toString(); } public static byte[] getBodyAsBytes(Context<String> response) { RackBody body = getBody(response); if (null == body) return null; List<byte[]> chunks = new ArrayList<byte[]>(); int length = 0; for (byte[] chunk : body.getBodyAsBytes()) { length += chunk.length; chunks.add(chunk); } byte[] ret = new byte[length]; int offset = 0; for (byte[] chunk : chunks) { int step = 0; for (byte b : chunk) { ret[offset + step++] = b; } offset += step; } return ret; } public String toString() { return "RackResponse[status=" + getStatus() + " body=" + getBody() + " headers=" + getHeaders() + "]"; } private class StringRackBody implements RackBody { private String body; public StringRackBody(String body) { this.body = body; } @Override public Type getType() { return Type.literal; } @Override public File getBodyAsFile() { throw new IllegalStateException("String body cannot be converted to file"); } @Override public Iterable<String> getBodyAsStrings() { return Collections.singleton(body); } @Override public Iterable<byte[]> getBodyAsBytes() { return Collections.singleton(body.getBytes()); // TODO charset? } @Override public byte[] getBytes(String charset) { try { return body.getBytes(charset); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return Rack.EMPTY_BYTE_ARRAY; } } @Override public byte[] getBytes() { return body.getBytes(); } } private class BytesRackBody implements RackBody { private byte[] body; public BytesRackBody(byte[] body) { this.body = body; } @Override public Type getType() { return Type.literal; } @Override public File getBodyAsFile() { throw new IllegalStateException("bytes body cannot be converted to file"); } @Override public Iterable<String> getBodyAsStrings() { return Collections.singleton(new String(body)); // TODO charset? } @Override public Iterable<byte[]> getBodyAsBytes() { return Collections.singleton(body); } @Override public byte[] getBytes() { return body; } @Override public byte[] getBytes(String charset) { return body; } } private class FileRackBody implements RackBody { private File body; public FileRackBody(File file) { this.body = file; } @Override public Type getType() { return Type.file; } @Override public File getBodyAsFile() { return body; } @Override public Iterable<String> getBodyAsStrings() { try { return Collections.singleton(StreamHelper.readAsString(new FileInputStream(body))); } catch (IOException e) { throw new IllegalStateException("Exception attempting to read file [" + body + "]", e); } } @Override public Iterable<byte[]> getBodyAsBytes() { try { return Collections.singleton(StreamHelper.readAsBytes(new FileInputStream(body))); } catch (IOException e) { throw new IllegalStateException("Exception attempting to read file [" + body + "]", e); } } @Override public byte[] getBytes() { throw new NotImplementedException(); } @Override public byte[] getBytes(String charset) { throw new NotImplementedException(); } } }