You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by mb...@apache.org on 2017/08/05 02:14:14 UTC

asterixdb git commit: [ASTERIXDB-2013][HYR] non-POST x-www-form-urlencoded, config null value handling

Repository: asterixdb
Updated Branches:
  refs/heads/master e0d8e5078 -> a14cebbf0


[ASTERIXDB-2013][HYR] non-POST x-www-form-urlencoded, config null value handling

- handle null config values when marshall/unmarshalling
- support non-post x-www-form-urlencoded directly
- don't assume POSTs are x-www-form-urlencoded
- add support to TestExecutor for POST/PUT form-urlencoded parameters

Change-Id: I670b815a5276d870f7d538d1ce9d8bef2d0fcf4f
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1911
Reviewed-by: Till Westmann <ti...@apache.org>
Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <je...@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>


Project: http://git-wip-us.apache.org/repos/asf/asterixdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/asterixdb/commit/a14cebbf
Tree: http://git-wip-us.apache.org/repos/asf/asterixdb/tree/a14cebbf
Diff: http://git-wip-us.apache.org/repos/asf/asterixdb/diff/a14cebbf

Branch: refs/heads/master
Commit: a14cebbf0c62cec4593eaa3e81b3b4919286be05
Parents: e0d8e50
Author: Michael Blow <mb...@apache.org>
Authored: Fri Aug 4 19:59:30 2017 -0400
Committer: Michael Blow <mb...@apache.org>
Committed: Fri Aug 4 19:13:50 2017 -0700

----------------------------------------------------------------------
 .../api/http/server/QueryResultApiServlet.java  |   2 +-
 .../api/http/server/QueryServiceServlet.java    |   6 +-
 .../api/http/server/QueryStatusApiServlet.java  |   2 +-
 .../hyracks/bootstrap/CCApplication.java        |   4 +-
 .../asterix/test/common/TestExecutor.java       | 185 +++++++++++--------
 .../org/apache/hyracks/api/config/IOption.java  |   7 +
 .../apache/hyracks/api/config/IOptionType.java  |   7 +
 .../hyracks-control-common/pom.xml              |   4 +
 .../control/common/config/ConfigManager.java    |   2 +-
 .../control/common/config/OptionTypes.java      |  69 ++++++-
 .../hyracks/http/api/IServletRequest.java       |  17 ++
 .../apache/hyracks/http/server/BaseRequest.java |  18 ++
 .../http/server/FormUrlEncodedRequest.java      |  99 ++++++++++
 .../apache/hyracks/http/server/PostRequest.java |  89 ---------
 .../hyracks/http/server/utils/HttpUtil.java     |  19 +-
 15 files changed, 343 insertions(+), 187 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java
index 42e23ba..901aff8 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryResultApiServlet.java
@@ -41,7 +41,7 @@ import io.netty.handler.codec.http.HttpResponseStatus;
 public class QueryResultApiServlet extends AbstractQueryApiServlet {
     private static final Logger LOGGER = Logger.getLogger(QueryResultApiServlet.class.getName());
 
-    public QueryResultApiServlet(ConcurrentMap<String, Object> ctx, String[] paths, IApplicationContext appCtx) {
+    public QueryResultApiServlet(ConcurrentMap<String, Object> ctx, IApplicationContext appCtx, String... paths) {
         super(appCtx, ctx, paths);
     }
 

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
index 9ee064e..1cec616 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
@@ -62,8 +62,6 @@ import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
-
-import io.netty.handler.codec.http.HttpHeaderNames;
 import io.netty.handler.codec.http.HttpResponseStatus;
 
 public class QueryServiceServlet extends AbstractQueryApiServlet {
@@ -317,9 +315,7 @@ public class QueryServiceServlet extends AbstractQueryApiServlet {
     }
 
     private RequestParameters getRequestParameters(IServletRequest request) throws IOException {
-        final String contentTypeParam = request.getHttpRequest().headers().get(HttpHeaderNames.CONTENT_TYPE);
-        int sep = contentTypeParam.indexOf(';');
-        final String contentType = sep < 0 ? contentTypeParam.trim() : contentTypeParam.substring(0, sep).trim();
+        final String contentType = HttpUtil.getContentTypeOnly(request);
         RequestParameters param = new RequestParameters();
         param.host = host(request);
         param.path = servletPath(request);

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
index 71dddc0..cec65f7 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryStatusApiServlet.java
@@ -41,7 +41,7 @@ import io.netty.handler.codec.http.HttpResponseStatus;
 public class QueryStatusApiServlet extends AbstractQueryApiServlet {
     private static final Logger LOGGER = Logger.getLogger(QueryStatusApiServlet.class.getName());
 
-    public QueryStatusApiServlet(ConcurrentMap<String, Object> ctx, String[] paths, IApplicationContext appCtx) {
+    public QueryStatusApiServlet(ConcurrentMap<String, Object> ctx, IApplicationContext appCtx, String... paths) {
         super(appCtx, ctx, paths);
     }
 

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java
index 3627974..e8636c8 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/hyracks/bootstrap/CCApplication.java
@@ -287,9 +287,9 @@ public class CCApplication extends BaseCCApplication {
             case Servlets.RUNNING_REQUESTS:
                 return new QueryCancellationServlet(ctx, paths);
             case Servlets.QUERY_STATUS:
-                return new QueryStatusApiServlet(ctx, paths, appCtx);
+                return new QueryStatusApiServlet(ctx, appCtx, paths);
             case Servlets.QUERY_RESULT:
-                return new QueryResultApiServlet(ctx, paths, appCtx);
+                return new QueryResultApiServlet(ctx, appCtx, paths);
             case Servlets.QUERY_SERVICE:
                 return new QueryServiceServlet(ctx, paths, appCtx, SQLPP,
                         ccExtensionManager.getCompilationProvider(SQLPP), getStatementExecutorFactory(),

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
index 10b528f..1079832 100644
--- a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
+++ b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
@@ -65,6 +65,7 @@ import org.apache.asterix.testframework.context.TestCaseContext.OutputFormat;
 import org.apache.asterix.testframework.context.TestFileContext;
 import org.apache.asterix.testframework.xml.ComparisonEnum;
 import org.apache.asterix.testframework.xml.TestCase.CompilationUnit;
+import org.apache.asterix.testframework.xml.TestCase.CompilationUnit.Parameter;
 import org.apache.asterix.testframework.xml.TestGroup;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
@@ -102,16 +103,17 @@ public class TestExecutor {
     // see
     // https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers/417184
     private static final long MAX_URL_LENGTH = 2000l;
-    private static final Pattern JAVA_BLOCK_COMMENT_PATTERN =
-            Pattern.compile("/\\*.*\\*/", Pattern.MULTILINE | Pattern.DOTALL);
+    private static final Pattern JAVA_BLOCK_COMMENT_PATTERN = Pattern.compile("/\\*.*\\*/",
+            Pattern.MULTILINE | Pattern.DOTALL);
     private static final Pattern JAVA_LINE_COMMENT_PATTERN = Pattern.compile("//.*$", Pattern.MULTILINE);
     private static final Pattern SHELL_LINE_COMMENT_PATTERN = Pattern.compile("#.*$", Pattern.MULTILINE);
     private static final Pattern REGEX_LINES_PATTERN = Pattern.compile("^(-)?/(.*)/([im]*)$");
-    private static final Pattern POLL_TIMEOUT_PATTERN =
-            Pattern.compile("polltimeoutsecs=(\\d+)(\\D|$)", Pattern.MULTILINE);
+    private static final Pattern POLL_TIMEOUT_PATTERN = Pattern.compile("polltimeoutsecs=(\\d+)(\\D|$)",
+            Pattern.MULTILINE);
     private static final Pattern POLL_DELAY_PATTERN = Pattern.compile("polldelaysecs=(\\d+)(\\D|$)", Pattern.MULTILINE);
     private static final Pattern HANDLE_VARIABLE_PATTERN = Pattern.compile("handlevariable=(\\w+)");
     private static final Pattern VARIABLE_REF_PATTERN = Pattern.compile("\\$(\\w+)");
+    private static final Pattern HTTP_PARAM_PATTERN = Pattern.compile("param (\\w+)=(.*)", Pattern.MULTILINE);
 
     public static final int TRUNCATE_THRESHOLD = 16384;
 
@@ -166,10 +168,10 @@ public class TestExecutor {
     public void runScriptAndCompareWithResult(File scriptFile, PrintWriter print, File expectedFile, File actualFile,
             ComparisonEnum compare) throws Exception {
         System.err.println("Expected results file: " + expectedFile.toString());
-        BufferedReader readerExpected =
-                new BufferedReader(new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
-        BufferedReader readerActual =
-                new BufferedReader(new InputStreamReader(new FileInputStream(actualFile), "UTF-8"));
+        BufferedReader readerExpected = new BufferedReader(
+                new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
+        BufferedReader readerActual = new BufferedReader(
+                new InputStreamReader(new FileInputStream(actualFile), "UTF-8"));
         boolean regex = false;
         try {
             if (ComparisonEnum.BINARY.equals(compare)) {
@@ -352,10 +354,10 @@ public class TestExecutor {
     public void runScriptAndCompareWithResultRegex(File scriptFile, File expectedFile, File actualFile)
             throws Exception {
         String lineExpected, lineActual;
-        try (BufferedReader readerExpected =
-                new BufferedReader(new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
-                BufferedReader readerActual =
-                        new BufferedReader(new InputStreamReader(new FileInputStream(actualFile), "UTF-8"))) {
+        try (BufferedReader readerExpected = new BufferedReader(
+                new InputStreamReader(new FileInputStream(expectedFile), "UTF-8"));
+                BufferedReader readerActual = new BufferedReader(
+                        new InputStreamReader(new FileInputStream(actualFile), "UTF-8"))) {
             StringBuilder actual = new StringBuilder();
             while ((lineActual = readerActual.readLine()) != null) {
                 actual.append(lineActual).append('\n');
@@ -510,8 +512,7 @@ public class TestExecutor {
         }
     }
 
-    public InputStream executeQuery(String str, OutputFormat fmt, URI uri, List<CompilationUnit.Parameter> params)
-            throws Exception {
+    public InputStream executeQuery(String str, OutputFormat fmt, URI uri, List<Parameter> params) throws Exception {
         HttpUriRequest method = constructHttpMethod(str, uri, "query", false, params);
         // Set accepted output response type
         method.setHeader("Accept", fmt.mimeType());
@@ -523,21 +524,19 @@ public class TestExecutor {
         return executeQueryService(str, fmt, uri, new ArrayList<>(), false);
     }
 
-    public InputStream executeQueryService(String str, OutputFormat fmt, URI uri,
-            List<CompilationUnit.Parameter> params, boolean jsonEncoded) throws Exception {
+    public InputStream executeQueryService(String str, OutputFormat fmt, URI uri, List<Parameter> params,
+            boolean jsonEncoded) throws Exception {
         return executeQueryService(str, fmt, uri, params, jsonEncoded, null, false);
     }
 
-    public InputStream executeQueryService(String str, OutputFormat fmt, URI uri,
-            List<CompilationUnit.Parameter> params, boolean jsonEncoded, Predicate<Integer> responseCodeValidator)
-            throws Exception {
+    public InputStream executeQueryService(String str, OutputFormat fmt, URI uri, List<Parameter> params,
+            boolean jsonEncoded, Predicate<Integer> responseCodeValidator) throws Exception {
         return executeQueryService(str, fmt, uri, params, jsonEncoded, responseCodeValidator, false);
     }
 
-    protected InputStream executeQueryService(String str, OutputFormat fmt, URI uri,
-            List<CompilationUnit.Parameter> params, boolean jsonEncoded, Predicate<Integer> responseCodeValidator,
-            boolean cancellable) throws Exception {
-        final List<CompilationUnit.Parameter> newParams = upsertParam(params, "format", fmt.mimeType());
+    protected InputStream executeQueryService(String str, OutputFormat fmt, URI uri, List<Parameter> params,
+            boolean jsonEncoded, Predicate<Integer> responseCodeValidator, boolean cancellable) throws Exception {
+        final List<Parameter> newParams = upsertParam(params, "format", fmt.mimeType());
         HttpUriRequest method = jsonEncoded ? constructPostMethodJson(str, uri, "statement", newParams)
                 : constructPostMethodUrl(str, uri, "statement", newParams);
         // Set accepted output response type
@@ -549,12 +548,11 @@ public class TestExecutor {
         return response.getEntity().getContent();
     }
 
-    protected List<CompilationUnit.Parameter> upsertParam(List<CompilationUnit.Parameter> params, String name,
-            String value) {
+    protected List<Parameter> upsertParam(List<Parameter> params, String name, String value) {
         boolean replaced = false;
-        List<CompilationUnit.Parameter> result = new ArrayList<>();
-        for (CompilationUnit.Parameter param : params) {
-            CompilationUnit.Parameter newParam = new CompilationUnit.Parameter();
+        List<Parameter> result = new ArrayList<>();
+        for (Parameter param : params) {
+            Parameter newParam = new Parameter();
             newParam.setName(param.getName());
             if (name.equals(param.getName())) {
                 newParam.setValue(value);
@@ -565,7 +563,7 @@ public class TestExecutor {
             result.add(newParam);
         }
         if (!replaced) {
-            CompilationUnit.Parameter newParam = new CompilationUnit.Parameter();
+            Parameter newParam = new Parameter();
             newParam.setName(name);
             newParam.setValue(value);
             result.add(newParam);
@@ -574,7 +572,7 @@ public class TestExecutor {
     }
 
     private HttpUriRequest constructHttpMethod(String statement, URI uri, String stmtParam, boolean postStmtAsParam,
-            List<CompilationUnit.Parameter> otherParams) throws URISyntaxException {
+            List<Parameter> otherParams) throws URISyntaxException {
         if (statement.length() + uri.toString().length() < MAX_URL_LENGTH) {
             // Use GET for small-ish queries
             return constructGetMethod(uri, upsertParam(otherParams, stmtParam, statement));
@@ -585,32 +583,49 @@ public class TestExecutor {
         }
     }
 
-    private HttpUriRequest constructGetMethod(URI endpoint, List<CompilationUnit.Parameter> params) {
+    private HttpUriRequest constructGetMethod(URI endpoint, List<Parameter> params) {
         RequestBuilder builder = RequestBuilder.get(endpoint);
-        for (CompilationUnit.Parameter param : params) {
+        for (Parameter param : params) {
             builder.addParameter(param.getName(), param.getValue());
         }
         builder.setCharset(StandardCharsets.UTF_8);
         return builder.build();
     }
 
-    private HttpUriRequest constructGetMethod(URI endpoint, OutputFormat fmt, List<CompilationUnit.Parameter> params) {
+    private HttpUriRequest buildRequest(String method, URI uri, List<Parameter> params) {
+        RequestBuilder builder = RequestBuilder.create(method);
+        builder.setUri(uri);
+        for (Parameter param : params) {
+            builder.addParameter(param.getName(), param.getValue());
+        }
+        builder.setCharset(StandardCharsets.UTF_8);
+        return builder.build();
+    }
+
+    private HttpUriRequest buildRequest(String method, URI uri, OutputFormat fmt, List<Parameter> params) {
+        HttpUriRequest request = buildRequest(method, uri, params);
+        // Set accepted output response type
+        request.setHeader("Accept", fmt.mimeType());
+        return request;
+    }
+
+    private HttpUriRequest constructGetMethod(URI endpoint, OutputFormat fmt, List<Parameter> params) {
         HttpUriRequest method = constructGetMethod(endpoint, params);
         // Set accepted output response type
         method.setHeader("Accept", fmt.mimeType());
         return method;
     }
 
-    private HttpUriRequest constructPostMethod(URI uri, List<CompilationUnit.Parameter> params) {
+    private HttpUriRequest constructPostMethod(URI uri, List<Parameter> params) {
         RequestBuilder builder = RequestBuilder.post(uri);
-        for (CompilationUnit.Parameter param : params) {
+        for (Parameter param : params) {
             builder.addParameter(param.getName(), param.getValue());
         }
         builder.setCharset(StandardCharsets.UTF_8);
         return builder.build();
     }
 
-    private HttpUriRequest constructPostMethod(URI uri, OutputFormat fmt, List<CompilationUnit.Parameter> params) {
+    private HttpUriRequest constructPostMethod(URI uri, OutputFormat fmt, List<Parameter> params) {
         HttpUriRequest method = constructPostMethod(uri, params);
         // Set accepted output response type
         method.setHeader("Accept", fmt.mimeType());
@@ -618,10 +633,10 @@ public class TestExecutor {
     }
 
     protected HttpUriRequest constructPostMethodUrl(String statement, URI uri, String stmtParam,
-            List<CompilationUnit.Parameter> otherParams) {
+            List<Parameter> otherParams) {
         RequestBuilder builder = RequestBuilder.post(uri);
         if (stmtParam != null) {
-            for (CompilationUnit.Parameter param : upsertParam(otherParams, stmtParam, statement)) {
+            for (Parameter param : upsertParam(otherParams, stmtParam, statement)) {
                 builder.addParameter(param.getName(), param.getValue());
             }
             builder.addParameter(stmtParam, statement);
@@ -634,14 +649,14 @@ public class TestExecutor {
     }
 
     protected HttpUriRequest constructPostMethodJson(String statement, URI uri, String stmtParam,
-            List<CompilationUnit.Parameter> otherParams) {
+            List<Parameter> otherParams) {
         if (stmtParam == null) {
             throw new NullPointerException("Statement parameter required.");
         }
         RequestBuilder builder = RequestBuilder.post(uri);
         ObjectMapper om = new ObjectMapper();
         ObjectNode content = om.createObjectNode();
-        for (CompilationUnit.Parameter param : upsertParam(otherParams, stmtParam, statement)) {
+        for (Parameter param : upsertParam(otherParams, stmtParam, statement)) {
             content.put(param.getName(), param.getValue());
         }
         try {
@@ -654,23 +669,26 @@ public class TestExecutor {
     }
 
     public InputStream executeJSONGet(OutputFormat fmt, URI uri) throws Exception {
-        return executeJSONGet(fmt, uri, code -> code == HttpStatus.SC_OK);
+        return executeJSON(fmt, "GET", uri, Collections.emptyList());
     }
 
-    public InputStream executeJSONGet(OutputFormat fmt, URI uri, Predicate<Integer> responseCodeValidator)
-            throws Exception {
-        HttpUriRequest request = constructGetMethod(uri, fmt, new ArrayList<>());
-        HttpResponse response = executeAndCheckHttpRequest(request, responseCodeValidator);
-        return response.getEntity().getContent();
+    public InputStream executeJSONGet(OutputFormat fmt, URI uri, List<Parameter> params,
+            Predicate<Integer> responseCodeValidator) throws Exception {
+        return executeJSON(fmt, "GET", uri, params, responseCodeValidator);
     }
 
-    public InputStream executeJSONPost(OutputFormat fmt, URI uri) throws Exception {
-        return executeJSONPost(fmt, uri, code -> code == HttpStatus.SC_OK);
+    public InputStream executeJSON(OutputFormat fmt, String method, URI uri, List<Parameter> params) throws Exception {
+        return executeJSON(fmt, method, uri, params, code -> code == HttpStatus.SC_OK);
     }
 
-    public InputStream executeJSONPost(OutputFormat fmt, URI uri, Predicate<Integer> responseCodeValidator)
+    public InputStream executeJSON(OutputFormat fmt, String method, URI uri, Predicate<Integer> responseCodeValidator)
             throws Exception {
-        HttpUriRequest request = constructPostMethod(uri, fmt, new ArrayList<>());
+        return executeJSON(fmt, method, uri, Collections.emptyList(), responseCodeValidator);
+    }
+
+    public InputStream executeJSON(OutputFormat fmt, String method, URI uri, List<Parameter> params,
+            Predicate<Integer> responseCodeValidator) throws Exception {
+        HttpUriRequest request = buildRequest(method, uri, fmt, params);
         HttpResponse response = executeAndCheckHttpRequest(request, responseCodeValidator);
         return response.getEntity().getContent();
     }
@@ -679,8 +697,8 @@ public class TestExecutor {
     // Insert and Delete statements are executed here
     public void executeUpdate(String str, URI uri) throws Exception {
         // Create a method instance.
-        HttpUriRequest request =
-                RequestBuilder.post(uri).setEntity(new StringEntity(str, StandardCharsets.UTF_8)).build();
+        HttpUriRequest request = RequestBuilder.post(uri).setEntity(new StringEntity(str, StandardCharsets.UTF_8))
+                .build();
 
         // Execute the method.
         executeAndCheckHttpRequest(request);
@@ -690,10 +708,10 @@ public class TestExecutor {
     public InputStream executeAnyAQLAsync(String statement, boolean defer, OutputFormat fmt, URI uri,
             Map<String, Object> variableCtx) throws Exception {
         // Create a method instance.
-        HttpUriRequest request =
-                RequestBuilder.post(uri).addParameter("mode", defer ? "asynchronous-deferred" : "asynchronous")
-                        .setEntity(new StringEntity(statement, StandardCharsets.UTF_8))
-                        .setHeader("Accept", fmt.mimeType()).build();
+        HttpUriRequest request = RequestBuilder.post(uri)
+                .addParameter("mode", defer ? "asynchronous-deferred" : "asynchronous")
+                .setEntity(new StringEntity(statement, StandardCharsets.UTF_8)).setHeader("Accept", fmt.mimeType())
+                .build();
 
         String handleVar = getHandleVariable(statement);
 
@@ -719,8 +737,8 @@ public class TestExecutor {
     // create function statement
     public void executeDDL(String str, URI uri) throws Exception {
         // Create a method instance.
-        HttpUriRequest request =
-                RequestBuilder.post(uri).setEntity(new StringEntity(str, StandardCharsets.UTF_8)).build();
+        HttpUriRequest request = RequestBuilder.post(uri).setEntity(new StringEntity(str, StandardCharsets.UTF_8))
+                .build();
 
         // Execute the method.
         executeAndCheckHttpRequest(request);
@@ -730,8 +748,8 @@ public class TestExecutor {
     // and returns the contents as a string
     // This string is later passed to REST API for execution.
     public String readTestFile(File testFile) throws Exception {
-        BufferedReader reader =
-                new BufferedReader(new InputStreamReader(new FileInputStream(testFile), StandardCharsets.UTF_8));
+        BufferedReader reader = new BufferedReader(
+                new InputStreamReader(new FileInputStream(testFile), StandardCharsets.UTF_8));
         String line;
         StringBuilder stringBuilder = new StringBuilder();
         String ls = System.getProperty("line.separator");
@@ -786,8 +804,8 @@ public class TestExecutor {
 
     private static String getProcessOutput(Process p) throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        Future<Integer> future =
-                Executors.newSingleThreadExecutor().submit(() -> IOUtils.copy(p.getInputStream(), new OutputStream() {
+        Future<Integer> future = Executors.newSingleThreadExecutor()
+                .submit(() -> IOUtils.copy(p.getInputStream(), new OutputStream() {
                     @Override
                     public void write(int b) throws IOException {
                         baos.write(b);
@@ -966,6 +984,7 @@ public class TestExecutor {
                 break;
             case "get":
             case "post":
+            case "put":
                 expectedResultFile = (queryCount.intValue() >= expectedResultFileCtxs.size()) ? null
                         : expectedResultFileCtxs.get(queryCount.intValue()).getFile();
                 actualResultFile = expectedResultFile == null ? null
@@ -1071,11 +1090,12 @@ public class TestExecutor {
         String handleVar = getHandleVariable(statement);
         final String trimmedPathAndQuery = stripLineComments(stripJavaComments(statement)).trim();
         final String variablesReplaced = replaceVarRef(trimmedPathAndQuery, variableCtx);
+        final List<Parameter> params = extractParameters(statement);
         InputStream resultStream;
         if ("http".equals(extension)) {
-            resultStream = executeHttp(reqType, variablesReplaced, fmt);
+            resultStream = executeHttp(reqType, variablesReplaced, fmt, params);
         } else if ("uri".equals(extension)) {
-            resultStream = executeURI(reqType, URI.create(variablesReplaced), fmt);
+            resultStream = executeURI(reqType, URI.create(variablesReplaced), fmt, params);
         } else {
             throw new IllegalArgumentException("Unexpected format for method " + reqType + ": " + extension);
         }
@@ -1100,7 +1120,7 @@ public class TestExecutor {
 
     public void executeQuery(OutputFormat fmt, String statement, Map<String, Object> variableCtx, String reqType,
             File testFile, File expectedResultFile, File actualResultFile, MutableInt queryCount, int numResultFiles,
-            List<CompilationUnit.Parameter> params, ComparisonEnum compare) throws Exception {
+            List<Parameter> params, ComparisonEnum compare) throws Exception {
         InputStream resultStream = null;
         if (testFile.getName().endsWith("aql")) {
             if (reqType.equalsIgnoreCase("query")) {
@@ -1158,7 +1178,7 @@ public class TestExecutor {
         long limitTime = startTime + TimeUnit.SECONDS.toMillis(timeoutSecs);
         ctx.setType(ctx.getType().substring("poll".length()));
         boolean expectedException = false;
-        Exception finalException;
+        Exception finalException = null;
         LOGGER.fine("polling for up to " + timeoutSecs + " seconds w/ " + retryDelaySecs + " second(s) delay");
         int responsesReceived = 0;
         final ExecutorService executorService = Executors.newSingleThreadExecutor();
@@ -1177,11 +1197,16 @@ public class TestExecutor {
                 if (responsesReceived == 0) {
                     throw new Exception(
                             "Poll limit (" + timeoutSecs + "s) exceeded without obtaining *any* result from server");
+                } else if (finalException != null) {
+                    throw new Exception("Poll limit (" + timeoutSecs
+                            + "s) exceeded without obtaining expected result; last exception:", finalException);
                 } else {
                     throw new Exception("Poll limit (" + timeoutSecs + "s) exceeded without obtaining expected result");
 
                 }
             } catch (Exception e) {
+                LOGGER.log(Level.FINE, "received exception on poll", e);
+                responsesReceived++;
                 if (isExpected(e, cUnit)) {
                     expectedException = true;
                     finalException = e;
@@ -1250,21 +1275,27 @@ public class TestExecutor {
         return tmpStmt;
     }
 
-    protected InputStream executeHttp(String ctxType, String endpoint, OutputFormat fmt) throws Exception {
+    protected static List<Parameter> extractParameters(String statement) {
+        List<Parameter> params = new ArrayList<>();
+        final Matcher m = HTTP_PARAM_PATTERN.matcher(statement);
+        while (m.find()) {
+            final Parameter param = new Parameter();
+            param.setName(m.group(1));
+            param.setValue(m.group(2));
+            params.add(param);
+        }
+        return params;
+    }
+
+    protected InputStream executeHttp(String ctxType, String endpoint, OutputFormat fmt, List<Parameter> params)
+            throws Exception {
         String[] split = endpoint.split("\\?");
         URI uri = createEndpointURI(split[0], split.length > 1 ? split[1] : null);
-        return executeURI(ctxType, uri, fmt);
+        return executeURI(ctxType, uri, fmt, params);
     }
 
-    private InputStream executeURI(String ctxType, URI uri, OutputFormat fmt) throws Exception {
-        switch (ctxType) {
-            case "get":
-                return executeJSONGet(fmt, uri);
-            case "post":
-                return executeJSONPost(fmt, uri);
-            default:
-                throw new AssertionError("Not implemented: " + ctxType);
-        }
+    private InputStream executeURI(String ctxType, URI uri, OutputFormat fmt, List<Parameter> params) throws Exception {
+        return executeJSON(fmt, ctxType.toUpperCase(), uri, params);
     }
 
     private void killNC(String nodeId, CompilationUnit cUnit) throws Exception {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOption.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOption.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOption.java
index 834d73c..b8e7635 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOption.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOption.java
@@ -20,6 +20,9 @@ package org.apache.hyracks.api.config;
 
 import java.util.function.Function;
 
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.text.WordUtils;
+
 public interface IOption {
 
     String name();
@@ -64,6 +67,10 @@ public interface IOption {
         return name().toLowerCase().replace("_", ".");
     }
 
+    default String json() {
+        return StringUtils.remove(WordUtils.capitalize("z" + name().toLowerCase(), '_').substring(1), '_');
+    }
+
     default String toIniString() {
         return "[" + section().sectionName() + "] " + ini();
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOptionType.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOptionType.java b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOptionType.java
index 1bd6097..d2a254f 100644
--- a/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOptionType.java
+++ b/hyracks-fullstack/hyracks/hyracks-api/src/main/java/org/apache/hyracks/api/config/IOptionType.java
@@ -18,6 +18,8 @@
  */
 package org.apache.hyracks.api.config;
 
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
 public interface IOptionType<T> {
     /**
      * @throws IllegalArgumentException when the supplied string cannot be interpreted
@@ -34,6 +36,11 @@ public interface IOptionType<T> {
     }
 
     /**
+     * @return the value in a format suitable for serialized JSON
+     */
+    void serializeJSONField(String fieldName, Object value, ObjectNode node);
+
+    /**
      * @return the value in a format suitable for serialized ini file
      */
     default String serializeToIni(Object value) {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml
index 5120047..80ef088 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/pom.xml
@@ -75,5 +75,9 @@
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-collections4</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+    </dependency>
   </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java
index fcaee6d..a595301 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/ConfigManager.java
@@ -425,7 +425,7 @@ public class ConfigManager implements IConfigManager, Serializable {
     }
 
     public List<String> getNodeNames() {
-        return Collections.unmodifiableList(new ArrayList(nodeSpecificMap.keySet()));
+        return Collections.unmodifiableList(new ArrayList<>(nodeSpecificMap.keySet()));
     }
 
     public IApplicationConfig getNodeEffectiveConfig(String nodeId) {

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/OptionTypes.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/OptionTypes.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/OptionTypes.java
index 02b9325..1e92a7a 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/OptionTypes.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/config/OptionTypes.java
@@ -21,14 +21,20 @@ package org.apache.hyracks.control.common.config;
 import java.net.MalformedURLException;
 import java.util.logging.Level;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.hyracks.api.config.IOptionType;
 import org.apache.hyracks.util.StorageUtil;
 
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
 public class OptionTypes {
 
     public static final IOptionType<Integer> INTEGER_BYTE_UNIT = new IOptionType<Integer>() {
         @Override
         public Integer parse(String s) {
+            if (s == null) {
+                return null;
+            }
             long result1 = StorageUtil.getByteValue(s);
             if (result1 > Integer.MAX_VALUE || result1 < Integer.MIN_VALUE) {
                 throw new IllegalArgumentException(
@@ -46,12 +52,17 @@ public class OptionTypes {
         public String serializeToHumanReadable(Object value) {
             return value + " (" + StorageUtil.toHumanReadableSize((int)value) + ")";
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, (int)value);
+        }
     };
 
     public static final IOptionType<Long> LONG_BYTE_UNIT = new IOptionType<Long>() {
         @Override
         public Long parse(String s) {
-            return StorageUtil.getByteValue(s);
+            return s == null ? null : StorageUtil.getByteValue(s);
         }
 
         @Override
@@ -63,6 +74,11 @@ public class OptionTypes {
         public String serializeToHumanReadable(Object value) {
             return value + " (" + StorageUtil.toHumanReadableSize((long)value) + ")";
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, (long)value);
+        }
     };
 
     public static final IOptionType<Integer> INTEGER = new IOptionType<Integer>() {
@@ -75,6 +91,11 @@ public class OptionTypes {
         public Class<Integer> targetType() {
             return Integer.class;
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, (int)value);
+        }
     };
 
     public static final IOptionType<Double> DOUBLE = new IOptionType<Double>() {
@@ -87,6 +108,11 @@ public class OptionTypes {
         public Class<Double> targetType() {
             return Double.class;
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, (double)value);
+        }
     };
 
     public static final IOptionType<String> STRING = new IOptionType<String>() {
@@ -99,6 +125,11 @@ public class OptionTypes {
         public Class<String> targetType() {
             return String.class;
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, (String)value);
+        }
     };
 
     public static final IOptionType<Long> LONG = new IOptionType<Long>() {
@@ -111,6 +142,11 @@ public class OptionTypes {
         public Class<Long> targetType() {
             return Long.class;
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, (long)value);
+        }
     };
 
     public static final IOptionType<Boolean> BOOLEAN = new IOptionType<Boolean>() {
@@ -123,12 +159,17 @@ public class OptionTypes {
         public Class<Boolean> targetType() {
             return Boolean.class;
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, (boolean)value);
+        }
     };
 
     public static final IOptionType<Level> LEVEL = new IOptionType<Level>() {
         @Override
         public Level parse(String s) {
-            return Level.parse(s);
+            return s == null ? null : Level.parse(s);
         }
 
         @Override
@@ -137,20 +178,25 @@ public class OptionTypes {
         }
 
         @Override
-        public Object serializeToJSON(Object value) {
-            return ((Level)value).getName();
+        public String serializeToJSON(Object value) {
+            return value == null ? null : ((Level)value).getName();
         }
 
         @Override
         public String serializeToIni(Object value) {
             return ((Level)value).getName();
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, serializeToJSON(value));
+        }
     };
 
     public static final IOptionType<String []> STRING_ARRAY = new IOptionType<String []>() {
         @Override
         public String [] parse(String s) {
-            return s.split("\\s*,\\s*");
+            return s == null ? null : s.split("\\s*,\\s*");
         }
 
         @Override
@@ -162,13 +208,18 @@ public class OptionTypes {
         public String serializeToIni(Object value) {
             return String.join(",", (String [])value);
         }
+
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, value == null ? null : StringUtils.join((String [])value, ','));
+        }
     };
 
     public static final IOptionType<java.net.URL> URL = new IOptionType<java.net.URL>() {
         @Override
         public java.net.URL parse(String s) {
             try {
-                return new java.net.URL(s);
+                return s == null ? null : new java.net.URL(s);
             } catch (MalformedURLException e) {
                 throw new IllegalArgumentException(e);
             }
@@ -178,8 +229,12 @@ public class OptionTypes {
         public Class<java.net.URL> targetType() {
             return java.net.URL.class;
         }
-    };
 
+        @Override
+        public void serializeJSONField(String fieldName, Object value, ObjectNode node) {
+            node.put(fieldName, value == null ? null : String.valueOf(value));
+        }
+    };
 
     private OptionTypes() {
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/api/IServletRequest.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/api/IServletRequest.java b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/api/IServletRequest.java
index 610c3d1..be201df 100644
--- a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/api/IServletRequest.java
+++ b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/api/IServletRequest.java
@@ -18,6 +18,9 @@
  */
 package org.apache.hyracks.http.api;
 
+import java.util.Map;
+import java.util.Set;
+
 import io.netty.handler.codec.http.FullHttpRequest;
 
 /**
@@ -38,6 +41,20 @@ public interface IServletRequest {
     String getParameter(CharSequence name);
 
     /**
+     * Get the names of all request parameters
+     *
+     * @return the list of parameter names
+     */
+    Set<String> getParameterNames();
+
+    /**
+     * Get the all request parameters
+     *
+     * @return the parameters
+     */
+    Map<String, String> getParameters();
+
+    /**
      * Get a request header
      *
      * @param name

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/BaseRequest.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/BaseRequest.java b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/BaseRequest.java
index 5b354af..0c633cf 100644
--- a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/BaseRequest.java
+++ b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/BaseRequest.java
@@ -19,8 +19,11 @@
 package org.apache.hyracks.http.server;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.hyracks.http.api.IServletRequest;
 import org.apache.hyracks.http.server.utils.HttpUtil;
@@ -54,6 +57,21 @@ public class BaseRequest implements IServletRequest {
     }
 
     @Override
+    public Set<String> getParameterNames() {
+        return Collections.unmodifiableSet(parameters.keySet());
+    }
+
+    @Override
+    public Map<String, String> getParameters() {
+        HashMap<String, String> paramMap = new HashMap<>();
+        for (String name : parameters.keySet()) {
+            paramMap.put(name, HttpUtil.getParameter(parameters, name));
+
+        }
+        return Collections.unmodifiableMap(paramMap);
+    }
+
+    @Override
     public String getHeader(CharSequence name) {
         return request.headers().get(name);
     }

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/FormUrlEncodedRequest.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/FormUrlEncodedRequest.java b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/FormUrlEncodedRequest.java
new file mode 100644
index 0000000..743a2c4
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/FormUrlEncodedRequest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.hyracks.http.server;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.hyracks.http.api.IServletRequest;
+import org.apache.hyracks.http.server.utils.HttpUtil;
+
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.QueryStringDecoder;
+import io.netty.handler.codec.http.multipart.Attribute;
+import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
+import io.netty.handler.codec.http.multipart.InterfaceHttpData;
+import io.netty.handler.codec.http.multipart.MixedAttribute;
+
+public class FormUrlEncodedRequest extends BaseRequest implements IServletRequest {
+
+    private final List<String> names;
+    private final List<String> values;
+
+    public static IServletRequest create(FullHttpRequest request) throws IOException {
+        List<String> names = new ArrayList<>();
+        List<String> values = new ArrayList<>();
+        HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(request);
+        try {
+            List<InterfaceHttpData> bodyHttpDatas = decoder.getBodyHttpDatas();
+            for (InterfaceHttpData data : bodyHttpDatas) {
+                if (data.getHttpDataType().equals(InterfaceHttpData.HttpDataType.Attribute)) {
+                    Attribute attr = (MixedAttribute) data;
+                    names.add(data.getName());
+                    values.add(attr.getValue());
+                }
+            }
+        } finally {
+            decoder.destroy();
+        }
+        return new FormUrlEncodedRequest(request, new QueryStringDecoder(request.uri()).parameters(), names, values);
+    }
+
+    protected FormUrlEncodedRequest(FullHttpRequest request, Map<String, List<String>> parameters, List<String> names,
+                                    List<String> values) {
+        super(request, parameters);
+        this.names = names;
+        this.values = values;
+    }
+
+    @Override
+    public String getParameter(CharSequence name) {
+        for (int i = 0; i < names.size(); i++) {
+            if (name.equals(names.get(i))) {
+                return values.get(i);
+            }
+        }
+        return HttpUtil.getParameter(parameters, name);
+    }
+
+    @Override
+    public Set<String> getParameterNames() {
+        HashSet<String> paramNames = new HashSet<>();
+        paramNames.addAll(parameters.keySet());
+        paramNames.addAll(names);
+        return Collections.unmodifiableSet(paramNames);
+    }
+
+    @Override
+    public Map<String, String> getParameters() {
+        HashMap<String, String> paramMap = new HashMap<>();
+        paramMap.putAll(super.getParameters());
+        for (int i = 0; i < names.size(); i++) {
+            paramMap.put(names.get(i), values.get(i));
+        }
+
+        return Collections.unmodifiableMap(paramMap);
+    }
+}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/PostRequest.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/PostRequest.java b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/PostRequest.java
deleted file mode 100644
index 1dcb088..0000000
--- a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/PostRequest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.hyracks.http.server;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.apache.hyracks.http.api.IServletRequest;
-import org.apache.hyracks.http.server.utils.HttpUtil;
-
-import io.netty.handler.codec.http.FullHttpRequest;
-import io.netty.handler.codec.http.QueryStringDecoder;
-import io.netty.handler.codec.http.multipart.Attribute;
-import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
-import io.netty.handler.codec.http.multipart.InterfaceHttpData;
-import io.netty.handler.codec.http.multipart.MixedAttribute;
-
-public class PostRequest extends BaseRequest implements IServletRequest {
-
-    private static final Logger LOGGER = Logger.getLogger(PostRequest.class.getName());
-
-    private final List<String> names;
-    private final List<String> values;
-
-    public static IServletRequest create(FullHttpRequest request) throws IOException {
-        List<String> names = new ArrayList<>();
-        List<String> values = new ArrayList<>();
-        HttpPostRequestDecoder decoder = null;
-        try {
-            decoder = new HttpPostRequestDecoder(request);
-        } catch (Exception e) {
-            //ignore. this means that the body of the POST request does not have key value pairs
-            LOGGER.log(Level.WARNING, "Failed to decode a post message. Fix the API not to have queries as POST body",
-                    e);
-        }
-        if (decoder != null) {
-            try {
-                List<InterfaceHttpData> bodyHttpDatas = decoder.getBodyHttpDatas();
-                for (InterfaceHttpData data : bodyHttpDatas) {
-                    if (data.getHttpDataType().equals(InterfaceHttpData.HttpDataType.Attribute)) {
-                        Attribute attr = (MixedAttribute) data;
-                        names.add(data.getName());
-                        values.add(attr.getValue());
-                    }
-                }
-            } finally {
-                decoder.destroy();
-            }
-        }
-        return new PostRequest(request, new QueryStringDecoder(request.uri()).parameters(), names, values);
-    }
-
-    protected PostRequest(FullHttpRequest request, Map<String, List<String>> parameters, List<String> names,
-            List<String> values) {
-        super(request, parameters);
-        this.names = names;
-        this.values = values;
-    }
-
-    @Override
-    public String getParameter(CharSequence name) {
-        for (int i = 0; i < names.size(); i++) {
-            if (name.equals(names.get(i))) {
-                return values.get(i);
-            }
-        }
-        return HttpUtil.getParameter(parameters, name);
-    }
-}

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/a14cebbf/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/utils/HttpUtil.java
----------------------------------------------------------------------
diff --git a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/utils/HttpUtil.java b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/utils/HttpUtil.java
index c11deef..2babc73 100644
--- a/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/utils/HttpUtil.java
+++ b/hyracks-fullstack/hyracks/hyracks-http/src/main/java/org/apache/hyracks/http/server/utils/HttpUtil.java
@@ -26,11 +26,11 @@ import java.util.Map;
 import org.apache.hyracks.http.api.IServletRequest;
 import org.apache.hyracks.http.api.IServletResponse;
 import org.apache.hyracks.http.server.BaseRequest;
-import org.apache.hyracks.http.server.PostRequest;
+import org.apache.hyracks.http.server.FormUrlEncodedRequest;
 
 import io.netty.handler.codec.http.FullHttpRequest;
 import io.netty.handler.codec.http.HttpHeaderNames;
-import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
 
 public class HttpUtil {
 
@@ -47,6 +47,7 @@ public class HttpUtil {
     public static class ContentType {
         public static final String APPLICATION_ADM = "application/x-adm";
         public static final String APPLICATION_JSON = "application/json";
+        public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
         public static final String CSV = "text/csv";
         public static final String IMG_PNG = "image/png";
         public static final String TEXT_HTML = "text/html";
@@ -57,7 +58,7 @@ public class HttpUtil {
     }
 
     public static String getParameter(Map<String, List<String>> parameters, CharSequence name) {
-        List<String> parameter = parameters.get(name);
+        List<String> parameter = parameters.get(String.valueOf(name));
         if (parameter == null) {
             return null;
         } else if (parameter.size() == 1) {
@@ -72,7 +73,17 @@ public class HttpUtil {
     }
 
     public static IServletRequest toServletRequest(FullHttpRequest request) throws IOException {
-        return request.method() == HttpMethod.POST ? PostRequest.create(request) : BaseRequest.create(request);
+        return ContentType.APPLICATION_X_WWW_FORM_URLENCODED.equals(getContentTypeOnly(request))
+                ? FormUrlEncodedRequest.create(request) : BaseRequest.create(request);
+    }
+
+    public static String getContentTypeOnly(IServletRequest request) {
+        return getContentTypeOnly(request.getHttpRequest());
+    }
+
+    public static String getContentTypeOnly(HttpRequest request) {
+        String contentType = request.headers().get(HttpHeaderNames.CONTENT_TYPE);
+        return contentType == null ? null : contentType.split(";")[0];
     }
 
     public static String getRequestBody(IServletRequest request) {