Compare commits

...

4 Commits

Author SHA1 Message Date
a1398045ac fixed native image configuration task
All checks were successful
CI / build (push) Successful in 37m16s
2025-02-26 17:42:02 +08:00
1f93602102 added healthcheck role
improved documentation

client configuration promoted to standalone class
2025-02-26 15:26:18 +08:00
c818463a2e fixed entrypoint in native Docker image 2025-02-25 22:26:29 +08:00
cd28563985 fixed typo 2025-02-25 22:06:52 +08:00
40 changed files with 748 additions and 271 deletions

View File

@@ -58,7 +58,7 @@ jobs:
cache-from: type=registry,ref=gitea.woggioni.net/woggioni/rbcs:buildx cache-from: type=registry,ref=gitea.woggioni.net/woggioni/rbcs:buildx
cache-to: type=registry,mode=max,compression=zstd,image-manifest=true,oci-mediatypes=true,ref=gitea.woggioni.net/woggioni/rbcs:buildx cache-to: type=registry,mode=max,compression=zstd,image-manifest=true,oci-mediatypes=true,ref=gitea.woggioni.net/woggioni/rbcs:buildx
- -
name: Build rbcs memcache Docker image name: Build rbcs native Docker image
uses: docker/build-push-action@v5.3.0 uses: docker/build-push-action@v5.3.0
with: with:
context: "docker/build/docker" context: "docker/build/docker"

148
README.md
View File

@@ -15,6 +15,7 @@ and throttling.
### Downloading the jar file ### Downloading the jar file
You can download the latest version from [this link](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-cli/) You can download the latest version from [this link](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-cli/)
Assuming you have Java 21 or later installed, you can launch the server directly with Assuming you have Java 21 or later installed, you can launch the server directly with
```bash ```bash
@@ -33,19 +34,30 @@ docker pull gitea.woggioni.net/woggioni/rbcs:latest
By default it will start an HTTP server bound to localhost and listening on port 8080 with no authentication, By default it will start an HTTP server bound to localhost and listening on port 8080 with no authentication,
writing data to the disk, that you can use for testing writing data to the disk, that you can use for testing
### Using the native executable
If you are on a Linux X86_64 machine you can download the native executable
from [here](https://gitea.woggioni.net/woggioni/-/packages/maven/net.woggioni:rbcs-cli/).
It behaves the same as the jar file but it doesn't require a JVM and it has faster startup times.
becausue of GraalVm's [closed-world assumption](https://www.graalvm.org/latest/reference-manual/native-image/basics/#static-analysis),
the native executable does not supports plugins, so it comes with all plugins embedded into it.
## Usage ## Usage
### Configuration ### Configuration
The location of the `rbcs.xml` configuration file depends on the operating system, The location of the `rbcs-server.xml` configuration file depends on the operating system,
Alternatively it can be changed setting the `RBCS_CONFIGURATION_DIR` environmental variable or `net.woggioni.rbcs.conf.dir` Java system property Alternatively it can be changed setting the `RBCS_CONFIGURATION_DIR` environmental variable or `net.woggioni.rbcs.conf.dir` Java system property
to the directory that contain the `rbcs.xml` file. to the directory that contain the `rbcs-server.xml` file.
The server configuration file follows the XML format and uses XML schema for validation The server configuration file follows the XML format and uses XML schema for validation
(you can find the schema for the main configuration file [here](https://gitea.woggioni.net/woggioni/rbcs/src/branch/master/rbcs-server/src/main/resources/net/woggioni/rbcs/server/schema/rbcs.xsd)). (you can find the schema for the main configuration file [here](https://gitea.woggioni.net/woggioni/rbcs/src/branch/master/rbcs-server/src/main/resources/net/woggioni/rbcs/server/schema/rbcs-server.xsd)).
The configuration values are enclosed inside XML attribute and support system property / environmental variable interpolation. The configuration values are enclosed inside XML attribute and support system property / environmental variable interpolation.
As an example, you can configure RBCS to read the server port number from the `RBCS_SERVER_PORT` environmental variable As an example, you can configure RBCS to read the server port number from the `RBCS_SERVER_PORT` environmental variable
and the bind address from the `rbc.bind.address` JVM system property with. and the bind address from the `rbc.bind.address` JVM system property with
```xml
<bind host="${sys:rpc.bind.address}" port="${env:RBCS_SERVER_PORT}"/>
```
Full documentation for all tags and attributes is available [here](doc/server_configuration.md). Full documentation for all tags and attributes is available [here](doc/server_configuration.md).
@@ -128,10 +140,136 @@ Read [Gradle documentation](https://docs.gradle.org/current/userguide/build_cach
Alternatively you can set those properties in your `<project>/pom.xml` Alternatively you can set those properties in your `<project>/pom.xml`
Read [here](https://maven.apache.org/extensions/maven-build-cache-extension/remote-cache.html) Read [here](https://maven.apache.org/extensions/maven-build-cache-extension/remote-cache.html)
for more informations for more informations
## Authentication
RBCS supports 2 authentication mechanisms:
- HTTP basic authentication
- TLS certificate authentication
### Configure HTTP basic authentication
Add a `<basic>` element to the `<authentication>` element in your `rbcs-server.xml`
```xml
<authentication>
<basic/>
</authentication>
```
### Configure TLS certificate authentication
Add a `<client-certificate>` element to the `<authentication>` element in your `rbcs-server.xml`
```xml
<authentication>
<client-certificate>
<user-extractor attribute-name="CN" pattern="(.*)"/>
<group-extractor attribute-name="O" pattern="(.*)"/>
</client-certificate>
</authentication>
```
The `<user-extractor>` here determines how the username is extracted from the
subject's X.500 name in the TLS certificate presented by the client, where `attribute-name`
is the `RelativeDistinguishedName` (RDN) identifier and pattern is a regular expression
that will be applied to extract the username from the first group present in the regex.
An error will be thrown if the regular expression contains no groups, while additional
groups are ignored.
Similarly, the `<group-extractor>` here determines how the group name is extracted from the
subject's X.500 name in the TLS certificate presented by the client.
Note that this allows to assign roles to incoming requests without necessarily assigning them
a username.
## Access control
RBCS supports role-based access control (RBAC), three roles are available:
- `Reader` can perform `GET` calls
- `Writer` can perform `PUT` calls
- `Healthcheck` can perform `TRACE` calls
Roles are assigned to groups so that a user will have a role only if that roles belongs
to one of the groups he is a member of.
There is also a special `<anonymous>` user
which matches any request who hasn't been authenticated and that can be assigned
to any group like a normal user. This permits to have a build cache that is
publicly readable but only writable by authenticated users (e.g. CI/CD pipeline).
### Defining users
Users can be defined in the `<authorization>` element
```xml
<authorization>
<users>
<user name="user1" password="kb/vNnkn2RvyPkTN6Q07uH0F7wI7u61MkManD3NHregRukBg4KHehfbqtLTb39fZjHA+SRH+EpEWDCf+Rihr5H5C1YN5qwmArV0p8O5ptC4="/>
<user name="user2" password="2J7MAhdIzZ3SO+JGB+K6wPhb4P5LH1L4L7yJCl5QrxNfAWRr5jTUExJRbcgbH1UfnkCbIO1p+xTDq+FCj3LFBZeMZUNZ47npN+WR7AX3VTo="/>
<anonymous/>
</users>
<groups>
<group name="readers">
<users>
<anonymous/>
</users>
<roles>
<reader/>
</roles>
</group>
<group name="writers">
<users>
<user ref="user1"/>
<user ref="user2"/>
</users>
<roles>
<reader/>
<writer/>
<healthcheck/>
</roles>
</group>
</groups>
</authorization>
```
The `password` attribute is only used for HTTP Basic authentication, so it can be omitted
if you use TLS certificate authentication. It must contain a password hash that can be derived from
the actual password using the following command
```bash
java -jar rbcs-cli.jar password
```
## Reliability
RBCS implements the [TRACE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/TRACE) HTTP method and this functionality can be used
as a health check (mind you need to have `Healthcheck` role in order to perform it and match the server's `prefix` in the URL).
## RBCS Client
RBCS ships with a command line client that can be used for testing, benchmarking or to manually
upload/download files to the cache. It must be configured with the `rbcs-client.xml`,
whose location follows the same logic of the `rbcs-server.xml`
### GET command
```bash
java -jar rbcs-cli.jar client -p $CLIENT_PROFILE_NAME get -k $CACHE_KEY -v $FILE_WHERE_THE_VALUE_WILL_BE_STORED
```
### PUT command
```bash
java -jar rbcs-cli.jar client -p $CLIENT_PROFILE_NAME put -k $CACHE_KEY -v $FILE_TO_BE_UPLOADED
```
## Logging
RBCS uses [logback](https://logback.qos.ch/) and ships with a [default logging configuration](./conf/logback.xml) that
can be overridden with `-Dlogback.configurationFile=path/to/custom/configuration.xml`, refer to
[Logback documentation](https://logback.qos.ch/manual/configuration.html) for more details about
how to configure Logback
## FAQ ## FAQ
### Why should I use a build cache? ### Why should I use a build cache?

125
doc/client_configuration.md Normal file
View File

@@ -0,0 +1,125 @@
# XML Schema Documentation: RBCS Client Configuration
This document provides detailed information about the XML schema for RBCS client configuration, which defines profiles for connecting to RBCS servers.
## Root Element
### `profiles`
The root element that contains a collection of server profiles.
- **Type**: `profilesType`
- **Contains**: Zero or more `profile` elements
## Complex Types
### `profilesType`
Defines the structure for the profiles collection.
- **Elements**:
- `profile`: Server connection profile (0 to unbounded)
### `profileType`
Defines a server connection profile with authentication, connection settings, and retry policies.
- **Attributes**:
- `name` (required): Name of the server profile, referenced with the '-p' parameter in rbcs-cli
- `base-url` (required): RBCs server URL
- `max-connections`: Maximum number of concurrent TCP connections (default: 50)
- `connection-timeout`: Timeout for establishing connections
- `enable-compression`: Whether to enable HTTP compression (default: true)
- **Elements** (in sequence):
- **Authentication** (choice of one):
- `no-auth`: Disable authentication
- `basic-auth`: Enable HTTP basic authentication
- `tls-client-auth`: Enable TLS certificate authentication
- `connection` (optional): Connection timeout settings
- `retry-policy` (optional): Retry policy for failed requests
- `tls-trust-store` (optional): Custom truststore for server certificate validation
### `connectionType`
Defines connection timeout settings.
- **Attributes**:
- `idle-timeout`: Close connection after inactivity period (default: PT30S - 30 seconds)
- `read-idle-timeout`: Close connection when no read occurs (default: PT60S - 60 seconds)
- `write-idle-timeout`: Close connection when no write occurs (default: PT60S - 60 seconds)
### `noAuthType`
Indicates no authentication should be used.
- No attributes or elements
### `basicAuthType`
Configures HTTP Basic Authentication.
- **Attributes**:
- `user` (required): Username for authentication
- `password` (required): Password for authentication
### `tlsClientAuthType`
Configures TLS client certificate authentication.
- **Attributes**:
- `key-store-file` (required): Path to the keystore file
- `key-store-password` (required): Password to open the keystore
- `key-alias` (required): Alias of the keystore entry with the private key
- `key-password` (optional): Private key entry's encryption password
### `retryType`
Defines retry policy using exponential backoff.
- **Attributes**:
- `max-attempts` (required): Maximum number of retry attempts
- `initial-delay`: Delay before first retry (default: PT1S - 1 second)
- `exp`: Exponent for computing next delay (default: 2.0)
### `trustStoreType`
Configures custom truststore for server certificate validation.
- **Attributes**:
- `file` (required): Path to the truststore file
- `password`: Truststore file password
- `check-certificate-status`: Whether to check certificate validity using CRL/OCSP
- `verify-server-certificate`: Whether to validate server certificates (default: true)
## Sample XML Document
```xml
<?xml version="1.0" encoding="UTF-8"?>
<profiles xmlns="urn:net.woggioni.rbcs.client">
<!-- Profile with basic authentication -->
<profile name="production-server"
base-url="https://rbcs.example.com/api"
max-connections="100"
enable-compression="true">
<basic-auth user="admin" password="secure_password123"/>
<connection idle-timeout="PT45S"
read-idle-timeout="PT90S"
write-idle-timeout="PT90S"/>
<retry-policy max-attempts="5"
initial-delay="PT2S"
exp="1.5"/>
<tls-trust-store file="/path/to/truststore.jks"
password="truststore_password"
check-certificate-status="true"/>
</profile>
<!-- Profile with TLS client authentication -->
<profile name="secure-server"
base-url="https://secure.example.com/api"
max-connections="25">
<tls-client-auth key-store-file="/path/to/keystore.p12"
key-store-password="keystore_password"
key-alias="client-cert"
key-password="key_password"/>
<retry-policy max-attempts="3"/>
</profile>
<!-- Profile with no authentication -->
<profile name="development"
base-url="http://localhost:8080/api"
enable-compression="false">
<no-auth/>
</profile>
</profiles>
```
This sample XML document demonstrates three different profiles with various authentication methods and configuration options as defined in the schema.

View File

@@ -1,4 +1,3 @@
### RBCS server configuration file elements and attributes ### RBCS server configuration file elements and attributes
#### Root Element: `server` #### Root Element: `server`
@@ -109,6 +108,7 @@ Configures TLS encryption.
- `password`: Keystore password - `password`: Keystore password
- `key-alias` (required): Private key alias - `key-alias` (required): Private key alias
- `key-password`: Private key password - `key-password`: Private key password
- `<truststore>`: Client certificate verification - `<truststore>`: Client certificate verification
**Attributes:** **Attributes:**
@@ -126,7 +126,7 @@ Configures TLS encryption.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd" xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd"
> >
<bind host="0.0.0.0" port="8080" incoming-connections-backlog-size="1024"/> <bind host="0.0.0.0" port="8080" incoming-connections-backlog-size="1024"/>
<connection <connection

View File

@@ -20,4 +20,4 @@ FROM scratch AS release-native
ADD rbcs-cli.upx /rbcs/rbcs-cli ADD rbcs-cli.upx /rbcs/rbcs-cli
ENV RBCS_CONFIGURATION_DIR="/rbcs" ENV RBCS_CONFIGURATION_DIR="/rbcs"
WORKDIR /rbcs WORKDIR /rbcs
ENTRYPOINT ["./rbcs/rbcs-cli"] ENTRYPOINT ["/rbcs/rbcs-cli"]

View File

@@ -1,5 +1,5 @@
package net.woggioni.rbcs.api; package net.woggioni.rbcs.api;
public enum Role { public enum Role {
Reader, Writer Reader, Writer, Healthcheck
} }

View File

@@ -90,13 +90,21 @@ Provider<EnvelopeJarTask> envelopeJarTaskProvider = tasks.named(EnvelopePlugin.E
} }
tasks.named(NativeImagePlugin.CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask) { tasks.named(NativeImagePlugin.CONFIGURE_NATIVE_IMAGE_TASK_NAME, NativeImageConfigurationTask) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.ORACLE
}
mainClass = "net.woggioni.rbcs.cli.graal.GraalNativeImageConfiguration" mainClass = "net.woggioni.rbcs.cli.graal.GraalNativeImageConfiguration"
setClasspath(configurations.configureNativeImageRuntimeClasspath + sourceSets.graal.output.classesDirs) classpath = project.files(
configurations.configureNativeImageRuntimeClasspath,
sourceSets.configureNativeImage.output
)
mergeConfiguration = false mergeConfiguration = false
systemProperty('logback.configurationFile', 'classpath:net/woggioni/rbcs/cli/logback.xml') systemProperty('logback.configurationFile', 'classpath:net/woggioni/rbcs/cli/logback.xml')
systemProperty('io.netty.leakDetectionLevel', 'DISABLED') systemProperty('io.netty.leakDetectionLevel', 'DISABLED')
modularity.inferModulePath = false modularity.inferModulePath = false
enabled = false enabled = true
} }
nativeImage { nativeImage {

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rbcs-client:profiles xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs-client="urn:net.woggioni.rbcs.client"
xs:schemaLocation="urn:net.woggioni.rbcs.client jpms://net.woggioni.rbcs.client/net/woggioni/rbcs/client/schema/rbcs-client.xsd"
>
<profile name="profile1" base-url="https://rbcs1.example.com/">
<no-auth/>
<connection write-idle-timeout="PT60S"
read-idle-timeout="PT60S"
idle-timeout="PT30S" />
</profile>
<profile name="profile2" base-url="https://rbcs2.example.com/">
<basic-auth user="user" password="password"/>
</profile>
</rbcs-client:profiles>

View File

@@ -1,74 +1,46 @@
{ {
"resources": { "resources":{
"includes": [ "includes":[{
{ "pattern":"\\QMETA-INF/MANIFEST.MF\\E"
"pattern": "\\QMETA-INF/MANIFEST.MF\\E" }, {
}, "pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E"
{ }, {
"pattern": "\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E" "pattern":"\\QMETA-INF/services/java.lang.System$LoggerFinder\\E"
}, }, {
{ "pattern":"\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E"
"pattern": "\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" }, {
}, "pattern":"\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E"
{ }, {
"pattern": "\\QMETA-INF/services/java.net.spi.InetAddressResolverProvider\\E" "pattern":"\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E"
}, }, {
{ "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
"pattern": "\\QMETA-INF/services/java.net.spi.URLStreamHandlerProvider\\E" }, {
}, "pattern":"\\QMETA-INF/services/javax.xml.parsers.DocumentBuilderFactory\\E"
{ }, {
"pattern": "\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E" "pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E"
}, }, {
{ "pattern":"\\QMETA-INF/services/net.woggioni.rbcs.api.CacheProvider\\E"
"pattern": "\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" }, {
}, "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E"
{ }, {
"pattern": "\\QMETA-INF/services/javax.xml.parsers.DocumentBuilderFactory\\E" "pattern":"\\Qclasspath:net/woggioni/rbcs/cli/logback.xml\\E"
}, }, {
{ "pattern":"\\Qlogback-test.scmo\\E"
"pattern": "\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E" }, {
}, "pattern":"\\Qlogback.scmo\\E"
{ }, {
"pattern": "\\QMETA-INF/services/net.woggioni.rbcs.api.CacheProvider\\E" "pattern":"\\Qnet/woggioni/rbcs/cli/logback.xml\\E"
}, }, {
{ "pattern":"\\Qnet/woggioni/rbcs/client/schema/rbcs-client.xsd\\E"
"pattern": "\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" }, {
}, "pattern":"\\Qnet/woggioni/rbcs/server/rbcs-default.xml\\E"
{ }, {
"pattern": "\\Qclasspath:net/woggioni/rbcs/cli/logback.xml\\E" "pattern":"\\Qnet/woggioni/rbcs/server/schema/rbcs-server.xsd\\E"
}, }, {
{ "pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
"pattern": "\\Qlogback-test.scmo\\E" }]},
}, "bundles":[{
{ "name":"com.sun.org.apache.xerces.internal.impl.xpath.regex.message",
"pattern": "\\Qlogback.scmo\\E" "locales":[""]
}, }]
{
"pattern": "\\Qnet/woggioni/rbcs/cli/logback.xml\\E"
},
{
"pattern": "\\Qnet/woggioni/rbcs/server/rbcs-default.xml\\E"
},
{
"pattern": "\\Qnet/woggioni/rbcs/server/schema/rbcs.xsd\\E"
},
{
"pattern": "\\Qnet/woggioni/rbcs/client/schema/rbcs-client.xsd\\E"
},
{
"pattern": "\\Q/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd\\E"
},
{
"pattern": "java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
}
]
},
"bundles": [
{
"name": "com.sun.org.apache.xerces.internal.impl.xpath.regex.message",
"locales": [
""
]
}
]
} }

View File

@@ -1,5 +1,8 @@
{ {
"types":[ "types":[
{
"name":"java.lang.String"
},
{ {
"name":"net.woggioni.rbcs.api.CacheValueMetadata" "name":"net.woggioni.rbcs.api.CacheValueMetadata"
} }

View File

@@ -1,12 +1,14 @@
package net.woggioni.rbcs.cli.graal package net.woggioni.rbcs.cli.graal
import net.woggioni.jwo.NullOutputStream
import net.woggioni.rbcs.api.Configuration import net.woggioni.rbcs.api.Configuration
import net.woggioni.rbcs.api.Configuration.User import net.woggioni.rbcs.api.Configuration.User
import net.woggioni.rbcs.api.Role import net.woggioni.rbcs.api.Role
import net.woggioni.rbcs.cli.RemoteBuildCacheServerCli import net.woggioni.rbcs.cli.RemoteBuildCacheServerCli
import net.woggioni.rbcs.cli.impl.commands.BenchmarkCommand import net.woggioni.rbcs.cli.impl.commands.BenchmarkCommand
import net.woggioni.rbcs.cli.impl.commands.GetCommand
import net.woggioni.rbcs.cli.impl.commands.HealthCheckCommand import net.woggioni.rbcs.cli.impl.commands.HealthCheckCommand
import net.woggioni.rbcs.client.RemoteBuildCacheClient import net.woggioni.rbcs.cli.impl.commands.PutCommand
import net.woggioni.rbcs.common.HostAndPort import net.woggioni.rbcs.common.HostAndPort
import net.woggioni.rbcs.common.PasswordSecurity.hashPassword import net.woggioni.rbcs.common.PasswordSecurity.hashPassword
import net.woggioni.rbcs.common.RBCS import net.woggioni.rbcs.common.RBCS
@@ -16,12 +18,15 @@ import net.woggioni.rbcs.server.cache.FileSystemCacheConfiguration
import net.woggioni.rbcs.server.cache.InMemoryCacheConfiguration import net.woggioni.rbcs.server.cache.InMemoryCacheConfiguration
import net.woggioni.rbcs.server.configuration.Parser import net.woggioni.rbcs.server.configuration.Parser
import net.woggioni.rbcs.server.memcache.MemcacheCacheConfiguration import net.woggioni.rbcs.server.memcache.MemcacheCacheConfiguration
import java.io.ByteArrayInputStream
import java.net.URI import java.net.URI
import java.nio.file.Path import java.nio.file.Path
import java.time.Duration import java.time.Duration
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.zip.Deflater import java.util.zip.Deflater
import net.woggioni.rbcs.client.Configuration as ClientConfiguration
import net.woggioni.rbcs.client.impl.Parser as ClientConfigurationParser
object GraalNativeImageConfiguration { object GraalNativeImageConfiguration {
@JvmStatic @JvmStatic
@@ -30,15 +35,16 @@ object GraalNativeImageConfiguration {
val serverDoc = RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL.openStream().use { val serverDoc = RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL.openStream().use {
Xml.parseXml(RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL, it) Xml.parseXml(RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL, it)
} }
Parser.parse(doc) Parser.parse(serverDoc)
val clientDoc = RemoteBuildCacheClient.Configuration.openStream().use { val url = URI.create("file:conf/rbcs-client.xml").toURL()
Xml.parseXml(RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL, it) val clientDoc = url.openStream().use {
Xml.parseXml(url, it)
} }
Parser.parse(doc) ClientConfigurationParser.parse(clientDoc)
val PASSWORD = "password" val PASSWORD = "password"
val readersGroup = Configuration.Group("readers", setOf(Role.Reader), null, null) val readersGroup = Configuration.Group("readers", setOf(Role.Reader, Role.Healthcheck), null, null)
val writersGroup = Configuration.Group("writers", setOf(Role.Writer), null, null) val writersGroup = Configuration.Group("writers", setOf(Role.Writer), null, null)
@@ -126,30 +132,44 @@ object GraalNativeImageConfiguration {
val serverHandle = RemoteBuildCacheServer(serverConfiguration).run() val serverHandle = RemoteBuildCacheServer(serverConfiguration).run()
val clientProfile = RemoteBuildCacheClient.Configuration.Profile( val clientProfile = ClientConfiguration.Profile(
URI.create("http://127.0.0.1:$serverPort/"), URI.create("http://127.0.0.1:$serverPort/"),
null, null,
RemoteBuildCacheClient.Configuration.Authentication.BasicAuthenticationCredentials("user3", PASSWORD), ClientConfiguration.Authentication.BasicAuthenticationCredentials("user3", PASSWORD),
Duration.ofSeconds(3), Duration.ofSeconds(3),
10, 10,
true, true,
RemoteBuildCacheClient.Configuration.RetryPolicy( ClientConfiguration.RetryPolicy(
3, 3,
1000, 1000,
1.2 1.2
), ),
RemoteBuildCacheClient.Configuration.TrustStore(null, null, false, false) ClientConfiguration.TrustStore(null, null, false, false)
) )
HealthCheckCommand.run(clientProfile) HealthCheckCommand.execute(clientProfile)
BenchmarkCommand.run( BenchmarkCommand.execute(
clientProfile, clientProfile,
1000, 1000,
0x100, 0x100,
true true
) )
PutCommand.execute(
clientProfile,
"some-file.bin",
ByteArrayInputStream(ByteArray(0x1000) { it.toByte() }),
"application/octet-setream",
"attachment; filename=\"some-file.bin\""
)
GetCommand.execute(
clientProfile,
"some-file.bin",
NullOutputStream()
)
serverHandle.sendShutdownSignal() serverHandle.sendShutdownSignal()
try { try {
serverHandle.get() serverHandle.get()

View File

@@ -11,7 +11,7 @@ import net.woggioni.rbcs.cli.impl.commands.PasswordHashCommand
import net.woggioni.rbcs.cli.impl.commands.PutCommand import net.woggioni.rbcs.cli.impl.commands.PutCommand
import net.woggioni.rbcs.cli.impl.commands.ServerCommand import net.woggioni.rbcs.cli.impl.commands.ServerCommand
import net.woggioni.rbcs.common.RbcsUrlStreamHandlerFactory import net.woggioni.rbcs.common.RbcsUrlStreamHandlerFactory
import net.woggioni.rbcs.common.contextLogger import net.woggioni.rbcs.common.createLogger
import picocli.CommandLine import picocli.CommandLine
import picocli.CommandLine.Model.CommandSpec import picocli.CommandLine.Model.CommandSpec
@@ -36,7 +36,7 @@ class RemoteBuildCacheServerCli : RbcsCommand() {
//We're running in an envelope jar and custom URL protocols won't work //We're running in an envelope jar and custom URL protocols won't work
RbcsUrlStreamHandlerFactory.install() RbcsUrlStreamHandlerFactory.install()
} }
val log = contextLogger() val log = createLogger<RemoteBuildCacheServerCli>()
val app = Application.builder("rbcs") val app = Application.builder("rbcs")
.configurationDirectoryEnvVar("RBCS_CONFIGURATION_DIR") .configurationDirectoryEnvVar("RBCS_CONFIGURATION_DIR")
.configurationDirectoryPropertyKey("net.woggioni.rbcs.conf.dir") .configurationDirectoryPropertyKey("net.woggioni.rbcs.conf.dir")

View File

@@ -5,6 +5,7 @@ import net.woggioni.jwo.LongMath
import net.woggioni.rbcs.api.CacheValueMetadata import net.woggioni.rbcs.api.CacheValueMetadata
import net.woggioni.rbcs.cli.impl.RbcsCommand import net.woggioni.rbcs.cli.impl.RbcsCommand
import net.woggioni.rbcs.cli.impl.converters.ByteSizeConverter import net.woggioni.rbcs.cli.impl.converters.ByteSizeConverter
import net.woggioni.rbcs.client.Configuration
import net.woggioni.rbcs.client.RemoteBuildCacheClient import net.woggioni.rbcs.client.RemoteBuildCacheClient
import net.woggioni.rbcs.common.createLogger import net.woggioni.rbcs.common.createLogger
import net.woggioni.rbcs.common.debug import net.woggioni.rbcs.common.debug
@@ -29,10 +30,10 @@ class BenchmarkCommand : RbcsCommand() {
companion object { companion object {
private val log = createLogger<BenchmarkCommand>() private val log = createLogger<BenchmarkCommand>()
fun run(profile : RemoteBuildCacheClient.Configuration.Profile, fun execute(profile : Configuration.Profile,
numberOfEntries : Int, numberOfEntries : Int,
entrySize : Int, entrySize : Int,
useRandomValue : Boolean, useRandomValue : Boolean,
) { ) {
val progressThreshold = LongMath.ceilDiv(numberOfEntries.toLong(), 20) val progressThreshold = LongMath.ceilDiv(numberOfEntries.toLong(), 20)
RemoteBuildCacheClient(profile).use { client -> RemoteBuildCacheClient(profile).use { client ->
@@ -175,7 +176,7 @@ class BenchmarkCommand : RbcsCommand() {
clientCommand.configuration.profiles[profileName] clientCommand.configuration.profiles[profileName]
?: throw IllegalArgumentException("Profile $profileName does not exist in configuration") ?: throw IllegalArgumentException("Profile $profileName does not exist in configuration")
} }
run( execute(
profile, profile,
numberOfEntries, numberOfEntries,
size, size,

View File

@@ -2,8 +2,11 @@ package net.woggioni.rbcs.cli.impl.commands
import net.woggioni.jwo.Application import net.woggioni.jwo.Application
import net.woggioni.rbcs.cli.impl.RbcsCommand import net.woggioni.rbcs.cli.impl.RbcsCommand
import net.woggioni.rbcs.client.RemoteBuildCacheClient import net.woggioni.rbcs.client.Configuration
import net.woggioni.rbcs.common.createLogger
import net.woggioni.rbcs.common.debug
import picocli.CommandLine import picocli.CommandLine
import java.lang.IllegalArgumentException
import java.nio.file.Path import java.nio.file.Path
@CommandLine.Command( @CommandLine.Command(
@@ -24,15 +27,20 @@ class ClientCommand(app : Application) : RbcsCommand() {
names = ["-p", "--profile"], names = ["-p", "--profile"],
description = ["Name of the client profile to be used"], description = ["Name of the client profile to be used"],
paramLabel = "PROFILE", paramLabel = "PROFILE",
required = true required = false
) )
var profileName : String? = null var profileName : String? = null
get() = field ?: throw IllegalArgumentException("A profile name must be specified using the '-p' command line parameter")
val configuration : RemoteBuildCacheClient.Configuration by lazy { val configuration : Configuration by lazy {
RemoteBuildCacheClient.Configuration.parse(configurationFile) Configuration.parse(configurationFile)
} }
override fun run() { override fun run() {
val log = createLogger<ClientCommand>()
log.debug {
"Using configuration file '$configurationFile'"
}
println("Available profiles:") println("Available profiles:")
configuration.profiles.forEach { (profileName, _) -> configuration.profiles.forEach { (profileName, _) ->
println(profileName) println(profileName)

View File

@@ -1,9 +1,11 @@
package net.woggioni.rbcs.cli.impl.commands package net.woggioni.rbcs.cli.impl.commands
import net.woggioni.rbcs.cli.impl.RbcsCommand import net.woggioni.rbcs.cli.impl.RbcsCommand
import net.woggioni.rbcs.client.Configuration
import net.woggioni.rbcs.client.RemoteBuildCacheClient import net.woggioni.rbcs.client.RemoteBuildCacheClient
import net.woggioni.rbcs.common.createLogger import net.woggioni.rbcs.common.createLogger
import picocli.CommandLine import picocli.CommandLine
import java.io.OutputStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
@@ -13,8 +15,20 @@ import java.nio.file.Path
showDefaultValues = true showDefaultValues = true
) )
class GetCommand : RbcsCommand() { class GetCommand : RbcsCommand() {
companion object{ companion object {
private val log = createLogger<GetCommand>() private val log = createLogger<GetCommand>()
fun execute(profile : Configuration.Profile, key : String, outputStream: OutputStream) {
RemoteBuildCacheClient(profile).use { client ->
client.get(key).thenApply { value ->
value?.let {
outputStream.use {
it.write(value)
}
} ?: throw NoSuchElementException("No value found for key $key")
}.get()
}
}
} }
@CommandLine.Spec @CommandLine.Spec
@@ -40,14 +54,6 @@ class GetCommand : RbcsCommand() {
clientCommand.configuration.profiles[profileName] clientCommand.configuration.profiles[profileName]
?: throw IllegalArgumentException("Profile $profileName does not exist in configuration") ?: throw IllegalArgumentException("Profile $profileName does not exist in configuration")
} }
RemoteBuildCacheClient(profile).use { client -> execute(profile, key, (output?.let(Files::newOutputStream) ?: System.out))
client.get(key).thenApply { value ->
value?.let {
(output?.let(Files::newOutputStream) ?: System.out).use {
it.write(value)
}
} ?: throw NoSuchElementException("No value found for key $key")
}.get()
}
} }
} }

View File

@@ -1,6 +1,7 @@
package net.woggioni.rbcs.cli.impl.commands package net.woggioni.rbcs.cli.impl.commands
import net.woggioni.rbcs.cli.impl.RbcsCommand import net.woggioni.rbcs.cli.impl.RbcsCommand
import net.woggioni.rbcs.client.Configuration
import net.woggioni.rbcs.client.RemoteBuildCacheClient import net.woggioni.rbcs.client.RemoteBuildCacheClient
import net.woggioni.rbcs.common.createLogger import net.woggioni.rbcs.common.createLogger
import picocli.CommandLine import picocli.CommandLine
@@ -16,7 +17,7 @@ class HealthCheckCommand : RbcsCommand() {
companion object{ companion object{
private val log = createLogger<HealthCheckCommand>() private val log = createLogger<HealthCheckCommand>()
fun run(profile : RemoteBuildCacheClient.Configuration.Profile) { fun execute(profile : Configuration.Profile) {
RemoteBuildCacheClient(profile).use { client -> RemoteBuildCacheClient(profile).use { client ->
val random = Random(SecureRandom.getInstance("NativePRNGNonBlocking").nextLong()) val random = Random(SecureRandom.getInstance("NativePRNGNonBlocking").nextLong())
val nonce = ByteArray(0xa0) val nonce = ByteArray(0xa0)
@@ -47,6 +48,6 @@ class HealthCheckCommand : RbcsCommand() {
clientCommand.configuration.profiles[profileName] clientCommand.configuration.profiles[profileName]
?: throw IllegalArgumentException("Profile $profileName does not exist in configuration") ?: throw IllegalArgumentException("Profile $profileName does not exist in configuration")
} }
run(profile) execute(profile)
} }
} }

View File

@@ -5,6 +5,7 @@ import net.woggioni.jwo.JWO
import net.woggioni.jwo.NullOutputStream import net.woggioni.jwo.NullOutputStream
import net.woggioni.rbcs.api.CacheValueMetadata import net.woggioni.rbcs.api.CacheValueMetadata
import net.woggioni.rbcs.cli.impl.RbcsCommand import net.woggioni.rbcs.cli.impl.RbcsCommand
import net.woggioni.rbcs.client.Configuration
import net.woggioni.rbcs.client.RemoteBuildCacheClient import net.woggioni.rbcs.client.RemoteBuildCacheClient
import net.woggioni.rbcs.common.createLogger import net.woggioni.rbcs.common.createLogger
import picocli.CommandLine import picocli.CommandLine
@@ -19,8 +20,22 @@ import java.util.UUID
showDefaultValues = true showDefaultValues = true
) )
class PutCommand : RbcsCommand() { class PutCommand : RbcsCommand() {
companion object{ companion object {
private val log = createLogger<PutCommand>() private val log = createLogger<PutCommand>()
fun execute(profile: Configuration.Profile,
actualKey : String,
inputStream: InputStream,
mimeType : String?,
contentDisposition: String?
) {
RemoteBuildCacheClient(profile).use { client ->
inputStream.use {
client.put(actualKey, it.readAllBytes(), CacheValueMetadata(contentDisposition, mimeType))
}.get()
println(profile.serverURI.resolve(actualKey))
}
}
} }
@@ -92,10 +107,7 @@ class PutCommand : RbcsCommand() {
} }
actualKey = key ?: UUID.randomUUID().toString() actualKey = key ?: UUID.randomUUID().toString()
} }
inputStream.use { execute(profile, actualKey, inputStream, mimeType, contentDisposition)
client.put(actualKey, it.readAllBytes(), CacheValueMetadata(contentDisposition, mimeType))
}.get()
println(profile.serverURI.resolve(actualKey))
} }
} }
} }

View File

@@ -0,0 +1,61 @@
package net.woggioni.rbcs.client
import net.woggioni.rbcs.client.impl.Parser
import net.woggioni.rbcs.common.Xml
import java.net.URI
import java.nio.file.Files
import java.nio.file.Path
import java.security.PrivateKey
import java.security.cert.X509Certificate
import java.time.Duration
data class Configuration(
val profiles: Map<String, Profile>
) {
sealed class Authentication {
data class TlsClientAuthenticationCredentials(
val key: PrivateKey,
val certificateChain: Array<X509Certificate>
) : Authentication()
data class BasicAuthenticationCredentials(val username: String, val password: String) : Authentication()
}
class TrustStore (
var file: Path?,
var password: String?,
var checkCertificateStatus: Boolean = false,
var verifyServerCertificate: Boolean = true,
)
class RetryPolicy(
val maxAttempts: Int,
val initialDelayMillis: Long,
val exp: Double
)
class Connection(
val readIdleTimeout: Duration,
val writeIdleTimeout: Duration,
val idleTimeout: Duration,
)
data class Profile(
val serverURI: URI,
val connection: Connection?,
val authentication: Authentication?,
val connectionTimeout: Duration?,
val maxConnections: Int,
val compressionEnabled: Boolean,
val retryPolicy: RetryPolicy?,
val tlsTruststore : TrustStore?
)
companion object {
fun parse(path: Path): Configuration {
return Files.newInputStream(path).use {
Xml.parseXml(path.toUri().toURL(), it)
}.let(Parser::parse)
}
}
}

View File

@@ -36,20 +36,14 @@ import io.netty.handler.timeout.IdleStateHandler
import io.netty.util.concurrent.Future import io.netty.util.concurrent.Future
import io.netty.util.concurrent.GenericFutureListener import io.netty.util.concurrent.GenericFutureListener
import net.woggioni.rbcs.api.CacheValueMetadata import net.woggioni.rbcs.api.CacheValueMetadata
import net.woggioni.rbcs.client.impl.Parser
import net.woggioni.rbcs.common.RBCS.loadKeystore import net.woggioni.rbcs.common.RBCS.loadKeystore
import net.woggioni.rbcs.common.Xml
import net.woggioni.rbcs.common.createLogger import net.woggioni.rbcs.common.createLogger
import net.woggioni.rbcs.common.debug import net.woggioni.rbcs.common.debug
import net.woggioni.rbcs.common.trace import net.woggioni.rbcs.common.trace
import java.io.IOException import java.io.IOException
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.net.URI import java.net.URI
import java.nio.file.Files
import java.nio.file.Path
import java.security.PrivateKey
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.time.Duration
import java.util.Base64 import java.util.Base64
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -69,59 +63,6 @@ class RemoteBuildCacheClient(private val profile: Configuration.Profile) : AutoC
private val sslContext: SslContext private val sslContext: SslContext
private val pool: ChannelPool private val pool: ChannelPool
data class Configuration(
val profiles: Map<String, Profile>
) {
sealed class Authentication {
data class TlsClientAuthenticationCredentials(
val key: PrivateKey,
val certificateChain: Array<X509Certificate>
) : Authentication()
data class BasicAuthenticationCredentials(val username: String, val password: String) : Authentication()
}
class TrustStore (
var file: Path?,
var password: String?,
var checkCertificateStatus: Boolean = false,
var verifyServerCertificate: Boolean = true,
)
class RetryPolicy(
val maxAttempts: Int,
val initialDelayMillis: Long,
val exp: Double
)
class Connection(
val readTimeout: Duration,
val writeTimeout: Duration,
val idleTimeout: Duration,
val readIdleTimeout: Duration,
val writeIdleTimeout: Duration
)
data class Profile(
val serverURI: URI,
val connection: Connection?,
val authentication: Authentication?,
val connectionTimeout: Duration?,
val maxConnections: Int,
val compressionEnabled: Boolean,
val retryPolicy: RetryPolicy?,
val tlsTruststore : TrustStore?
)
companion object {
fun parse(path: Path): Configuration {
return Files.newInputStream(path).use {
Xml.parseXml(path.toUri().toURL(), it)
}.let(Parser::parse)
}
}
}
init { init {
group = NioEventLoopGroup() group = NioEventLoopGroup()
sslContext = SslContextBuilder.forClient().also { builder -> sslContext = SslContextBuilder.forClient().also { builder ->
@@ -212,19 +153,6 @@ class RemoteBuildCacheClient(private val profile: Configuration.Profile) : AutoC
val pipeline: ChannelPipeline = ch.pipeline() val pipeline: ChannelPipeline = ch.pipeline()
profile.connection?.also { conn -> profile.connection?.also { conn ->
val readTimeout = conn.readTimeout.toMillis()
val writeTimeout = conn.writeTimeout.toMillis()
if (readTimeout > 0 || writeTimeout > 0) {
pipeline.addLast(
IdleStateHandler(
false,
readTimeout,
writeTimeout,
0,
TimeUnit.MILLISECONDS
)
)
}
val readIdleTimeout = conn.readIdleTimeout.toMillis() val readIdleTimeout = conn.readIdleTimeout.toMillis()
val writeIdleTimeout = conn.writeIdleTimeout.toMillis() val writeIdleTimeout = conn.writeIdleTimeout.toMillis()
val idleTimeout = conn.idleTimeout.toMillis() val idleTimeout = conn.idleTimeout.toMillis()

View File

@@ -1,7 +1,7 @@
package net.woggioni.rbcs.client.impl package net.woggioni.rbcs.client.impl
import net.woggioni.rbcs.api.exception.ConfigurationException import net.woggioni.rbcs.api.exception.ConfigurationException
import net.woggioni.rbcs.client.RemoteBuildCacheClient import net.woggioni.rbcs.client.Configuration
import net.woggioni.rbcs.common.Xml.Companion.asIterable import net.woggioni.rbcs.common.Xml.Companion.asIterable
import net.woggioni.rbcs.common.Xml.Companion.renderAttribute import net.woggioni.rbcs.common.Xml.Companion.renderAttribute
import org.w3c.dom.Document import org.w3c.dom.Document
@@ -16,9 +16,9 @@ import java.time.temporal.ChronoUnit
object Parser { object Parser {
fun parse(document: Document): RemoteBuildCacheClient.Configuration { fun parse(document: Document): Configuration {
val root = document.documentElement val root = document.documentElement
val profiles = mutableMapOf<String, RemoteBuildCacheClient.Configuration.Profile>() val profiles = mutableMapOf<String, Configuration.Profile>()
for (child in root.asIterable()) { for (child in root.asIterable()) {
val tagName = child.localName val tagName = child.localName
@@ -28,10 +28,10 @@ object Parser {
child.renderAttribute("name") ?: throw ConfigurationException("name attribute is required") child.renderAttribute("name") ?: throw ConfigurationException("name attribute is required")
val uri = child.renderAttribute("base-url")?.let(::URI) val uri = child.renderAttribute("base-url")?.let(::URI)
?: throw ConfigurationException("base-url attribute is required") ?: throw ConfigurationException("base-url attribute is required")
var authentication: RemoteBuildCacheClient.Configuration.Authentication? = null var authentication: Configuration.Authentication? = null
var retryPolicy: RemoteBuildCacheClient.Configuration.RetryPolicy? = null var retryPolicy: Configuration.RetryPolicy? = null
var connection : RemoteBuildCacheClient.Configuration.Connection? = null var connection : Configuration.Connection? = null
var trustStore : RemoteBuildCacheClient.Configuration.TrustStore? = null var trustStore : Configuration.TrustStore? = null
for (gchild in child.asIterable()) { for (gchild in child.asIterable()) {
when (gchild.localName) { when (gchild.localName) {
"tls-client-auth" -> { "tls-client-auth" -> {
@@ -52,7 +52,7 @@ object Parser {
.toList() .toList()
.toTypedArray() .toTypedArray()
authentication = authentication =
RemoteBuildCacheClient.Configuration.Authentication.TlsClientAuthenticationCredentials( Configuration.Authentication.TlsClientAuthenticationCredentials(
key, key,
certChain certChain
) )
@@ -64,7 +64,7 @@ object Parser {
val password = gchild.renderAttribute("password") val password = gchild.renderAttribute("password")
?: throw ConfigurationException("password attribute is required") ?: throw ConfigurationException("password attribute is required")
authentication = authentication =
RemoteBuildCacheClient.Configuration.Authentication.BasicAuthenticationCredentials( Configuration.Authentication.BasicAuthenticationCredentials(
username, username,
password password
) )
@@ -83,7 +83,7 @@ object Parser {
gchild.renderAttribute("exp") gchild.renderAttribute("exp")
?.let(String::toDouble) ?.let(String::toDouble)
?: 2.0f ?: 2.0f
retryPolicy = RemoteBuildCacheClient.Configuration.RetryPolicy( retryPolicy = Configuration.RetryPolicy(
maxAttempts, maxAttempts,
initialDelay.toMillis(), initialDelay.toMillis(),
exp.toDouble() exp.toDouble()
@@ -91,22 +91,16 @@ object Parser {
} }
"connection" -> { "connection" -> {
val writeTimeout = gchild.renderAttribute("write-timeout")
?.let(Duration::parse) ?: Duration.of(0, ChronoUnit.SECONDS)
val readTimeout = gchild.renderAttribute("read-timeout")
?.let(Duration::parse) ?: Duration.of(0, ChronoUnit.SECONDS)
val idleTimeout = gchild.renderAttribute("idle-timeout") val idleTimeout = gchild.renderAttribute("idle-timeout")
?.let(Duration::parse) ?: Duration.of(30, ChronoUnit.SECONDS) ?.let(Duration::parse) ?: Duration.of(30, ChronoUnit.SECONDS)
val readIdleTimeout = gchild.renderAttribute("read-idle-timeout") val readIdleTimeout = gchild.renderAttribute("read-idle-timeout")
?.let(Duration::parse) ?: Duration.of(60, ChronoUnit.SECONDS) ?.let(Duration::parse) ?: Duration.of(60, ChronoUnit.SECONDS)
val writeIdleTimeout = gchild.renderAttribute("write-idle-timeout") val writeIdleTimeout = gchild.renderAttribute("write-idle-timeout")
?.let(Duration::parse) ?: Duration.of(60, ChronoUnit.SECONDS) ?.let(Duration::parse) ?: Duration.of(60, ChronoUnit.SECONDS)
connection = RemoteBuildCacheClient.Configuration.Connection( connection = Configuration.Connection(
readTimeout,
writeTimeout,
idleTimeout,
readIdleTimeout, readIdleTimeout,
writeIdleTimeout, writeIdleTimeout,
idleTimeout,
) )
} }
@@ -118,7 +112,7 @@ object Parser {
?.let(String::toBoolean) ?: false ?.let(String::toBoolean) ?: false
val verifyServerCertificate = gchild.renderAttribute("verify-server-certificate") val verifyServerCertificate = gchild.renderAttribute("verify-server-certificate")
?.let(String::toBoolean) ?: true ?.let(String::toBoolean) ?: true
trustStore = RemoteBuildCacheClient.Configuration.TrustStore(file, password, checkCertificateStatus, verifyServerCertificate) trustStore = Configuration.TrustStore(file, password, checkCertificateStatus, verifyServerCertificate)
} }
} }
} }
@@ -131,7 +125,7 @@ object Parser {
?.let(String::toBoolean) ?.let(String::toBoolean)
?: true ?: true
profiles[name] = RemoteBuildCacheClient.Configuration.Profile( profiles[name] = Configuration.Profile(
uri, uri,
connection, connection,
authentication, authentication,
@@ -144,6 +138,6 @@ object Parser {
} }
} }
} }
return RemoteBuildCacheClient.Configuration(profiles) return Configuration(profiles)
} }
} }

View File

@@ -15,75 +15,237 @@
<xs:complexType name="profileType"> <xs:complexType name="profileType">
<xs:sequence> <xs:sequence>
<xs:choice> <xs:choice>
<xs:element name="no-auth" type="rbcs-client:noAuthType"/> <xs:element name="no-auth" type="rbcs-client:noAuthType">
<xs:element name="basic-auth" type="rbcs-client:basicAuthType"/> <xs:annotation>
<xs:element name="tls-client-auth" type="rbcs-client:tlsClientAuthType"/> <xs:documentation>
Disable authentication.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="basic-auth" type="rbcs-client:basicAuthType">
<xs:annotation>
<xs:documentation>
Enable HTTP basic authentication.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="tls-client-auth" type="rbcs-client:tlsClientAuthType">
<xs:annotation>
<xs:documentation>
Enable TLS certificate authentication.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:choice> </xs:choice>
<xs:element name="connection" type="rbcs-client:connectionType" minOccurs="0" /> <xs:element name="connection" type="rbcs-client:connectionType" minOccurs="0" >
<xs:element name="retry-policy" type="rbcs-client:retryType" minOccurs="0"/> <xs:annotation>
<xs:element name="tls-trust-store" type="rbcs-client:trustStoreType" minOccurs="0"/> <xs:documentation>
Set inactivity timeouts for connections to this server,
if not present, connections are only closed on network errors.
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="retry-policy" type="rbcs-client:retryType" minOccurs="0">
<xs:annotation>
<xs:documentation>
Set a retry policy for this server, if not present requests won't be retried
</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="tls-trust-store" type="rbcs-client:trustStoreType" minOccurs="0">
<xs:annotation>
<xs:documentation>
If set, specify an alternative truststore to validate the server certificate.
If not present the system truststore is used.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence> </xs:sequence>
<xs:attribute name="name" type="xs:token" use="required"/> <xs:attribute name="name" type="xs:token" use="required">
<xs:attribute name="base-url" type="xs:anyURI" use="required"/> <xs:annotation>
<xs:attribute name="max-connections" type="xs:positiveInteger" default="50"/> <xs:documentation>
<xs:attribute name="connection-timeout" type="xs:duration"/> Name of this server profile, to be referred to from rbcs-cli with the '-p' parameter
<xs:attribute name="enable-compression" type="xs:boolean" default="true"/> </xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="base-url" type="xs:anyURI" use="required">
<xs:annotation>
<xs:documentation>
RBCs server URL
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="max-connections" type="xs:positiveInteger" default="50">
<xs:annotation>
<xs:documentation>
Maximum number of concurrent TCP connection to open with this server
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="connection-timeout" type="xs:duration">
<xs:annotation>
<xs:documentation>
Enable HTTP compression when communicating to this server
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="enable-compression" type="xs:boolean" default="true">
<xs:annotation>
<xs:documentation>
Enable HTTP compression when communicating to this server
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType> </xs:complexType>
<xs:complexType name="connectionType"> <xs:complexType name="connectionType">
<xs:attribute name="read-timeout" type="xs:duration" use="optional" default="PT0S"/> <xs:attribute name="idle-timeout" type="xs:duration" use="optional" default="PT30S">
<xs:attribute name="write-timeout" type="xs:duration" use="optional" default="PT0S"/> <xs:annotation>
<xs:attribute name="idle-timeout" type="xs:duration" use="optional" default="PT30S"/> <xs:documentation>
<xs:attribute name="read-idle-timeout" type="xs:duration" use="optional" default="PT60S"/> The client will close the connection with the server
<xs:attribute name="write-idle-timeout" type="xs:duration" use="optional" default="PT60S"/> when neither a read nor a write was performed for the specified period of time.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="read-idle-timeout" type="xs:duration" use="optional" default="PT60S">
<xs:annotation>
<xs:documentation>
The client will close the connection with the server
when no read was performed for the specified period of time.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="write-idle-timeout" type="xs:duration" use="optional" default="PT60S">
<xs:annotation>
<xs:documentation>
The client will close the connection with the server
when no write was performed for the specified period of time.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType> </xs:complexType>
<xs:complexType name="noAuthType"/> <xs:complexType name="noAuthType">
<xs:annotation>
<xs:documentation>
Add this tag to not use any type of authentication when talking to the RBCS server
</xs:documentation>
</xs:annotation>
</xs:complexType>
<xs:complexType name="basicAuthType"> <xs:complexType name="basicAuthType">
<xs:attribute name="user" type="xs:token" use="required"/> <xs:annotation>
<xs:attribute name="password" type="xs:string" use="required"/> <xs:documentation>
Add this tag to enable HTTP basic authentication for the communication to this server,
mind that HTTP basic authentication sends credentials directly over the network, so make sure
your communication is protected by TLS (i.e. your server's URL starts with "https")
</xs:documentation>
</xs:annotation>
<xs:attribute name="user" type="xs:token" use="required">
<xs:annotation>
<xs:documentation>
Username for HTTP basic authentication
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="password" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>
Password used for HTTP basic authentication
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType> </xs:complexType>
<xs:complexType name="tlsClientAuthType"> <xs:complexType name="tlsClientAuthType">
<xs:attribute name="key-store-file" type="xs:anyURI" use="required"/> <xs:attribute name="key-store-file" type="xs:anyURI" use="required">
<xs:attribute name="key-store-password" type="xs:string" use="required"/> <xs:annotation>
<xs:attribute name="key-alias" type="xs:token" use="required"/> <xs:documentation>
<xs:attribute name="key-password" type="xs:string" use="optional"/> System path to the keystore file
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="key-store-password" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>
Password to open they keystore file
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="key-alias" type="xs:token" use="required">
<xs:annotation>
<xs:documentation>
Alias of the keystore entry containing the private key
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="key-password" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>
Private key entry's encryption password
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType> </xs:complexType>
<xs:complexType name="retryType"> <xs:complexType name="retryType">
<xs:attribute name="max-attempts" type="xs:positiveInteger" use="required"/> <xs:annotation>
<xs:attribute name="initial-delay" type="xs:duration" default="PT1S"/> <xs:documentation>
<xs:attribute name="exp" type="xs:double" default="2.0"/> Retry policy to use in case of failures, based on exponential backoff
https://en.wikipedia.org/wiki/Exponential_backoff
</xs:documentation>
</xs:annotation>
<xs:attribute name="max-attempts" type="xs:positiveInteger" use="required">
<xs:annotation>
<xs:documentation>
Maximum number of attempts, after which the call will result in an error,
throwing an exception related to the last received failure
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="initial-delay" type="xs:duration" default="PT1S">
<xs:annotation>
<xs:documentation>
Delay to apply before retrying after the first failed call
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="exp" type="xs:double" default="2.0">
<xs:annotation>
<xs:documentation>
Exponent to apply to compute the next delay
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType> </xs:complexType>
<xs:complexType name="trustStoreType"> <xs:complexType name="trustStoreType">
<xs:attribute name="file" type="xs:string" use="required"> <xs:attribute name="file" type="xs:string" use="required">
<xs:annotation> <xs:annotation>
<xs:documentation> <xs:documentation>
Path to the trustore file Path to the truststore file
</xs:documentation> </xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="password" type="xs:string"> <xs:attribute name="password" type="xs:string">
<xs:annotation> <xs:annotation>
<xs:documentation> <xs:documentation>
Trustore file password Truststore file password
</xs:documentation> </xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="check-certificate-status" type="xs:boolean"> <xs:attribute name="check-certificate-status" type="xs:boolean">
<xs:annotation> <xs:annotation>
<xs:documentation> <xs:documentation>
Whether or not check the certificate validity using CRL/OCSP Whether or not check the server certificate validity using CRL/OCSP
</xs:documentation> </xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="verify-server-certificate" type="xs:boolean" use="optional" default="true"> <xs:attribute name="verify-server-certificate" type="xs:boolean" use="optional" default="true">
<xs:annotation> <xs:annotation>
<xs:documentation> <xs:documentation>
If false, the client will blindly trust the provided server certificate If false, the client will blindly trust the certificate provided by the server
</xs:documentation> </xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>

View File

@@ -30,7 +30,7 @@ The plugins currently supports the following configuration attributes:
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xmlns:rbcs-memcache="urn:net.woggioni.rbcs.server.memcache" xmlns:rbcs-memcache="urn:net.woggioni.rbcs.server.memcache"
xs:schemaLocation="urn:net.woggioni.rbcs.server.memcache jpms://net.woggioni.rbcs.server.memcache/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd" xs:schemaLocation="urn:net.woggioni.rbcs.server.memcache jpms://net.woggioni.rbcs.server.memcache/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd"
> >
... ...
<cache xs:type="rbcs-memcache:memcacheCacheType" <cache xs:type="rbcs-memcache:memcacheCacheType"

View File

@@ -4,7 +4,7 @@
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xmlns:xs="http://www.w3.org/2001/XMLSchema"> xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import schemaLocation="jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd" namespace="urn:net.woggioni.rbcs.server"/> <xs:import schemaLocation="jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd" namespace="urn:net.woggioni.rbcs.server"/>
<xs:complexType name="memcacheServerType"> <xs:complexType name="memcacheServerType">
<xs:attribute name="host" type="xs:token" use="required"/> <xs:attribute name="host" type="xs:token" use="required"/>

View File

@@ -8,8 +8,9 @@ class RoleAuthorizer : Authorizer {
companion object { companion object {
private val METHOD_MAP = mapOf( private val METHOD_MAP = mapOf(
Role.Reader to setOf(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE), Role.Reader to setOf(HttpMethod.GET, HttpMethod.HEAD),
Role.Writer to setOf(HttpMethod.PUT, HttpMethod.POST) Role.Writer to setOf(HttpMethod.PUT, HttpMethod.POST),
Role.Healthcheck to setOf(HttpMethod.TRACE)
) )
} }

View File

@@ -12,7 +12,7 @@ import java.util.zip.Deflater
class FileSystemCacheProvider : CacheProvider<FileSystemCacheConfiguration> { class FileSystemCacheProvider : CacheProvider<FileSystemCacheConfiguration> {
override fun getXmlSchemaLocation() = "classpath:net/woggioni/rbcs/server/schema/rbcs.xsd" override fun getXmlSchemaLocation() = "classpath:net/woggioni/rbcs/server/schema/rbcs-server.xsd"
override fun getXmlType() = "fileSystemCacheType" override fun getXmlType() = "fileSystemCacheType"

View File

@@ -11,7 +11,7 @@ import java.util.zip.Deflater
class InMemoryCacheProvider : CacheProvider<InMemoryCacheConfiguration> { class InMemoryCacheProvider : CacheProvider<InMemoryCacheConfiguration> {
override fun getXmlSchemaLocation() = "classpath:net/woggioni/rbcs/server/schema/rbcs.xsd" override fun getXmlSchemaLocation() = "classpath:net/woggioni/rbcs/server/schema/rbcs-server.xsd"
override fun getXmlType() = "inMemoryCacheType" override fun getXmlType() = "inMemoryCacheType"

View File

@@ -193,6 +193,7 @@ object Parser {
when (it.localName) { when (it.localName) {
"reader" -> Role.Reader "reader" -> Role.Reader
"writer" -> Role.Writer "writer" -> Role.Writer
"healthcheck" -> Role.Healthcheck
else -> throw UnsupportedOperationException("Illegal node '${it.localName}'") else -> throw UnsupportedOperationException("Illegal node '${it.localName}'")
} }
}.toSet() }.toSet()

View File

@@ -2,7 +2,7 @@
<rbcs:server <rbcs:server
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd"> xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd">
<bind host="127.0.0.1" port="8080" incoming-connections-backlog-size="1024"/> <bind host="127.0.0.1" port="8080" incoming-connections-backlog-size="1024"/>
<cache xs:type="rbcs:fileSystemCacheType" path="${sys:java.io.tmpdir}/rbcs" max-age="P7D"/> <cache xs:type="rbcs:fileSystemCacheType" path="${sys:java.io.tmpdir}/rbcs" max-age="P7D"/>
</rbcs:server> </rbcs:server>

View File

@@ -424,7 +424,8 @@
<xs:attribute name="password" type="xs:string" use="optional"> <xs:attribute name="password" type="xs:string" use="optional">
<xs:annotation> <xs:annotation>
<xs:documentation> <xs:documentation>
User's password used in HTTP basic authentication User's password hash used for HTTP basic authentication, this has to be generated with
the `password` subcommand of `rbcs-cli`
</xs:documentation> </xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
@@ -498,18 +499,12 @@
</xs:attribute> </xs:attribute>
</xs:complexType> </xs:complexType>
<xs:simpleType name="role" final="restriction" >
<xs:restriction base="xs:token">
<xs:enumeration value="READER" />
<xs:enumeration value="WRITER" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="rolesType"> <xs:complexType name="rolesType">
<xs:sequence> <xs:sequence>
<xs:choice maxOccurs="unbounded"> <xs:choice maxOccurs="unbounded">
<xs:element name="writer"/> <xs:element name="writer"/>
<xs:element name="reader"/> <xs:element name="reader"/>
<xs:element name="healthcheck"/>
</xs:choice> </xs:choice>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>

View File

@@ -47,6 +47,7 @@ abstract class AbstractTlsServerTest : AbstractServerTest() {
protected val readersGroup = Configuration.Group("readers", setOf(Role.Reader), null, null) protected val readersGroup = Configuration.Group("readers", setOf(Role.Reader), null, null)
protected val writersGroup = Configuration.Group("writers", setOf(Role.Writer), null, null) protected val writersGroup = Configuration.Group("writers", setOf(Role.Writer), null, null)
protected val healthCheckGroup = Configuration.Group("healthcheckers", setOf(Role.Healthcheck), null, null)
protected val random = Random(101325) protected val random = Random(101325)
protected val keyValuePair = newEntry(random) protected val keyValuePair = newEntry(random)
private val serverPath : String? = null private val serverPath : String? = null

View File

@@ -18,6 +18,7 @@ class TlsServerTest : AbstractTlsServerTest() {
Configuration.User("user1", null, setOf(readersGroup), null), Configuration.User("user1", null, setOf(readersGroup), null),
Configuration.User("user2", null, setOf(writersGroup), null), Configuration.User("user2", null, setOf(writersGroup), null),
Configuration.User("user3", null, setOf(readersGroup, writersGroup), null), Configuration.User("user3", null, setOf(readersGroup, writersGroup), null),
Configuration.User("user4", null, setOf(healthCheckGroup), null),
Configuration.User("", null, setOf(readersGroup), null) Configuration.User("", null, setOf(readersGroup), null)
) )
@@ -140,7 +141,24 @@ class TlsServerTest : AbstractTlsServerTest() {
val client: HttpClient = getHttpClient(null) val client: HttpClient = getHttpClient(null)
val requestBuilder = newRequestBuilder("").method( val requestBuilder = newRequestBuilder("").method(
"TRACE", "TRACE",
HttpRequest.BodyPublishers.ofByteArray("sfgsdgfaiousfiuhsd".toByteArray()) HttpRequest.BodyPublishers.ofByteArray("this is an healthcheck".toByteArray())
)
val response: HttpResponse<ByteArray> =
client.send(requestBuilder.build(), HttpResponse.BodyHandlers.ofByteArray())
Assertions.assertEquals(HttpResponseStatus.FORBIDDEN.code(), response.statusCode())
}
@Test
@Order(9)
fun traceAsHealthcheckUser() {
val user = cfg.users.values.find {
Role.Healthcheck in it.roles
} ?: throw RuntimeException("Reader user not found")
val client: HttpClient = getHttpClient(getClientKeyStore(ca, X500Name("CN=${user.name}")))
val requestBuilder = newRequestBuilder("").method(
"TRACE",
HttpRequest.BodyPublishers.ofByteArray("this is an healthcheck".toByteArray())
) )
val response: HttpResponse<ByteArray> = val response: HttpResponse<ByteArray> =

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd"> xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd">
<bind host="127.0.0.1" port="11443"/> <bind host="127.0.0.1" port="11443"/>
<cache xs:type="rbcs:fileSystemCacheType" path="/tmp/rbcs" max-age="P7D"/> <cache xs:type="rbcs:fileSystemCacheType" path="/tmp/rbcs" max-age="P7D"/>
<authorization> <authorization>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd"> xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd">
<bind host="127.0.0.1" port="11443"/> <bind host="127.0.0.1" port="11443"/>
<cache xs:type="rbcs:fileSystemCacheType" path="/tmp/rbcs" max-age="P7D"/> <cache xs:type="rbcs:fileSystemCacheType" path="/tmp/rbcs" max-age="P7D"/>
<authorization> <authorization>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd"> xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd">
<bind host="127.0.0.1" port="11443"/> <bind host="127.0.0.1" port="11443"/>
<cache xs:type="rbcs:fileSystemCacheType" path="/tmp/rbcs" max-age="P7D"/> <cache xs:type="rbcs:fileSystemCacheType" path="/tmp/rbcs" max-age="P7D"/>
<authorization> <authorization>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd"> xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd">
<bind host="127.0.0.1" port="11443"/> <bind host="127.0.0.1" port="11443"/>
<cache xs:type="rbcs:fileSystemCacheType" path="/tmp/rbcs" max-age="P7D"/> <cache xs:type="rbcs:fileSystemCacheType" path="/tmp/rbcs" max-age="P7D"/>
<authorization> <authorization>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd"> xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd">
<bind host="127.0.0.1" port="11443" incoming-connections-backlog-size="22"/> <bind host="127.0.0.1" port="11443" incoming-connections-backlog-size="22"/>
<connection <connection
read-idle-timeout="PT10M" read-idle-timeout="PT10M"

View File

@@ -2,7 +2,7 @@
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xmlns:rbcs-memcache="urn:net.woggioni.rbcs.server.memcache" xmlns:rbcs-memcache="urn:net.woggioni.rbcs.server.memcache"
xs:schemaLocation="urn:net.woggioni.rbcs.server.memcache jpms://net.woggioni.rbcs.server.memcache/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd" xs:schemaLocation="urn:net.woggioni.rbcs.server.memcache jpms://net.woggioni.rbcs.server.memcache/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd"
> >
<bind host="0.0.0.0" port="8443" incoming-connections-backlog-size="4096"/> <bind host="0.0.0.0" port="8443" incoming-connections-backlog-size="4096"/>
<connection <connection

View File

@@ -2,7 +2,7 @@
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xmlns:rbcs-memcache="urn:net.woggioni.rbcs.server.memcache" xmlns:rbcs-memcache="urn:net.woggioni.rbcs.server.memcache"
xs:schemaLocation="urn:net.woggioni.rbcs.server.memcache jpms://net.woggioni.rbcs.server.memcache/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd"> xs:schemaLocation="urn:net.woggioni.rbcs.server.memcache jpms://net.woggioni.rbcs.server.memcache/net/woggioni/rbcs/server/memcache/schema/rbcs-memcache.xsd urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd">
<bind host="127.0.0.1" port="11443" incoming-connections-backlog-size="50"/> <bind host="127.0.0.1" port="11443" incoming-connections-backlog-size="50"/>
<connection <connection
read-idle-timeout="PT10M" read-idle-timeout="PT10M"

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" <rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rbcs="urn:net.woggioni.rbcs.server" xmlns:rbcs="urn:net.woggioni.rbcs.server"
xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs.xsd"> xs:schemaLocation="urn:net.woggioni.rbcs.server jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/schema/rbcs-server.xsd">
<bind host="127.0.0.1" port="11443" incoming-connections-backlog-size="180"/> <bind host="127.0.0.1" port="11443" incoming-connections-backlog-size="180"/>
<connection <connection
read-idle-timeout="PT10M" read-idle-timeout="PT10M"

View File

@@ -1,3 +1,10 @@
# RBCS servlet
This is a minimal implementation of RBCs using Jakarta servlet API, it relies
on the servlet container (Tomcat in this example) for authentication, authorization,
throttling, encryption and compression. It only supports in-memory caching.
The main purpose is to provide a performance comparison for RBCS Netty implementation.
## How to run ## How to run
```bash ```bash