Develop refactor request context (#12331)

* Add RequestContext and RequestContextHolder.

* build RequestContext when request start.

* Refactor ClientAttributesFilter with RequestContext.

* InstanceController get client ip from request context.

* SubscribeServiceRequestHandler get app from requestcontext.

* config http api support use request context get user, app and source ip.

* Rename nacos request context filter.

* Unified naming request get source ip by request context.

* For checkstyle.
This commit is contained in:
杨翊 SionYang 2024-07-10 16:54:19 +08:00 committed by GitHub
parent 2aa9fc51bc
commit 2233e6556c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 1952 additions and 137 deletions

View File

@ -18,6 +18,9 @@ package com.alibaba.nacos.config.server.utils;
import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.utils.WebUtils;
import com.alibaba.nacos.plugin.auth.api.IdentityContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -28,30 +31,24 @@ import javax.servlet.http.HttpServletRequest;
*/ */
public class RequestUtil { public class RequestUtil {
private static final String X_REAL_IP = "X-Real-IP";
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
private static final String X_FORWARDED_FOR_SPLIT_SYMBOL = ",";
public static final String CLIENT_APPNAME_HEADER = "Client-AppName"; public static final String CLIENT_APPNAME_HEADER = "Client-AppName";
/** /**
* get real client ip * Get real client ip from context first, if no value, use
* * {@link com.alibaba.nacos.core.utils.WebUtils#getRemoteIp(HttpServletRequest)}.
* <p>first use X-Forwarded-For header https://zh.wikipedia.org/wiki/X-Forwarded-For next nginx X-Real-IP last
* {@link HttpServletRequest#getRemoteAddr()}
* *
* @param request {@link HttpServletRequest} * @param request {@link HttpServletRequest}
* @return remote ip address. * @return remote ip address.
*/ */
public static String getRemoteIp(HttpServletRequest request) { public static String getRemoteIp(HttpServletRequest request) {
String xForwardedFor = request.getHeader(X_FORWARDED_FOR); String remoteIp = RequestContextHolder.getContext().getBasicContext().getAddressContext().getSourceIp();
if (!StringUtils.isBlank(xForwardedFor)) { if (StringUtils.isBlank(remoteIp)) {
return xForwardedFor.split(X_FORWARDED_FOR_SPLIT_SYMBOL)[0].trim(); remoteIp = RequestContextHolder.getContext().getBasicContext().getAddressContext().getRemoteIp();
} }
String nginxHeader = request.getHeader(X_REAL_IP); if (StringUtils.isBlank(remoteIp)) {
return StringUtils.isBlank(nginxHeader) ? request.getRemoteAddr() : nginxHeader; remoteIp = WebUtils.getRemoteIp(request);
}
return remoteIp;
} }
/** /**
@ -61,7 +58,12 @@ public class RequestUtil {
* @return may be return null * @return may be return null
*/ */
public static String getAppName(HttpServletRequest request) { public static String getAppName(HttpServletRequest request) {
return request.getHeader(CLIENT_APPNAME_HEADER); String result = RequestContextHolder.getContext().getBasicContext().getApp();
return isUnknownApp(result) ? request.getHeader(CLIENT_APPNAME_HEADER) : result;
}
private static boolean isUnknownApp(String appName) {
return StringUtils.isBlank(appName) || StringUtils.equalsIgnoreCase("unknown", appName);
} }
/** /**
@ -71,8 +73,12 @@ public class RequestUtil {
* @return may be return null * @return may be return null
*/ */
public static String getSrcUserName(HttpServletRequest request) { public static String getSrcUserName(HttpServletRequest request) {
String result = (String) request.getSession() IdentityContext identityContext = RequestContextHolder.getContext().getAuthContext().getIdentityContext();
.getAttribute(com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID); String result = StringUtils.EMPTY;
if (null != identityContext) {
result = (String) identityContext.getParameter(
com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID);
}
// If auth is disabled, get username from parameters by agreed key // If auth is disabled, get username from parameters by agreed key
return StringUtils.isBlank(result) ? request.getParameter(Constants.USERNAME) : result; return StringUtils.isBlank(result) ? request.getParameter(Constants.USERNAME) : result;
} }

View File

@ -17,11 +17,13 @@
package com.alibaba.nacos.config.server.utils; package com.alibaba.nacos.config.server.utils;
import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.plugin.auth.api.IdentityContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -32,8 +34,13 @@ class RequestUtilTest {
private static final String X_FORWARDED_FOR = "X-Forwarded-For"; private static final String X_FORWARDED_FOR = "X-Forwarded-For";
@AfterEach
void tearDown() {
RequestContextHolder.removeContext();
}
@Test @Test
void testGetRemoteIp() { void testGetRemoteIpFromRequest() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Mockito.when(request.getRemoteAddr()).thenReturn("127.0.0.1"); Mockito.when(request.getRemoteAddr()).thenReturn("127.0.0.1");
@ -56,29 +63,33 @@ class RequestUtilTest {
} }
@Test @Test
void testGetAppName() { void testGetAppNameFromContext() {
RequestContextHolder.getContext().getBasicContext().setApp("contextApp");
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Mockito.when(request.getHeader(eq(RequestUtil.CLIENT_APPNAME_HEADER))).thenReturn("test");
assertEquals("contextApp", RequestUtil.getAppName(request));
}
@Test
void testGetAppNameFromRequest() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Mockito.when(request.getHeader(eq(RequestUtil.CLIENT_APPNAME_HEADER))).thenReturn("test"); Mockito.when(request.getHeader(eq(RequestUtil.CLIENT_APPNAME_HEADER))).thenReturn("test");
assertEquals("test", RequestUtil.getAppName(request)); assertEquals("test", RequestUtil.getAppName(request));
} }
@Test @Test
void testGetSrcUserNameV1() { void testGetSrcUserNameFromContext() {
IdentityContext identityContext = new IdentityContext();
identityContext.setParameter(com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID, "test");
RequestContextHolder.getContext().getAuthContext().setIdentityContext(identityContext);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpSession session = Mockito.mock(HttpSession.class);
Mockito.when(request.getSession()).thenReturn(session);
Mockito.when(session.getAttribute(eq(com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID))).thenReturn("test");
assertEquals("test", RequestUtil.getSrcUserName(request)); assertEquals("test", RequestUtil.getSrcUserName(request));
} }
@Test @Test
void testGetSrcUserNameV2() { void testGetSrcUserNameFromRequest() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpSession session = Mockito.mock(HttpSession.class);
Mockito.when(request.getSession()).thenReturn(session);
Mockito.when(session.getAttribute(eq(com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID))).thenReturn(null);
Mockito.when(request.getParameter(eq(Constants.USERNAME))).thenReturn("parameterName"); Mockito.when(request.getParameter(eq(Constants.USERNAME))).thenReturn("parameterName");
assertEquals("parameterName", RequestUtil.getSrcUserName(request)); assertEquals("parameterName", RequestUtil.getSrcUserName(request));
} }
} }

View File

@ -22,6 +22,8 @@ import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.code.ControllerMethodsCache; import com.alibaba.nacos.core.code.ControllerMethodsCache;
import com.alibaba.nacos.core.context.RequestContext;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.core.utils.WebUtils;
import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.IdentityContext;
@ -120,11 +122,16 @@ public class AuthFilter implements Filter {
Resource resource = protocolAuthService.parseResource(req, secured); Resource resource = protocolAuthService.parseResource(req, secured);
IdentityContext identityContext = protocolAuthService.parseIdentity(req); IdentityContext identityContext = protocolAuthService.parseIdentity(req);
boolean result = protocolAuthService.validateIdentity(identityContext, resource); boolean result = protocolAuthService.validateIdentity(identityContext, resource);
RequestContext requestContext = RequestContextHolder.getContext();
requestContext.getAuthContext().setIdentityContext(identityContext);
requestContext.getAuthContext().setResource(resource);
if (null == requestContext.getAuthContext().getAuthResult()) {
requestContext.getAuthContext().setAuthResult(result);
}
if (!result) { if (!result) {
// TODO Get reason of failure // TODO Get reason of failure
throw new AccessException("Validate Identity failed."); throw new AccessException("Validate Identity failed.");
} }
injectIdentityId(req, identityContext);
String action = secured.action().toString(); String action = secured.action().toString();
result = protocolAuthService.validateAuthority(identityContext, new Permission(resource, action)); result = protocolAuthService.validateAuthority(identityContext, new Permission(resource, action));
if (!result) { if (!result) {
@ -146,21 +153,4 @@ public class AuthFilter implements Filter {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server failed, " + e.getMessage()); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server failed, " + e.getMessage());
} }
} }
/**
* Set identity id to request session, make sure some actual logic can get identity information.
*
* <p>May be replaced with whole identityContext.
*
* @param request http request
* @param identityContext identity context
*/
private void injectIdentityId(HttpServletRequest request, IdentityContext identityContext) {
String identityId = identityContext.getParameter(
com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID, StringUtils.EMPTY);
request.getSession()
.setAttribute(com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID, identityId);
request.getSession().setAttribute(com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_CONTEXT,
identityContext);
}
} }

View File

@ -24,6 +24,8 @@ import com.alibaba.nacos.auth.GrpcProtocolAuthService;
import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.config.AuthConfigs; import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.core.context.RequestContext;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.remote.AbstractRequestFilter; import com.alibaba.nacos.core.remote.AbstractRequestFilter;
import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.core.utils.Loggers;
import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.IdentityContext;
@ -75,6 +77,12 @@ public class RemoteRequestAuthFilter extends AbstractRequestFilter {
Resource resource = protocolAuthService.parseResource(request, secured); Resource resource = protocolAuthService.parseResource(request, secured);
IdentityContext identityContext = protocolAuthService.parseIdentity(request); IdentityContext identityContext = protocolAuthService.parseIdentity(request);
boolean result = protocolAuthService.validateIdentity(identityContext, resource); boolean result = protocolAuthService.validateIdentity(identityContext, resource);
RequestContext requestContext = RequestContextHolder.getContext();
requestContext.getAuthContext().setIdentityContext(identityContext);
requestContext.getAuthContext().setResource(resource);
if (null == requestContext.getAuthContext().getAuthResult()) {
requestContext.getAuthContext().setAuthResult(result);
}
if (!result) { if (!result) {
// TODO Get reason of failure // TODO Get reason of failure
throw new AccessException("Validate Identity failed."); throw new AccessException("Validate Identity failed.");

View File

@ -0,0 +1,96 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context;
import com.alibaba.nacos.core.context.addition.AuthContext;
import com.alibaba.nacos.core.context.addition.BasicContext;
import com.alibaba.nacos.core.context.addition.EngineContext;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Nacos request context.
*
* @author xiweng.yy
*/
public class RequestContext {
/**
* Optional, the request id.
* <ul>
* <li>For HTTP request, the id not usage, will generate automatically.</li>
* <li>For GRPC, the id is same with real request id.</li>
* </ul>
*/
private String requestId;
/**
* Request start timestamp.
*/
private final long requestTimestamp;
private final BasicContext basicContext;
private final EngineContext engineContext;
private final AuthContext authContext;
private final Map<String, Object> extensionContexts;
RequestContext(long requestTimestamp) {
this.requestId = UUID.randomUUID().toString();
this.requestTimestamp = requestTimestamp;
this.basicContext = new BasicContext();
this.engineContext = new EngineContext();
this.authContext = new AuthContext();
this.extensionContexts = new HashMap<>(1);
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public String getRequestId() {
return requestId;
}
public long getRequestTimestamp() {
return requestTimestamp;
}
public BasicContext getBasicContext() {
return basicContext;
}
public EngineContext getEngineContext() {
return engineContext;
}
public AuthContext getAuthContext() {
return authContext;
}
public Object getExtensionContext(String key) {
return extensionContexts.get(key);
}
public void addExtensionContext(String key, Object value) {
extensionContexts.put(key, value);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context;
import java.util.function.Supplier;
/**
* Holder for request context for each worker thread.
*
* @author xiweng.yy
*/
public class RequestContextHolder {
private static final Supplier<RequestContext> REQUEST_CONTEXT_FACTORY = () -> {
long requestTimestamp = System.currentTimeMillis();
return new RequestContext(requestTimestamp);
};
private static final ThreadLocal<RequestContext> CONTEXT_HOLDER = ThreadLocal.withInitial(REQUEST_CONTEXT_FACTORY);
public static RequestContext getContext() {
return CONTEXT_HOLDER.get();
}
public static void removeContext() {
CONTEXT_HOLDER.remove();
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.addition;
/**
* Nacos request address information context.
*
* @author xiweng.yy
*/
public class AddressContext {
/**
* Request source ip, it's the ip of the client, most situations are same with remoteIp.
*/
private String sourceIp;
/**
* Request source port, it's the port of the client, most situations are same with remoteIp.
*/
private int sourcePort;
/**
* Request connection ip, it should be got from the socket, which means the ip seen by nacos server.
*/
private String remoteIp;
/**
* Request connection port, it should be got from the socket, which means the port seen by nacos server.
*/
private int remotePort;
/**
* Request host, it's the host of the client, nullable when can't get it from request or connection.
*/
private String host;
public String getSourceIp() {
return sourceIp;
}
public void setSourceIp(String sourceIp) {
this.sourceIp = sourceIp;
}
public int getSourcePort() {
return sourcePort;
}
public void setSourcePort(int sourcePort) {
this.sourcePort = sourcePort;
}
public String getRemoteIp() {
return remoteIp;
}
public void setRemoteIp(String remoteIp) {
this.remoteIp = remoteIp;
}
public int getRemotePort() {
return remotePort;
}
public void setRemotePort(int remotePort) {
this.remotePort = remotePort;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.addition;
import com.alibaba.nacos.plugin.auth.api.IdentityContext;
import com.alibaba.nacos.plugin.auth.api.Resource;
/**
* Nacos auth context, store and transport some auth plugin information to handler or trace log.
*
* @author xiweng.yy
*/
public class AuthContext {
private IdentityContext identityContext;
private Resource resource;
/**
* Auth result, default is {@code true} or {@code false}.
*
* <p>TODO with more auth result by auth plugin.
*/
private Object authResult;
public IdentityContext getIdentityContext() {
return identityContext;
}
public void setIdentityContext(IdentityContext identityContext) {
this.identityContext = identityContext;
}
public Resource getResource() {
return resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
public Object getAuthResult() {
return authResult;
}
public void setAuthResult(Object authResult) {
this.authResult = authResult;
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.addition;
import com.alibaba.nacos.api.common.Constants;
/**
* Nacos request basic information context.
*
* @author xiweng.yy
*/
public class BasicContext {
private static final String DEFAULT_APP = "unknown";
public static final String HTTP_PROTOCOL = "HTTP";
public static final String GRPC_PROTOCOL = "GRPC";
private final AddressContext addressContext;
/**
* Request user agent, such as Nacos-Java-client:v2.4.0
*/
private String userAgent;
/**
* Request protocol type, HTTP or GRPC and so on.
*/
private String requestProtocol;
/**
* Request target.
* <ul>
* <li>For HTTP protocol it should be `${Method} ${URI}`, such as `POST /v2/ns/instance`</li>
* <li>For GRPC protocol, it should be `${requestClass}`, such as `InstanceRequest`</li>
* </ul>
*/
private String requestTarget;
/**
* Optional, mark the app name of the request from when client set app name, default `unknown`.
*/
private String app;
/**
* Optional, mark the encoding of the request from when client set encoding, default `UTF-8`.
*/
private String encoding;
public BasicContext() {
this.addressContext = new AddressContext();
this.app = DEFAULT_APP;
this.encoding = Constants.ENCODE;
}
public AddressContext getAddressContext() {
return addressContext;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public String getRequestProtocol() {
return requestProtocol;
}
public void setRequestProtocol(String requestProtocol) {
this.requestProtocol = requestProtocol;
}
public String getRequestTarget() {
return requestTarget;
}
public void setRequestTarget(String requestTarget) {
this.requestTarget = requestTarget;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public String getEncoding() {
return encoding;
}
public void setEncoding(String encoding) {
this.encoding = encoding;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.addition;
import com.alibaba.nacos.common.utils.VersionUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Nacos engine context, to store some environment and engine information context. Such as version or system information.
*
* @author xiweng.yy
*/
public class EngineContext {
/**
* Nacos server version, such as v2.4.0.
*/
private String version;
private final Map<String, String> contexts;
public EngineContext() {
version = VersionUtils.version;
contexts = new HashMap<>(1);
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getContext(String key) {
return contexts.get(key);
}
public String getContext(String key, String defaultValue) {
return contexts.getOrDefault(key, defaultValue);
}
public void setContext(String key, String value) {
contexts.put(key, value);
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.remote;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Spring Configuration for request context of HTTP.
*
* @author xiweng.yy
*/
@Configuration
public class HttpRequestContextConfig {
@Bean
public FilterRegistrationBean<HttpRequestContextFilter> requestContextFilterRegistration(
HttpRequestContextFilter requestContextFilter) {
FilterRegistrationBean<HttpRequestContextFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(requestContextFilter);
registration.addUrlPatterns("/*");
registration.setName("nacosRequestContextFilter");
registration.setOrder(Integer.MIN_VALUE);
return registration;
}
@Bean
public HttpRequestContextFilter nacosRequestContextFilter() {
return new HttpRequestContextFilter();
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.remote;
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.context.RequestContext;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.context.addition.BasicContext;
import com.alibaba.nacos.core.utils.WebUtils;
import org.apache.http.HttpHeaders;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import static com.alibaba.nacos.api.common.Constants.CLIENT_APPNAME_HEADER;
/**
* The Filter to add request context for HTTP protocol.
*
* @author xiweng.yy
*/
public class HttpRequestContextFilter implements Filter {
private static final String PATTERN_REQUEST_TARGET = "%s %s";
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
RequestContext requestContext = RequestContextHolder.getContext();
try {
requestContext.getBasicContext().setRequestProtocol(BasicContext.HTTP_PROTOCOL);
HttpServletRequest request = (HttpServletRequest) servletRequest;
setRequestTarget(request, requestContext);
setEncoding(request, requestContext);
setAddressContext(request, requestContext);
setOtherBasicContext(request, requestContext);
filterChain.doFilter(servletRequest, servletResponse);
} finally {
RequestContextHolder.removeContext();
}
}
private void setRequestTarget(HttpServletRequest request, RequestContext requestContext) {
String uri = request.getRequestURI();
String method = request.getMethod();
requestContext.getBasicContext().setRequestTarget(String.format(PATTERN_REQUEST_TARGET, method, uri));
}
private void setEncoding(HttpServletRequest request, RequestContext requestContext) {
String encoding = request.getCharacterEncoding();
if (StringUtils.isNotBlank(encoding)) {
requestContext.getBasicContext().setEncoding(encoding);
}
}
private void setAddressContext(HttpServletRequest request, RequestContext requestContext) {
String remoteAddress = request.getRemoteAddr();
int remotePort = request.getRemotePort();
String sourceIp = WebUtils.getRemoteIp(request);
String host = request.getHeader(HttpHeaders.HOST);
requestContext.getBasicContext().getAddressContext().setRemoteIp(remoteAddress);
requestContext.getBasicContext().getAddressContext().setRemotePort(remotePort);
requestContext.getBasicContext().getAddressContext().setSourceIp(sourceIp);
requestContext.getBasicContext().getAddressContext().setHost(host);
}
private void setOtherBasicContext(HttpServletRequest request, RequestContext requestContext) {
String userAgent = WebUtils.getUserAgent(request);
requestContext.getBasicContext().setUserAgent(userAgent);
String app = getAppName(request);
if (StringUtils.isNotBlank(app)) {
requestContext.getBasicContext().setApp(app);
}
}
private String getAppName(HttpServletRequest request) {
String app = request.getHeader(HttpHeaderConsts.APP_FILED);
if (StringUtils.isBlank(app)) {
app = request.getHeader(CLIENT_APPNAME_HEADER);
}
return app;
}
}

View File

@ -35,7 +35,7 @@ public class CheckConfiguration {
FilterRegistrationBean<ParamCheckerFilter> registration = new FilterRegistrationBean<>(); FilterRegistrationBean<ParamCheckerFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(checkerFilter); registration.setFilter(checkerFilter);
registration.addUrlPatterns("/*"); registration.addUrlPatterns("/*");
registration.setName("checkerFilter"); registration.setName("requestContextFilter");
registration.setOrder(8); registration.setOrder(8);
return registration; return registration;
} }

View File

@ -197,6 +197,24 @@ public class ConnectionMeta {
this.clientIp = clientIp; this.clientIp = clientIp;
} }
/**
* Getter method for property <tt>remoteIp</tt>.
*
* @return property value of remoteIp
*/
public String getRemoteIp() {
return remoteIp;
}
/**
* Getter method for property <tt>remotePort</tt>.
*
* @return property value of remotePort
*/
public int getRemotePort() {
return remotePort;
}
/** /**
* Getter method for property <tt>connectionId</tt>. * Getter method for property <tt>connectionId</tt>.
* *

View File

@ -27,7 +27,12 @@ import com.alibaba.nacos.api.remote.response.ErrorResponse;
import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.Response;
import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.api.remote.response.ResponseCode;
import com.alibaba.nacos.api.remote.response.ServerCheckResponse; import com.alibaba.nacos.api.remote.response.ServerCheckResponse;
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
import com.alibaba.nacos.common.remote.client.grpc.GrpcUtils; import com.alibaba.nacos.common.remote.client.grpc.GrpcUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.context.RequestContext;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.context.addition.BasicContext;
import com.alibaba.nacos.core.monitor.MetricsMonitor; import com.alibaba.nacos.core.monitor.MetricsMonitor;
import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.Connection;
import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.ConnectionManager;
@ -186,6 +191,7 @@ public class GrpcRequestAcceptor extends RequestGrpc.RequestImplBase {
requestMeta.setLabels(connection.getMetaInfo().getLabels()); requestMeta.setLabels(connection.getMetaInfo().getLabels());
requestMeta.setAbilityTable(connection.getAbilityTable()); requestMeta.setAbilityTable(connection.getAbilityTable());
connectionManager.refreshActiveTime(requestMeta.getConnectionId()); connectionManager.refreshActiveTime(requestMeta.getConnectionId());
prepareRequestContext(request, requestMeta, connection);
Response response = requestHandler.handleRequest(request, requestMeta); Response response = requestHandler.handleRequest(request, requestMeta);
Payload payloadResponse = GrpcUtils.convert(response); Payload payloadResponse = GrpcUtils.convert(response);
traceIfNecessary(payloadResponse, false); traceIfNecessary(payloadResponse, false);
@ -212,8 +218,26 @@ public class GrpcRequestAcceptor extends RequestGrpc.RequestImplBase {
responseObserver.onCompleted(); responseObserver.onCompleted();
MetricsMonitor.recordGrpcRequestEvent(type, false, MetricsMonitor.recordGrpcRequestEvent(type, false,
ResponseCode.FAIL.getCode(), e.getClass().getSimpleName(), request.getModule(), System.nanoTime() - startTime); ResponseCode.FAIL.getCode(), e.getClass().getSimpleName(), request.getModule(), System.nanoTime() - startTime);
} finally {
RequestContextHolder.removeContext();
} }
} }
private void prepareRequestContext(Request request, RequestMeta requestMeta, Connection connection) {
RequestContext requestContext = RequestContextHolder.getContext();
requestContext.setRequestId(request.getRequestId());
requestContext.getBasicContext().setUserAgent(requestMeta.getClientVersion());
requestContext.getBasicContext().setRequestProtocol(BasicContext.GRPC_PROTOCOL);
requestContext.getBasicContext().setRequestTarget(request.getClass().getSimpleName());
String app = connection.getMetaInfo().getAppName();
if (StringUtils.isBlank(app)) {
app = request.getHeader(HttpHeaderConsts.APP_FILED, "unknown");
}
requestContext.getBasicContext().setApp(app);
requestContext.getBasicContext().getAddressContext().setRemoteIp(connection.getMetaInfo().getRemoteIp());
requestContext.getBasicContext().getAddressContext().setRemotePort(connection.getMetaInfo().getRemotePort());
requestContext.getBasicContext().getAddressContext().setSourceIp(connection.getMetaInfo().getClientIp());
}
} }

View File

@ -54,6 +54,12 @@ public class WebUtils {
private static final String TMP_SUFFIX = ".tmp"; private static final String TMP_SUFFIX = ".tmp";
private static final String X_REAL_IP = "X-Real-IP";
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
private static final String X_FORWARDED_FOR_SPLIT_SYMBOL = ",";
/** /**
* get target value from parameterMap, if not found will throw {@link IllegalArgumentException}. * get target value from parameterMap, if not found will throw {@link IllegalArgumentException}.
* *
@ -248,4 +254,22 @@ public class WebUtils {
deferredResult.setResult(t); deferredResult.setResult(t);
}); });
} }
/**
* get real client ip
*
* <p>first use X-Forwarded-For header https://zh.wikipedia.org/wiki/X-Forwarded-For next nginx X-Real-IP last
* {@link HttpServletRequest#getRemoteAddr()}
*
* @param request {@link HttpServletRequest}
* @return remote ip address.
*/
public static String getRemoteIp(HttpServletRequest request) {
String xForwardedFor = request.getHeader(X_FORWARDED_FOR);
if (!StringUtils.isBlank(xForwardedFor)) {
return xForwardedFor.split(X_FORWARDED_FOR_SPLIT_SYMBOL)[0].trim();
}
String nginxHeader = request.getHeader(X_REAL_IP);
return StringUtils.isBlank(nginxHeader) ? request.getRemoteAddr() : nginxHeader;
}
} }

View File

@ -21,7 +21,9 @@ import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.config.AuthConfigs; import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.constant.HttpHeaderConsts;
import com.alibaba.nacos.core.code.ControllerMethodsCache; import com.alibaba.nacos.core.code.ControllerMethodsCache;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.sys.env.Constants; import com.alibaba.nacos.sys.env.Constants;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@ -58,6 +60,11 @@ class AuthFilterTest {
@Mock @Mock
private ControllerMethodsCache methodsCache; private ControllerMethodsCache methodsCache;
@AfterEach
void tearDown() {
RequestContextHolder.removeContext();
}
@Test @Test
void testDoFilter() { void testDoFilter() {
try { try {

View File

@ -24,7 +24,9 @@ import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.Response;
import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.auth.config.AuthConfigs; import com.alibaba.nacos.auth.config.AuthConfigs;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.core.remote.RequestHandler;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@ -50,6 +52,11 @@ class RemoteRequestAuthFilterTest {
@Mock @Mock
private AuthConfigs authConfigs; private AuthConfigs authConfigs;
@AfterEach
void tearDown() {
RequestContextHolder.removeContext();
}
@Test @Test
void testFilter() { void testFilter() {
Mockito.when(authConfigs.isAuthEnabled()).thenReturn(true); Mockito.when(authConfigs.isAuthEnabled()).thenReturn(true);

View File

@ -0,0 +1,43 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class RequestContextHolderTest {
@AfterEach
void tearDown() {
RequestContextHolder.removeContext();
}
@Test
void testGetContext() {
long timestamp = System.currentTimeMillis();
RequestContext requestContext = RequestContextHolder.getContext();
assertNotNull(requestContext);
assertNotNull(requestContext.getRequestId());
assertTrue(requestContext.getRequestTimestamp() >= timestamp);
assertNotNull(requestContext.getBasicContext());
assertNotNull(requestContext.getEngineContext());
assertNotNull(requestContext.getAuthContext());
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
class RequestContextTest {
long requestTimestamp;
RequestContext requestContext;
@BeforeEach
void setUp() {
requestTimestamp = System.currentTimeMillis();
requestContext = new RequestContext(requestTimestamp);
}
@Test
public void testGetRequestId() {
String requestId = requestContext.getRequestId();
assertNotNull(requestId);
assertNotNull(UUID.fromString(requestId));
requestContext.setRequestId("testRequestId");
assertEquals("testRequestId", requestContext.getRequestId());
}
@Test
public void testGetRequestTimestamp() {
assertEquals(requestTimestamp, requestContext.getRequestTimestamp());
}
@Test
public void testSetExtensionContext() {
assertNull(requestContext.getExtensionContext("testKey"));
requestContext.addExtensionContext("testKey", "testValue");
assertEquals("testValue", requestContext.getExtensionContext("testKey"));
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.addition;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
class AddressContextTest {
AddressContext addressContext;
@BeforeEach
void setUp() {
addressContext = new AddressContext();
}
@Test
void testSetSourceIp() {
assertNull(addressContext.getSourceIp());
addressContext.setSourceIp("127.0.0.1");
assertEquals("127.0.0.1", addressContext.getSourceIp());
}
@Test
void testSetSourcePort() {
assertEquals(0, addressContext.getSourcePort());
addressContext.setSourcePort(8080);
assertEquals(8080, addressContext.getSourcePort());
}
@Test
void testSetRemoteIp() {
assertNull(addressContext.getRemoteIp());
addressContext.setRemoteIp("127.0.0.1");
assertEquals("127.0.0.1", addressContext.getRemoteIp());
}
@Test
void testSetRemotePort() {
assertEquals(0, addressContext.getRemotePort());
addressContext.setRemotePort(8080);
assertEquals(8080, addressContext.getRemotePort());
}
@Test
void testSetHost() {
assertNull(addressContext.getHost());
addressContext.setHost("127.0.0.1");
assertEquals("127.0.0.1", addressContext.getHost());
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.addition;
import com.alibaba.nacos.plugin.auth.api.IdentityContext;
import com.alibaba.nacos.plugin.auth.api.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
class AuthContextTest {
AuthContext authContext;
@BeforeEach
void setUp() {
authContext = new AuthContext();
}
@Test
void testSetIdentityContext() {
IdentityContext identityContext = new IdentityContext();
assertNull(authContext.getIdentityContext());
authContext.setIdentityContext(identityContext);
assertSame(identityContext, authContext.getIdentityContext());
}
@Test
void testSetResource() {
Resource resource = new Resource("", "", "", "", new Properties());
assertNull(authContext.getResource());
authContext.setResource(resource);
assertSame(resource, authContext.getResource());
}
@Test
void testSetAuthResult() {
assertNull(authContext.getAuthResult());
authContext.setAuthResult(true);
assertTrue((boolean) authContext.getAuthResult());
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.addition;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.naming.remote.request.InstanceRequest;
import com.alibaba.nacos.common.utils.VersionUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
class BasicContextTest {
BasicContext basicContext;
@BeforeEach
void setUp() {
basicContext = new BasicContext();
}
@Test
void testGetAddressContext() {
assertNotNull(basicContext.getAddressContext());
}
@Test
void testSetUserAgent() {
assertNull(basicContext.getUserAgent());
basicContext.setUserAgent(VersionUtils.getFullClientVersion());
assertEquals(VersionUtils.getFullClientVersion(), basicContext.getUserAgent());
}
@Test
void testSetRequestProtocol() {
assertNull(basicContext.getRequestProtocol());
basicContext.setRequestProtocol(BasicContext.HTTP_PROTOCOL);
assertEquals(BasicContext.HTTP_PROTOCOL, basicContext.getRequestProtocol());
basicContext.setRequestProtocol(BasicContext.GRPC_PROTOCOL);
assertEquals(BasicContext.GRPC_PROTOCOL, basicContext.getRequestProtocol());
}
@Test
void testSetRequestTarget() {
assertNull(basicContext.getRequestTarget());
basicContext.setRequestTarget("POST /v2/ns/instance");
assertEquals("POST /v2/ns/instance", basicContext.getRequestTarget());
basicContext.setRequestTarget(InstanceRequest.class.getSimpleName());
assertEquals(InstanceRequest.class.getSimpleName(), basicContext.getRequestTarget());
}
@Test
void testSetApp() {
assertEquals("unknown", basicContext.getApp());
basicContext.setApp("testApp");
assertEquals("testApp", basicContext.getApp());
}
@Test
void testSetEncoding() {
assertEquals(Constants.ENCODE, basicContext.getEncoding());
basicContext.setEncoding("GBK");
assertEquals("GBK", basicContext.getEncoding());
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.addition;
import com.alibaba.nacos.common.utils.VersionUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
class EngineContextTest {
EngineContext engineContext;
@BeforeEach
void setUp() {
engineContext = new EngineContext();
}
@Test
void testSetVersion() {
assertEquals(VersionUtils.version, engineContext.getVersion());
engineContext.setVersion("testVersion");
assertEquals("testVersion", engineContext.getVersion());
}
@Test
void testSetContext() {
assertNull(engineContext.getContext("test"));
assertEquals("default", engineContext.getContext("test", "default"));
engineContext.setContext("test", "testValue");
assertEquals("testValue", engineContext.getContext("test"));
assertEquals("testValue", engineContext.getContext("test", "default"));
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.remote;
import org.junit.jupiter.api.Test;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import static org.junit.jupiter.api.Assertions.assertEquals;
class HttpRequestContextConfigTest {
@Test
void testRequestContextFilterRegistration() {
HttpRequestContextConfig contextConfig = new HttpRequestContextConfig();
HttpRequestContextFilter filter = contextConfig.nacosRequestContextFilter();
FilterRegistrationBean<HttpRequestContextFilter> actual = contextConfig.requestContextFilterRegistration(
filter);
assertEquals(filter, actual.getFilter());
assertEquals("/*", actual.getUrlPatterns().iterator().next());
assertEquals(Integer.MIN_VALUE, actual.getOrder());
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.core.context.remote;
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
import com.alibaba.nacos.core.context.RequestContext;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.context.addition.BasicContext;
import org.apache.http.HttpHeaders;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opentest4j.AssertionFailedError;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class HttpRequestContextFilterTest {
@Mock
private MockHttpServletRequest servletRequest;
@Mock
private MockHttpServletResponse servletResponse;
@Mock
private Servlet servlet;
HttpRequestContextFilter filter;
@BeforeEach
void setUp() {
filter = new HttpRequestContextFilter();
RequestContextHolder.getContext();
when(servletRequest.getHeader(HttpHeaders.HOST)).thenReturn("localhost");
when(servletRequest.getHeader(HttpHeaders.USER_AGENT)).thenReturn("Nacos-Java-Client:v1.4.7");
when(servletRequest.getHeader(HttpHeaderConsts.APP_FILED)).thenReturn("testApp");
when(servletRequest.getMethod()).thenReturn("GET");
when(servletRequest.getRequestURI()).thenReturn("/test/path");
when(servletRequest.getCharacterEncoding()).thenReturn("GBK");
when(servletRequest.getRemoteAddr()).thenReturn("1.1.1.1");
when(servletRequest.getRemotePort()).thenReturn(3306);
when(servletRequest.getHeader("X-Forwarded-For")).thenReturn("2.2.2.2");
}
@AfterEach
void tearDown() {
RequestContextHolder.removeContext();
}
@Test
public void testDoFilterSetsCorrectContextValues() throws Exception {
MockNextFilter nextFilter = new MockNextFilter("testApp", "GBK");
filter.doFilter(servletRequest, servletResponse, new MockFilterChain(servlet, nextFilter));
if (null != nextFilter.error) {
throw nextFilter.error;
}
}
@Test
public void testDoFilterWithoutEncoding() throws Exception {
when(servletRequest.getCharacterEncoding()).thenReturn("");
MockNextFilter nextFilter = new MockNextFilter("testApp", "UTF-8");
filter.doFilter(servletRequest, servletResponse, new MockFilterChain(servlet, nextFilter));
if (null != nextFilter.error) {
throw nextFilter.error;
}
}
@Test
public void testGetAppNameWithFallback() throws Exception {
when(servletRequest.getHeader(HttpHeaderConsts.APP_FILED)).thenReturn("");
MockNextFilter nextFilter = new MockNextFilter("unknown", "GBK");
filter.doFilter(servletRequest, servletResponse, new MockFilterChain(servlet, nextFilter));
if (null != nextFilter.error) {
throw nextFilter.error;
}
}
private static class MockNextFilter implements Filter {
private final String app;
private final String encoding;
AssertionError error;
public MockNextFilter(String app, String encoding) {
this.app = app;
this.encoding = encoding;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
RequestContext requestContext = RequestContextHolder.getContext();
BasicContext basicContext = requestContext.getBasicContext();
assertEquals("GET /test/path", basicContext.getRequestTarget());
assertEquals(encoding, basicContext.getEncoding());
assertEquals("Nacos-Java-Client:v1.4.7", basicContext.getUserAgent());
assertEquals(app, basicContext.getApp());
assertEquals("1.1.1.1", basicContext.getAddressContext().getRemoteIp());
assertEquals("2.2.2.2", basicContext.getAddressContext().getSourceIp());
assertEquals(3306, basicContext.getAddressContext().getRemotePort());
assertEquals("localhost", basicContext.getAddressContext().getHost());
} catch (AssertionFailedError error) {
this.error = error;
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
}

View File

@ -18,12 +18,15 @@ package com.alibaba.nacos.core.utils;
import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.constant.HttpHeaderConsts;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
/** /**
* {@link WebUtils} unit tests. * {@link WebUtils} unit tests.
@ -33,6 +36,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/ */
class WebUtilsTest { class WebUtilsTest {
private static final String X_REAL_IP = "X-Real-IP";
private static final String X_FORWARDED_FOR = "X-Forwarded-For";
@Test @Test
void testRequired() { void testRequired() {
final String key = "key"; final String key = "key";
@ -80,4 +87,27 @@ class WebUtilsTest {
servletRequest.addHeader(HttpHeaderConsts.ACCEPT_ENCODING, "gzip, deflate, br"); servletRequest.addHeader(HttpHeaderConsts.ACCEPT_ENCODING, "gzip, deflate, br");
assertEquals("gzip", WebUtils.getAcceptEncoding(servletRequest)); assertEquals("gzip", WebUtils.getAcceptEncoding(servletRequest));
} }
@Test
void testGetRemoteIp() {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Mockito.when(request.getRemoteAddr()).thenReturn("127.0.0.1");
assertEquals("127.0.0.1", WebUtils.getRemoteIp(request));
Mockito.when(request.getHeader(eq(X_REAL_IP))).thenReturn("127.0.0.2");
assertEquals("127.0.0.2", WebUtils.getRemoteIp(request));
Mockito.when(request.getHeader(eq(X_FORWARDED_FOR))).thenReturn("127.0.0.3");
assertEquals("127.0.0.3", WebUtils.getRemoteIp(request));
Mockito.when(request.getHeader(eq(X_FORWARDED_FOR))).thenReturn("127.0.0.3, 127.0.0.4");
assertEquals("127.0.0.3", WebUtils.getRemoteIp(request));
Mockito.when(request.getHeader(eq(X_FORWARDED_FOR))).thenReturn("");
assertEquals("127.0.0.2", WebUtils.getRemoteIp(request));
Mockito.when(request.getHeader(eq(X_REAL_IP))).thenReturn("");
assertEquals("127.0.0.1", WebUtils.getRemoteIp(request));
}
} }

View File

@ -51,6 +51,7 @@ import com.alibaba.nacos.naming.pojo.Subscriber;
import com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder; import com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder;
import com.alibaba.nacos.naming.pojo.instance.HttpRequestInstanceBuilder; import com.alibaba.nacos.naming.pojo.instance.HttpRequestInstanceBuilder;
import com.alibaba.nacos.naming.pojo.instance.InstanceExtensionHandler; import com.alibaba.nacos.naming.pojo.instance.InstanceExtensionHandler;
import com.alibaba.nacos.naming.utils.NamingRequestUtil;
import com.alibaba.nacos.naming.web.CanDistro; import com.alibaba.nacos.naming.web.CanDistro;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
@ -120,7 +121,8 @@ public class InstanceController {
.setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build();
getInstanceOperator().registerInstance(namespaceId, serviceName, instance); getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), "", false, namespaceId, NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(),
NamingRequestUtil.getSourceIpForHttpRequest(request), false, namespaceId,
NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), instance.getIp(), NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), instance.getIp(),
instance.getPort())); instance.getPort()));
return "ok"; return "ok";
@ -145,9 +147,10 @@ public class InstanceController {
NamingUtils.checkServiceNameFormat(serviceName); NamingUtils.checkServiceNameFormat(serviceName);
getInstanceOperator().removeInstance(namespaceId, serviceName, instance); getInstanceOperator().removeInstance(namespaceId, serviceName, instance);
NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), "", false, NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(),
DeregisterInstanceReason.REQUEST, namespaceId, NamingUtils.getGroupName(serviceName), NamingRequestUtil.getSourceIpForHttpRequest(request), false, DeregisterInstanceReason.REQUEST,
NamingUtils.getServiceName(serviceName), instance.getIp(), instance.getPort())); namespaceId, NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName),
instance.getIp(), instance.getPort()));
return "ok"; return "ok";
} }
@ -169,7 +172,8 @@ public class InstanceController {
Instance instance = HttpRequestInstanceBuilder.newBuilder() Instance instance = HttpRequestInstanceBuilder.newBuilder()
.setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build(); .setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build();
getInstanceOperator().updateInstance(namespaceId, serviceName, instance); getInstanceOperator().updateInstance(namespaceId, serviceName, instance);
NotifyCenter.publishEvent(new UpdateInstanceTraceEvent(System.currentTimeMillis(), "", namespaceId, NotifyCenter.publishEvent(new UpdateInstanceTraceEvent(System.currentTimeMillis(),
NamingRequestUtil.getSourceIpForHttpRequest(request), namespaceId,
NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), instance.getIp(), NamingUtils.getGroupName(serviceName), NamingUtils.getServiceName(serviceName), instance.getIp(),
instance.getPort(), instance.getMetadata())); instance.getPort(), instance.getMetadata()));
return "ok"; return "ok";
@ -267,7 +271,6 @@ public class InstanceController {
return Collections.emptyList(); return Collections.emptyList();
} }
/** /**
* Patch instance. * Patch instance.
* *

View File

@ -56,6 +56,7 @@ import com.alibaba.nacos.naming.paramcheck.NamingInstanceMetadataBatchHttpParamE
import com.alibaba.nacos.naming.pojo.InstanceOperationInfo; import com.alibaba.nacos.naming.pojo.InstanceOperationInfo;
import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.pojo.Subscriber;
import com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder; import com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder;
import com.alibaba.nacos.naming.utils.NamingRequestUtil;
import com.alibaba.nacos.naming.web.CanDistro; import com.alibaba.nacos.naming.web.CanDistro;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
@ -115,9 +116,9 @@ public class InstanceControllerV2 {
instanceServiceV2.registerInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), instanceServiceV2.registerInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm),
instance); instance);
NotifyCenter.publishEvent( NotifyCenter.publishEvent(
new RegisterInstanceTraceEvent(System.currentTimeMillis(), "", false, instanceForm.getNamespaceId(), new RegisterInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIp(), false,
instanceForm.getGroupName(), instanceForm.getServiceName(), instance.getIp(), instanceForm.getNamespaceId(), instanceForm.getGroupName(), instanceForm.getServiceName(),
instance.getPort())); instance.getIp(), instance.getPort()));
return Result.success("ok"); return Result.success("ok");
} }
@ -136,9 +137,10 @@ public class InstanceControllerV2 {
Instance instance = buildInstance(instanceForm); Instance instance = buildInstance(instanceForm);
instanceServiceV2.removeInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), instanceServiceV2.removeInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm),
instance); instance);
NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), "", false, NotifyCenter.publishEvent(
DeregisterInstanceReason.REQUEST, instanceForm.getNamespaceId(), instanceForm.getGroupName(), new DeregisterInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIp(), false,
instanceForm.getServiceName(), instance.getIp(), instance.getPort())); DeregisterInstanceReason.REQUEST, instanceForm.getNamespaceId(), instanceForm.getGroupName(),
instanceForm.getServiceName(), instance.getIp(), instance.getPort()));
return Result.success("ok"); return Result.success("ok");
} }
@ -158,9 +160,9 @@ public class InstanceControllerV2 {
instanceServiceV2.updateInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm), instanceServiceV2.updateInstance(instanceForm.getNamespaceId(), buildCompositeServiceName(instanceForm),
instance); instance);
NotifyCenter.publishEvent( NotifyCenter.publishEvent(
new UpdateInstanceTraceEvent(System.currentTimeMillis(), "", instanceForm.getNamespaceId(), new UpdateInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIp(),
instanceForm.getGroupName(), instanceForm.getServiceName(), instance.getIp(), instanceForm.getNamespaceId(), instanceForm.getGroupName(), instanceForm.getServiceName(),
instance.getPort(), instance.getMetadata())); instance.getIp(), instance.getPort(), instance.getMetadata()));
return Result.success("ok"); return Result.success("ok");
} }
@ -231,7 +233,6 @@ public class InstanceControllerV2 {
return Collections.emptyList(); return Collections.emptyList();
} }
/** /**
* Patch instance. * Patch instance.
* *
@ -463,4 +464,5 @@ public class InstanceControllerV2 {
private String buildCompositeServiceName(InstanceMetadataBatchOperationForm form) { private String buildCompositeServiceName(InstanceMetadataBatchOperationForm form) {
return NamingUtils.getGroupedName(form.getServiceName(), form.getGroupName()); return NamingUtils.getGroupedName(form.getServiceName(), form.getGroupName());
} }
} }

View File

@ -130,8 +130,8 @@ public class InstanceOperatorClientImpl implements InstanceOperator {
throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR,
"service not found, namespace: " + namespaceId + ", service: " + service); "service not found, namespace: " + namespaceId + ", service: " + service);
} }
String metadataId = InstancePublishInfo String metadataId = InstancePublishInfo.genMetadataId(instance.getIp(), instance.getPort(),
.genMetadataId(instance.getIp(), instance.getPort(), instance.getClusterName()); instance.getClusterName());
metadataOperateService.updateInstanceMetadata(service, metadataId, buildMetadata(instance)); metadataOperateService.updateInstanceMetadata(service, metadataId, buildMetadata(instance));
} }
@ -149,8 +149,8 @@ public class InstanceOperatorClientImpl implements InstanceOperator {
Service service = getService(namespaceId, serviceName, true); Service service = getService(namespaceId, serviceName, true);
Instance instance = getInstance(namespaceId, serviceName, patchObject.getCluster(), patchObject.getIp(), Instance instance = getInstance(namespaceId, serviceName, patchObject.getCluster(), patchObject.getIp(),
patchObject.getPort()); patchObject.getPort());
String metadataId = InstancePublishInfo String metadataId = InstancePublishInfo.genMetadataId(instance.getIp(), instance.getPort(),
.genMetadataId(instance.getIp(), instance.getPort(), instance.getClusterName()); instance.getClusterName());
Optional<InstanceMetadata> instanceMetadata = metadataManager.getInstanceMetadata(service, metadataId); Optional<InstanceMetadata> instanceMetadata = metadataManager.getInstanceMetadata(service, metadataId);
InstanceMetadata newMetadata = instanceMetadata.map(this::cloneMetadata).orElseGet(InstanceMetadata::new); InstanceMetadata newMetadata = instanceMetadata.map(this::cloneMetadata).orElseGet(InstanceMetadata::new);
mergeMetadata(newMetadata, patchObject); mergeMetadata(newMetadata, patchObject);
@ -189,8 +189,8 @@ public class InstanceOperatorClientImpl implements InstanceOperator {
} }
ServiceInfo serviceInfo = serviceStorage.getData(service); ServiceInfo serviceInfo = serviceStorage.getData(service);
ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null); ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null);
ServiceInfo result = ServiceUtil ServiceInfo result = ServiceUtil.selectInstancesWithHealthyProtection(serviceInfo, serviceMetadata, cluster,
.selectInstancesWithHealthyProtection(serviceInfo, serviceMetadata, cluster, healthOnly, true, subscriber.getIp()); healthOnly, true, subscriber.getIp());
// adapt for v1.x sdk // adapt for v1.x sdk
result.setName(NamingUtils.getGroupedName(result.getName(), result.getGroupName())); result.setName(NamingUtils.getGroupedName(result.getName(), result.getGroupName()));
return result; return result;
@ -329,12 +329,8 @@ public class InstanceOperatorClientImpl implements InstanceOperator {
private void createIpPortClientIfAbsent(String clientId) { private void createIpPortClientIfAbsent(String clientId) {
if (!clientManager.contains(clientId)) { if (!clientManager.contains(clientId)) {
ClientAttributes clientAttributes; ClientAttributes clientAttributes = ClientAttributesFilter.getCurrentClientAttributes()
if (ClientAttributesFilter.threadLocalClientAttributes.get() != null) { .orElse(new ClientAttributes());
clientAttributes = ClientAttributesFilter.threadLocalClientAttributes.get();
} else {
clientAttributes = new ClientAttributes();
}
clientManager.clientConnected(clientId, clientAttributes); clientManager.clientConnected(clientId, clientAttributes);
} }
} }

View File

@ -33,6 +33,7 @@ import com.alibaba.nacos.core.remote.RequestHandler;
import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl;
import com.alibaba.nacos.naming.utils.InstanceUtil; import com.alibaba.nacos.naming.utils.InstanceUtil;
import com.alibaba.nacos.naming.utils.NamingRequestUtil;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -55,8 +56,8 @@ public class InstanceRequestHandler extends RequestHandler<InstanceRequest, Inst
@Secured(action = ActionTypes.WRITE) @Secured(action = ActionTypes.WRITE)
@ExtractorManager.Extractor(rpcExtractor = InstanceRequestParamExtractor.class) @ExtractorManager.Extractor(rpcExtractor = InstanceRequestParamExtractor.class)
public InstanceResponse handle(InstanceRequest request, RequestMeta meta) throws NacosException { public InstanceResponse handle(InstanceRequest request, RequestMeta meta) throws NacosException {
Service service = Service Service service = Service.newService(request.getNamespace(), request.getGroupName(), request.getServiceName(),
.newService(request.getNamespace(), request.getGroupName(), request.getServiceName(), true); true);
InstanceUtil.setInstanceIdIfEmpty(request.getInstance(), service.getGroupedServiceName()); InstanceUtil.setInstanceIdIfEmpty(request.getInstance(), service.getGroupedServiceName());
switch (request.getType()) { switch (request.getType()) {
case NamingRemoteConstants.REGISTER_INSTANCE: case NamingRemoteConstants.REGISTER_INSTANCE:
@ -73,16 +74,17 @@ public class InstanceRequestHandler extends RequestHandler<InstanceRequest, Inst
throws NacosException { throws NacosException {
clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId()); clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());
NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(),
meta.getClientIp(), true, service.getNamespace(), service.getGroup(), service.getName(), NamingRequestUtil.getSourceIpForGrpcRequest(meta), true, service.getNamespace(), service.getGroup(),
request.getInstance().getIp(), request.getInstance().getPort())); service.getName(), request.getInstance().getIp(), request.getInstance().getPort()));
return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE); return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);
} }
private InstanceResponse deregisterInstance(Service service, InstanceRequest request, RequestMeta meta) { private InstanceResponse deregisterInstance(Service service, InstanceRequest request, RequestMeta meta) {
clientOperationService.deregisterInstance(service, request.getInstance(), meta.getConnectionId()); clientOperationService.deregisterInstance(service, request.getInstance(), meta.getConnectionId());
NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(),
meta.getClientIp(), true, DeregisterInstanceReason.REQUEST, service.getNamespace(), NamingRequestUtil.getSourceIpForGrpcRequest(meta), true, DeregisterInstanceReason.REQUEST,
service.getGroup(), service.getName(), request.getInstance().getIp(), request.getInstance().getPort())); service.getNamespace(), service.getGroup(), service.getName(), request.getInstance().getIp(),
request.getInstance().getPort()));
return new InstanceResponse(NamingRemoteConstants.DE_REGISTER_INSTANCE); return new InstanceResponse(NamingRemoteConstants.DE_REGISTER_INSTANCE);
} }

View File

@ -35,6 +35,7 @@ import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient;
import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl; import com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl;
import com.alibaba.nacos.naming.utils.InstanceUtil; import com.alibaba.nacos.naming.utils.InstanceUtil;
import com.alibaba.nacos.naming.utils.NamingRequestUtil;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -45,13 +46,13 @@ import org.springframework.stereotype.Component;
*/ */
@Component @Component
public class PersistentInstanceRequestHandler extends RequestHandler<PersistentInstanceRequest, InstanceResponse> { public class PersistentInstanceRequestHandler extends RequestHandler<PersistentInstanceRequest, InstanceResponse> {
private final PersistentClientOperationServiceImpl clientOperationService; private final PersistentClientOperationServiceImpl clientOperationService;
public PersistentInstanceRequestHandler(PersistentClientOperationServiceImpl clientOperationService) { public PersistentInstanceRequestHandler(PersistentClientOperationServiceImpl clientOperationService) {
this.clientOperationService = clientOperationService; this.clientOperationService = clientOperationService;
} }
@Override @Override
@TpsControl(pointName = "RemoteNamingInstanceRegisterDeregister", name = "RemoteNamingInstanceRegisterDeregister") @TpsControl(pointName = "RemoteNamingInstanceRegisterDeregister", name = "RemoteNamingInstanceRegisterDeregister")
@Secured(action = ActionTypes.WRITE) @Secured(action = ActionTypes.WRITE)
@ -75,8 +76,9 @@ public class PersistentInstanceRequestHandler extends RequestHandler<PersistentI
Instance instance = request.getInstance(); Instance instance = request.getInstance();
String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), false); String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), false);
clientOperationService.registerInstance(service, instance, clientId); clientOperationService.registerInstance(service, instance, clientId);
NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), meta.getClientIp(), true, NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(),
service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort())); NamingRequestUtil.getSourceIpForGrpcRequest(meta), true, service.getNamespace(), service.getGroup(),
service.getName(), instance.getIp(), instance.getPort()));
return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE); return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);
} }
@ -84,9 +86,9 @@ public class PersistentInstanceRequestHandler extends RequestHandler<PersistentI
Instance instance = request.getInstance(); Instance instance = request.getInstance();
String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), false); String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), false);
clientOperationService.deregisterInstance(service, instance, clientId); clientOperationService.deregisterInstance(service, instance, clientId);
NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), meta.getClientIp(), true, NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(),
DeregisterInstanceReason.REQUEST, service.getNamespace(), service.getGroup(), service.getName(), NamingRequestUtil.getSourceIpForGrpcRequest(meta), true, DeregisterInstanceReason.REQUEST,
instance.getIp(), instance.getPort())); service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort()));
return new InstanceResponse(NamingRemoteConstants.DE_REGISTER_INSTANCE); return new InstanceResponse(NamingRemoteConstants.DE_REGISTER_INSTANCE);
} }
} }

View File

@ -30,6 +30,7 @@ import com.alibaba.nacos.naming.core.v2.index.ServiceStorage;
import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager;
import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.utils.NamingRequestUtil;
import com.alibaba.nacos.naming.utils.ServiceUtil; import com.alibaba.nacos.naming.utils.ServiceUtil;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -65,7 +66,7 @@ public class ServiceQueryRequestHandler extends RequestHandler<ServiceQueryReque
ServiceInfo result = serviceStorage.getData(service); ServiceInfo result = serviceStorage.getData(service);
ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null); ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(null);
result = ServiceUtil.selectInstancesWithHealthyProtection(result, serviceMetadata, cluster, healthyOnly, true, result = ServiceUtil.selectInstancesWithHealthyProtection(result, serviceMetadata, cluster, healthyOnly, true,
meta.getClientIp()); NamingRequestUtil.getSourceIpForGrpcRequest(meta));
return QueryServiceResponse.buildSuccessResponse(result); return QueryServiceResponse.buildSuccessResponse(result);
} }
} }

View File

@ -27,6 +27,7 @@ import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.trace.event.naming.SubscribeServiceTraceEvent; import com.alibaba.nacos.common.trace.event.naming.SubscribeServiceTraceEvent;
import com.alibaba.nacos.common.trace.event.naming.UnsubscribeServiceTraceEvent; import com.alibaba.nacos.common.trace.event.naming.UnsubscribeServiceTraceEvent;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.control.TpsControl;
import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.ExtractorManager;
import com.alibaba.nacos.core.paramcheck.impl.SubscribeServiceRequestParamExtractor; import com.alibaba.nacos.core.paramcheck.impl.SubscribeServiceRequestParamExtractor;
@ -36,6 +37,7 @@ import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager;
import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl;
import com.alibaba.nacos.naming.pojo.Subscriber; import com.alibaba.nacos.naming.pojo.Subscriber;
import com.alibaba.nacos.naming.utils.NamingRequestUtil;
import com.alibaba.nacos.naming.utils.ServiceUtil; import com.alibaba.nacos.naming.utils.ServiceUtil;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -70,22 +72,24 @@ public class SubscribeServiceRequestHandler extends RequestHandler<SubscribeServ
String namespaceId = request.getNamespace(); String namespaceId = request.getNamespace();
String serviceName = request.getServiceName(); String serviceName = request.getServiceName();
String groupName = request.getGroupName(); String groupName = request.getGroupName();
String app = request.getHeader("app", "unknown"); String app = RequestContextHolder.getContext().getBasicContext().getApp();
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName); String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
Service service = Service.newService(namespaceId, groupName, serviceName, true); Service service = Service.newService(namespaceId, groupName, serviceName, true);
Subscriber subscriber = new Subscriber(meta.getClientIp(), meta.getClientVersion(), app, meta.getClientIp(), Subscriber subscriber = new Subscriber(meta.getClientIp(), meta.getClientVersion(), app, meta.getClientIp(),
namespaceId, groupedServiceName, 0, request.getClusters()); namespaceId, groupedServiceName, 0, request.getClusters());
ServiceInfo serviceInfo = ServiceUtil.selectInstancesWithHealthyProtection(serviceStorage.getData(service), ServiceInfo serviceInfo = ServiceUtil.selectInstancesWithHealthyProtection(serviceStorage.getData(service),
metadataManager.getServiceMetadata(service).orElse(null), subscriber.getCluster(), false, metadataManager.getServiceMetadata(service).orElse(null), subscriber.getCluster(), false, true,
true, subscriber.getIp()); subscriber.getIp());
if (request.isSubscribe()) { if (request.isSubscribe()) {
clientOperationService.subscribeService(service, subscriber, meta.getConnectionId()); clientOperationService.subscribeService(service, subscriber, meta.getConnectionId());
NotifyCenter.publishEvent(new SubscribeServiceTraceEvent(System.currentTimeMillis(), NotifyCenter.publishEvent(new SubscribeServiceTraceEvent(System.currentTimeMillis(),
meta.getClientIp(), service.getNamespace(), service.getGroup(), service.getName())); NamingRequestUtil.getSourceIpForGrpcRequest(meta), service.getNamespace(), service.getGroup(),
service.getName()));
} else { } else {
clientOperationService.unsubscribeService(service, subscriber, meta.getConnectionId()); clientOperationService.unsubscribeService(service, subscriber, meta.getConnectionId());
NotifyCenter.publishEvent(new UnsubscribeServiceTraceEvent(System.currentTimeMillis(), NotifyCenter.publishEvent(new UnsubscribeServiceTraceEvent(System.currentTimeMillis(),
meta.getClientIp(), service.getNamespace(), service.getGroup(), service.getName())); NamingRequestUtil.getSourceIpForGrpcRequest(meta), service.getNamespace(), service.getGroup(),
service.getName()));
} }
return new SubscribeServiceResponse(ResponseCode.SUCCESS.getCode(), "success", serviceInfo); return new SubscribeServiceResponse(ResponseCode.SUCCESS.getCode(), "success", serviceInfo);
} }

View File

@ -0,0 +1,77 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.naming.utils;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.context.addition.AddressContext;
import com.alibaba.nacos.core.utils.WebUtils;
import javax.servlet.http.HttpServletRequest;
/**
* Naming request util.
*
* @author xiweng.yy
*/
public class NamingRequestUtil {
/**
* Get source ip from request context.
*
* @return source ip, null if not found
*/
public static String getSourceIp() {
AddressContext addressContext = RequestContextHolder.getContext().getBasicContext().getAddressContext();
String sourceIp = addressContext.getSourceIp();
if (StringUtils.isBlank(sourceIp)) {
sourceIp = addressContext.getRemoteIp();
}
return sourceIp;
}
/**
* Get source ip from request context first, if it can't found, get from http request.
*
* @param httpServletRequest http request
* @return source ip, null if not found
*/
public static String getSourceIpForHttpRequest(HttpServletRequest httpServletRequest) {
String sourceIp = getSourceIp();
// If can't get from request context, get from http request.
if (StringUtils.isBlank(sourceIp)) {
sourceIp = WebUtils.getRemoteIp(httpServletRequest);
}
return sourceIp;
}
/**
* Get source ip from request context first, if it can't found, get from http request.
*
* @param meta grpc request meta
* @return source ip, null if not found
*/
public static String getSourceIpForGrpcRequest(RequestMeta meta) {
String sourceIp = getSourceIp();
// If can't get from request context, get from grpc request meta.
if (StringUtils.isBlank(sourceIp)) {
sourceIp = meta.getClientIp();
}
return sourceIp;
}
}

View File

@ -20,6 +20,7 @@ import com.alibaba.nacos.common.constant.HttpHeaderConsts;
import com.alibaba.nacos.common.utils.HttpMethod; import com.alibaba.nacos.common.utils.HttpMethod;
import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.InternetAddressUtil;
import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.core.utils.WebUtils;
import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes;
import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient; import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient;
@ -35,6 +36,7 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
/** /**
* <p> * <p>
@ -56,7 +58,14 @@ public class ClientAttributesFilter implements Filter {
@Autowired @Autowired
private ClientManager clientManager; private ClientManager clientManager;
public static ThreadLocal<ClientAttributes> threadLocalClientAttributes = new ThreadLocal<>(); public static Optional<ClientAttributes> getCurrentClientAttributes() {
Object clientAttributes = RequestContextHolder.getContext()
.getExtensionContext(ClientAttributes.class.getSimpleName());
if (clientAttributes instanceof ClientAttributes) {
return Optional.of((ClientAttributes) clientAttributes);
}
return Optional.empty();
}
@Override @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
@ -65,38 +74,32 @@ public class ClientAttributesFilter implements Filter {
String uri = request.getRequestURI(); String uri = request.getRequestURI();
String method = request.getMethod(); String method = request.getMethod();
try { try {
try { if (isRegisterInstanceUri(uri, method)) {
if (isRegisterInstanceUri(uri, method)) { //register
//register ClientAttributes attributes = getClientAttributes();
ClientAttributes requestClientAttributes = getClientAttributes(request); RequestContextHolder.getContext()
threadLocalClientAttributes.set(requestClientAttributes); .addExtensionContext(ClientAttributes.class.getSimpleName(), attributes);
} else if (isBeatUri(uri, method)) { } else if (isBeatUri(uri, method)) {
//beat //beat
String ip = WebUtils.optional(request, IP, StringUtils.EMPTY); String ip = WebUtils.optional(request, IP, StringUtils.EMPTY);
int port = Integer.parseInt(WebUtils.optional(request, PORT, ZERO)); int port = Integer.parseInt(WebUtils.optional(request, PORT, ZERO));
String clientId = IpPortBasedClient String clientId = IpPortBasedClient.getClientId(ip + InternetAddressUtil.IP_PORT_SPLITER + port, true);
.getClientId(ip + InternetAddressUtil.IP_PORT_SPLITER + port, true); IpPortBasedClient client = (IpPortBasedClient) clientManager.getClient(clientId);
IpPortBasedClient client = (IpPortBasedClient) clientManager.getClient(clientId); if (client != null) {
if (client != null) { ClientAttributes requestClientAttributes = getClientAttributes();
ClientAttributes requestClientAttributes = getClientAttributes(request); //update clientAttributes,when client version attributes is null,then update.
//update clientAttributes,when client version attributes is null,then update. if (canUpdateClientAttributes(client, requestClientAttributes)) {
if (canUpdateClientAttributes(client, requestClientAttributes)) { client.setAttributes(requestClientAttributes);
client.setAttributes(requestClientAttributes);
}
} }
} }
} catch (Exception e) {
Loggers.SRV_LOG.error("handler client attributes error", e);
}
try {
filterChain.doFilter(request, servletResponse);
} catch (ServletException e) {
throw new RuntimeException(e);
}
} finally {
if (threadLocalClientAttributes.get() != null) {
threadLocalClientAttributes.remove();
} }
} catch (Exception e) {
Loggers.SRV_LOG.error("handler client attributes error", e);
}
try {
filterChain.doFilter(request, servletResponse);
} catch (ServletException e) {
throw new RuntimeException(e);
} }
} }
@ -104,19 +107,18 @@ public class ClientAttributesFilter implements Filter {
return ((UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT return ((UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT
+ UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + BEAT_URI).equals(uri) || ( + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + BEAT_URI).equals(uri) || (
UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2
+ UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + BEAT_URI).equals(uri)) && HttpMethod.PUT + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + BEAT_URI).equals(uri))
.equals(httpMethod); && HttpMethod.PUT.equals(httpMethod);
} }
private boolean isRegisterInstanceUri(String uri, String httpMethod) { private boolean isRegisterInstanceUri(String uri, String httpMethod) {
return ((UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT return ((UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT
+ UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT).equals(uri) || (UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT).equals(uri) || (UtilsAndCommons.NACOS_SERVER_CONTEXT
+ UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2 + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT) + UtilsAndCommons.DEFAULT_NACOS_NAMING_CONTEXT_V2
.equals(uri)) && HttpMethod.POST.equals(httpMethod); + UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT).equals(uri)) && HttpMethod.POST.equals(httpMethod);
} }
private static boolean canUpdateClientAttributes(IpPortBasedClient client, private boolean canUpdateClientAttributes(IpPortBasedClient client, ClientAttributes requestClientAttributes) {
ClientAttributes requestClientAttributes) {
if (requestClientAttributes.getClientAttribute(HttpHeaderConsts.CLIENT_VERSION_HEADER) == null) { if (requestClientAttributes.getClientAttribute(HttpHeaderConsts.CLIENT_VERSION_HEADER) == null) {
return false; return false;
} }
@ -127,10 +129,10 @@ public class ClientAttributesFilter implements Filter {
return true; return true;
} }
public static ClientAttributes getClientAttributes(HttpServletRequest request) { private ClientAttributes getClientAttributes() {
String version = request.getHeader(HttpHeaderConsts.CLIENT_VERSION_HEADER); String version = RequestContextHolder.getContext().getBasicContext().getUserAgent();
String app = request.getHeader(HttpHeaderConsts.APP_FILED); String app = RequestContextHolder.getContext().getBasicContext().getApp();
String clientIp = request.getRemoteAddr(); String clientIp = RequestContextHolder.getContext().getBasicContext().getAddressContext().getSourceIp();
ClientAttributes clientAttributes = new ClientAttributes(); ClientAttributes clientAttributes = new ClientAttributes();
if (version != null) { if (version != null) {
clientAttributes.addClientAttribute(HttpHeaderConsts.CLIENT_VERSION_HEADER, version); clientAttributes.addClientAttribute(HttpHeaderConsts.CLIENT_VERSION_HEADER, version);

View File

@ -23,12 +23,14 @@ import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest; import com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest;
import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse;
import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage;
import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager;
import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata;
import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl;
import com.alibaba.nacos.naming.selector.SelectorManager; import com.alibaba.nacos.naming.selector.SelectorManager;
import com.alibaba.nacos.sys.utils.ApplicationUtils; import com.alibaba.nacos.sys.utils.ApplicationUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
@ -78,6 +80,11 @@ class SubscribeServiceRequestHandlerTest {
Mockito.when(applicationContext.getBean(SelectorManager.class)).thenReturn(selectorManager); Mockito.when(applicationContext.getBean(SelectorManager.class)).thenReturn(selectorManager);
} }
@AfterEach
void tearDown() {
RequestContextHolder.removeContext();
}
@Test @Test
void testHandle() throws NacosException { void testHandle() throws NacosException {
Instance instance = new Instance(); Instance instance = new Instance();

View File

@ -0,0 +1,82 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.naming.utils;
import com.alibaba.nacos.api.remote.request.RequestMeta;
import com.alibaba.nacos.core.context.RequestContextHolder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import javax.servlet.http.HttpServletRequest;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class NamingRequestUtilTest {
@Mock
HttpServletRequest request;
@Mock
RequestMeta meta;
@BeforeEach
void setUp() {
RequestContextHolder.getContext().getBasicContext().getAddressContext().setRemoteIp("1.1.1.1");
RequestContextHolder.getContext().getBasicContext().getAddressContext().setSourceIp("2.2.2.2");
}
@AfterEach
void tearDown() {
RequestContextHolder.removeContext();
}
@Test
void testGetSourceIp() {
assertEquals("2.2.2.2", NamingRequestUtil.getSourceIp());
RequestContextHolder.getContext().getBasicContext().getAddressContext().setSourceIp(null);
assertEquals("1.1.1.1", NamingRequestUtil.getSourceIp());
RequestContextHolder.getContext().getBasicContext().getAddressContext().setRemoteIp(null);
assertNull(NamingRequestUtil.getSourceIp());
}
@Test
void getSourceIpForHttpRequest() {
when(request.getRemoteAddr()).thenReturn("3.3.3.3");
assertEquals("2.2.2.2", NamingRequestUtil.getSourceIpForHttpRequest(request));
RequestContextHolder.getContext().getBasicContext().getAddressContext().setSourceIp(null);
assertEquals("1.1.1.1", NamingRequestUtil.getSourceIpForHttpRequest(request));
RequestContextHolder.getContext().getBasicContext().getAddressContext().setRemoteIp(null);
assertEquals("3.3.3.3", NamingRequestUtil.getSourceIpForHttpRequest(request));
}
@Test
void getSourceIpForGrpcRequest() {
when(meta.getClientIp()).thenReturn("3.3.3.3");
assertEquals("2.2.2.2", NamingRequestUtil.getSourceIpForGrpcRequest(meta));
RequestContextHolder.getContext().getBasicContext().getAddressContext().setSourceIp(null);
assertEquals("1.1.1.1", NamingRequestUtil.getSourceIpForGrpcRequest(meta));
RequestContextHolder.getContext().getBasicContext().getAddressContext().setRemoteIp(null);
assertEquals("3.3.3.3", NamingRequestUtil.getSourceIpForGrpcRequest(meta));
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 1999-2023 Alibaba Group Holding Ltd.
*
* Licensed 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 com.alibaba.nacos.naming.web;
import com.alibaba.nacos.common.constant.HttpHeaderConsts;
import com.alibaba.nacos.core.context.RequestContextHolder;
import com.alibaba.nacos.naming.core.v2.client.ClientAttributes;
import com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient;
import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager;
import com.alibaba.nacos.naming.misc.UtilsAndCommons;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockFilterChain;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ClientAttributesFilterTest {
@Mock
ClientManager clientManager;
@Mock
IpPortBasedClient client;
@Mock
HttpServletRequest request;
@Mock
HttpServletResponse response;
@Mock
Servlet servlet;
@InjectMocks
ClientAttributesFilter filter;
@BeforeEach
void setUp() {
RequestContextHolder.getContext().getBasicContext().setUserAgent("Nacos-Java-Client:v2.4.0");
RequestContextHolder.getContext().getBasicContext().setApp("testApp");
RequestContextHolder.getContext().getBasicContext().getAddressContext().setRemoteIp("1.1.1.1");
RequestContextHolder.getContext().getBasicContext().getAddressContext().setSourceIp("2.2.2.2");
}
@AfterEach
void tearDown() {
RequestContextHolder.removeContext();
}
@Test
void testDoFilterForRegisterUri() throws IOException {
when(request.getRequestURI()).thenReturn(
UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT
+ UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT);
when(request.getMethod()).thenReturn("POST");
filter.doFilter(request, response, new MockFilterChain(servlet, new MockRegisterFilter()));
}
@Test
void testDoFilterForBeatUri() throws IOException {
when(request.getParameter("ip")).thenReturn("127.0.0.1");
when(request.getParameter("port")).thenReturn("8848");
when(request.getParameter("encoding")).thenReturn("utf-8");
when(clientManager.getClient("127.0.0.1:8848#true")).thenReturn(client);
when(request.getRequestURI()).thenReturn(
UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_NAMING_CONTEXT
+ UtilsAndCommons.NACOS_NAMING_INSTANCE_CONTEXT + "/beat");
when(request.getMethod()).thenReturn("PUT");
filter.doFilter(request, response, new MockFilterChain());
verify(client).setAttributes(any(ClientAttributes.class));
}
private static class MockRegisterFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
Optional<ClientAttributes> clientAttributes = ClientAttributesFilter.getCurrentClientAttributes();
assertTrue(clientAttributes.isPresent());
assertEquals("Nacos-Java-Client:v2.4.0",
clientAttributes.get().getClientAttribute(HttpHeaderConsts.CLIENT_VERSION_HEADER));
assertEquals("testApp", clientAttributes.get().getClientAttribute(HttpHeaderConsts.APP_FILED));
assertEquals("2.2.2.2", clientAttributes.get().getClientAttribute(HttpHeaderConsts.CLIENT_IP));
}
}
}

220
style/NacosCheckStyle_9.xml Normal file
View File

@ -0,0 +1,220 @@
<?xml version="1.0"?>
<!--
~ Copyright 1999-2018 Alibaba Group Holding Ltd.
~
~ Licensed 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.
-->
<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="error"/>
<property name="fileExtensions" value="java, properties, xml"/>
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="LineLength">
<property name="fileExtensions" value="java"/>
<property name="max" value="150"/>
<property name="ignorePattern"
value="^implements.*|^extends.*|^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="SuppressWarningsFilter"/>
<module name="TreeWalker">
<module name="SuppressionCommentFilter"/>
<module name="SuppressWarningsHolder" />
<!-- Name Checker -->
<module name="OuterTypeFilename"/>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName"/>
<module name="MemberName"/>
<module name="ParameterName"/>
<module name="LambdaParameterName"/>
<module name="CatchParameterName"/>
<module name="LocalVariableName"/>
<module name="ClassTypeParameterName"/>
<module name="MethodTypeParameterName"/>
<module name="InterfaceTypeParameterName"/>
<module name="MethodName"/>
<module name="ConstantName"/>
<module name="StaticVariableName"/>
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="1"/>
<property name="allowedAbbreviations" value="VO"/>
</module>
<!-- Import Checker -->
<module name="AvoidStarImport"/>
<module name="UnusedImports"/>
<module name="RedundantImport"/>
<!-- Block Checker -->
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected|ignore(d)?"/>
</module>
<module name="LeftCurly"/>
<module name="RightCurly"/>
<module name="NeedBraces"/>
<!-- Javadoc Checker -->
<module name="JavadocMethod">
<property name="accessModifiers" value="public"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowedAnnotations"
value="Override, Test, Before, After, BeforeClass, AfterClass, Parameterized, Parameters, Bean"/>
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/>
</module>
<module name="MissingJavadocMethod">
<property name="scope" value="public"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations"
value="Override, Test, Before, After, BeforeClass, AfterClass, Parameterized, Parameters, Bean"/>
<property name="ignoreMethodNamesRegex" value="^set[A-Z].*|^get[A-Z].*|main"/>
<property name="tokens" value="METHOD_DEF, ANNOTATION_FIELD_DEF"/>
</module>
<module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module>
<module name="InvalidJavadocPosition"/>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<module name="JavadocParagraph"/>
<module name="NonEmptyAtclauseDescription"/>
<!-- Coding Checker -->
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="OneStatementPerLine"/>
<module name="MultipleVariableDeclarations"/>
<module name="MissingSwitchDefault"/>
<module name="FallThrough"/>
<module name="NoFinalizer"/>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
</module>
<!-- Miscellaneous Checker -->
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="Indentation">
<property name="arrayInitIndent" value="8"/>
<property name="lineWrappingIndentation" value="8"/>
</module>
<module name="CommentsIndentation">
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
</module>
<module name="ArrayTypeStyle"/>
<module name="UpperEll"/>
<!-- Design Checker -->
<module name="OneTopLevelClass"/>
<!-- Whitespace -->
<module name="NoLineWrap"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
</module>
<module name="EmptyLineSeparator">
<property name="allowMultipleEmptyLines" value="false"/>
<property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="GenericWhitespace">
<message key="ws.followed"
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded"
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow"
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="MethodParamPad"/>
<module name="NoWhitespaceBefore"/>
<module name="ParenPad"/>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens"
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
</module>
<!-- Modifier Checker -->
<module name="ModifierOrder"/>
<!-- Annotation Checker -->
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
</module>
</module>

View File

@ -53,6 +53,8 @@ Volunteer wanted.
3. Import `style/NacosCheckStyle.xml` to checkstyle plugin. 3. Import `style/NacosCheckStyle.xml` to checkstyle plugin.
4. Scan and check your modified code by plugin. 4. Scan and check your modified code by plugin.
> If you install the latest version of CheckStyle plugin, it may not support the previous version of CheckStyle(9.0), you can modify the `style/NacosCheckStyle.xml` file to `style/NacosCheckStyle_9.xml` instead.
[checkstyle插件idea安装](https://plugins.jetbrains.com/plugin/1065-checkstyle-idea) [checkstyle插件idea安装](https://plugins.jetbrains.com/plugin/1065-checkstyle-idea)
1. `Preferences/Settings --> Other Settings --> Checkstyle` 或者 `Preferences/Settings --> Tools --> Checkstyle` 1. `Preferences/Settings --> Other Settings --> Checkstyle` 或者 `Preferences/Settings --> Tools --> Checkstyle`
@ -60,6 +62,8 @@ Volunteer wanted.
3. 导入源代码下`style/NacosCheckStyle.xml`文件到checkstyle插件。 3. 导入源代码下`style/NacosCheckStyle.xml`文件到checkstyle插件。
4. 用checkstyle插件扫描你修改的代码。 4. 用checkstyle插件扫描你修改的代码。
> 如果安装的CheckStyle的插件较新已不支持9.0之前的Checkstyle版本将上述第3步的`style/NacosCheckStyle.xml`文件修改为`style/NacosCheckStyle_9.xml`即可。
### eclipse IDE ### eclipse IDE
#### p3c #### p3c