Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form fields are null in the Request object in Jetty 12.0.15 #12673

Open
keenuser opened this issue Jan 6, 2025 · 6 comments
Open

Form fields are null in the Request object in Jetty 12.0.15 #12673

keenuser opened this issue Jan 6, 2025 · 6 comments
Labels
Bug For general bugs on Jetty side

Comments

@keenuser
Copy link

keenuser commented Jan 6, 2025

Jetty version(s)
Jetty 12.0.15

Jetty Environment
EE8

Java version/vendor (use: java -version)
OpenJDK 21.0.2

OS type/version
Windows Server 2016

Description
I am using a custom request logger (MyJettyRequestLogger) to log some custom attributes into Jetty request logs. Things were working fine with Jetty 10.x, but broken in Jetty 12.0.15 for me. Here is the simplified implementation (changed a few things according to Jetty 12, earlier I was directly using HttpServletRequest objects in MyJettyRequestLogger class):

import java.util.Set;

import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Fields;

public class MyJettyRequestLogger extends CustomRequestLog
{
   private static final Set<String> auditUrls = Set.of("/system/web/controller/platform/common/search/getcustomersearchresult.jsp");

   public MyJettyRequestLogger(Writer writer, String logStr)
   {
      super(writer, logStr);
   }

   @Override
   public void log(Request request, Response response)
   {
      setCustomAttributesToLog(request, response);
      super.log(request, response);
   }

   private void setCustomAttributesToLog(Request request, Response response)
   {
      String requestURI = HttpURI.build(request.getHttpURI()).getPath();
      String auditParams = setAuditAttributes(request, requestURI);
      if (!auditParams.isEmpty())
         request.setAttribute("auditParams", auditParams);

   }

   private String setAuditAttributes(Request request, String requestURI)
   {
      StringBuilder logBuffer = new StringBuilder();
      if ("post".equalsIgnoreCase(request.getMethod()) && auditUrls.contains(requestURI))
      {
         try
         {
            Fields params = Request.getParameters(request); // PROBLEM HERE
            if (params != null)
            {
               boolean first = true;
               logBuffer.append("[Params:");
               for (Fields.Field param : params)
               {
                  if (first)
                     first = false;
                  else
                     logBuffer.append("&");
                  logBuffer.append(param.getName()).append("=").append(String.join(",", param.getValues()));
               }
               logBuffer.append("] ");
            }
         }
         catch (Exception e)
         {
            throw new RuntimeException(e);
         }
      }
      return logBuffer.toString();
   }
}

In the above code Request.getParameters(request) is giving me query parameters but not the form field parameters in the POST request. After debugging it into Jetty's code, I came to know that form field parameters are returned as NULL from the following method from org.eclipse.jetty.server.Request

static CompletableFuture<Fields> getParametersAsync(Request request)
{
    Fields queryFields = Request.extractQueryParameters(request);
    CompletableFuture<Fields> contentFields = FormFields.from(request);
    return contentFields.thenApply(formFields -> Fields.combine(queryFields, formFields));
}

Note that I am using EE8 for deploying my war. What am I doing wrong here?

Thanks in advance for any help.

Here is the request block as I captured using chrome developer tool:

"request": {
          "method": "POST",
          "url": "https://eg5590ain.mydev.net/system/web/controller/platform/common/search/getcustomersearchresult.jsp?_dc=1735640244596&__token__=8b83dcc7-43c2-4e33-a27b-02f8793ecb83",
          "httpVersion": "http/2.0",
          "headers": [
            {
              "name": ":authority",
              "value": "eg5590ain.mydev.net"
            },
            {
              "name": ":method",
              "value": "POST"
            },
            {
              "name": ":path",
              "value": "/system/web/controller/platform/common/search/getcustomersearchresult.jsp?_dc=1735640244596&__token__=8b83dcc7-43c2-4e33-a27b-02f8793ecb83"
            },
            {
              "name": ":scheme",
              "value": "https"
            },
            {
              "name": "accept",
              "value": "application/json"
            },
            {
              "name": "accept-encoding",
              "value": "gzip, deflate, br, zstd"
            },
            {
              "name": "accept-language",
              "value": "en-us"
            },
            {
              "name": "content-length",
              "value": "556"
            },
            {
              "name": "content-type",
              "value": "application/x-www-form-urlencoded; charset=UTF-8"
            },
            {
              "name": "origin",
              "value": "https://eg5590ain.mydev.net"
            },
            {
              "name": "priority",
              "value": "u=1, i"
            },
            {
              "name": "referer",
              "value": "https://eg5590ain.mydev.net/system/web/apps/agent/"
            },
            {
              "name": "sec-ch-ua",
              "value": "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\""
            },
            {
              "name": "sec-ch-ua-mobile",
              "value": "?0"
            },
            {
              "name": "sec-ch-ua-platform",
              "value": "\"Windows\""
            },
            {
              "name": "sec-fetch-dest",
              "value": "empty"
            },
            {
              "name": "sec-fetch-mode",
              "value": "cors"
            },
            {
              "name": "sec-fetch-site",
              "value": "same-origin"
            },
            {
              "name": "user-agent",
              "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
            },
            {
              "name": "x-requested-with",
              "value": "XMLHttpRequest"
            }
          ],
          "queryString": [
            {
              "name": "_dc",
              "value": "1735640244596"
            },
            {
              "name": "__token__",
              "value": "8b83dcc7-43c2-4e33-a27b-02f8793ecb83"
            }
          ],
          "cookies": [],
          "headersSize": -1,
          "bodySize": 556,
          "postData": {
            "mimeType": "application/x-www-form-urlencoded; charset=UTF-8",
            "text": "customer.customer_type%231=casemgmt%3A%3Acustomer_search_data%23%253D%253D%23and%23false%23false%23-1%23i%231%231&id=-1&type=customer&language=en&country=us&fromIndex=0&sortCol=1&pageSize=50&ascending=false&__time__=1735640244591&page=1&start=0&limit=25&language=en&country=us",
            "params": [
              {
                "name": "customer.customer_type%231",
                "value": "casemgmt%3A%3Acustomer_search_data%23%253D%253D%23and%23false%23false%23-1%23i%231%231"
              },
              {
                "name": "type",
                "value": "customer"
              },
              {
                "name": "language",
                "value": "en"
              },
              {
                "name": "country",
                "value": "us"
              },
              {
                "name": "fromIndex",
                "value": "0"
              },
              {
                "name": "sortCol",
                "value": "1"
              },
              {
                "name": "pageSize",
                "value": "50"
              },
              {
                "name": "ascending",
                "value": "false"
              },
              {
                "name": "__time__",
                "value": "1735640244591"
              },
              {
                "name": "page",
                "value": "1"
              },
              {
                "name": "start",
                "value": "0"
              },
              {
                "name": "limit",
                "value": "25"
              },
              {
                "name": "language",
                "value": "en"
              },
              {
                "name": "country",
                "value": "us"
              }
            ]
          }
        }
@keenuser keenuser added the Bug For general bugs on Jetty side label Jan 6, 2025
@joakime
Copy link
Contributor

joakime commented Jan 6, 2025

Did any code access the request body before the MyJettyRequestLogger kicked in?

@joakime
Copy link
Contributor

joakime commented Jan 6, 2025

Also, use 12.0.16, as Request.getParametersAsync(Request request) is now deprecated.
That code path is different now.

@keenuser
Copy link
Author

keenuser commented Jan 6, 2025

Did any code access the request body before the MyJettyRequestLogger kicked in?

Probably yes (there are bunch of filters so need some time to figure out). Does it matter?

Also, use 12.0.16, as Request.getParametersAsync(Request request) is now deprecated. That code path is different now.

Will give it a try. Thanks.

@joakime
Copy link
Contributor

joakime commented Jan 6, 2025

Did any code access the request body before the MyJettyRequestLogger kicked in?

Probably yes (there are bunch of filters so need some time to figure out). Does it matter?

Yes, it matters.

If nothing accessed the Request body content, and then you reach the RequestLog the Request & Response are complete, there's no longer an opportunity to read the request at that point in time.

If nothing read the body for the request parameters, then there's no content left at RequestLog time to read the request parameters.

@keenuser
Copy link
Author

keenuser commented Jan 8, 2025

Also, use 12.0.16, as Request.getParametersAsync(Request request) is now deprecated. That code path is different now.

This did not work, same issue as 12.0.15.

Did any code access the request body before the MyJettyRequestLogger kicked in?

Probably yes (there are bunch of filters so need some time to figure out). Does it matter?

Yes, it matters.

If nothing accessed the Request body content, and then you reach the RequestLog the Request & Response are complete, there's no longer an opportunity to read the request at that point in time.

If nothing read the body for the request parameters, then there's no content left at RequestLog time to read the request parameters.

Is there any change in Jetty 12.0.15 as compared to Jetty 10.0.24 in this regard? The exact same code works in Jetty 10.

@joakime
Copy link
Contributor

joakime commented Jan 8, 2025

Is there any change in Jetty 12.0.15 as compared to Jetty 10.0.24 in this regard? The exact same code works in Jetty 10.

That shouldn't have worked in Jetty 10 either.
The same rules have applied since Jetty 9.4.0.

We've even submitted PRs / fixes to other projects that did the same thing.

To state this as simply as possible.

"You cannot cause HTTP I/O during RequestLog."

Using a features like Request.getParameters() will cause I/O if not read before.

If you follow the 12.0.x source for that method, you'll see that it will access the Request Attributes to find previously parsed FormFields, that's the only case which is guaranteed to work during RequestLog.

Same is true for Jetty 10.0.x, see the _contentParamsExtracted field on org.eclipse.jetty.server.Request object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug For general bugs on Jetty side
Projects
None yet
Development

No branches or pull requests

2 participants