From d27580f6b3467e70120a220badd9d3879fec97c8 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 15 Apr 2024 15:37:05 +0200 Subject: [PATCH] Add Remote Address to WebGraphQlRequest This commit makes available the client remote address to various GraphQL request implementations. Closes gh-945 --- .../WebGraphQlHandlerGraphQlTransport.java | 2 +- .../graphql/server/WebGraphQlRequest.java | 80 ++++++++++++++----- .../server/WebSocketGraphQlRequest.java | 47 ++++++----- .../server/webflux/GraphQlHttpHandler.java | 3 +- .../server/webflux/GraphQlSseHandler.java | 3 +- .../webflux/GraphQlWebSocketHandler.java | 3 +- .../server/webmvc/GraphQlHttpHandler.java | 1 + .../server/webmvc/GraphQlSseHandler.java | 3 +- .../webmvc/GraphQlWebSocketHandler.java | 2 +- .../server/webmvc/TestWebSocketSession.java | 2 +- 10 files changed, 100 insertions(+), 46 deletions(-) diff --git a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java index b1e4bb18..eaadadf1 100644 --- a/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java +++ b/spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/WebGraphQlHandlerGraphQlTransport.java @@ -78,7 +78,7 @@ final class WebGraphQlHandlerGraphQlTransport extends AbstractDirectGraphQlTrans protected Mono executeInternal(ExecutionGraphQlRequest executionRequest) { WebGraphQlRequest request = new WebGraphQlRequest( - this.url, this.headers, null, Collections.emptyMap(), executionRequest, + this.url, this.headers, null, null, Collections.emptyMap(), executionRequest, idGenerator.generateId().toString(), null); return this.graphQlHandler.handleRequest(request).cast(ExecutionGraphQlResponse.class); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java index 43aca472..2dd5bb9f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java @@ -16,6 +16,7 @@ package org.springframework.graphql.server; +import java.net.InetSocketAddress; import java.net.URI; import java.util.Collections; import java.util.Locale; @@ -58,8 +59,51 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements private final MultiValueMap cookies; + @Nullable + private final InetSocketAddress remoteAddress; + private final Map attributes; + /** + * Create an instance. + * @param uri the URL for the HTTP request or WebSocket handshake + * @param headers the HTTP request headers + * @param cookies the HTTP request cookies + * @param remoteAddress the HTTP client remote address + * @param attributes request attributes + * @param body the deserialized content of the GraphQL request + * @param id an identifier for the GraphQL request + * @param locale the locale from the HTTP request, if any + * @since 1.3.0 + */ + public WebGraphQlRequest(URI uri, HttpHeaders headers, @Nullable MultiValueMap cookies, + @Nullable InetSocketAddress remoteAddress, Map attributes, + GraphQlRequest body, String id, @Nullable Locale locale) { + this(uri, headers, cookies, remoteAddress, attributes, body.getDocument(), + body.getOperationName(), body.getVariables(), body.getExtensions(), id, locale); + } + + /** + * Variant of {@link #WebGraphQlRequest(URI, HttpHeaders, MultiValueMap, Map, GraphQlRequest, String, Locale)} + * with a Map for the request body. + * @param uri the URL for the HTTP request or WebSocket handshake + * @param headers the HTTP request headers + * @param cookies the HTTP request cookies + * @param remoteAddress the HTTP client remote address + * @param attributes request attributes + * @param body the deserialized content of the GraphQL request + * @param id an identifier for the GraphQL request + * @param locale the locale from the HTTP request, if any + * @since 1.3.0 + */ + public WebGraphQlRequest( + URI uri, HttpHeaders headers, @Nullable MultiValueMap cookies, + @Nullable InetSocketAddress remoteAddress, Map attributes, + Map body, String id, @Nullable Locale locale) { + + this(uri, headers, cookies, remoteAddress, attributes, getQuery(body), getOperation(body), + getMap(VARIABLES_KEY, body), getMap(EXTENSIONS_KEY, body), id, locale); + } /** * Create an instance. @@ -71,12 +115,14 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements * @param id an identifier for the GraphQL request * @param locale the locale from the HTTP request, if any * @since 1.2.5 + * @deprecated since 1.3.0 in favor {@link #WebGraphQlRequest(URI, HttpHeaders, MultiValueMap, InetSocketAddress, Map, Map, String, Locale)} */ + @Deprecated(since = "1.3.0", forRemoval = true) public WebGraphQlRequest( URI uri, HttpHeaders headers, @Nullable MultiValueMap cookies, Map attributes, GraphQlRequest body, String id, @Nullable Locale locale) { - this(uri, headers, cookies, attributes, body.getDocument(), + this(uri, headers, cookies, null, attributes, body.getDocument(), body.getOperationName(), body.getVariables(), body.getExtensions(), id, locale); } @@ -91,12 +137,14 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements * @param id an identifier for the GraphQL request * @param locale the locale from the HTTP request, if any * @since 1.1.3 + * @deprecated since 1.3.0 in favor {@link #WebGraphQlRequest(URI, HttpHeaders, MultiValueMap, InetSocketAddress, Map, Map, String, Locale)} */ + @Deprecated(since = "1.3.0", forRemoval = true) public WebGraphQlRequest( URI uri, HttpHeaders headers, @Nullable MultiValueMap cookies, Map attributes, Map body, String id, @Nullable Locale locale) { - this(uri, headers, cookies, attributes, getQuery(body), getOperation(body), + this(uri, headers, cookies, null, attributes, getQuery(body), getOperation(body), getMap(VARIABLES_KEY, body), getMap(EXTENSIONS_KEY, body), id, locale); } @@ -127,24 +175,10 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements return (Map) value; } - /** - * Create an instance. - * @param uri the URL for the HTTP request or WebSocket handshake - * @param headers the HTTP request headers - * @param body the deserialized content of the GraphQL request - * @param id an identifier for the GraphQL request - * @param locale the locale from the HTTP request, if any - * @deprecated as of 1.1.3 in favor of - * {@link #WebGraphQlRequest(URI, HttpHeaders, MultiValueMap, Map, GraphQlRequest, String, Locale)} - */ - @Deprecated(since = "1.1.3", forRemoval = true) - public WebGraphQlRequest(URI uri, HttpHeaders headers, Map body, String id, @Nullable Locale locale) { - this(uri, headers, null, Collections.emptyMap(), body, id, locale); - } - private WebGraphQlRequest( URI uri, HttpHeaders headers, @Nullable MultiValueMap cookies, - Map attributes, String document, @Nullable String operationName, + @Nullable InetSocketAddress remoteAddress, Map attributes, + String document, @Nullable String operationName, @Nullable Map variables, @Nullable Map extensions, String id, @Nullable Locale locale) { @@ -156,6 +190,7 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements this.uri = UriComponentsBuilder.fromUri(uri).build(true); this.headers = headers; this.cookies = (cookies != null) ? CollectionUtils.unmodifiableMultiValueMap(cookies) : EMPTY_COOKIES; + this.remoteAddress = remoteAddress; this.attributes = Collections.unmodifiableMap(attributes); } @@ -182,6 +217,15 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements return this.cookies; } + /** + * Return the remote address of the client, if available. + * @since 1.3.0 + */ + @Nullable + public InetSocketAddress getRemoteAddress() { + return this.remoteAddress; + } + /** * Return the request or WebSocket session attributes. * @since 1.1.3 diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketGraphQlRequest.java index 00a7a578..f81c8995 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/WebSocketGraphQlRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the original author or authors. + * Copyright 2020-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,8 @@ package org.springframework.graphql.server; +import java.net.InetSocketAddress; import java.net.URI; -import java.util.Collections; import java.util.Locale; import java.util.Map; @@ -41,24 +41,6 @@ public class WebSocketGraphQlRequest extends WebGraphQlRequest { private final WebSocketSessionInfo sessionInfo; - /** - * Create an instance. - * @param uri the URL for the HTTP request or WebSocket handshake - * @param headers the HTTP request headers - * @param body the deserialized content of the GraphQL request - * @param id the id from the GraphQL over WebSocket {@code "subscribe"} message - * @param locale the locale from the HTTP request, if any - * @param sessionInfo the WebSocket session id - * @deprecated as of 1.1.3 in favor of the constructor with cookies - */ - @Deprecated(since = "1.1.3", forRemoval = true) - public WebSocketGraphQlRequest( - URI uri, HttpHeaders headers, Map body, String id, @Nullable Locale locale, - WebSocketSessionInfo sessionInfo) { - - this(uri, headers, null, Collections.emptyMap(), body, id, locale, sessionInfo); - } - /** * Create an instance. * @param uri the URL for the HTTP request or WebSocket handshake @@ -70,13 +52,36 @@ public class WebSocketGraphQlRequest extends WebGraphQlRequest { * @param locale the locale from the HTTP request, if any * @param sessionInfo the WebSocket session id * @since 1.1.3 + * @deprecated in favor of {@link #WebSocketGraphQlRequest(URI, HttpHeaders, MultiValueMap, InetSocketAddress, Map, Map, String, Locale, WebSocketSessionInfo)} */ + @Deprecated(since = "1.3.0", forRemoval = true) public WebSocketGraphQlRequest( URI uri, HttpHeaders headers, @Nullable MultiValueMap cookies, Map attributes, Map body, String id, @Nullable Locale locale, WebSocketSessionInfo sessionInfo) { - super(uri, headers, cookies, attributes, body, id, locale); + this(uri, headers, cookies, null, attributes, body, id, locale, sessionInfo); + } + + /** + * Create an instance. + * @param uri the URL for the HTTP request or WebSocket handshake + * @param headers the HTTP request headers + * @param cookies the HTTP request cookies + * @param remoteAddress the client remote address + * @param attributes session attributes + * @param body the deserialized content of the GraphQL request + * @param id the id from the GraphQL over WebSocket {@code "subscribe"} message + * @param locale the locale from the HTTP request, if any + * @param sessionInfo the WebSocket session id + * @since 1.3.0 + */ + public WebSocketGraphQlRequest( + URI uri, HttpHeaders headers, @Nullable MultiValueMap cookies, + @Nullable InetSocketAddress remoteAddress, Map attributes, Map body, + String id, @Nullable Locale locale, WebSocketSessionInfo sessionInfo) { + + super(uri, headers, cookies, remoteAddress, attributes, body, id, locale); Assert.notNull(sessionInfo, "WebSocketSessionInfo is required"); this.sessionInfo = sessionInfo; } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java index a186da1b..099e6f1e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlHttpHandler.java @@ -74,7 +74,8 @@ public class GraphQlHttpHandler extends AbstractGraphQlHttpHandler { .flatMap((body) -> { WebGraphQlRequest graphQlRequest = new WebGraphQlRequest( serverRequest.uri(), serverRequest.headers().asHttpHeaders(), - serverRequest.cookies(), serverRequest.attributes(), body, + serverRequest.cookies(), serverRequest.remoteAddress().orElse(null), + serverRequest.attributes(), body, serverRequest.exchange().getRequest().getId(), serverRequest.exchange().getLocaleContext().getLocale()); if (logger.isDebugEnabled()) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java index 4a2f7d26..d656bd44 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlSseHandler.java @@ -70,7 +70,8 @@ public class GraphQlSseHandler extends AbstractGraphQlHttpHandler { .flatMap((body) -> { WebGraphQlRequest graphQlRequest = new WebGraphQlRequest( serverRequest.uri(), serverRequest.headers().asHttpHeaders(), - serverRequest.cookies(), serverRequest.attributes(), body, + serverRequest.cookies(), serverRequest.remoteAddress().orElse(null), + serverRequest.attributes(), body, serverRequest.exchange().getRequest().getId(), serverRequest.exchange().getLocaleContext().getLocale()); if (logger.isDebugEnabled()) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java index 8a8e868a..b5e7932c 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webflux/GraphQlWebSocketHandler.java @@ -172,7 +172,8 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { } WebSocketGraphQlRequest request = new WebSocketGraphQlRequest( handshakeInfo.getUri(), handshakeInfo.getHeaders(), handshakeInfo.getCookies(), - handshakeInfo.getAttributes(), payload, id, null, sessionInfo); + handshakeInfo.getRemoteAddress(), handshakeInfo.getAttributes(), + payload, id, null, sessionInfo); if (logger.isDebugEnabled()) { logger.debug("Executing: " + request); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java index d05fc456..2604c8c4 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlHttpHandler.java @@ -81,6 +81,7 @@ public class GraphQlHttpHandler extends AbstractGraphQlHttpHandler { WebGraphQlRequest graphQlRequest = new WebGraphQlRequest( serverRequest.uri(), serverRequest.headers().asHttpHeaders(), initCookies(serverRequest), + serverRequest.remoteAddress().orElse(null), serverRequest.attributes(), readBody(serverRequest), this.idGenerator.generateId().toString(), LocaleContextHolder.getLocale()); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlSseHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlSseHandler.java index 5ca16d03..c21649e7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlSseHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlSseHandler.java @@ -69,7 +69,8 @@ public class GraphQlSseHandler extends AbstractGraphQlHttpHandler { WebGraphQlRequest graphQlRequest = new WebGraphQlRequest( serverRequest.uri(), serverRequest.headers().asHttpHeaders(), initCookies(serverRequest), - serverRequest.attributes(), readBody(serverRequest), this.idGenerator.generateId().toString(), + serverRequest.remoteAddress().orElse(null), serverRequest.attributes(), + readBody(serverRequest), this.idGenerator.generateId().toString(), LocaleContextHolder.getLocale()); if (logger.isDebugEnabled()) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java index a03673b2..6dce0114 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/webmvc/GraphQlWebSocketHandler.java @@ -230,7 +230,7 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub Assert.notNull(uri, "Expected handshake url"); HttpHeaders headers = session.getHandshakeHeaders(); WebSocketGraphQlRequest request = new WebSocketGraphQlRequest( - uri, headers, null, session.getAttributes(), payload, id, null, state.getSessionInfo()); + uri, headers, null, session.getRemoteAddress(), session.getAttributes(), payload, id, null, state.getSessionInfo()); if (logger.isDebugEnabled()) { logger.debug("Executing: " + request); } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/TestWebSocketSession.java b/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/TestWebSocketSession.java index 9d080661..59eb9ab1 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/TestWebSocketSession.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/TestWebSocketSession.java @@ -85,7 +85,7 @@ public class TestWebSocketSession implements WebSocketSession { @Override public InetSocketAddress getRemoteAddress() { - throw new UnsupportedOperationException(); + return null; } @Override