Skip to content

Commit

Permalink
Fix fs char (#10101)
Browse files Browse the repository at this point in the history
* Add PathEncoder for OS to encode if dataId, ns, group contains illegal char for file system

* Add copyright

* Fix checkstyle

* Fix pmd

* fix PathEncoderManager as singleton

* fix ut

* fix checkstyle
  • Loading branch information
Daydreamer-ia authored Mar 27, 2023
1 parent f7756b7 commit e002cef
Show file tree
Hide file tree
Showing 8 changed files with 479 additions and 1 deletion.
1 change: 1 addition & 0 deletions common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
<filtering>true</filtering>
<includes>
<include>nacos-version.txt</include>
<include>META-INF/services/*</include>
</includes>
</resource>
</resources>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.common.pathencoder;

/**
* To encode path if illegal,an os may have a PathEncoder.
*
* @author daydreamer-ia
*/
public interface PathEncoder {

/**
* encode path.
*
* @param str origin
* @param charset charset
* @return new path
*/
String encode(String str, String charset);

/**
* decode path.
*
* @param str new path
* @param charset charset
* @return origin path
*/
String decode(String str, String charset);

/**
* return simple lowercase os name.
*
* @return simple lowercase os name
*/
String name();

/**
* whether to encode.
*
* @param key key
* @return whether to encode.
*/
boolean needEncode(String key);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* 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.common.pathencoder;

import com.alibaba.nacos.common.spi.NacosServiceLoader;

import java.nio.charset.Charset;
import java.util.Collection;

/**
* To expose interface from {@link PathEncoder}.
*
* @author daydreamer-ia
*/
public class PathEncoderManager {

/**
* singleton.
*/
private static final PathEncoderManager INSTANCE = new PathEncoderManager();

/**
* encoder.
*/
private PathEncoder targetEncoder = null;

private PathEncoderManager() {
// load path encoder
Collection<PathEncoder> load = NacosServiceLoader.load(PathEncoder.class);
if (!load.isEmpty()) {
String currentOs = System.getProperty("os.name").toLowerCase();
for (PathEncoder pathEncoder : load) {
// match first
if (currentOs.contains(pathEncoder.name())) {
targetEncoder = pathEncoder;
break;
}
}
}
}

/**
* encode path if necessary.
*
* @param path origin path
* @param charset charset of origin path
* @return encoded path
*/
public String encode(String path, String charset) {
if (path == null || charset == null) {
return path;
}
if (targetEncoder != null && targetEncoder.needEncode(path)) {
return targetEncoder.encode(path, charset);
}
return path;
}

/**
* encode path if necessary.
*
* @param path origin path
* @return encoded path
*/
public String encode(String path) {
return encode(path, Charset.defaultCharset().name());
}

/**
* decode path.
*
* @param path encoded path
* @param charset charset of encoded path
* @return origin path
*/
public String decode(String path, String charset) {
if (path == null || charset == null) {
return path;
}
if (targetEncoder != null) {
return targetEncoder.decode(path, charset);
}
return path;
}

/**
* decode path.
*
* @param path encoded path
* @return origin path
*/
public String decode(String path) {
return decode(path, Charset.defaultCharset().name());
}

/**
* get singleton.
*
* @return singleton.
*/
public static PathEncoderManager getInstance() {
return INSTANCE;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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.common.pathencoder.impl;

import com.alibaba.nacos.common.pathencoder.PathEncoder;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

/**
* Encode path if illegal char reach in Windows.
*
* @author daydreamer-ia
*/
public class WindowsEncoder implements PathEncoder {

/**
* 不应该含有 / : ? " < > | \.
*/
private static final String PATTERN_EXP = "[^/:*?\"<>|\\\\]+";

private static final Map<String, String> REG_MAPPING = new HashMap<>();

private static final Map<String, String> CHAR_MAPPING = new HashMap<>();

private static final Pattern PATTERN = Pattern.compile(PATTERN_EXP);

static {
// reg
REG_MAPPING.put("\\\\", "%A1%");
REG_MAPPING.put("/", "%A2%");
REG_MAPPING.put(":", "%A3%");
REG_MAPPING.put("\\*", "%A4%");
REG_MAPPING.put("\\?", "%A5%");
REG_MAPPING.put("\"", "%A6%");
REG_MAPPING.put("<", "%A7%");
REG_MAPPING.put(">", "%A8%");
REG_MAPPING.put("\\|", "%A9%");

// char
CHAR_MAPPING.put("%A1%", "\\\\");
CHAR_MAPPING.put("%A2%", "/");
CHAR_MAPPING.put("%A3%", ":");
CHAR_MAPPING.put("%A4%", "*");
CHAR_MAPPING.put("%A5%", "?");
CHAR_MAPPING.put("%A6%", "\"");
CHAR_MAPPING.put("%A7%", "<");
CHAR_MAPPING.put("%A8%", ">");
CHAR_MAPPING.put("%A9%", "|");
}

@Override
public String encode(String str, String charset) {
for (Map.Entry<String, String> entry : REG_MAPPING.entrySet()) {
str = str.replaceAll(entry.getKey(), entry.getValue());
}
return str;
}

@Override
public String decode(String str, String charset) {
for (Map.Entry<String, String> entry : CHAR_MAPPING.entrySet()) {
str = str.replaceAll(entry.getKey(), entry.getValue());
}
return str;
}

@Override
public String name() {
return "window";
}

@Override
public boolean needEncode(String key) {
if (key == null) {
key = "";
}
return !PATTERN.matcher(key).matches();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#
#
# 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.
#
#

com.alibaba.nacos.common.pathencoder.impl.WindowsEncoder
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.common.pathencoder;

import com.alibaba.nacos.common.pathencoder.impl.WindowsEncoder;
import junit.framework.TestCase;
import org.junit.Assert;

import java.lang.reflect.Field;

public class PathEncoderManagerTest extends TestCase {

/**
* test expose method.
*/
public void test() throws Exception {
// load static
PathEncoderManager instance = PathEncoderManager.getInstance();
// remove windows impl
Field targetEncoder = PathEncoderManager.class.getDeclaredField("targetEncoder");
targetEncoder.setAccessible(true);
// remain old path encoder
final Object origin = targetEncoder.get(instance);
targetEncoder.set(instance, null);
// try to encode, non windows
String case1 = "aa||a";
Assert.assertEquals(PathEncoderManager.getInstance().encode(case1), case1);
String case2 = "aa%A9%%A9%a";
Assert.assertEquals(PathEncoderManager.getInstance().decode(case2), case2);
// try to encode if in windows
targetEncoder.set(instance, new WindowsEncoder());
Assert.assertEquals(PathEncoderManager.getInstance().encode(case1), case2);
Assert.assertEquals(PathEncoderManager.getInstance().decode(case2), case1);
// set origin
targetEncoder.set(instance, origin);
}

}
Loading

0 comments on commit e002cef

Please sign in to comment.