Skip to content
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
159 changes: 58 additions & 101 deletions src/main/java/net/spy/memcached/ArcusClient.java

Large diffs are not rendered by default.

180 changes: 180 additions & 0 deletions src/main/java/net/spy/memcached/KeyValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
* arcus-java-client : Arcus Java client
* Copyright 2010-2014 NAVER Corp.
* Copyright 2014-present JaM2in Co., 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 net.spy.memcached;

import java.util.Collection;

/**
* Validator for memcached keys.
*/
public final class KeyValidator {

private static final int MAX_KEY_LENGTH = 4000;
private static final int MAX_MKEY_LENGTH = 250;
private static final int MAX_BKEY_BYTE_ARRAY_LENGTH = 31;

private final byte delimiter;

public KeyValidator(byte delimiter) {
this.delimiter = delimiter;
}

/**
* Validate cache key.
*
* @param key the cache key to validate
* @throws IllegalArgumentException if the key is invalid
*/
public void validateKey(String key) {
boolean hasPrefix = false;

byte[] keyBytes = KeyUtil.getKeyBytes(key);
if (keyBytes.length > MAX_KEY_LENGTH) {
throw new IllegalArgumentException("Key is too long (maxlen = "
+ MAX_KEY_LENGTH + ")");
} else if (keyBytes.length == 0) {
throw new IllegalArgumentException(
"Key must contain at least one character.");
}
// Validate the key
for (byte b : keyBytes) {
if (b == ' ' || b == '\n' || b == '\r' || b == 0) {
throw new IllegalArgumentException(
"Key contains invalid characters: ``" + key + "''");
}
if (b == delimiter) {
hasPrefix = true;
}
}

// Validate the prefix
if (hasPrefix) {
if (keyBytes[0] == '-') {
throw new IllegalArgumentException(
"Key contains invalid prefix: ``" + key + "''");
}
for (byte b : keyBytes) {
if (b == delimiter) {
break;
}
if (!(('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
(b == '_') || (b == '-') || (b == '+') || (b == '.'))) {
throw new IllegalArgumentException(
"Key contains invalid prefix: ``" + key + "''");
}
}
}
}

/**
* Validate cache keys.
*
* @param keyList the cache keys to validate
* @throws IllegalArgumentException if the key list is null, empty, or contains invalid keys
*/
public void validateKey(Collection<String> keyList) {
if (keyList == null) {
throw new IllegalArgumentException("Key list is null.");
} else if (keyList.isEmpty()) {
throw new IllegalArgumentException("Key list is empty.");
}

for (String key : keyList) {
validateKey(key);
}
}

/**
* Validate map key.
*
* @param mkey the mkey to validate
* @throws IllegalArgumentException if the mkey is invalid
*/
public void validateMKey(String mkey) {
if (mkey == null) {
throw new IllegalArgumentException("mkey is null");
}

byte[] keyBytes = KeyUtil.getKeyBytes(mkey);
if (keyBytes.length > MAX_MKEY_LENGTH) {
throw new IllegalArgumentException("MKey is too long (maxlen = "
+ MAX_MKEY_LENGTH + ")");
}
if (keyBytes.length == 0) {
throw new IllegalArgumentException("MKey must contain at least one character.");
}
// Validate the mkey
for (byte b : keyBytes) {
if (b == ' ' || b == '\n' || b == '\r' || b == 0) {
throw new IllegalArgumentException("MKey contains invalid characters: ``"
+ mkey + "''");
}
}
}

/**
* Validate map keys.
*
* @param mkeyList the mkeys to validate
* @throws IllegalArgumentException if the mkey list is null, empty, or contains invalid mkeys
*/
public void validateMKey(Collection<String> mkeyList) {
if (mkeyList == null) {
throw new IllegalArgumentException("mkeyList is null.");
} else if (mkeyList.isEmpty()) {
throw new IllegalArgumentException("mkeyList is empty.");
}

for (String mkey : mkeyList) {
validateMKey(mkey);
}
}

/**
* Validate byte type bkeys.
*
* @param bkeys the bkeys to validate
* @throws IllegalArgumentException if the bkey is invalid
*/
public static void validateBKey(byte[]... bkeys) {
for (byte[] bkey : bkeys) {
if (bkey == null) {
throw new IllegalArgumentException("bkey is null");
}
if (bkey.length > MAX_BKEY_BYTE_ARRAY_LENGTH) {
throw new IllegalArgumentException("bkey size exceeded 31");
}
}
}

/**
* Validate long type bkeys.
*
* @param bkeys the bkeys to validate
* @throws IllegalArgumentException if the bkey is invalid
*/
public static void validateBKey(long... bkeys) {
for (long bkey : bkeys) {
if (bkey < 0) {
throw new IllegalArgumentException(
String.format("not supported unsigned long bkey : %s, use byte array bkey", bkey));
}
}
}
}
53 changes: 5 additions & 48 deletions src/main/java/net/spy/memcached/MemcachedClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public class MemcachedClient extends SpyThread

protected final Transcoder<Object> transcoder;

private final byte delimiter;
protected final KeyValidator keyValidator;

private static final String DEFAULT_MEMCACHED_CLIENT_NAME = "MemcachedClient";

Expand Down Expand Up @@ -213,7 +213,7 @@ public MemcachedClient(ConnectionFactory cf, String name, List<InetSocketAddress
conn = cf.createConnection(name, addrs);
assert conn != null : "Connection factory failed to make a connection";
operationTimeout = cf.getOperationTimeout();
delimiter = cf.getDelimiter();
keyValidator = new KeyValidator(cf.getDelimiter());
setName("Memcached IO over " + conn);
setDaemon(cf.isDaemon());
start();
Expand Down Expand Up @@ -283,49 +283,6 @@ public OperationFactory getOpFact() {
return opFact;
}

protected void validateKey(String key) {
boolean hasPrefix = false;

byte[] keyBytes = KeyUtil.getKeyBytes(key);
if (keyBytes.length > MAX_KEY_LENGTH) {
throw new IllegalArgumentException("Key is too long (maxlen = "
+ MAX_KEY_LENGTH + ")");
}
if (keyBytes.length == 0) {
throw new IllegalArgumentException(
"Key must contain at least one character.");
}
// Validate the key
for (byte b : keyBytes) {
if (b == ' ' || b == '\n' || b == '\r' || b == 0) {
throw new IllegalArgumentException(
"Key contains invalid characters: ``" + key + "''");
}
if (b == delimiter) {
hasPrefix = true;
}
}

// Validate the prefix
if (hasPrefix) {
if (keyBytes[0] == '-') {
throw new IllegalArgumentException(
"Key contains invalid prefix: ``" + key + "''");
}
for (byte b : keyBytes) {
if (b == delimiter) {
break;
}
if (!(('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z') ||
('0' <= b && b <= '9') ||
(b == '_') || (b == '-') || (b == '+') || (b == '.'))) {
throw new IllegalArgumentException(
"Key contains invalid prefix: ``" + key + "''");
}
}
}
}

protected void checkState() {
if (shuttingDown) {
throw new IllegalStateException("Shutting down");
Expand All @@ -342,7 +299,7 @@ protected void checkState() {
* @return the Operation
*/
public Operation addOp(final String key, final Operation op) {
validateKey(key);
keyValidator.validateKey(key);
checkState();
conn.addOperation(key, op);
return op;
Expand Down Expand Up @@ -1067,7 +1024,7 @@ public <T> BulkFuture<Map<String, T>> asyncGetBulk(Collection<String> keys,
for (String key : keys) {
if (tcIter.hasNext()) {
tcMap.put(key, tcIter.next());
validateKey(key);
keyValidator.validateKey(key);
}
}

Expand Down Expand Up @@ -1200,7 +1157,7 @@ public <T> BulkFuture<Map<String, CASValue<T>>> asyncGetsBulk(Collection<String>
for (String key : keys) {
if (tcIter.hasNext()) {
tcMap.put(key, tcIter.next());
validateKey(key);
keyValidator.validateKey(key);
}
}

Expand Down
4 changes: 0 additions & 4 deletions src/main/java/net/spy/memcached/MemcachedClientIF.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
* This interface is provided as a helper for testing clients of the MemcachedClient.
*/
public interface MemcachedClientIF {
/**
* Maximum supported key length.
*/
int MAX_KEY_LENGTH = 4000;

Collection<SocketAddress> getAvailableServers();

Expand Down
5 changes: 3 additions & 2 deletions src/main/java/net/spy/memcached/collection/BKeyObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.util.Objects;

import net.spy.memcached.KeyValidator;
import net.spy.memcached.util.BTreeUtil;

public class BKeyObject implements Comparable<BKeyObject> {
Expand All @@ -32,14 +33,14 @@ public enum BKeyType {
private final ByteArrayBKey byteArrayBKey;

public BKeyObject(long longBKey) {
BTreeUtil.validateBkey(longBKey);
KeyValidator.validateBKey(longBKey);
this.type = BKeyType.LONG;
this.longBKey = longBKey;
this.byteArrayBKey = null;
}

public BKeyObject(byte[] byteArrayBKey) {
BTreeUtil.validateBkey(byteArrayBKey);
KeyValidator.validateBKey(byteArrayBKey);
this.type = BKeyType.BYTEARRAY;
this.longBKey = null;
this.byteArrayBKey = new ByteArrayBKey(byteArrayBKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.util.Arrays;

import net.spy.memcached.KeyValidator;
import net.spy.memcached.util.BTreeUtil;

public class ByteArrayBKey implements Comparable<ByteArrayBKey> {
Expand All @@ -37,7 +38,7 @@ public class ByteArrayBKey implements Comparable<ByteArrayBKey> {
private final byte[] bkey;

public ByteArrayBKey(byte[] bkey) {
BTreeUtil.validateBkey(bkey);
KeyValidator.validateBKey(bkey);
this.bkey = bkey;
}

Expand Down
21 changes: 0 additions & 21 deletions src/main/java/net/spy/memcached/util/BTreeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
public final class BTreeUtil {

private static final String HEXES = "0123456789ABCDEF";
private static final int MAX_BKEY_BYTE_ARRAY_SIZE = 31;

private BTreeUtil() {
}
Expand Down Expand Up @@ -86,26 +85,6 @@ public static int compareByteArraysInLexOrder(byte[] array1, byte[] array2) {
return array1.length - array2.length;
}

public static void validateBkey(byte[] ...bkeys) {
for (byte[] bkey : bkeys) {
if (bkey == null) {
throw new IllegalArgumentException("bkey is null");
}
if (bkey.length > MAX_BKEY_BYTE_ARRAY_SIZE) {
throw new IllegalArgumentException("bkey size exceeded 31");
}
}
}

public static void validateBkey(long ...bkeys) {
for (long bkey : bkeys) {
if (bkey < 0) {
throw new IllegalArgumentException(
String.format("not supported unsigned long bkey : %s, use byte array bkey", bkey));
}
}
}

public static <T> Element<T> makeBTreeElement(BKeyObject bkey,
CachedData cachedData,
Transcoder<T> tc) {
Expand Down
6 changes: 4 additions & 2 deletions src/test/java/net/spy/memcached/util/BTreeUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import java.util.Arrays;

import net.spy.memcached.KeyValidator;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertThrows;
Expand Down Expand Up @@ -90,15 +92,15 @@ void testCompareDifferentLengthByteArrays() throws Exception {
@Test
void testInValidSizeBkey() {
assertThrows(IllegalArgumentException.class, () -> {
BTreeUtil.validateBkey(new byte[32]);
KeyValidator.validateBKey(new byte[32]);
});
}

@Test
void testMinusLongBkey() {
final long bkey = -1;
assertThrows(IllegalArgumentException.class, () -> {
BTreeUtil.validateBkey(bkey);
KeyValidator.validateBKey(bkey);
});
}
}