diff --git a/testlib/src/main/java/org/zstack/testlib/EnvSpec.groovy b/testlib/src/main/java/org/zstack/testlib/EnvSpec.groovy index 5491d321f1b..d5efa889e04 100755 --- a/testlib/src/main/java/org/zstack/testlib/EnvSpec.groovy +++ b/testlib/src/main/java/org/zstack/testlib/EnvSpec.groovy @@ -4,6 +4,7 @@ import groovy.transform.AutoClone import org.codehaus.groovy.runtime.InvokerHelper import org.springframework.http.* import org.springframework.http.client.HttpComponentsClientHttpRequestFactory +import org.springframework.web.multipart.MultipartHttpServletRequest import org.springframework.web.client.RestTemplate import org.zstack.compute.vm.VmGlobalConfig import org.zstack.configuration.SqlForeignKeyGenerator @@ -987,21 +988,78 @@ class EnvSpec extends ApiHelper implements Node { } HttpEntity getEntityFromRequest(HttpServletRequest req) { - StringBuilder sb = new StringBuilder() - String line - while ((line = req.getReader().readLine()) != null) { - sb.append(line) - } - req.getReader().close() - HttpHeaders header = new HttpHeaders() for (Enumeration e = req.getHeaderNames() ; e.hasMoreElements() ;) { String name = e.nextElement().toString() header.add(name, req.getHeader(name)) } + + StringBuilder sb = new StringBuilder() + if (req.getContentType()?.toLowerCase()?.startsWith("multipart/")) { + sb.append(readMultipartBody(req)) + } else { + def reader = req.getReader() + try { + String line + while ((line = reader.readLine()) != null) { + sb.append(line) + } + } finally { + reader.close() + } + } + return new HttpEntity(sb.toString(), header) } + protected String readMultipartBody(HttpServletRequest req) { + byte[] raw = req.inputStream.bytes + if (raw.length > 0) { + return new String(raw, "UTF-8") + } + + if (req instanceof MultipartHttpServletRequest) { + return readSpringMultipartBody(req as MultipartHttpServletRequest) + } + + StringBuilder sb = new StringBuilder() + try { + req.getParts().each { part -> + appendMultipartPart(sb, part.name, part.submittedFileName, part.contentType, part.inputStream.bytes) + } + } catch (Throwable t) { + logger.debug("failed to read multipart parts for ${req.requestURI}", t) + } + return sb.toString() + } + + protected String readSpringMultipartBody(MultipartHttpServletRequest req) { + StringBuilder sb = new StringBuilder() + req.parameterMap.each { String name, String[] values -> + values.each { value -> + appendMultipartPart(sb, name, null, "text/plain", value == null ? new byte[0] : value.getBytes("UTF-8")) + } + } + req.fileMap.each { String name, file -> + appendMultipartPart(sb, name, file.originalFilename, file.contentType, file.bytes) + } + return sb.toString() + } + + protected void appendMultipartPart(StringBuilder sb, String name, String filename, String contentType, byte[] content) { + sb.append("Content-Disposition: form-data; name=\"").append(name).append("\"") + if (filename != null) { + sb.append("; filename=\"").append(filename).append("\"") + } + sb.append("\n") + if (contentType != null) { + sb.append("Content-Type: ").append(contentType).append("\n") + } + sb.append("\n") + sb.append(new String(content == null ? new byte[0] : content, "UTF-8")) + sb.append("\n") + } + void handleConditionSimulatorHttpRequests(HttpServletRequest req, HttpEntity entity, HttpServletResponse rsp) { def url = req.getRequestURI() if (httpConditionHandlers[url] == null || httpConditionHandlers[url].isEmpty()) { @@ -1222,4 +1280,4 @@ class EnvSpec extends ApiHelper implements Node { void resetAllMessageSize() { messageHandlerCounters.clear() } -} \ No newline at end of file +} diff --git a/testlib/src/main/java/org/zstack/testlib/TestLibController.java b/testlib/src/main/java/org/zstack/testlib/TestLibController.java index 286eae91b04..545acb8b278 100755 --- a/testlib/src/main/java/org/zstack/testlib/TestLibController.java +++ b/testlib/src/main/java/org/zstack/testlib/TestLibController.java @@ -27,7 +27,7 @@ public class TestLibController { private static final ExecutorService pool = Executors.newFixedThreadPool(32); @RequestMapping( - value = "/**", + value = {"/**", "/v1/sites/**", "/v1/quota/**"}, method = { RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.GET, RequestMethod.HEAD, RequestMethod.OPTIONS, RequestMethod.PATCH, RequestMethod.TRACE @@ -39,6 +39,11 @@ public void handle(HttpServletRequest request, HttpServletResponse response) thr return; } + if (isMultipartRequest(request)) { + Test.handleHttp(request, response); + return; + } + final AsyncContext asyncContext = request.startAsync(); asyncContext.setTimeout(TestConfigUtils.getMessageTimeoutMillisConfig()); @@ -59,6 +64,11 @@ public void handle(HttpServletRequest request, HttpServletResponse response) thr }); } + private boolean isMultipartRequest(HttpServletRequest request) { + String contentType = request.getContentType(); + return contentType != null && contentType.toLowerCase().startsWith("multipart/"); + } + @PreDestroy public void shutdownPool() { logger.info("Shutting down TestLibController pool");