Add helper class for formatting multipart mime

This commit is contained in:
Jeremy Lakeman 2016-10-10 09:17:38 +10:30
parent 62a1ca46bc
commit 30b2c1ea3f
11 changed files with 187 additions and 107 deletions

View File

@ -50,6 +50,8 @@ public abstract class AbstractId {
return binary;
}
public abstract String getMimeType();
public AbstractId(String hex) throws InvalidHexException {
if (hex==null)
throw new InvalidHexException(this, "null is not a invalid hex value");

View File

@ -36,4 +36,8 @@ public class BundleId extends SigningKey {
super(binary);
}
@Override
public String getMimeType() {
return null;
}
}

View File

@ -29,6 +29,11 @@ public class BundleKey extends AbstractId {
return 32;
}
@Override
public String getMimeType() {
return null; //TODO?
}
public BundleKey(String hex) throws InvalidHexException {
super(hex);
}

View File

@ -41,4 +41,8 @@ public class BundleSecret extends AbstractId {
super(binary);
}
@Override
public String getMimeType() {
return "rhizome/bundle-secret";
}
}

View File

@ -31,6 +31,11 @@ public class FileHash extends AbstractId {
return BINARY_SIZE;
}
@Override
public String getMimeType() {
return null;
}
public FileHash(String hex) throws InvalidHexException {
super(hex);
}

View File

@ -0,0 +1,97 @@
package org.servalproject.servaldna;
import org.servalproject.servaldna.rhizome.RhizomeIncompleteManifest;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.HttpURLConnection;
/**
* Created by jeremy on 5/10/16.
*/
public class PostHelper {
private HttpURLConnection conn;
private String boundary;
private OutputStream output;
private PrintStream writer;
public PostHelper(HttpURLConnection conn) {
this.conn = conn;
boundary = Long.toHexString(System.currentTimeMillis());
}
public void connect() throws IOException {
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
conn.connect();
output = conn.getOutputStream();
writer = new PrintStream(output, false, "UTF-8");
}
private void quoteString(StringBuilder sb, String unquoted)
{
if (unquoted == null) {
sb.append("null");
return;
}
sb.append('"');
for (int i = 0; i < unquoted.length(); ++i) {
char c = unquoted.charAt(i);
if (c == '"' || c == '\\')
sb.append('\\');
sb.append(c);
}
sb.append('"');
}
public void writeHeading(String name, String filename, String type, String encoding)
{
StringBuilder sb = new StringBuilder();
sb.append("\r\n--").append(boundary).append("\r\n");
sb.append("Content-Disposition: form-data; name=");
quoteString(sb, name);
if (filename!=null) {
sb.append("; filename=");
quoteString(sb, filename);
}
sb.append("\r\n");
sb.append("Content-Type: ").append(type).append("\r\n");
if (encoding!=null)
sb.append("Content-Transfer-Encoding: ").append(encoding).append("\r\n");
sb.append("\r\n");
writer.print(sb.toString());
}
public void writeField(String name, String value){
writeHeading(name, null, "text/plain; charset=utf-8", null);
writer.print(value);
}
public void writeField(String name, AbstractId value){
writeHeading(name, null, value.getMimeType(), "hex");
writer.print(value.toHex());
}
public void writeField(String name, String filename, InputStream stream) throws IOException {
writeHeading(name, filename, "application/octet-stream", "binary");
writer.flush();
byte[] buffer = new byte[4096];
int n;
while ((n = stream.read(buffer)) > 0)
output.write(buffer, 0, n);
}
public void writeField(String name, RhizomeIncompleteManifest manifest) throws IOException {
writeHeading(name, null, "rhizome/manifest; format=\"text+binarysig\"", "binary");
manifest.toTextFormat(writer);
}
public void close(){
writer.print("\r\n--" + boundary + "--\r\n");
writer.flush();
writer.close();
}
}

View File

@ -24,4 +24,9 @@ public class SigningKey extends AbstractId {
public int getBinarySize() {
return BINARY_SIZE;
}
@Override
public String getMimeType() {
return "serval-mesh/id";
}
}

View File

@ -31,6 +31,11 @@ public class SubscriberId extends AbstractId {
return BINARY_SIZE;
}
@Override
public String getMimeType() {
return "serval-mesh/sid";
}
public SubscriberId(String hex) throws InvalidHexException {
super(hex);
}

View File

@ -22,14 +22,13 @@ package org.servalproject.servaldna.meshms;
import org.servalproject.json.JSONInputException;
import org.servalproject.json.JSONTokeniser;
import org.servalproject.servaldna.PostHelper;
import org.servalproject.servaldna.ServalDFailureException;
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
import org.servalproject.servaldna.ServalDInterfaceException;
import org.servalproject.servaldna.SubscriberId;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;
@ -122,20 +121,10 @@ public class MeshMSCommon
public static MeshMSStatus sendMessage(ServalDHttpConnectionFactory connector, SubscriberId sid1, SubscriberId sid2, String text) throws IOException, ServalDInterfaceException, MeshMSException
{
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/meshms/" + sid1.toHex() + "/" + sid2.toHex() + "/sendmessage");
String boundary = Long.toHexString(System.currentTimeMillis());
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
conn.connect();
OutputStream ost = conn.getOutputStream();
PrintStream wr = new PrintStream(ost, false, "UTF-8");
wr.print("--" + boundary + "\r\n");
wr.print("Content-Disposition: form-data; name=\"message\"\r\n");
wr.print("Content-Type: text/plain; charset=utf-8\r\n");
wr.print("\r\n");
wr.print(text);
wr.print("\r\n--" + boundary + "--\r\n");
wr.close();
PostHelper helper = new PostHelper(conn);
helper.connect();
helper.writeField("message", text);
helper.close();
JSONTokeniser json = MeshMSCommon.receiveRestfulResponse(conn, HttpURLConnection.HTTP_CREATED);
Status status = decodeRestfulStatus(json);
throwRestfulResponseExceptions(status, conn.getURL());

View File

@ -26,6 +26,7 @@ import org.servalproject.servaldna.BundleId;
import org.servalproject.servaldna.BundleKey;
import org.servalproject.servaldna.BundleSecret;
import org.servalproject.servaldna.FileHash;
import org.servalproject.servaldna.PostHelper;
import org.servalproject.servaldna.ServalDFailureException;
import org.servalproject.servaldna.ServalDHttpConnectionFactory;
import org.servalproject.servaldna.ServalDInterfaceException;
@ -34,7 +35,6 @@ import org.servalproject.servaldna.SubscriberId;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
@ -384,56 +384,17 @@ public class RhizomeCommon
RhizomeEncryptionException
{
HttpURLConnection conn = connector.newServalDHttpConnection("/restful/rhizome/insert");
String boundary = Long.toHexString(System.currentTimeMillis());
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
conn.connect();
OutputStream ost = conn.getOutputStream();
PrintStream wr = new PrintStream(ost, false, "UTF-8");
wr.print(new Object(){}.getClass().getEnclosingClass().getName());
if (author != null) {
wr.print("\r\n--" + boundary + "\r\n");
wr.print("Content-Disposition: form-data; name=\"bundle-author\"\r\n");
wr.print("Content-Type: serval-mesh/sid\r\n");
wr.print("Content-Transfer-Encoding: hex\r\n");
wr.print("\r\n");
wr.print(author.toHex());
}
if (secret != null) {
wr.print("\r\n--" + boundary + "\r\n");
wr.print("Content-Disposition: form-data; name=\"bundle-secret\"\r\n");
wr.print("Content-Type: rhizome/bundle-secret\r\n");
wr.print("Content-Transfer-Encoding: hex\r\n");
wr.print("\r\n");
wr.print(secret.toHex());
}
wr.print("\r\n--" + boundary + "\r\n");
wr.print("Content-Disposition: form-data; name=\"manifest\"\r\n");
wr.print("Content-Type: rhizome/manifest; format=\"text+binarysig\"\r\n");
wr.print("Content-Transfer-Encoding: binary\r\n");
wr.print("\r\n");
wr.flush();
manifest.toTextFormat(ost);
if (payloadStream != null) {
wr.print("\r\n--" + boundary + "\r\n");
wr.print("Content-Disposition: form-data; name=\"payload\"");
if (fileName != null) {
wr.print("; filename=");
wr.print(quoteString(fileName));
}
wr.print("\r\n");
wr.print("Content-Type: application/octet-stream\r\n");
wr.print("Content-Transfer-Encoding: binary\r\n");
wr.print("\r\n");
wr.flush();
byte[] buffer = new byte[4096];
int n;
while ((n = payloadStream.read(buffer)) > 0)
ost.write(buffer, 0, n);
}
wr.print("\r\n--" + boundary + "--\r\n");
wr.close();
PostHelper helper = new PostHelper(conn);
helper.connect();
if (author != null)
helper.writeField("bundle-author", author);
if (secret != null)
helper.writeField("bundle-secret", secret);
helper.writeField("manifest", manifest);
if (payloadStream != null)
helper.writeField("payload", fileName, payloadStream);
helper.close();
int[] expected_response_codes = { HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_CREATED };
Status status = RhizomeCommon.receiveResponse(conn, expected_response_codes);
try {

View File

@ -20,22 +20,21 @@
package org.servalproject.servaldna.rhizome;
import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.io.UnsupportedEncodingException;
import org.servalproject.servaldna.AbstractId;
import org.servalproject.servaldna.BundleId;
import org.servalproject.servaldna.BundleKey;
import org.servalproject.servaldna.FileHash;
import org.servalproject.servaldna.SubscriberId;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import org.servalproject.servaldna.AbstractId;
import org.servalproject.servaldna.SubscriberId;
import org.servalproject.servaldna.BundleId;
import org.servalproject.servaldna.FileHash;
import org.servalproject.servaldna.BundleKey;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
public class RhizomeIncompleteManifest {
@ -80,36 +79,40 @@ public class RhizomeIncompleteManifest {
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public void toTextFormat(PrintStream writer){
if (id != null)
writer.print("id=" + id.toHex() + "\n");
if (version != null)
writer.print("version=" + version + "\n");
if (filesize != null)
writer.print("filesize=" + filesize + "\n");
if (filehash != null)
writer.print("filehash=" + filehash.toHex() + "\n");
if (sender != null)
writer.print("sender=" + sender.toHex() + "\n");
if (recipient != null)
writer.print("recipient=" + recipient.toHex() + "\n");
if (BK != null)
writer.print("BK=" + BK.toHex() + "\n");
if (crypt != null)
writer.print("crypt=" + crypt + "\n");
if (tail != null)
writer.print("tail=" + tail + "\n");
if (date != null)
writer.print("date=" + date + "\n");
if (service != null)
writer.print("service=" + service + "\n");
if (name != null)
writer.print("name=" + name + "\n");
for (Map.Entry<String,String> e: extraFields.entrySet())
writer.print(e.getKey() + "=" + e.getValue() + "\n");
}
public void toTextFormat(OutputStream os) throws IOException
{
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
if (id != null)
osw.write("id=" + id.toHex() + "\n");
if (version != null)
osw.write("version=" + version + "\n");
if (filesize != null)
osw.write("filesize=" + filesize + "\n");
if (filehash != null)
osw.write("filehash=" + filehash.toHex() + "\n");
if (sender != null)
osw.write("sender=" + sender.toHex() + "\n");
if (recipient != null)
osw.write("recipient=" + recipient.toHex() + "\n");
if (BK != null)
osw.write("BK=" + BK.toHex() + "\n");
if (crypt != null)
osw.write("crypt=" + crypt + "\n");
if (tail != null)
osw.write("tail=" + tail + "\n");
if (date != null)
osw.write("date=" + date + "\n");
if (service != null)
osw.write("service=" + service + "\n");
if (name != null)
osw.write("name=" + name + "\n");
for (Map.Entry<String,String> e: extraFields.entrySet())
osw.write(e.getKey() + "=" + e.getValue() + "\n");
osw.flush();
PrintStream wr = new PrintStream(os, false, "UTF-8");
toTextFormat(wr);
wr.flush();
}
/** Construct a Rhizome manifest from its text format representation.