Rawven commented on issue #13965:
URL: https://github.com/apache/dubbo/issues/13965#issuecomment-2049311237

   # Triple Rest Cors Plan
   
   
   ### 1. **Cors related implementation**
   
   Under the rest/cors package of dubbo-rpc-triple module
   
   CorsMeta configuration class currently supports the following modules and 
methods related to inspection and processing of some attributes.
   
   ```java
   
   public class CorsMeta {
   .........
       private List<String> allowedOrigins;
       private List<OriginPattern> allowedOriginPatterns;
       private List<String> allowedMethods;
       private List<HttpMethods> resolvedMethods = DEFAULT_METHODS;
       private List<String> allowedHeaders;
       private List<String> exposedHeaders;
       private Boolean allowCredentials;
       private Boolean allowPrivateNetwork;
       private Long maxAge;
    .........
   ```
   
   CorsProcessor, used to process requests. The following are the main methods 
of DefaultCorsProcesso
   
   ```java
   
   public class DefaultCorsProcessor implements CorsProcessor {
       public boolean process(CorsMeta config, HttpRequest request, 
HttpResponse response) {
           // set vary header
           setVaryHeaders(response);
           // skip if is not a cors request
           if (!isCorsRequest(request)) {
               return true;
           }
           // skip if origin already contains in Access-Control-Allow-Origin 
header
           if (response.header(RestConstants.ACCESS_CONTROL_ALLOW_ORIGIN) != 
null)...
   
           if (config == null) {
               // if no cors config and is a preflight request
               if (isPreFlight(request)) {
                   reject(response);
                   return false;
               }
               return true;
           }
           // handle cors request
           return handleInternal(request, response, config, 
isPreFlight(request));
       }
   
       private boolean isPreFlight(HttpRequest request) {
           // preflight request is a OPTIONS request with 
           // Access-Control-Request-Method header
           return request.method().equals(HttpMethods.OPTIONS.name())
                   && 
request.header(RestConstants.ACCESS_CONTROL_REQUEST_METHOD) != null;
       }
   
       private boolean isCorsRequest(HttpRequest request) {
           // skip if request has no origin header
           String origin = request.header(RestConstants.ORIGIN);
           if (origin == null) {
               return false;
           }
           try {
               URI uri = new URI(origin);
               // return true if origin is not the same as request's scheme, 
host and port
               return !(Objects.equals(uri.getScheme(), request.scheme())
                       && uri.getHost().equals(request.serverName())
                       //getPortByScheme
                       && getPort(uri.getScheme(), uri.getPort()) == 
getPort(request.scheme(), request.serverPort()));
           } catch (URISyntaxException e) {
               // skip if origin is not a valid URI
               return false;
           }
       }
   
       protected boolean handleInternal(HttpRequest request, HttpResponse 
response, CorsMeta config, boolean isPreLight) {
           //check origin,method,header are allowed
           String allowOrigin = 
config.checkOrigin(request.header(RestConstants.ORIGIN));
           ........
           List<HttpMethods> allowHttpMethods = 
config.checkHttpMethods(getHttpMethods(request, isPreLight));
           .......
           List<String> allowHeaders = 
config.checkHeaders(getHttpHeaders(request, isPreLight));
           ......
           response.setHeader(RestConstants.ACCESS_CONTROL_ALLOW_ORIGIN, 
allowOrigin);
           //set allow method if is prelight
           if (isPreLight) {
               response.setHeader(
                       RestConstants.ACCESS_CONTROL_ALLOW_METHODS,
                      ......   
           if (isPreLight && !allowHeaders.isEmpty()) ......
             if (isPreLight && config.getMaxAge() != null)....
           // set related config
           if (!CollectionUtils.isEmpty(config.getExposedHeaders()))...
           if (Boolean.TRUE.equals(config.getAllowCredentials())) .....
           if (Boolean.TRUE.equals(config.getAllowPrivateNetwork()) .....
           return true;
       }
   }
   
   ```
   
   ### 2.**Initialize Cors configuration**
   
   **Global configuration**
   
   Add RestCorsConfig under org.apache.dubbo.config.RestConfig
   
   Add the CorsMeta attribute under 
org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping
   
   ```java
   public class RestConfig implements Serializable {
   .......
       /**
        *  The config is used to set the Global CORS configuration properties.
        */
       private CorsConfig corsConfig;
   .......    
   }
   public final class RequestMapping implements Condition<RequestMapping, 
HttpRequest> {
       ......... 
       private final CorsMeta corsMeta;
   }
   
   ```
   
   **Configuration parsing will combine global, class, and method 
configurations in 
org.apache.dubbo.rpc.protocol.tri.rest.mapping.DefaultRequestMappingRegistry**
   
   ```java
   public final class DefaultRequestMappingRegistry implements 
RequestMappingRegistry {
       private CorsMeta globalCorsMeta;
       .....
      
       @Override
       public void register(Invoker<?> invoker) {
           new MethodWalker().walk(service.getClass(), (classes, consumer) -> {
               for (RequestMappingResolver resolver : resolvers) {
                   .....
                   // combine gloabl config in class 
                   
classMapping.setCorsMeta(classMapping.getCorsMeta().combine(getGlobalCorsMeta()));
                   consumer.accept((methods) -> {
                       .....
                       //combine class in method
                       methodMapping = classMapping.combine(methodMapping);
                       ....
                   });
               }
           });
       }
       
       private CorsMeta getGlobalCorsMeta() {
           if (globalCorsMeta == null) {
               Configuration globalConfiguration =
                       
ConfigurationUtils.getGlobalConfiguration(ApplicationModel.defaultModel());
               globalCorsMeta = CorsUtil.resolveGlobalMeta(globalConfiguration);
           }
           return globalCorsMeta;
       }
       
   ```
   
   **spring@CrossOrigin annotation configuration**
   
   ```java
   
   @Activate(onClass = "org.springframework.web.bind.annotation.RequestMapping")
   public class SpringMvcRequestMappingResolver implements 
RequestMappingResolver {
       public RequestMapping resolve(ServiceMeta serviceMeta) {
       ......
           AnnotationMeta<?> crossOrigin = 
serviceMeta.findMergedAnnotation(Annotations.CrossOrigin);
           return builder(requestMapping, responseStatus)
                    ....
                   .cors(createCorsMeta(crossOrigin))
                   .build();
       }
   
       public RequestMapping resolve(MethodMeta methodMeta) 
           AnnotationMeta<?> crossOrigin = 
methodMeta.findMergedAnnotation(Annotations.CrossOrigin);
           return builder(requestMapping, responseStatus)
                    ......
                   .cors(createCorsMeta(crossOrigin))
                   .build();
       }
       
       private CorsMeta createCorsMeta(AnnotationMeta<?> crossOrigin) {
           CorsMeta meta = new CorsMeta();
           if (crossOrigin == null) {
               return meta;
           }
           
meta.setAllowCredentials(Boolean.valueOf(crossOrigin.getString("allowCredentials")));
           
meta.setAllowedHeaders(Arrays.asList(crossOrigin.getStringArray("allowedHeaders")));
           .....
           return meta;
       }
   }
   
   ```
   
   ### 3.**Perform request interception processing**
   
   **Add the CorsProcessor member variable under 
org.apache.dubbo.rpc.protocol.tri.rest.mapping.DefaultRequestMappingRegistry, 
and add the following processing code in the lookup method**
   
   ```java
   
   public final class DefaultRequestMappingRegistry implements 
RequestMappingRegistry {
       .......
       private CorsMeta globalCorsMeta;
       private final CorsProcessor corsProcessor;
   
       public DefaultRequestMappingRegistry(FrameworkModel frameworkModel){
           corsProcessor = 
frameworkModel.getBeanFactory().getOrRegisterBean(CorsProcessor.class);
       }
       public HandlerMeta lookup(HttpRequest request, HttpResponse response) {
           .......
           // Handle preflight requests for matching
           String method = preprocessingCors(request, response);
   
           List<Candidate> candidates = new ArrayList<>(size);
           for (int i = 0; i < size; i++) {
               Match<Registration> match = matches.get(i);
               RequestMapping mapping = match.getValue().mapping.match(request, 
match.getExpression());
               if (mapping != null) {
                   Candidate candidate = new Candidate();
                   candidate.mapping = mapping;
                   candidate.meta = match.getValue().meta;
                   candidate.expression = match.getExpression();
                   candidate.variableMap = match.getVariableMap();
                   candidates.add(candidate);
               }
           }
           ........
           RequestMapping mapping = winner.mapping;
           
           //Handle request
           processCors(method, mapping, request, response);
           ....
       }
   
       private String preprocessingCors(HttpRequest request, HttpResponse 
response) {
           if (Objects.equals(request.method(), HttpMethods.OPTIONS.name())) {
               if (CorsProcessor.isPreFlight(request)) {
                   // replace option by request-method
                   String realMethod = 
request.header(RestConstants.ACCESS_CONTROL_REQUEST_METHOD);
                   request.setMethod(realMethod);
                   return realMethod;
               } else {
                   throw new HttpResultPayloadException(HttpResult.builder()
                           ....
           }
           return null;
       }
   
       private void processCors(String method, RequestMapping mapping, 
HttpRequest request, HttpResponse response) {
           if (method != null) {
               request.setMethod(HttpMethods.OPTIONS.name());
           }
           if (!corsProcessor.process(mapping.getCorsMeta(), request, 
response)) {
               throw new HttpResultPayloadException(HttpResult.builder()
                       .......
           }
       }
       ............
   ```


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

To unsubscribe, e-mail: [email protected]

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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to