[java-plugin-shibd] branch main updated: Reimplement HTTP request remoting against "uri" field alone.

Codeberg noreply at shibboleth.net
Tue Jun 9 12:49:37 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository java-plugin-shibd.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd/commit/4687a0e652ce192ffd7839fec72e69af47c2c3d1

The following commit(s) were added to refs/heads/main by this push:
     new 4687a0e  Reimplement HTTP request remoting against "uri" field alone.
4687a0e is described below

commit 4687a0e652ce192ffd7839fec72e69af47c2c3d1
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Tue Jun 9 08:49:18 2026 -0400

    Reimplement HTTP request remoting against "uri" field alone.
---
 .../sp/messaging/RemotedHttpServletRequest.java    |  90 ++++++++++++++----
 .../impl/RemotedHttpServletRequestTest.java        | 105 ++++++++++++++++-----
 2 files changed, 154 insertions(+), 41 deletions(-)

diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletRequest.java b/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletRequest.java
index a0dba53..aae72ae 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletRequest.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/messaging/RemotedHttpServletRequest.java
@@ -83,18 +83,30 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
     /** Field holding name of {@link DDF} structure containing request. */
     @Nonnull @NotEmpty public static final String STRUCTURE_NAME = "http";
 
-    /** Field holding request URL (full URL without query string). */
-    @Nonnull @NotEmpty public static final String REQUEST_URL = "url";
-
     /** Field holding request URI (path without query string). */
     @Nonnull @NotEmpty public static final String REQUEST_URI = "uri";
 
+    /** Field holding scheme. */
+    @Nonnull @NotEmpty public static final String SCHEME = "scheme";
+
+    /** Field holding port. */
+    @Nonnull @NotEmpty public static final String PORT = "port";
+    
     /** Field holding method. */
     @Nonnull @NotEmpty public static final String METHOD = "method";
 
+    /** Field holding protocol. */
+    @Nonnull @NotEmpty public static final String PROTOCOL = "method";
+    
+    /** Field holding server hostname. */
+    @Nonnull @NotEmpty public static final String HOSTNAME = "hostname";
+    
     /** Field holding client address. */
     @Nonnull @NotEmpty public static final String REMOTE_ADDR = "remote_addr";
 
+    /** Field holding REMOTE_USER. */
+    @Nonnull @NotEmpty public static final String REMOTE_USER = "remote_user";
+    
     /** Field holding client address. */
     @Nonnull @NotEmpty public static final String LOCAL_ADDR = "local_addr";
     
@@ -110,6 +122,9 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
     /** Field holding content length. */
     @Nonnull @NotEmpty public static final String CONTENT_LENGTH = "content_length";
 
+    /** Field holding headers. */
+    @Nonnull @NotEmpty public static final String HEADERS = "headers";
+    
     /** Empty byte array for empty bodies. */
     @Nonnull private static final byte[] EMPTY_BODY = new byte[0];
     
@@ -122,6 +137,9 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
     /** Underlying object containing remoted data. */
     @Nonnull private final DDF obj;
     
+    /** Cached, reconstructed URL from component parts. */
+    @Nullable private String reconstructedURL;
+    
     /** Decoder to apply to byte array data. */
     @Nonnull private final CharsetDecoder decoder;
     
@@ -278,23 +296,30 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
     
     /** {@inheritDoc} */
     public String getProtocol() {
-        final String protocol = obj.getmember("protocol").string();
+        final String protocol = obj.getmember(PROTOCOL).string();
         return protocol != null ? protocol : "HTTP/1.1";
     }
 
     /** {@inheritDoc} */
     public String getScheme() {
-        return obj.getmember("scheme").string();
+        return obj.getmember(SCHEME).string();
     }
 
     /** {@inheritDoc} */
     public String getServerName() {
-        return decodeUnsafeString(obj.getmember("hostname").unsafe_string(), decoder, null);
+        final DDF hostname = obj.getmember(HOSTNAME);
+        if (hostname.isstring()) {
+            return hostname.string();
+        } else if (hostname.isunsafestring()) {
+            return decodeUnsafeString(hostname.unsafe_string(), decoder, null);
+        } else {
+            return null;
+        }
     }
 
     /** {@inheritDoc} */
     public int getServerPort() {
-        final Integer i = obj.getmember("port").integer();
+        final Integer i = obj.getmember(PORT).integer();
         return i != null ? i : -1;
     }
 
@@ -447,7 +472,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
         if (name == null) {
             return -1;
         }
-        final DDF h = obj.getmember("headers").getmember(name);
+        final DDF h = obj.getmember(HEADERS).getmember(name);
         if (h.isstring()) {
             try {
                 // TODO: there are a ton of valid formats but I don't think we really will need this anyway.
@@ -469,7 +494,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
         if (name == null) {
             return null;
         }
-        return decodeUnsafeString(obj.getmember("headers").getmember(name).unsafe_string(), decoder, null);
+        return decodeUnsafeString(obj.getmember(HEADERS).getmember(name).unsafe_string(), decoder, null);
     }
 
     /** {@inheritDoc} */
@@ -477,7 +502,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
         if (name == null) {
             return Collections.emptyEnumeration();
         }
-        final String s = decodeUnsafeString(obj.getmember("headers").getmember(name).unsafe_string(), decoder, null);
+        final String s = decodeUnsafeString(obj.getmember(HEADERS).getmember(name).unsafe_string(), decoder, null);
         if (s != null) {
             return Collections.enumeration(Collections.singletonList(s));
         }
@@ -486,7 +511,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
 
     /** {@inheritDoc} */
     public Enumeration<String> getHeaderNames() {
-        return Collections.enumeration(obj.getmember("headers").asMap().keySet());
+        return Collections.enumeration(obj.getmember(HEADERS).asMap().keySet());
     }
 
     /** {@inheritDoc} */
@@ -494,7 +519,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
         if (name == null) {
             return -1;
         }
-        final DDF h = obj.getmember("headers").getmember(name);
+        final DDF h = obj.getmember(HEADERS).getmember(name);
         if (h.isstring()) {
             return Integer.parseInt(decodeUnsafeString(h.unsafe_string(), decoder, null));
         }
@@ -530,7 +555,7 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
 
     /** {@inheritDoc} */
     public String getRemoteUser() {
-        return obj.getmember("remote_user").string();
+        return obj.getmember(REMOTE_USER).string();
     }
 
     /** {@inheritDoc} */
@@ -550,13 +575,46 @@ public class RemotedHttpServletRequest implements HttpServletRequest {
 
     /** {@inheritDoc} */
     public String getRequestURI() {
-        return decodeUnsafeString(obj.getmember(REQUEST_URI).unsafe_string(), decoder, '?');
+        final DDF uri = obj.getmember(REQUEST_URI);
+        if (uri.isstring()) {
+            return uri.string();
+        } else if (uri.isunsafestring()) {
+            // The "uri" used to contain the query string but is not intended to post-alpha2.
+            return decodeUnsafeString(uri.unsafe_string(), decoder, '?');
+        } else {
+            return null;
+        }
     }
 
     /** {@inheritDoc} */
     public StringBuffer getRequestURL() {
-        final String url = decodeUnsafeString(obj.getmember(REQUEST_URL).unsafe_string(), decoder, '?');
-        return new StringBuffer(url != null ? url : "");
+        if (reconstructedURL == null) {
+            // Rebuild the URL from the constituent parts.
+            final String scheme = getScheme();
+            final String hostname = getServerName();
+            final String uri = getRequestURI();
+            if (scheme == null || hostname == null || uri == null) {
+                throw new IllegalStateException("Remote request information is incomplete.");
+            }
+
+            final StringBuilder url = new StringBuilder(scheme);
+            url.append("://").append(hostname);
+            int port = getServerPort();
+            if ("https".equals(scheme)) {
+                if (port != 443) {
+                    url.append(':').append(port);
+                }
+            } else if ("http".equals(scheme)) {
+                if (port != 80) {
+                    url.append(':').append(port);
+                }
+            } else {
+                url.append(':').append(port);
+            }
+            url.append(uri);
+            reconstructedURL = url.toString();
+        }
+        return new StringBuffer(reconstructedURL);
     }
 
     /** {@inheritDoc} */
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/messaging/impl/RemotedHttpServletRequestTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/messaging/impl/RemotedHttpServletRequestTest.java
index 2cb7619..1608618 100644
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/messaging/impl/RemotedHttpServletRequestTest.java
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/messaging/impl/RemotedHttpServletRequestTest.java
@@ -19,11 +19,15 @@ import static org.testng.Assert.*;
 import java.io.IOException;
 import java.util.List;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 import jakarta.servlet.http.Cookie;
 import net.shibboleth.sp.ddf.DDF;
 import net.shibboleth.sp.messaging.RemotedHttpServletRequest;
 
 import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 import org.testng.reporters.Files;
 
@@ -57,7 +61,12 @@ public class RemotedHttpServletRequestTest {
         assertEquals(req.getRemoteAddr(), null);
         assertEquals(req.getRemoteUser(), null);
         assertEquals(req.getRequestURI(), null);
-        assertEquals(req.getRequestURL().toString(), "");
+        try {
+            req.getRequestURL().toString();
+            fail("Expected IllegalStateException");
+        } catch (final IllegalStateException e) {
+            
+        }
         assertEquals(req.getScheme(), null);
         assertFalse(req.isSecure());
         assertEquals(req.getServerPort(), -1);
@@ -66,17 +75,16 @@ public class RemotedHttpServletRequestTest {
     @Test
     public void testBasics() throws IOException {
         obj.structure();
-        obj.addmember("content_length").integer(100);
-        obj.addmember("content_type").string("text/xml");
-        obj.addmember("method").string("POST");
-        obj.addmember("body").unsafe_string("<foo/>".getBytes(Charsets.UTF_8));
-        obj.addmember("port").integer(80);
-        obj.addmember("remote_addr").string("127.0.0.1");
-        obj.addmember("remote_user").string("jdoe");
-        obj.addmember("hostname").unsafe_string("localhost".getBytes(Charsets.UTF_8));
-        obj.addmember("uri").unsafe_string("/endpoint".getBytes(Charsets.UTF_8));
-        obj.addmember("url").unsafe_string("http://localhost/endpoint".getBytes(Charsets.UTF_8));
-        obj.addmember("scheme").string("http");
+        obj.addmember(RemotedHttpServletRequest.CONTENT_LENGTH).integer(100);
+        obj.addmember(RemotedHttpServletRequest.CONTENT_TYPE).string("text/xml");
+        obj.addmember(RemotedHttpServletRequest.METHOD).string("POST");
+        obj.addmember(RemotedHttpServletRequest.BODY).unsafe_string("<foo/>".getBytes(Charsets.UTF_8));
+        obj.addmember(RemotedHttpServletRequest.REMOTE_ADDR).string("127.0.0.1");
+        obj.addmember(RemotedHttpServletRequest.REMOTE_USER).string("jdoe");
+        obj.addmember(RemotedHttpServletRequest.SCHEME).string("http");
+        obj.addmember(RemotedHttpServletRequest.PORT).integer(80);
+        obj.addmember(RemotedHttpServletRequest.HOSTNAME).unsafe_string("localhost".getBytes(Charsets.UTF_8));
+        obj.addmember(RemotedHttpServletRequest.REQUEST_URI).unsafe_string("/endpoint".getBytes(Charsets.UTF_8));
         
         assertEquals(req.getContentLength(), 100);
         assertEquals(req.getContentType(), "text/xml");
@@ -96,12 +104,59 @@ public class RemotedHttpServletRequestTest {
         assertEquals(req.getServerPort(), 80);        
     }
     
+    @Test(dataProvider="fields")
+    public void testURLConstruction(@Nonnull final String scheme, @Nonnull final String hostname, final int port,
+            @Nonnull final String path, @Nullable final String query) throws IOException {
+        obj.structure();
+        obj.addmember(RemotedHttpServletRequest.SCHEME).string(scheme);
+        obj.addmember(RemotedHttpServletRequest.PORT).integer(port);
+        obj.addmember(RemotedHttpServletRequest.HOSTNAME).unsafe_string(hostname.getBytes(Charsets.UTF_8));
+        obj.addmember(RemotedHttpServletRequest.REQUEST_URI).unsafe_string(path.getBytes(Charsets.UTF_8));
+        if (query != null) {
+            obj.addmember(RemotedHttpServletRequest.QUERY_STRING).unsafe_string(query.getBytes(Charsets.UTF_8));
+        }
+        
+        assertEquals(req.getRequestURI(), path);
+        final StringBuilder url = new StringBuilder(scheme + "://" + hostname);
+        if ("https".equals(scheme)) {
+            if (port != 443) {
+                url.append(':').append(port);
+            }
+        } else if ("http".equals(scheme)) {
+            if (port != 80) {
+                url.append(':').append(port);
+            }
+        } else {
+            url.append(':').append(port);
+        }
+        url.append(path);
+        assertEquals(req.getRequestURL().toString(), url.toString());
+    }
+    
+    @DataProvider(name = "fields")
+    public Object[][] getStatuses() throws Exception {
+        return new Object[][] {
+            new Object[] {
+                    "https", "sp.example.org", Integer.valueOf(443), "/path", null
+            },
+            new Object[] {
+                    "https", "sp.example.org", Integer.valueOf(8443), "/path/to/resource", "foo=bar"
+            },
+            new Object[] {
+                    "http", "sp.example.org", Integer.valueOf(80), "/path/to/resource", null
+            },
+            new Object[] {
+                    "http", "sp.example.org", Integer.valueOf(8080), "/path", null
+            },
+        };
+    }
+    
     @Test
     public void testOneQueryParameter() throws IOException {
         obj.structure();
-        obj.addmember("content_type").string("text/xml");
-        obj.addmember("body").unsafe_string("<foo/>".getBytes(Charsets.UTF_8));
-        obj.addmember("query").string("foo=bar+baz");
+        obj.addmember(RemotedHttpServletRequest.CONTENT_TYPE).string("text/xml");
+        obj.addmember(RemotedHttpServletRequest.BODY).unsafe_string("<foo/>".getBytes(Charsets.UTF_8));
+        obj.addmember(RemotedHttpServletRequest.QUERY_STRING).string("foo=bar+baz");
         
         assertEquals(req.getParameterNames().nextElement(), "foo");
         assertEquals(req.getParameter("foo"), "bar baz");
@@ -110,9 +165,9 @@ public class RemotedHttpServletRequestTest {
     @Test
     public void testMultiQueryParameters() throws IOException {
         obj.structure();
-        obj.addmember("content_type").string("text/xml");
-        obj.addmember("body").unsafe_string("<foo/>".getBytes(Charsets.UTF_8));
-        obj.addmember("query").string("foo=bar+baz&zork=grue&foo=baf");
+        obj.addmember(RemotedHttpServletRequest.CONTENT_TYPE).string("text/xml");
+        obj.addmember(RemotedHttpServletRequest.BODY).unsafe_string("<foo/>".getBytes(Charsets.UTF_8));
+        obj.addmember(RemotedHttpServletRequest.QUERY_STRING).string("foo=bar+baz&zork=grue&foo=baf");
         
         assertEquals(req.getParameter("foo"), "bar baz");
         assertEquals(req.getParameterValues("foo"), List.of("bar baz", "baf").toArray());
@@ -122,9 +177,9 @@ public class RemotedHttpServletRequestTest {
     @Test
     public void testFormParameters() throws IOException {
         obj.structure();
-        obj.addmember("content_type").string("application/x-www-form-urlencoded");
-        obj.addmember("body").unsafe_string("foo=baf".getBytes(Charsets.UTF_8));
-        obj.addmember("query").string("foo=bar+baz&zork=grue");
+        obj.addmember(RemotedHttpServletRequest.CONTENT_TYPE).string("application/x-www-form-urlencoded");
+        obj.addmember(RemotedHttpServletRequest.BODY).unsafe_string("foo=baf".getBytes(Charsets.UTF_8));
+        obj.addmember(RemotedHttpServletRequest.QUERY_STRING).string("foo=bar+baz&zork=grue");
         
         assertEquals(req.getParameter("foo"), "bar baz");
         assertEquals(req.getParameterValues("foo"), List.of("bar baz", "baf").toArray());
@@ -134,8 +189,8 @@ public class RemotedHttpServletRequestTest {
     @Test
     public void testHeaders() throws IOException {
         obj.structure();
-        obj.addmember("headers.foo").unsafe_string("bar".getBytes("UTF-8"));
-        obj.addmember("headers.zork").unsafe_string("grue".getBytes("UTF-8"));
+        obj.addmember(RemotedHttpServletRequest.HEADERS + ".foo").unsafe_string("bar".getBytes("UTF-8"));
+        obj.addmember(RemotedHttpServletRequest.HEADERS + ".zork").unsafe_string("grue".getBytes("UTF-8"));
         
         assertEquals(req.getHeaders("foo").nextElement(), "bar");
         assertEquals(req.getHeader("zork"), "grue");
@@ -145,7 +200,7 @@ public class RemotedHttpServletRequestTest {
     @Test
     public void testCookie() throws IOException {
         obj.structure();
-        obj.addmember("headers.Cookie").unsafe_string("foo=bar;".getBytes("UTF-8"));
+        obj.addmember(RemotedHttpServletRequest.HEADERS + ".Cookie").unsafe_string("foo=bar;".getBytes("UTF-8"));
         
         final Cookie[] cookies = req.getCookies();
         
@@ -157,7 +212,7 @@ public class RemotedHttpServletRequestTest {
     @Test
     public void testCookies() throws IOException {
         obj.structure();
-        obj.addmember("headers.Cookie").unsafe_string("foo=bar; zork=grue".getBytes("UTF-8"));
+        obj.addmember(RemotedHttpServletRequest.HEADERS + ".Cookie").unsafe_string("foo=bar; zork=grue".getBytes("UTF-8"));
         
         final Cookie[] cookies = req.getCookies();
         

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list