Skip to content
Permalink
Browse files
Optimize file path builder and have separate handler for streaming file
patch by Francisco Guerrero, Saranya Krishnakumar; reviewed by Yifan Cai, Dinesh Joshi for CASSANDRASC-37
  • Loading branch information
sarankk authored and yifan-c committed Jun 27, 2022
1 parent 2e233ec commit 24a08f22707901f7641e48f0c26e54b05c0e03c3
Showing 28 changed files with 1,827 additions and 816 deletions.
@@ -8,7 +8,7 @@ version: 2.1
aliases:
base_job: &base_job
machine:
image: ubuntu-1604:202007-01
image: ubuntu-2004:202010-01
working_directory: ~/repo
environment:
TERM: dumb
@@ -18,6 +18,7 @@ aliases:
working_directory: ~/repo
environment:
TERM: dumb
TZ: "America/Los_Angeles"

# we might modify this in the future to accept a parameter for the java package to install
commands:
@@ -71,7 +72,7 @@ jobs:
- checkout
- install_common
- install_kube

- install_java:
version: adoptopenjdk-8-hotspot
- run: sudo update-java-alternatives -s adoptopenjdk-8-hotspot-amd64 && java -version
@@ -141,9 +142,12 @@ jobs:
rpm_build_install:
<<: *centos
steps:
- run: sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
- run: sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
- run: dnf -qy distro-sync
- run: dnf -qy install java-11-openjdk git
- checkout
- run: yum install -y java-11-openjdk-devel # the image uses root by default, no need for sudo
- run: JAVA_HOME=/usr/lib/jvm/java-11-openjdk ./gradlew -i buildRpm
- run: JAVA_HOME=/usr/lib/jvm/java-11-openjdk-11.0.13.0.8-4.el8_5.x86_64 ${PWD}/gradlew -i buildRpm
- run: yum install -y ./build/distributions/cassandra-sidecar*.rpm
- run: test -f /opt/cassandra-sidecar/bin/cassandra-sidecar

@@ -22,6 +22,7 @@ test {
}

dependencies {
compile "io.vertx:vertx-web:${project.vertxVersion}"
compile 'org.slf4j:slf4j-api:1.7.25'
compile 'ch.qos.logback:logback-core:1.2.3'
compile 'ch.qos.logback:logback-classic:1.2.3'
@@ -0,0 +1,48 @@
package org.apache.cassandra.sidecar.common.data;

import org.apache.cassandra.sidecar.common.utils.ValidationUtils;

/**
* Contains the keyspace and table name in Cassandra
*/
public class QualifiedTableName
{
private final String keyspace;
private final String tableName;

/**
* Constructs a qualified name with the given {@code keyspace} and {@code tableName}
*
* @param keyspace the keyspace in Cassandra
* @param tableName the table name in Cassandra
*/
public QualifiedTableName(String keyspace, String tableName)
{
this.keyspace = ValidationUtils.validateKeyspaceName(keyspace);
this.tableName = ValidationUtils.validateTableName(tableName);
}

/**
* @return the keyspace in Cassandra
*/
public String getKeyspace()
{
return keyspace;
}

/**
* @return the table name in Cassandra
*/
public String getTableName()
{
return tableName;
}

/**
* {@inheritDoc}
*/
public String toString()
{
return keyspace + "." + tableName;
}
}
@@ -0,0 +1,45 @@
package org.apache.cassandra.sidecar.common.data;

import org.apache.cassandra.sidecar.common.utils.ValidationUtils;

/**
* Represents an SSTable component that includes a keyspace, table name and component name
*/
public class SSTableComponent extends QualifiedTableName
{
private final String componentName;

/**
* Constructor for the holder class
*
* @param keyspace the keyspace in Cassandra
* @param tableName the table name in Cassandra
* @param componentName the name of the SSTable component
*/
public SSTableComponent(String keyspace, String tableName, String componentName)
{
super(keyspace, tableName);
this.componentName = ValidationUtils.validateComponentName(componentName);
}

/**
* @return the name of the SSTable component
*/
public String getComponentName()
{
return componentName;
}

/**
* {@inheritDoc}
*/
@Override
public String toString()
{
return "SSTableComponent{" +
"keyspace='" + getKeyspace() + '\'' +
", tableName='" + getTableName() + '\'' +
", componentName='" + componentName + '\'' +
'}';
}
}
@@ -0,0 +1,47 @@
package org.apache.cassandra.sidecar.common.data;

import org.apache.cassandra.sidecar.common.utils.ValidationUtils;

/**
* Holder class for the {@code org.apache.cassandra.sidecar.routes.StreamSSTableComponentHandler}
* request parameters
*/
public class StreamSSTableComponentRequest extends SSTableComponent
{
private final String snapshotName;

/**
* Constructor for the holder class
*
* @param keyspace the keyspace in Cassandra
* @param tableName the table name in Cassandra
* @param snapshotName the name of the snapshot
* @param componentName the name of the SSTable component
*/
public StreamSSTableComponentRequest(String keyspace, String tableName, String snapshotName, String componentName)
{
super(keyspace, tableName, componentName);
this.snapshotName = ValidationUtils.validateSnapshotName(snapshotName);
}

/**
* @return the name of the snapshot
*/
public String getSnapshotName()
{
return snapshotName;
}

/**
* {@inheritDoc}
*/
public String toString()
{
return "StreamSSTableComponentRequest{" +
"keyspace='" + getKeyspace() + '\'' +
", tableName='" + getTableName() + '\'' +
", snapshot='" + snapshotName + '\'' +
", componentName='" + getComponentName() + '\'' +
'}';
}
}
@@ -0,0 +1,85 @@
package org.apache.cassandra.sidecar.common.utils;

import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.ext.web.handler.HttpException;
import org.jetbrains.annotations.NotNull;

/**
* Miscellaneous methods used for validation.
*/
public class ValidationUtils
{
private static final Set<String> FORBIDDEN_DIRS = new HashSet<>(Arrays.asList("system_schema",
"system_traces",
"system_distributed",
"system",
"system_auth",
"system_views",
"system_virtual_schema"));
private static final String CHARS_ALLOWED_PATTERN = "[a-zA-Z0-9_-]+";
private static final Pattern PATTERN_WORD_CHARS = Pattern.compile(CHARS_ALLOWED_PATTERN);
private static final String REGEX_COMPONENT = CHARS_ALLOWED_PATTERN + "(.db|.cql|.json|.crc32|TOC.txt)";
private static final String REGEX_DB_TOC_COMPONENT = CHARS_ALLOWED_PATTERN + "(.db|TOC.txt)";

public static String validateKeyspaceName(final String keyspace)
{
Objects.requireNonNull(keyspace, "keyspace must not be null");
validatePattern(keyspace, "keyspace");
if (FORBIDDEN_DIRS.contains(keyspace))
throw new HttpException(HttpResponseStatus.FORBIDDEN.code(), "Forbidden keyspace: " + keyspace);
return keyspace;
}

public static String validateTableName(final String tableName)
{
Objects.requireNonNull(tableName, "tableName must not be null");
validatePattern(tableName, "table name");
return tableName;
}

public static String validateSnapshotName(final String snapshotName)
{
Objects.requireNonNull(snapshotName, "snapshotName must not be null");
// most UNIX systems only disallow file separator and null characters for directory names
if (snapshotName.contains(File.separator) || snapshotName.contains("\0"))
throw new HttpException(HttpResponseStatus.BAD_REQUEST.code(),
"Invalid characters in snapshot name: " + snapshotName);
return snapshotName;
}

public static String validateComponentName(String componentName)
{
return validateComponentNameByRegex(componentName, REGEX_COMPONENT);
}

public static String validateDbOrTOCComponentName(String componentName)
{
return validateComponentNameByRegex(componentName, REGEX_DB_TOC_COMPONENT);
}

@NotNull
private static String validateComponentNameByRegex(String componentName, String regex)
{
Objects.requireNonNull(componentName, "componentName must not be null");
if (!componentName.matches(regex))
throw new HttpException(HttpResponseStatus.BAD_REQUEST.code(),
"Invalid component name: " + componentName);
return componentName;
}

private static void validatePattern(String input, String name)
{
final Matcher matcher = PATTERN_WORD_CHARS.matcher(input);
if (!matcher.matches())
throw new HttpException(HttpResponseStatus.BAD_REQUEST.code(),
"Invalid characters in " + name + ": " + input);
}
}

0 comments on commit 24a08f2

Please sign in to comment.