Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#noissue] Refactor PathRewriter #10337

Merged
merged 1 commit into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2023 NAVER Corp.
*
* 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.navercorp.pinpoint.web.servlet;

public class DefaultVersionToken implements VersionToken {
private static final String prefix = "v";

@Override
public boolean isVersion(String token) {
if (!token.startsWith(prefix)) {
return false;
}
for (int i = prefix.length(); i < token.length(); i++) {
final char c = token.charAt(i);
if (!Character.isDigit(c)) {
return false;
}

}
return true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,27 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
* @author youngjin.kim2
*/
public class HttpIntentRoutingFilter implements Filter {

private static final FrontApplicationRegistration DEFAULT_FRONT_REGISTRATION = FrontApplicationRegistration.of("/");

private static final List<String> RESOURCE_BASE_PATHS = List.of(
"assets",
"fonts",
"img"
);
public class HttpIntentRoutingFilter extends HttpFilter {

private final Logger logger = LogManager.getLogger(this.getClass());

private final List<FrontApplicationRegistration> registrations;
private final VersionPrefixRewriter rewriter = new VersionPrefixRewriter();

public HttpIntentRoutingFilter(List<FrontApplicationRegistration> registrations) {
this.registrations = Objects.requireNonNullElse(registrations, List.of(DEFAULT_FRONT_REGISTRATION));
}

public HttpIntentRoutingFilter() {
this(List.of(DEFAULT_FRONT_REGISTRATION));
}

@Override
Expand Down Expand Up @@ -86,79 +72,14 @@ private void route(
) throws ServletException, IOException {
String uri = request.getRequestURI();

if (uri.startsWith("/api/")) {
final String rewriteUrl = rewriter.rewrite(uri);
if (rewriteUrl == null) {
chain.doFilter(request, response);
return;
}

for (FrontApplicationRegistration registration: this.registrations) {
if (uri.startsWith(registration.getBasePath())) {
routeToFrontApplication(request, response, chain, registration);
return;
}
}

chain.doFilter(request, response);
}

private void routeToFrontApplication(
HttpServletRequest request,
ServletResponse response,
FilterChain chain,
FrontApplicationRegistration frontRegistration
) throws ServletException, IOException {
String basePath = frontRegistration.getBasePath();
String path = trimFirstSlash(request.getRequestURI().substring(basePath.length()));
String firstToken = path.split("/", 2)[0];
if (isStaticResource(firstToken)) {
chain.doFilter(request, response);
} else {
String entryHtml = basePath + "/index.html";
forwardTo(request, response, entryHtml);
logger.debug("requestUri: {} --(forward)--> {}", request.getRequestURI(), entryHtml);
}
}

private static void forwardTo(
HttpServletRequest request,
ServletResponse response,
String entryHtml
) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher(entryHtml);
logger.debug("requestUri: {} --(forward)--> {}", uri, rewriteUrl);
RequestDispatcher dispatcher = request.getRequestDispatcher(rewriteUrl);
dispatcher.forward(request, response);
}

private static boolean isStaticResource(String firstToken) {
for (String resourceBasePath : RESOURCE_BASE_PATHS) {
if (firstToken.equals(resourceBasePath)) {
return true;
}
}
return firstToken.indexOf('.') != -1;
}

private static String trimFirstSlash(String s) {
if (s.length() > 0 && s.charAt(0) == '/') {
return s.substring(1);
}
return s;
}

public static class FrontApplicationRegistration {

private final String basePath;

public FrontApplicationRegistration(String basePath) {
this.basePath = Objects.requireNonNull(basePath, "basePath");
}

public static FrontApplicationRegistration of(String basePath) {
return new FrontApplicationRegistration(basePath);
}

public String getBasePath() {
return basePath;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2023 NAVER Corp.
*
* 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.navercorp.pinpoint.web.servlet;

import java.util.List;
import java.util.Objects;
import java.util.StringTokenizer;

public class VersionPrefixRewriter {

static final String[] SPECIAL_PATHS = {
"api"
};

static final String[] RESOURCE_PATHS = {
"assets",
"fonts",
"img"
};

static final String MAIN = "/index.html";

private final VersionToken versionToken = new DefaultVersionToken();
private final String page;

private final List<String> specialPaths;
private final List<String> resourcePaths;


public VersionPrefixRewriter() {
this(MAIN, List.of(SPECIAL_PATHS), List.of(RESOURCE_PATHS));
}

public VersionPrefixRewriter(String page, List<String> specialPaths, List<String> resourcePaths) {
this.page = Objects.requireNonNull(page, "page");
this.specialPaths = Objects.requireNonNull(specialPaths, "specialPaths");
this.resourcePaths = Objects.requireNonNull(resourcePaths, "resourcePaths");
}

public String rewrite(String uri) {

final StringTokenizer tokenizer = new StringTokenizer(uri, "/");
if (!tokenizer.hasMoreTokens()) {
return page;
}

String command = tokenizer.nextToken();
if (specialPaths.contains(command)) {
return null;
}
// next token
String version = null;
if (versionToken.isVersion(command)) {
version = command;
command = nextToken(tokenizer);
}
if (command != null) {
// static resource
if (isStaticResource(command)) {
return null;
}
}

if (version == null) {
return page;
} else {
return '/' + version + page;
}
}

private String nextToken(StringTokenizer tokenizer) {
if (tokenizer.hasMoreTokens()) {
return tokenizer.nextToken();
}
return null;
}

private boolean isStaticResource(String command) {
if (resourcePaths.contains(command)) {
return true;
}
return command.indexOf('.') != -1;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2023 NAVER Corp.
*
* 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.navercorp.pinpoint.web.servlet;

public interface VersionToken {
boolean isVersion(String token);
}
17 changes: 3 additions & 14 deletions web/src/main/resources/applicationContext-web.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="commandHeaderTBaseSerializerFactory" class="com.navercorp.pinpoint.thrift.io.CommandHeaderTBaseSerializerFactory" factory-method="getDefaultInstance">
</bean>
Expand Down Expand Up @@ -52,18 +52,7 @@
<constructor-arg ref="metadataRowKeyDistributor2"/>
</bean>

<util:list id="frontApplicationRegistrations">
<bean class="com.navercorp.pinpoint.web.servlet.HttpIntentRoutingFilter$FrontApplicationRegistration">
<constructor-arg name="basePath" value="/v3" />
</bean>
<bean class="com.navercorp.pinpoint.web.servlet.HttpIntentRoutingFilter$FrontApplicationRegistration">
<constructor-arg name="basePath" value="/" />
</bean>
</util:list>

<bean id="rewriteFilter" class="com.navercorp.pinpoint.web.servlet.HttpIntentRoutingFilter">
<constructor-arg name="registrations" ref="frontApplicationRegistrations" />
</bean>
<bean id="rewriteFilter" class="com.navercorp.pinpoint.web.servlet.HttpIntentRoutingFilter"/>

<bean id="additionalServletRegistrationBeanFactory" class="com.navercorp.pinpoint.web.servlet.AdditionalServletRegistrationBeanFactory" />
<bean id="apiServletRegistrationBean" factory-bean="additionalServletRegistrationBeanFactory" factory-method="addRegistration">
Expand Down
Loading