Created
October 10, 2015 19:02
-
-
Save JensWalter/0f19780d131d903879a2 to your computer and use it in GitHub Desktop.
FormDataHandler for com.sun.net.httpserver.HttpHandler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.trivium.glue.binding.http; | |
import com.sun.net.httpserver.Headers; | |
import com.sun.net.httpserver.HttpExchange; | |
import com.sun.net.httpserver.HttpHandler; | |
import java.io.ByteArrayOutputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.nio.charset.Charset; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.logging.Level; | |
import java.util.logging.Logger; | |
public abstract class FormDataHandler implements HttpHandler { | |
@Override | |
public void handle(HttpExchange httpExchange) throws IOException { | |
Headers headers = httpExchange.getRequestHeaders(); | |
String contentType = headers.getFirst("Content-Type"); | |
if(contentType.startsWith("multipart/form-data")){ | |
//found form data | |
String boundary = contentType.substring(contentType.indexOf("boundary=")+9); | |
// as of rfc7578 - prepend "\r\n--" | |
byte[] boundaryBytes = ("\r\n--" + boundary).getBytes(Charset.forName("UTF-8")); | |
byte[] payload = getInputAsBinary(httpExchange.getRequestBody()); | |
ArrayList<MultiPart> list = new ArrayList<>(); | |
List<Integer> offsets = searchBytes(payload, boundaryBytes, 0, payload.length - 1); | |
for(int idx=0;idx<offsets.size();idx++){ | |
int startPart = offsets.get(idx); | |
int endPart = payload.length; | |
if(idx<offsets.size()-1){ | |
endPart = offsets.get(idx+1); | |
} | |
byte[] part = Arrays.copyOfRange(payload,startPart,endPart); | |
//look for header | |
int headerEnd = indexOf(part,"\r\n\r\n".getBytes(Charset.forName("UTF-8")),0,part.length-1); | |
if(headerEnd>0) { | |
MultiPart p = new MultiPart(); | |
byte[] head = Arrays.copyOfRange(part, 0, headerEnd); | |
String header = new String(head); | |
// extract name from header | |
int nameIndex = header.indexOf("\r\nContent-Disposition: form-data; name="); | |
if (nameIndex >= 0) { | |
int startMarker = nameIndex + 39; | |
//check for extra filename field | |
int fileNameStart = header.indexOf("; filename="); | |
if (fileNameStart >= 0) { | |
String filename = header.substring(fileNameStart + 11, header.indexOf("\r\n", fileNameStart)); | |
p.filename = filename.replace('"', ' ').replace('\'', ' ').trim(); | |
p.name = header.substring(startMarker, fileNameStart).replace('"', ' ').replace('\'', ' ').trim(); | |
p.type = PartType.FILE; | |
} else { | |
int endMarker = header.indexOf("\r\n", startMarker); | |
if (endMarker == -1) | |
endMarker = header.length(); | |
p.name = header.substring(startMarker, endMarker).replace('"', ' ').replace('\'', ' ').trim(); | |
p.type = PartType.TEXT; | |
} | |
} else { | |
// skip entry if no name is found | |
continue; | |
} | |
// extract content type from header | |
int typeIndex = header.indexOf("\r\nContent-Type:"); | |
if (typeIndex >= 0) { | |
int startMarker = typeIndex + 15; | |
int endMarker = header.indexOf("\r\n", startMarker); | |
if (endMarker == -1) | |
endMarker = header.length(); | |
p.contentType = header.substring(startMarker, endMarker).trim(); | |
} | |
//handle content | |
if (p.type == PartType.TEXT) { | |
//extract text value | |
byte[] body = Arrays.copyOfRange(part, headerEnd + 4, part.length); | |
p.value = new String(body); | |
} else { | |
//must be a file upload | |
p.bytes = Arrays.copyOfRange(part, headerEnd + 4, part.length); | |
} | |
list.add(p); | |
} | |
} | |
handle(httpExchange,list); | |
}else{ | |
//if no form data is present, still call handle method | |
handle(httpExchange,null); | |
} | |
} | |
public abstract void handle(HttpExchange httpExchange,List<MultiPart> parts) throws IOException; | |
public static byte[] getInputAsBinary(InputStream requestStream) { | |
ByteArrayOutputStream bos = new ByteArrayOutputStream(); | |
try { | |
byte[] buf = new byte[100000]; | |
int bytesRead=0; | |
while ((bytesRead = requestStream.read(buf)) != -1){ | |
//while (requestStream.available() > 0) { | |
// int i = requestStream.read(buf); | |
bos.write(buf, 0, bytesRead); | |
} | |
requestStream.close(); | |
bos.close(); | |
} catch (IOException e) { | |
Logger log = Logger.getLogger(HttpUtils.class.getName()); | |
log.log(Level.SEVERE, "error while decoding http input stream", e); | |
} | |
return bos.toByteArray(); | |
} | |
/** | |
* Search bytes in byte array returns indexes within this byte-array of all | |
* occurrences of the specified(search bytes) byte array in the specified | |
* range | |
* borrowed from https://github.com/riversun/finbin/blob/master/src/main/java/org/riversun/finbin/BinarySearcher.java | |
* | |
* @param srcBytes | |
* @param searchBytes | |
* @param searchStartIndex | |
* @param searchEndIndex | |
* @return result index list | |
*/ | |
public List<Integer> searchBytes(byte[] srcBytes, byte[] searchBytes, int searchStartIndex, int searchEndIndex) { | |
final int destSize = searchBytes.length; | |
final List<Integer> positionIndexList = new ArrayList<Integer>(); | |
int cursor = searchStartIndex; | |
while (cursor < searchEndIndex + 1) { | |
int index = indexOf(srcBytes, searchBytes, cursor, searchEndIndex); | |
if (index >= 0) { | |
positionIndexList.add(index); | |
cursor = index + destSize; | |
} else { | |
cursor++; | |
} | |
} | |
return positionIndexList; | |
} | |
/** | |
* Returns the index within this byte-array of the first occurrence of the | |
* specified(search bytes) byte array.<br> | |
* Starting the search at the specified index, and end at the specified | |
* index. | |
* borrowed from https://github.com/riversun/finbin/blob/master/src/main/java/org/riversun/finbin/BinarySearcher.java | |
* | |
* @param srcBytes | |
* @param searchBytes | |
* @param startIndex | |
* @param endIndex | |
* @return | |
*/ | |
public int indexOf(byte[] srcBytes, byte[] searchBytes, int startIndex, int endIndex) { | |
if (searchBytes.length == 0 || (endIndex - startIndex + 1) < searchBytes.length) { | |
return -1; | |
} | |
int maxScanStartPosIdx = srcBytes.length - searchBytes.length; | |
final int loopEndIdx; | |
if (endIndex < maxScanStartPosIdx) { | |
loopEndIdx = endIndex; | |
} else { | |
loopEndIdx = maxScanStartPosIdx; | |
} | |
int lastScanIdx = -1; | |
label: // goto label | |
for (int i = startIndex; i <= loopEndIdx; i++) { | |
for (int j = 0; j < searchBytes.length; j++) { | |
if (srcBytes[i + j] != searchBytes[j]) { | |
continue label; | |
} | |
lastScanIdx = i + j; | |
} | |
if (endIndex < lastScanIdx || lastScanIdx - i + 1 < searchBytes.length) { | |
// it becomes more than the last index | |
// or less than the number of search bytes | |
return -1; | |
} | |
return i; | |
} | |
return -1; | |
} | |
public static class MultiPart { | |
public PartType type; | |
public String contentType; | |
public String name; | |
public String filename; | |
public String value; | |
public byte[] bytes; | |
} | |
public enum PartType{ | |
TEXT,FILE | |
} | |
} |
Wow thank you so much! This is Awesome. Amazing work!
I think you need after line
List offsets = searchBytes(payload, boundaryBytes, 0, payload.length - 1);
add line
offsets.add(0, Integer.valueOf(0));
without it you lose first multipart element
Amazing work, dude. Saved me a lot of time. Thanks man!
As @SENikitin said, one line (30) must be modified.
ArrayList<MultiPart> list = new ArrayList<>();
List<Integer> offsets = searchBytes(payload, boundaryBytes, 0, payload.length - 1);
for(int idx=0;idx<offsets.size();idx++){
int startPart = offsets.get(idx);
...
↓
ArrayList<MultiPart> list = new ArrayList<>();
List<Integer> offsets = searchBytes(payload, boundaryBytes, 0, payload.length - 1);
offsets.add(0, Integer.valueOf(0));
for(int idx=0;idx<offsets.size();idx++){
int startPart = offsets.get(idx);
...
Hi,
Like the others, your code (and the corrections in the previous comments) did save me tons of hours!
Thank you all!!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks Chief...You've saved me alot of time.