All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetdrone.vertx.yoke.middleware.BodyParser Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
/**
 * Copyright 2011-2014 the original author or authors.
 */
package com.jetdrone.vertx.yoke.middleware;

import com.jetdrone.vertx.yoke.Middleware;
import com.jetdrone.vertx.yoke.core.JSON;
import com.jetdrone.vertx.yoke.core.YokeFileUpload;
import org.jetbrains.annotations.NotNull;
import org.vertx.java.core.Handler;
import org.vertx.java.core.buffer.Buffer;
import org.vertx.java.core.http.HttpServerFileUpload;
import org.vertx.java.core.json.DecodeException;

import java.util.HashMap;

/** # BodyParser
 *
 * Parse request bodies, supports *application/json*, *application/x-www-form-urlencoded*, and *multipart/form-data*.
 *
 * Once data has been parsed the result is visible in the field `body` of the request.
 *
 * If the content type was *multipart/form-data* and there were uploaded files the files are ```files()``` returns
 * `Map<String, HttpServerFileUpload>`.
 *
 * ### Limitations
 *
 * Currently when parsing *multipart/form-data* if there are several files uploaded under the same name, only the last
 * is preserved.
 */
public class BodyParser extends Middleware {

    /**
     * Location on the file system to store the uploaded files.
     */
    private final String uploadDir;

    /** Instantiates a Body parser with a configurable upload directory.
     *
     * 
     *      Yoke yoke = new Yoke(...);
     *      yoke.use(new BodyParser("/upload"));
     * 
* * @param uploadDir upload directory path */ public BodyParser(@NotNull String uploadDir) { this.uploadDir = uploadDir; } /** Instantiates a Body parser using the system default temp directory. * *
     *      Yoke yoke = new Yoke(...);
     *      yoke.use(new BodyParser());
     * 
*/ public BodyParser() { this(System.getProperty("java.io.tmpdir")); } /** Handler for the parser. When the request method is GET or HEAD this is a Noop middleware. * If not the middleware verifies if there is a body and according to its headers tries to * parse it as JSON, form data or multi part upload. * * @param request http yoke request * @param next middleware to be called next */ @Override public void handle(@NotNull final YokeRequest request, @NotNull final Handler next) { final String method = request.method(); // GET and HEAD have no setBody if ("GET".equals(method) || "HEAD".equals(method) || !request.hasBody()) { next.handle(null); } else { final String contentType = request.getHeader("content-type"); final boolean isJSON = contentType != null && contentType.contains("application/json"); final boolean isMULTIPART = contentType != null && contentType.contains("multipart/form-data"); final boolean isURLENCODEC = contentType != null && contentType.contains("application/x-www-form-urlencoded"); final Buffer buffer = (!isMULTIPART && !isURLENCODEC) ? new Buffer(0) : null; // enable the parsing at Vert.x level request.expectMultiPart(true); if (isMULTIPART) { request.uploadHandler(new Handler() { @Override public void handle(final HttpServerFileUpload fileUpload) { if (request.files() == null) { request.setFiles(new HashMap()); } final YokeFileUpload upload = new YokeFileUpload(vertx(), fileUpload, uploadDir); // setup callbacks fileUpload.exceptionHandler(new Handler() { @Override public void handle(Throwable throwable) { next.handle(throwable); } }); // stream to the generated path fileUpload.streamToFileSystem(upload.path()); // store a reference in the request request.files().put(fileUpload.name(), upload); // set up a callback to remove the file from the file system when the request completes request.response().endHandler(new Handler() { @Override public void handle(Void event) { if (upload.isTransient()) { upload.delete(); } } }); } }); } request.dataHandler(new Handler() { long size = 0; final long limit = request.bodyLengthLimit(); @Override public void handle(Buffer event) { if (limit != -1) { size += event.length(); if (size < limit) { if (!isMULTIPART && !isURLENCODEC) { buffer.appendBuffer(event); } } else { request.dataHandler(null); request.endHandler(null); request.put("canceled", true); next.handle(413); } } else { if (!isMULTIPART && !isURLENCODEC) { buffer.appendBuffer(event); } } } }); request.endHandler(new Handler() { @Override public void handle(Void _void) { if (isJSON) { if (buffer != null && buffer.length() > 0) { try { String content = buffer.toString(); request.setBody(JSON.decode(content)); } catch (DecodeException e) { next.handle(400); return; } if (!request.get("canceled", false)) { next.handle(null); } } else if (buffer != null && buffer.length() == 0) { // special case for IE and Safari than even for 0 content length, send content type header if (request.contentLength() == 0) { request.setBody(null); if (!request.get("canceled", false)) { next.handle(null); } } else { next.handle(400); } } else { next.handle(400); } } else { if (buffer != null) { request.setBody(buffer); } if (!request.get("canceled", false)) { next.handle(null); } } } }); } } }