jamesfredley commented on code in PR #16786:
URL: https://github.com/apache/grails-core/pull/16786#discussion_r4401194500


##########
grails-async/plugin/src/main/groovy/org/grails/plugins/web/async/CompletedHttpServletRequest.groovy:
##########
@@ -0,0 +1,735 @@
+/*
+ *  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
+ *
+ *    https://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.grails.plugins.web.async
+
+import java.nio.charset.Charset
+import java.nio.charset.StandardCharsets
+import java.security.Principal
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.concurrent.ConcurrentHashMap
+
+import groovy.transform.CompileStatic
+
+import jakarta.servlet.AsyncContext
+import jakarta.servlet.DispatcherType
+import jakarta.servlet.ReadListener
+import jakarta.servlet.RequestDispatcher
+import jakarta.servlet.ServletConnection
+import jakarta.servlet.ServletContext
+import jakarta.servlet.ServletInputStream
+import jakarta.servlet.ServletRequest
+import jakarta.servlet.ServletResponse
+import jakarta.servlet.http.Cookie
+import jakarta.servlet.http.HttpServletMapping
+import jakarta.servlet.http.HttpServletRequest
+import jakarta.servlet.http.HttpServletResponse
+import jakarta.servlet.http.HttpSession
+import jakarta.servlet.http.HttpUpgradeHandler
+import jakarta.servlet.http.MappingMatch
+import jakarta.servlet.http.Part
+
+import org.springframework.util.LinkedCaseInsensitiveMap
+
+/**
+ * A copy of a request, taken as the request completed, which a task that 
outlives the request reads in its place.
+ *
+ * <p>The container recycles a request once it completes, so a task that read 
the request itself from then on
+ * would fail. The copy holds the attributes, parameters, headers, locales and 
cookies of the request, the part
+ * of its body nothing had read, its session and what identifies it. 
Attributes a task sets stay with the copy.
+ * What only the live request can do, such as dispatching, processing 
asynchronously, creating a session or
+ * checking a role, is not available.</p>
+ *
+ * @since 8.0.1
+ */
+@CompileStatic
+class CompletedHttpServletRequest implements HttpServletRequest {
+
+    private static final byte[] NO_BODY = new byte[0]
+    private static final List<String> DATE_HEADER_FORMS = List.of(
+            'EEE, dd MMM yyyy HH:mm:ss zzz',
+            'EEEE, dd-MMM-yy HH:mm:ss zzz',
+            'EEE MMM dd HH:mm:ss yyyy')
+
+    private final Map<String, Object> attributes = new ConcurrentHashMap<>()
+    private final byte[] body
+    private final Map<String, String[]> parameters
+    private final Map<String, List<String>> headers
+    private final List<Locale> locales
+    private final Cookie[] cookies
+    private final HttpSession session
+    private final ServletContext servletContext
+    private final Principal userPrincipal
+    private final DispatcherType dispatcherType
+    private final HttpServletMapping httpServletMapping
+    private final ServletConnection servletConnection
+    private final String authType
+    private final String contentType
+    private final long contentLength
+    private final String method
+    private final String requestURI
+    private final String requestURL
+    private final String queryString
+    private final String contextPath
+    private final String servletPath
+    private final String pathInfo
+    private final String pathTranslated
+    private final String protocol
+    private final String scheme
+    private final String serverName
+    private final int serverPort
+    private final boolean secure
+    private final String remoteAddr
+    private final String remoteHost
+    private final int remotePort
+    private final String localName
+    private final String localAddr
+    private final int localPort
+    private final String remoteUser
+    private final String requestedSessionId
+    private final boolean requestedSessionIdValid
+    private final boolean requestedSessionIdFromCookie
+    private final boolean requestedSessionIdFromURL
+    private final String requestId
+    private final String protocolRequestId
+    private volatile String characterEncoding
+
+    private CompletedHttpServletRequest(HttpServletRequest request) {
+        for (String name : Collections.list(request.attributeNames)) {
+            Object value = request.getAttribute(name)
+            if (value != null) {
+                attributes.put(name, value)

Review Comment:
   Skipping WEB_REQUEST and an attribute whose value is the request still 
copies the dispatcher multipart wrapper. GrailsDispatcherServlet.checkMultipart 
stores that wrapper on WebUtils.MULTIPART_HTTP_SERVLET_REQUEST_ATTRIBUTE. The 
wrapper is not the request, so this loop keeps it, and the wrapper still points 
at the container request. A task that outlives the request keeps the recycled 
request through that attribute. Leave that attribute out, or copy the wrapper 
without the live request.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to