Compare commits
11 Commits
0.2.0-alph
...
0.2.0-RC1
Author | SHA1 | Date | |
---|---|---|---|
a1398045ac
|
|||
1f93602102
|
|||
c818463a2e
|
|||
cd28563985
|
|||
8ef2d9c64e
|
|||
1510956989
|
|||
ac4f0fdd19
|
|||
37da03c719
|
|||
60bc4375cf
|
|||
725fe22b80
|
|||
ca18b63f27
|
@@ -39,9 +39,9 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
pull: true
|
pull: true
|
||||||
tags: |
|
tags: |
|
||||||
gitea.woggioni.net/woggioni/rbcs:latest
|
gitea.woggioni.net/woggioni/rbcs:vanilla
|
||||||
gitea.woggioni.net/woggioni/rbcs:${{ steps.retrieve-version.outputs.VERSION }}
|
gitea.woggioni.net/woggioni/rbcs:vanilla-${{ steps.retrieve-version.outputs.VERSION }}
|
||||||
target: release
|
target: release-vanilla
|
||||||
cache-from: type=registry,ref=gitea.woggioni.net/woggioni/rbcs:buildx
|
cache-from: type=registry,ref=gitea.woggioni.net/woggioni/rbcs:buildx
|
||||||
-
|
-
|
||||||
name: Build rbcs memcache Docker image
|
name: Build rbcs memcache Docker image
|
||||||
@@ -57,6 +57,20 @@ jobs:
|
|||||||
target: release-memcache
|
target: release-memcache
|
||||||
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 native Docker image
|
||||||
|
uses: docker/build-push-action@v5.3.0
|
||||||
|
with:
|
||||||
|
context: "docker/build/docker"
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
pull: true
|
||||||
|
tags: |
|
||||||
|
gitea.woggioni.net/woggioni/rbcs:latest
|
||||||
|
gitea.woggioni.net/woggioni/rbcs:${{ steps.retrieve-version.outputs.VERSION }}
|
||||||
|
gitea.woggioni.net/woggioni/rbcs:native
|
||||||
|
gitea.woggioni.net/woggioni/rbcs:native-${{ steps.retrieve-version.outputs.VERSION }}
|
||||||
|
target: release-native
|
||||||
- name: Publish artifacts
|
- name: Publish artifacts
|
||||||
env:
|
env:
|
||||||
PUBLISHER_TOKEN: ${{ secrets.PUBLISHER_TOKEN }}
|
PUBLISHER_TOKEN: ${{ secrets.PUBLISHER_TOKEN }}
|
||||||
|
148
README.md
148
README.md
@@ -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?
|
||||||
|
|
||||||
|
@@ -38,7 +38,7 @@ allprojects { subproject ->
|
|||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
modularity.inferModulePath = true
|
modularity.inferModulePath = true
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(21)
|
languageVersion = JavaLanguageVersion.of(23)
|
||||||
vendor = JvmVendorSpec.ORACLE
|
vendor = JvmVendorSpec.ORACLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
125
doc/client_configuration.md
Normal file
125
doc/client_configuration.md
Normal 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.
|
178
doc/server_configuration.md
Normal file
178
doc/server_configuration.md
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
### RBCS server configuration file elements and attributes
|
||||||
|
|
||||||
|
#### Root Element: `server`
|
||||||
|
The root element that contains all server configuration.
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `path` (optional): URI path prefix for cache requests. Example: if set to "cache", requests would be made to "http://www.example.com/cache/KEY"
|
||||||
|
|
||||||
|
#### Child Elements
|
||||||
|
|
||||||
|
#### `<bind>`
|
||||||
|
Configures server socket settings.
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `host` (required): Server bind address
|
||||||
|
- `port` (required): Server port number
|
||||||
|
- `incoming-connections-backlog-size` (optional, default: 1024): Maximum queue length for incoming connection indications
|
||||||
|
|
||||||
|
#### `<connection>`
|
||||||
|
Configures connection handling parameters.
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `idle-timeout` (optional, default: PT30S): Connection timeout when no activity
|
||||||
|
- `read-idle-timeout` (optional, default: PT60S): Connection timeout when no reads
|
||||||
|
- `write-idle-timeout` (optional, default: PT60S): Connection timeout when no writes
|
||||||
|
- `max-request-size` (optional, default: 0x4000000): Maximum allowed request body size
|
||||||
|
|
||||||
|
#### `<event-executor>`
|
||||||
|
Configures event execution settings.
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `use-virtual-threads` (optional, default: true): Whether to use virtual threads for the server handler
|
||||||
|
|
||||||
|
#### `<cache>`
|
||||||
|
Defines cache storage implementation. Two types are available:
|
||||||
|
|
||||||
|
##### InMemory Cache
|
||||||
|
|
||||||
|
A simple storage backend that uses an hash map to store data in memory
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `max-age` (default: P1D): Cache entry lifetime
|
||||||
|
- `max-size` (default: 0x1000000): Maximum cache size in bytes
|
||||||
|
- `digest` (default: MD5): Key hashing algorithm
|
||||||
|
- `enable-compression` (default: true): Enable deflate compression
|
||||||
|
- `compression-level` (default: -1): Compression level (-1 to 9)
|
||||||
|
- `chunk-size` (default: 0x10000): Maximum socket write size
|
||||||
|
|
||||||
|
##### FileSystem Cache
|
||||||
|
|
||||||
|
A storage backend that stores data in a folder on the disk
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `path`: Storage directory path
|
||||||
|
- `max-age` (default: P1D): Cache entry lifetime
|
||||||
|
- `digest` (default: MD5): Key hashing algorithm
|
||||||
|
- `enable-compression` (default: true): Enable deflate compression
|
||||||
|
- `compression-level` (default: -1): Compression level
|
||||||
|
- `chunk-size` (default: 0x10000): Maximum in-memory cache value size
|
||||||
|
|
||||||
|
#### `<authorization>`
|
||||||
|
Configures user and group-based access control.
|
||||||
|
|
||||||
|
##### `<users>`
|
||||||
|
List of registered users.
|
||||||
|
- Contains `<user>` elements:
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `name` (required): Username
|
||||||
|
- `password` (optional): For basic authentication
|
||||||
|
- Can contain an `anonymous` element to allow for unauthenticated access
|
||||||
|
|
||||||
|
##### `<groups>`
|
||||||
|
List of user groups.
|
||||||
|
- Contains `<group>` elements:
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `name`: Group name
|
||||||
|
- Can contain:
|
||||||
|
- `users`: List of user references
|
||||||
|
- `roles`: List of roles (READER/WRITER)
|
||||||
|
- `user-quota`: Per-user quota
|
||||||
|
- `group-quota`: Group-wide quota
|
||||||
|
|
||||||
|
#### `<authentication>`
|
||||||
|
Configures authentication mechanism. Options:
|
||||||
|
- `<basic>`: HTTP basic authentication
|
||||||
|
- `<client-certificate>`: TLS certificate authentication, it uses attributes of the subject's X.500 name
|
||||||
|
to extract the username and group of the client.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```xml
|
||||||
|
<client-certificate>
|
||||||
|
<user-extractor attribute-name="CN" pattern="(.*)"/>
|
||||||
|
<group-extractor attribute-name="O" pattern="(.*)"/>
|
||||||
|
</client-certificate>
|
||||||
|
```
|
||||||
|
- `<none>`: No authentication
|
||||||
|
|
||||||
|
#### `<tls>`
|
||||||
|
Configures TLS encryption.
|
||||||
|
|
||||||
|
**Child Elements:**
|
||||||
|
- `<keystore>`: Server certificate configuration
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `file` (required): Keystore file path
|
||||||
|
- `password`: Keystore password
|
||||||
|
- `key-alias` (required): Private key alias
|
||||||
|
- `key-password`: Private key password
|
||||||
|
|
||||||
|
- `<truststore>`: Client certificate verification
|
||||||
|
|
||||||
|
**Attributes:**
|
||||||
|
- `file` (required): Truststore file path
|
||||||
|
- `password`: Truststore password
|
||||||
|
- `check-certificate-status`: Enable CRL/OCSP checking
|
||||||
|
- `require-client-certificate` (default: false): Require client certificates
|
||||||
|
|
||||||
|
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
# Complete configuration example
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<rbcs:server xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
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-server.xsd"
|
||||||
|
>
|
||||||
|
<bind host="0.0.0.0" port="8080" incoming-connections-backlog-size="1024"/>
|
||||||
|
<connection
|
||||||
|
max-request-size="67108864"
|
||||||
|
idle-timeout="PT10S"
|
||||||
|
read-idle-timeout="PT20S"
|
||||||
|
write-idle-timeout="PT20S"
|
||||||
|
read-timeout="PT5S"
|
||||||
|
write-timeout="PT5S"/>
|
||||||
|
<event-executor use-virtual-threads="true"/>
|
||||||
|
<cache xs:type="rbcs:inMemoryCacheType" max-age="P7D" enable-compression="false" max-size="0x10000000" />
|
||||||
|
<!--cache xs:type="rbcs:fileSystemCacheType" max-age="P7D" enable-compression="false" path="${sys:java.io.tmpdir}/rbcs"/-->
|
||||||
|
<authorization>
|
||||||
|
<users>
|
||||||
|
<user name="user1" password="II+qeNLft2pZ/JVNo9F7jpjM/BqEcfsJW27NZ6dPVs8tAwHbxrJppKYsbL7J/SMl">
|
||||||
|
<quota calls="100" period="PT1S"/>
|
||||||
|
</user>
|
||||||
|
<user name="user2" password="v6T9+q6/VNpvLknji3ixPiyz2YZCQMXj2FN7hvzbfc2Ig+IzAHO0iiBCH9oWuBDq"/>
|
||||||
|
<anonymous>
|
||||||
|
<quota calls="10" period="PT60S" initial-available-calls="10" max-available-calls="10"/>
|
||||||
|
</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/>
|
||||||
|
</roles>
|
||||||
|
</group>
|
||||||
|
</groups>
|
||||||
|
</authorization>
|
||||||
|
<authentication>
|
||||||
|
<basic/>
|
||||||
|
</authentication>
|
||||||
|
</rbcs:server>
|
||||||
|
|
||||||
|
```
|
@@ -3,7 +3,7 @@ RUN adduser -D luser
|
|||||||
USER luser
|
USER luser
|
||||||
WORKDIR /home/luser
|
WORKDIR /home/luser
|
||||||
|
|
||||||
FROM base-release AS release
|
FROM base-release AS release-vanilla
|
||||||
ADD rbcs-cli-envelope-*.jar rbcs.jar
|
ADD rbcs-cli-envelope-*.jar rbcs.jar
|
||||||
ENTRYPOINT ["java", "-XX:+UseSerialGC", "-XX:GCTimeRatio=24", "-jar", "/home/luser/rbcs.jar", "server"]
|
ENTRYPOINT ["java", "-XX:+UseSerialGC", "-XX:GCTimeRatio=24", "-jar", "/home/luser/rbcs.jar", "server"]
|
||||||
|
|
||||||
@@ -15,3 +15,9 @@ RUN --mount=type=bind,source=.,target=/build/distributions tar -xf /build/distri
|
|||||||
WORKDIR /home/luser
|
WORKDIR /home/luser
|
||||||
ADD logback.xml .
|
ADD logback.xml .
|
||||||
ENTRYPOINT ["java", "-Dlogback.configurationFile=logback.xml", "-XX:+UseSerialGC", "-XX:GCTimeRatio=24", "-jar", "/home/luser/rbcs.jar", "server"]
|
ENTRYPOINT ["java", "-Dlogback.configurationFile=logback.xml", "-XX:+UseSerialGC", "-XX:GCTimeRatio=24", "-jar", "/home/luser/rbcs.jar", "server"]
|
||||||
|
|
||||||
|
FROM scratch AS release-native
|
||||||
|
ADD rbcs-cli.upx /rbcs/rbcs-cli
|
||||||
|
ENV RBCS_CONFIGURATION_DIR="/rbcs"
|
||||||
|
WORKDIR /rbcs
|
||||||
|
ENTRYPOINT ["/rbcs/rbcs-cli"]
|
||||||
|
24
docker/README.md
Normal file
24
docker/README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# RBCS Docker images
|
||||||
|
There are 3 image flavours:
|
||||||
|
- vanilla
|
||||||
|
- memcache
|
||||||
|
- native
|
||||||
|
|
||||||
|
The `vanilla` image only contains the envelope
|
||||||
|
jar file with no plugins and is based on `eclipse-temurin:21-jre-alpine`
|
||||||
|
|
||||||
|
The `memcache` image is similar to the `vanilla` image, except that it also contains
|
||||||
|
the `rbcs-server-memcache` plugin in the `plugins` folder, use this image if you don't want to use the `native`
|
||||||
|
image and want to use memcache as the cache backend
|
||||||
|
|
||||||
|
The `native` image contains a native, statically-linked executable created with GraalVM
|
||||||
|
that has no userspace dependencies. It also embeds the memcache plugin inside the executable.
|
||||||
|
Use this image for maximum efficiency and minimal memory footprint.
|
||||||
|
|
||||||
|
## Which image shoud I use?
|
||||||
|
The `native` image uses Java's SerialGC, so it's ideal for constrained environment like containers or small servers,
|
||||||
|
if you have a lot of resources and want to squeeze out the maximum throughput you should consider the
|
||||||
|
`vanilla` or `memcache` image, then choose and fine tune the garbage collector.
|
||||||
|
|
||||||
|
Also the `native` image is only available for the `x86_64` architecture at the moment,
|
||||||
|
while `vanilla` and `memcache` also ship a `aarch64` variant.
|
@@ -4,7 +4,7 @@ org.gradle.caching=true
|
|||||||
|
|
||||||
rbcs.version = 0.2.0
|
rbcs.version = 0.2.0
|
||||||
|
|
||||||
lys.version = 2025.02.08
|
lys.version = 2025.02.26
|
||||||
|
|
||||||
gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven
|
gitea.maven.url = https://gitea.woggioni.net/api/packages/woggioni/maven
|
||||||
docker.registry.url=gitea.woggioni.net
|
docker.registry.url=gitea.woggioni.net
|
||||||
|
@@ -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
|
||||||
}
|
}
|
@@ -13,10 +13,20 @@ import net.woggioni.gradle.envelope.EnvelopePlugin
|
|||||||
import net.woggioni.gradle.envelope.EnvelopeJarTask
|
import net.woggioni.gradle.envelope.EnvelopeJarTask
|
||||||
import net.woggioni.gradle.graalvm.NativeImageConfigurationTask
|
import net.woggioni.gradle.graalvm.NativeImageConfigurationTask
|
||||||
import net.woggioni.gradle.graalvm.NativeImagePlugin
|
import net.woggioni.gradle.graalvm.NativeImagePlugin
|
||||||
import net.woggioni.gradle.graalvm.NativeImageTask
|
import net.woggioni.gradle.graalvm.UpxTask
|
||||||
import net.woggioni.gradle.graalvm.JlinkPlugin
|
import net.woggioni.gradle.graalvm.JlinkPlugin
|
||||||
import net.woggioni.gradle.graalvm.JlinkTask
|
import net.woggioni.gradle.graalvm.JlinkTask
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
configureNativeImage {
|
||||||
|
java {
|
||||||
|
}
|
||||||
|
kotlin {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
release {
|
release {
|
||||||
transitive = false
|
transitive = false
|
||||||
@@ -24,9 +34,25 @@ configurations {
|
|||||||
canBeResolved = true
|
canBeResolved = true
|
||||||
visible = true
|
visible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configureNativeImageImplementation {
|
||||||
|
extendsFrom implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
configureNativeImageRuntimeOnly {
|
||||||
|
extendsFrom runtimeOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeImage {
|
||||||
|
extendsFrom runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
configureNativeImageImplementation project
|
||||||
|
configureNativeImageImplementation project(':rbcs-server-memcache')
|
||||||
|
|
||||||
implementation catalog.jwo
|
implementation catalog.jwo
|
||||||
implementation catalog.slf4j.api
|
implementation catalog.slf4j.api
|
||||||
implementation catalog.picocli
|
implementation catalog.picocli
|
||||||
@@ -37,6 +63,8 @@ dependencies {
|
|||||||
// runtimeOnly catalog.slf4j.jdk14
|
// runtimeOnly catalog.slf4j.jdk14
|
||||||
runtimeOnly catalog.logback.classic
|
runtimeOnly catalog.logback.classic
|
||||||
// runtimeOnly catalog.slf4j.simple
|
// runtimeOnly catalog.slf4j.simple
|
||||||
|
nativeImage project(':rbcs-server-memcache')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,6 +77,8 @@ tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile) {
|
|||||||
options.javaModuleMainClass = mainClassName
|
options.javaModuleMainClass = mainClassName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Provider<Jar> jarTaskProvider = tasks.named(JavaPlugin.JAR_TASK_NAME, Jar)
|
||||||
|
|
||||||
Provider<EnvelopeJarTask> envelopeJarTaskProvider = tasks.named(EnvelopePlugin.ENVELOPE_JAR_TASK_NAME, EnvelopeJarTask.class) {
|
Provider<EnvelopeJarTask> envelopeJarTaskProvider = tasks.named(EnvelopePlugin.ENVELOPE_JAR_TASK_NAME, EnvelopeJarTask.class) {
|
||||||
mainModule = mainModuleName
|
mainModule = mainModuleName
|
||||||
mainClass = mainClassName
|
mainClass = mainClassName
|
||||||
@@ -60,15 +90,36 @@ 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) {
|
||||||
mainClass = mainClassName
|
javaLauncher = javaToolchains.launcherFor {
|
||||||
mainModule = mainModuleName
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
vendor = JvmVendorSpec.ORACLE
|
||||||
|
}
|
||||||
|
|
||||||
|
mainClass = "net.woggioni.rbcs.cli.graal.GraalNativeImageConfiguration"
|
||||||
|
classpath = project.files(
|
||||||
|
configurations.configureNativeImageRuntimeClasspath,
|
||||||
|
sourceSets.configureNativeImage.output
|
||||||
|
)
|
||||||
|
mergeConfiguration = false
|
||||||
|
systemProperty('logback.configurationFile', 'classpath:net/woggioni/rbcs/cli/logback.xml')
|
||||||
|
systemProperty('io.netty.leakDetectionLevel', 'DISABLED')
|
||||||
|
modularity.inferModulePath = false
|
||||||
|
enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named(NativeImagePlugin.NATIVE_IMAGE_TASK_NAME, NativeImageTask) {
|
nativeImage {
|
||||||
mainClass = mainClassName
|
mainClass = mainClassName
|
||||||
mainModule = mainModuleName
|
// mainModule = mainModuleName
|
||||||
useMusl = true
|
useMusl = true
|
||||||
buildStaticImage = true
|
buildStaticImage = true
|
||||||
|
linkAtBuildTime = false
|
||||||
|
classpath = project.files(jarTaskProvider, configurations.nativeImage)
|
||||||
|
compressExecutable = true
|
||||||
|
compressionLevel = 10
|
||||||
|
useLZMA = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Provider<UpxTask> upxTaskProvider = tasks.named(NativeImagePlugin.UPX_TASK_NAME, UpxTask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named(JlinkPlugin.JLINK_TASK_NAME, JlinkTask) {
|
tasks.named(JlinkPlugin.JLINK_TASK_NAME, JlinkTask) {
|
||||||
@@ -86,14 +137,20 @@ tasks.named(JavaPlugin.PROCESS_RESOURCES_TASK_NAME, ProcessResources) {
|
|||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
release(envelopeJarTaskProvider)
|
release(envelopeJarTaskProvider)
|
||||||
|
release(upxTaskProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
publications {
|
publications {
|
||||||
maven(MavenPublication) {
|
maven(MavenPublication) {
|
||||||
artifact envelopeJar
|
artifact envelopeJar
|
||||||
|
artifact(upxTaskProvider) {
|
||||||
|
classifier = "linux-x86_64"
|
||||||
|
extension = "exe"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
15
rbcs-cli/conf/rbcs-client.xml
Normal file
15
rbcs-cli/conf/rbcs-client.xml
Normal 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>
|
6
rbcs-cli/native-image/jni-config.json
Normal file
6
rbcs-cli/native-image/jni-config.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"java.lang.Boolean",
|
||||||
|
"methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
|
||||||
|
}
|
||||||
|
]
|
@@ -1,2 +1,2 @@
|
|||||||
Args=-H:Optimize=3 --gc=serial --initialize-at-run-time=io.netty
|
Args=-O3 --gc=serial --install-exit-handlers --initialize-at-run-time=io.netty --enable-url-protocols=jpms --initialize-at-build-time=net.woggioni.rbcs.common.RbcsUrlStreamHandlerFactory,net.woggioni.rbcs.common.RbcsUrlStreamHandlerFactory$JpmsHandler
|
||||||
#-H:TraceClassInitialization=io.netty.handler.ssl.BouncyCastleAlpnSslUtils
|
#-H:TraceClassInitialization=io.netty.handler.ssl.BouncyCastleAlpnSslUtils
|
8
rbcs-cli/native-image/predefined-classes-config.json
Normal file
8
rbcs-cli/native-image/predefined-classes-config.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"type":"agent-extracted",
|
||||||
|
"classes":[
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
2
rbcs-cli/native-image/proxy-config.json
Normal file
2
rbcs-cli/native-image/proxy-config.json
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[
|
||||||
|
]
|
756
rbcs-cli/native-image/reflect-config.json
Normal file
756
rbcs-cli/native-image/reflect-config.json
Normal file
@@ -0,0 +1,756 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"android.os.Build$VERSION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder",
|
||||||
|
"queryAllPublicMethods":true,
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.classic.joran.SerializedModelConfigurator",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.classic.util.DefaultJoranConfigurator",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.core.ConsoleAppender",
|
||||||
|
"queryAllPublicMethods":true,
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setTarget","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.core.OutputStreamAppender",
|
||||||
|
"methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.core.encoder.Encoder",
|
||||||
|
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder",
|
||||||
|
"methods":[{"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase",
|
||||||
|
"methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ch.qos.logback.core.spi.ContextAware",
|
||||||
|
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.aayushatharva.brotli4j.Brotli4jLoader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.github.luben.zstd.Zstd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.AESCipher$General",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.ARCFOURCipher",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.DESCipher",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.DESedeCipher",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.DHParameters",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.HmacCore$HmacSHA512",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.PBKDF2Core$HmacSHA512",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.crypto.provider.TlsMasterSecretGenerator",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.org.apache.xerces.internal.impl.dv.xs.ExtendedSchemaDVFactoryImpl",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.org.apache.xerces.internal.impl.dv.xs.SchemaDVFactoryImpl",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"groovy.lang.Closure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.bootstrap.ServerBootstrap$1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.buffer.AbstractByteBufAllocator",
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.buffer.AbstractReferenceCountedByteBuf",
|
||||||
|
"fields":[{"name":"refCnt"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.AbstractChannelHandlerContext",
|
||||||
|
"fields":[{"name":"handlerState"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.ChannelDuplexHandler",
|
||||||
|
"methods":[{"name":"bind","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.netty.channel.ChannelPromise"] }, {"name":"close","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.ChannelHandlerAdapter",
|
||||||
|
"methods":[{"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.ChannelInboundHandlerAdapter",
|
||||||
|
"methods":[{"name":"channelActive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.ChannelInitializer",
|
||||||
|
"methods":[{"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.ChannelOutboundBuffer",
|
||||||
|
"fields":[{"name":"totalPendingSize"}, {"name":"unwritable"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.ChannelOutboundHandlerAdapter",
|
||||||
|
"methods":[{"name":"bind","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.CombinedChannelDuplexHandler",
|
||||||
|
"methods":[{"name":"bind","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.netty.channel.ChannelPromise"] }, {"name":"channelActive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.DefaultChannelConfig",
|
||||||
|
"fields":[{"name":"autoRead"}, {"name":"writeBufferWaterMark"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.DefaultChannelPipeline",
|
||||||
|
"fields":[{"name":"estimatorHandle"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.DefaultChannelPipeline$HeadContext",
|
||||||
|
"methods":[{"name":"bind","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.netty.channel.ChannelPromise"] }, {"name":"channelActive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.DefaultChannelPipeline$TailContext",
|
||||||
|
"methods":[{"name":"channelActive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.SimpleChannelInboundHandler",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.embedded.EmbeddedChannel$2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.pool.SimpleChannelPool$1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.channel.socket.nio.NioSocketChannel",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.ByteToMessageDecoder",
|
||||||
|
"methods":[{"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.MessageAggregator",
|
||||||
|
"methods":[{"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.MessageToByteEncoder",
|
||||||
|
"methods":[{"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.MessageToMessageCodec",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.MessageToMessageDecoder",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.compression.JdkZlibDecoder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.compression.JdkZlibEncoder",
|
||||||
|
"methods":[{"name":"close","parameterTypes":["io.netty.channel.ChannelHandlerContext","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.http.HttpClientCodec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.http.HttpContentDecoder",
|
||||||
|
"methods":[{"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.http.HttpContentDecompressor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.http.HttpContentEncoder",
|
||||||
|
"methods":[{"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.http.HttpObjectAggregator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.http.HttpServerCodec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.codec.memcache.binary.BinaryMemcacheClientCodec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.stream.ChunkedWriteHandler",
|
||||||
|
"methods":[{"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"flush","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.handler.timeout.IdleStateHandler",
|
||||||
|
"methods":[{"name":"channelActive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.internal.tcnative.SSLContext"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.AbstractReferenceCounted",
|
||||||
|
"fields":[{"name":"refCnt"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.DefaultAttributeMap",
|
||||||
|
"fields":[{"name":"attributes"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.DefaultAttributeMap$DefaultAttribute",
|
||||||
|
"fields":[{"name":"attributeMap"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.Recycler$DefaultHandle",
|
||||||
|
"fields":[{"name":"state"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.ReferenceCountUtil",
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.concurrent.DefaultPromise",
|
||||||
|
"fields":[{"name":"result"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.concurrent.SingleThreadEventExecutor",
|
||||||
|
"fields":[{"name":"state"}, {"name":"threadProperties"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields",
|
||||||
|
"fields":[{"name":"producerLimit"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields",
|
||||||
|
"fields":[{"name":"consumerIndex"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields",
|
||||||
|
"fields":[{"name":"producerIndex"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.internal.shaded.org.jctools.queues.unpadded.MpscUnpaddedArrayQueueConsumerIndexField",
|
||||||
|
"fields":[{"name":"consumerIndex"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.internal.shaded.org.jctools.queues.unpadded.MpscUnpaddedArrayQueueProducerIndexField",
|
||||||
|
"fields":[{"name":"producerIndex"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"io.netty.util.internal.shaded.org.jctools.queues.unpadded.MpscUnpaddedArrayQueueProducerLimitField",
|
||||||
|
"fields":[{"name":"producerLimit"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.io.FilePermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.lang.Object",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.lang.ProcessHandle",
|
||||||
|
"methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.lang.RuntimePermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.lang.System",
|
||||||
|
"methods":[{"name":"console","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.lang.Thread",
|
||||||
|
"fields":[{"name":"threadLocalRandomProbe"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.net.NetPermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.net.SocketPermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.net.URLPermission",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.nio.Bits",
|
||||||
|
"fields":[{"name":"MAX_MEMORY"}, {"name":"UNALIGNED"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.nio.Buffer",
|
||||||
|
"fields":[{"name":"address"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.nio.ByteBuffer",
|
||||||
|
"methods":[{"name":"alignedSlice","parameterTypes":["int"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.nio.DirectByteBuffer",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["long","long"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.nio.channels.spi.SelectorProvider",
|
||||||
|
"methods":[{"name":"openServerSocketChannel","parameterTypes":["java.net.ProtocolFamily"] }, {"name":"openSocketChannel","parameterTypes":["java.net.ProtocolFamily"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.nio.file.Path"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.nio.file.Paths",
|
||||||
|
"methods":[{"name":"get","parameterTypes":["java.lang.String","java.lang.String[]"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.security.AlgorithmParametersSpi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.security.AllPermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.security.KeyStoreSpi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.security.SecureRandomParameters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.security.SecurityPermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.sql.Connection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.sql.Driver"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.sql.DriverManager",
|
||||||
|
"methods":[{"name":"getConnection","parameterTypes":["java.lang.String"] }, {"name":"getDriver","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.sql.Time",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["long"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.sql.Timestamp",
|
||||||
|
"methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.Duration",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.Instant",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.LocalDate",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.LocalDateTime",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.LocalTime",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.MonthDay",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.OffsetDateTime",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.OffsetTime",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.Period",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.Year",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.YearMonth",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.ZoneId",
|
||||||
|
"methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.ZoneOffset",
|
||||||
|
"methods":[{"name":"of","parameterTypes":["java.lang.String"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.time.ZonedDateTime",
|
||||||
|
"methods":[{"name":"parse","parameterTypes":["java.lang.CharSequence"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.util.PropertyPermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.util.concurrent.ForkJoinTask",
|
||||||
|
"fields":[{"name":"aux"}, {"name":"status"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.util.concurrent.atomic.AtomicBoolean",
|
||||||
|
"fields":[{"name":"value"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.util.concurrent.atomic.AtomicReference",
|
||||||
|
"fields":[{"name":"value"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.util.concurrent.atomic.Striped64",
|
||||||
|
"fields":[{"name":"base"}, {"name":"cellsBusy"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.util.concurrent.atomic.Striped64$Cell",
|
||||||
|
"fields":[{"name":"value"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.util.zip.Adler32",
|
||||||
|
"methods":[{"name":"update","parameterTypes":["java.nio.ByteBuffer"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"java.util.zip.CRC32",
|
||||||
|
"methods":[{"name":"update","parameterTypes":["java.nio.ByteBuffer"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"javax.security.auth.x500.X500Principal",
|
||||||
|
"fields":[{"name":"thisX500Name"}],
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["sun.security.x509.X500Name"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"javax.smartcardio.CardPermission"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"jdk.internal.misc.Unsafe",
|
||||||
|
"methods":[{"name":"getUnsafe","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.RemoteBuildCacheServerCli",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.RemoteBuildCacheServerCli$VersionProvider",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true,
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.RbcsCommand",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.commands.BenchmarkCommand",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.commands.ClientCommand",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.commands.GetCommand",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.commands.HealthCheckCommand",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.commands.PasswordHashCommand",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.commands.PutCommand",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.commands.ServerCommand",
|
||||||
|
"allDeclaredFields":true,
|
||||||
|
"queryAllDeclaredMethods":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.converters.ByteSizeConverter",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.converters.DurationConverter",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.cli.impl.converters.OutputStreamConverter",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.client.RemoteBuildCacheClient$sendRequest$1$operationComplete$responseHandler$1",
|
||||||
|
"methods":[{"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.client.RemoteBuildCacheClient$sendRequest$1$operationComplete$timeoutHandler$1",
|
||||||
|
"methods":[{"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.RemoteBuildCacheServer$HttpChunkContentCompressor",
|
||||||
|
"methods":[{"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.RemoteBuildCacheServer$NettyHttpBasicAuthenticator"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.RemoteBuildCacheServer$ServerInitializer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.RemoteBuildCacheServer$ServerInitializer$initChannel$4",
|
||||||
|
"methods":[{"name":"userEventTriggered","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.auth.AbstractNettyHttpAuthenticator",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.cache.FileSystemCacheHandler",
|
||||||
|
"methods":[{"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.cache.InMemoryCacheHandler",
|
||||||
|
"methods":[{"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.exception.ExceptionHandler",
|
||||||
|
"methods":[{"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.handler.CacheContentHandler",
|
||||||
|
"methods":[{"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.handler.MaxRequestSizeHandler",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.handler.ServerHandler",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"write","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object","io.netty.channel.ChannelPromise"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.handler.TraceHandler",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.memcache.MemcacheCacheHandler",
|
||||||
|
"methods":[{"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.memcache.client.MemcacheClient$sendRequest$1$operationComplete$handler$1",
|
||||||
|
"methods":[{"name":"channelInactive","parameterTypes":["io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.server.throttling.ThrottlingHandler",
|
||||||
|
"methods":[{"name":"channelRead","parameterTypes":["io.netty.channel.ChannelHandlerContext","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.misc.Unsafe",
|
||||||
|
"fields":[{"name":"theUnsafe"}],
|
||||||
|
"methods":[{"name":"copyMemory","parameterTypes":["java.lang.Object","long","java.lang.Object","long","long"] }, {"name":"getAndAddLong","parameterTypes":["java.lang.Object","long","long"] }, {"name":"getAndSetObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] }, {"name":"invokeCleaner","parameterTypes":["java.nio.ByteBuffer"] }, {"name":"storeFence","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.nio.ch.SelectorImpl",
|
||||||
|
"fields":[{"name":"publicSelectedKeys"}, {"name":"selectedKeys"}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.pkcs12.PKCS12KeyStore",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.DSA$SHA224withDSA",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.DSA$SHA256withDSA",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.JavaKeyStore$JKS",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.MD5",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.NativePRNG",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.NativePRNG$NonBlocking",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.SHA",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.SHA2$SHA224",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.SHA2$SHA256",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.SHA5$SHA384",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.SHA5$SHA512",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.provider.X509Factory",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.rsa.PSSParameters",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.rsa.RSAKeyFactory$Legacy",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.rsa.RSAPSSSignature",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.rsa.RSASignature$SHA224withRSA",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.ssl.KeyManagerFactoryImpl$SunX509",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.ssl.SSLContextImpl$DefaultSSLContext",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.ssl.SSLContextImpl$TLSContext",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":[] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.AuthorityInfoAccessExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.AuthorityKeyIdentifierExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.BasicConstraintsExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.CRLDistributionPointsExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.CertificatePoliciesExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.KeyUsageExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.NetscapeCertTypeExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.PrivateKeyUsageExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.SubjectAlternativeNameExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"sun.security.x509.SubjectKeyIdentifierExtension",
|
||||||
|
"methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Object"] }]
|
||||||
|
}
|
||||||
|
]
|
46
rbcs-cli/native-image/resource-config.json
Normal file
46
rbcs-cli/native-image/resource-config.json
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"resources":{
|
||||||
|
"includes":[{
|
||||||
|
"pattern":"\\QMETA-INF/MANIFEST.MF\\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.net.spi.URLStreamHandlerProvider\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\QMETA-INF/services/java.nio.channels.spi.SelectorProvider\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\QMETA-INF/services/javax.xml.parsers.DocumentBuilderFactory\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\QMETA-INF/services/net.woggioni.rbcs.api.CacheProvider\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\Qclasspath:net/woggioni/rbcs/cli/logback.xml\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\Qlogback-test.scmo\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\Qlogback.scmo\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\Qnet/woggioni/rbcs/cli/logback.xml\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\Qnet/woggioni/rbcs/client/schema/rbcs-client.xsd\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\Qnet/woggioni/rbcs/server/rbcs-default.xml\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"\\Qnet/woggioni/rbcs/server/schema/rbcs-server.xsd\\E"
|
||||||
|
}, {
|
||||||
|
"pattern":"java.base:\\Qsun/text/resources/LineBreakIteratorData\\E"
|
||||||
|
}]},
|
||||||
|
"bundles":[{
|
||||||
|
"name":"com.sun.org.apache.xerces.internal.impl.xpath.regex.message",
|
||||||
|
"locales":[""]
|
||||||
|
}]
|
||||||
|
}
|
14
rbcs-cli/native-image/serialization-config.json
Normal file
14
rbcs-cli/native-image/serialization-config.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"types":[
|
||||||
|
{
|
||||||
|
"name":"java.lang.String"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"net.woggioni.rbcs.api.CacheValueMetadata"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lambdaCapturingTypes":[
|
||||||
|
],
|
||||||
|
"proxies":[
|
||||||
|
]
|
||||||
|
}
|
@@ -0,0 +1,181 @@
|
|||||||
|
package net.woggioni.rbcs.cli.graal
|
||||||
|
|
||||||
|
import net.woggioni.jwo.NullOutputStream
|
||||||
|
import net.woggioni.rbcs.api.Configuration
|
||||||
|
import net.woggioni.rbcs.api.Configuration.User
|
||||||
|
import net.woggioni.rbcs.api.Role
|
||||||
|
import net.woggioni.rbcs.cli.RemoteBuildCacheServerCli
|
||||||
|
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.PutCommand
|
||||||
|
import net.woggioni.rbcs.common.HostAndPort
|
||||||
|
import net.woggioni.rbcs.common.PasswordSecurity.hashPassword
|
||||||
|
import net.woggioni.rbcs.common.RBCS
|
||||||
|
import net.woggioni.rbcs.common.Xml
|
||||||
|
import net.woggioni.rbcs.server.RemoteBuildCacheServer
|
||||||
|
import net.woggioni.rbcs.server.cache.FileSystemCacheConfiguration
|
||||||
|
import net.woggioni.rbcs.server.cache.InMemoryCacheConfiguration
|
||||||
|
import net.woggioni.rbcs.server.configuration.Parser
|
||||||
|
import net.woggioni.rbcs.server.memcache.MemcacheCacheConfiguration
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.net.URI
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
import java.util.concurrent.ExecutionException
|
||||||
|
import java.util.zip.Deflater
|
||||||
|
import net.woggioni.rbcs.client.Configuration as ClientConfiguration
|
||||||
|
import net.woggioni.rbcs.client.impl.Parser as ClientConfigurationParser
|
||||||
|
|
||||||
|
object GraalNativeImageConfiguration {
|
||||||
|
@JvmStatic
|
||||||
|
fun main(vararg args : String) {
|
||||||
|
|
||||||
|
val serverDoc = RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL.openStream().use {
|
||||||
|
Xml.parseXml(RemoteBuildCacheServer.DEFAULT_CONFIGURATION_URL, it)
|
||||||
|
}
|
||||||
|
Parser.parse(serverDoc)
|
||||||
|
|
||||||
|
val url = URI.create("file:conf/rbcs-client.xml").toURL()
|
||||||
|
val clientDoc = url.openStream().use {
|
||||||
|
Xml.parseXml(url, it)
|
||||||
|
}
|
||||||
|
ClientConfigurationParser.parse(clientDoc)
|
||||||
|
|
||||||
|
val PASSWORD = "password"
|
||||||
|
val readersGroup = Configuration.Group("readers", setOf(Role.Reader, Role.Healthcheck), null, null)
|
||||||
|
val writersGroup = Configuration.Group("writers", setOf(Role.Writer), null, null)
|
||||||
|
|
||||||
|
|
||||||
|
val users = listOf(
|
||||||
|
User("user1", hashPassword(PASSWORD), setOf(readersGroup), null),
|
||||||
|
User("user2", hashPassword(PASSWORD), setOf(writersGroup), null),
|
||||||
|
User("user3", hashPassword(PASSWORD), setOf(readersGroup, writersGroup), null),
|
||||||
|
User("", null, setOf(readersGroup), null),
|
||||||
|
User("user4", hashPassword(PASSWORD), setOf(readersGroup),
|
||||||
|
Configuration.Quota(1, Duration.of(1, ChronoUnit.DAYS), 0, 1)
|
||||||
|
),
|
||||||
|
User("user5", hashPassword(PASSWORD), setOf(readersGroup),
|
||||||
|
Configuration.Quota(1, Duration.of(5, ChronoUnit.SECONDS), 0, 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val serverPort = RBCS.getFreePort()
|
||||||
|
|
||||||
|
val caches = listOf<Configuration.Cache>(
|
||||||
|
InMemoryCacheConfiguration(
|
||||||
|
maxAge = Duration.ofSeconds(3600),
|
||||||
|
digestAlgorithm = "MD5",
|
||||||
|
compressionLevel = Deflater.DEFAULT_COMPRESSION,
|
||||||
|
compressionEnabled = false,
|
||||||
|
maxSize = 0x1000000,
|
||||||
|
chunkSize = 0x1000
|
||||||
|
),
|
||||||
|
FileSystemCacheConfiguration(
|
||||||
|
Path.of(System.getProperty("java.io.tmpdir")).resolve("rbcs"),
|
||||||
|
maxAge = Duration.ofSeconds(3600),
|
||||||
|
digestAlgorithm = "MD5",
|
||||||
|
compressionLevel = Deflater.DEFAULT_COMPRESSION,
|
||||||
|
compressionEnabled = false,
|
||||||
|
chunkSize = 0x1000
|
||||||
|
),
|
||||||
|
MemcacheCacheConfiguration(
|
||||||
|
listOf(MemcacheCacheConfiguration.Server(
|
||||||
|
HostAndPort("127.0.0.1", 11211),
|
||||||
|
1000,
|
||||||
|
4)
|
||||||
|
),
|
||||||
|
Duration.ofSeconds(60),
|
||||||
|
"MD5",
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
0x1000
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
for (cache in caches) {
|
||||||
|
val serverConfiguration = Configuration(
|
||||||
|
"127.0.0.1",
|
||||||
|
serverPort,
|
||||||
|
100,
|
||||||
|
null,
|
||||||
|
Configuration.EventExecutor(true),
|
||||||
|
Configuration.Connection(
|
||||||
|
Duration.ofSeconds(10),
|
||||||
|
Duration.ofSeconds(15),
|
||||||
|
Duration.ofSeconds(15),
|
||||||
|
0x10000,
|
||||||
|
),
|
||||||
|
users.asSequence().map { it.name to it }.toMap(),
|
||||||
|
sequenceOf(writersGroup, readersGroup).map { it.name to it }.toMap(),
|
||||||
|
cache,
|
||||||
|
Configuration.BasicAuthentication(),
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
MemcacheCacheConfiguration(
|
||||||
|
listOf(
|
||||||
|
MemcacheCacheConfiguration.Server(
|
||||||
|
HostAndPort("127.0.0.1", 11211),
|
||||||
|
1000,
|
||||||
|
4
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Duration.ofSeconds(60),
|
||||||
|
"MD5",
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
0x1000
|
||||||
|
)
|
||||||
|
|
||||||
|
val serverHandle = RemoteBuildCacheServer(serverConfiguration).run()
|
||||||
|
|
||||||
|
|
||||||
|
val clientProfile = ClientConfiguration.Profile(
|
||||||
|
URI.create("http://127.0.0.1:$serverPort/"),
|
||||||
|
null,
|
||||||
|
ClientConfiguration.Authentication.BasicAuthenticationCredentials("user3", PASSWORD),
|
||||||
|
Duration.ofSeconds(3),
|
||||||
|
10,
|
||||||
|
true,
|
||||||
|
ClientConfiguration.RetryPolicy(
|
||||||
|
3,
|
||||||
|
1000,
|
||||||
|
1.2
|
||||||
|
),
|
||||||
|
ClientConfiguration.TrustStore(null, null, false, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
HealthCheckCommand.execute(clientProfile)
|
||||||
|
|
||||||
|
BenchmarkCommand.execute(
|
||||||
|
clientProfile,
|
||||||
|
1000,
|
||||||
|
0x100,
|
||||||
|
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()
|
||||||
|
try {
|
||||||
|
serverHandle.get()
|
||||||
|
} catch (ee : ExecutionException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RemoteBuildCacheServerCli.main("--help")
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
|
|
||||||
@@ -23,15 +23,20 @@ class RemoteBuildCacheServerCli : RbcsCommand() {
|
|||||||
|
|
||||||
class VersionProvider : AbstractVersionProvider()
|
class VersionProvider : AbstractVersionProvider()
|
||||||
companion object {
|
companion object {
|
||||||
|
private fun setPropertyIfNotPresent(key: String, value: String) {
|
||||||
|
System.getProperty(key) ?: System.setProperty(key, value)
|
||||||
|
}
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(vararg args: String) {
|
fun main(vararg args: String) {
|
||||||
|
setPropertyIfNotPresent("logback.configurationFile", "net/woggioni/rbcs/cli/logback.xml")
|
||||||
|
setPropertyIfNotPresent("io.netty.leakDetectionLevel", "DISABLED")
|
||||||
val currentClassLoader = RemoteBuildCacheServerCli::class.java.classLoader
|
val currentClassLoader = RemoteBuildCacheServerCli::class.java.classLoader
|
||||||
Thread.currentThread().contextClassLoader = currentClassLoader
|
Thread.currentThread().contextClassLoader = currentClassLoader
|
||||||
if(currentClassLoader.javaClass.name == "net.woggioni.envelope.loader.ModuleClassLoader") {
|
if(currentClassLoader.javaClass.name == "net.woggioni.envelope.loader.ModuleClassLoader") {
|
||||||
//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")
|
||||||
|
@@ -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
|
||||||
@@ -26,8 +27,123 @@ import kotlin.random.Random
|
|||||||
showDefaultValues = true
|
showDefaultValues = true
|
||||||
)
|
)
|
||||||
class BenchmarkCommand : RbcsCommand() {
|
class BenchmarkCommand : RbcsCommand() {
|
||||||
companion object{
|
companion object {
|
||||||
private val log = createLogger<BenchmarkCommand>()
|
private val log = createLogger<BenchmarkCommand>()
|
||||||
|
|
||||||
|
fun execute(profile : Configuration.Profile,
|
||||||
|
numberOfEntries : Int,
|
||||||
|
entrySize : Int,
|
||||||
|
useRandomValue : Boolean,
|
||||||
|
) {
|
||||||
|
val progressThreshold = LongMath.ceilDiv(numberOfEntries.toLong(), 20)
|
||||||
|
RemoteBuildCacheClient(profile).use { client ->
|
||||||
|
val entryGenerator = sequence {
|
||||||
|
val random = Random(SecureRandom.getInstance("NativePRNGNonBlocking").nextLong())
|
||||||
|
while (true) {
|
||||||
|
val key = JWO.bytesToHex(random.nextBytes(16))
|
||||||
|
val value = if (useRandomValue) {
|
||||||
|
random.nextBytes(entrySize)
|
||||||
|
} else {
|
||||||
|
val byteValue = random.nextInt().toByte()
|
||||||
|
ByteArray(entrySize) { _ -> byteValue }
|
||||||
|
}
|
||||||
|
yield(key to value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info {
|
||||||
|
"Starting insertion"
|
||||||
|
}
|
||||||
|
val entries = let {
|
||||||
|
val completionCounter = AtomicLong(0)
|
||||||
|
val completionQueue = LinkedBlockingQueue<Pair<String, ByteArray>>(numberOfEntries)
|
||||||
|
val start = Instant.now()
|
||||||
|
val semaphore = Semaphore(profile.maxConnections * 5)
|
||||||
|
val iterator = entryGenerator.take(numberOfEntries).iterator()
|
||||||
|
while (completionCounter.get() < numberOfEntries) {
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
val entry = iterator.next()
|
||||||
|
semaphore.acquire()
|
||||||
|
val future =
|
||||||
|
client.put(entry.first, entry.second, CacheValueMetadata(null, null)).thenApply { entry }
|
||||||
|
future.whenComplete { result, ex ->
|
||||||
|
if (ex != null) {
|
||||||
|
log.error(ex.message, ex)
|
||||||
|
} else {
|
||||||
|
completionQueue.put(result)
|
||||||
|
}
|
||||||
|
semaphore.release()
|
||||||
|
val completed = completionCounter.incrementAndGet()
|
||||||
|
if (completed.mod(progressThreshold) == 0L) {
|
||||||
|
log.debug {
|
||||||
|
"Inserted $completed / $numberOfEntries"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Thread.sleep(Duration.of(500, ChronoUnit.MILLIS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val inserted = completionQueue.toList()
|
||||||
|
val end = Instant.now()
|
||||||
|
log.info {
|
||||||
|
val elapsed = Duration.between(start, end).toMillis()
|
||||||
|
val opsPerSecond = String.format("%.2f", numberOfEntries.toDouble() / elapsed * 1000)
|
||||||
|
"Insertion rate: $opsPerSecond ops/s"
|
||||||
|
}
|
||||||
|
inserted
|
||||||
|
}
|
||||||
|
log.info {
|
||||||
|
"Inserted ${entries.size} entries"
|
||||||
|
}
|
||||||
|
log.info {
|
||||||
|
"Starting retrieval"
|
||||||
|
}
|
||||||
|
if (entries.isNotEmpty()) {
|
||||||
|
val completionCounter = AtomicLong(0)
|
||||||
|
val semaphore = Semaphore(profile.maxConnections * 5)
|
||||||
|
val start = Instant.now()
|
||||||
|
val it = entries.iterator()
|
||||||
|
while (completionCounter.get() < entries.size) {
|
||||||
|
if (it.hasNext()) {
|
||||||
|
val entry = it.next()
|
||||||
|
semaphore.acquire()
|
||||||
|
val future = client.get(entry.first).thenApply {
|
||||||
|
if (it == null) {
|
||||||
|
log.error {
|
||||||
|
"Missing entry for key '${entry.first}'"
|
||||||
|
}
|
||||||
|
} else if (!entry.second.contentEquals(it)) {
|
||||||
|
log.error {
|
||||||
|
"Retrieved a value different from what was inserted for key '${entry.first}'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
future.whenComplete { _, _ ->
|
||||||
|
val completed = completionCounter.incrementAndGet()
|
||||||
|
if (completed.mod(progressThreshold) == 0L) {
|
||||||
|
log.debug {
|
||||||
|
"Retrieved $completed / ${entries.size}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
semaphore.release()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Thread.sleep(Duration.of(500, ChronoUnit.MILLIS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val end = Instant.now()
|
||||||
|
log.info {
|
||||||
|
val elapsed = Duration.between(start, end).toMillis()
|
||||||
|
val opsPerSecond = String.format("%.2f", entries.size.toDouble() / elapsed * 1000)
|
||||||
|
"Retrieval rate: $opsPerSecond ops/s"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error("Skipping retrieval benchmark as it was not possible to insert any entry in the cache")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandLine.Spec
|
@CommandLine.Spec
|
||||||
@@ -60,113 +176,11 @@ 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")
|
||||||
}
|
}
|
||||||
val progressThreshold = LongMath.ceilDiv(numberOfEntries.toLong(), 20)
|
execute(
|
||||||
RemoteBuildCacheClient(profile).use { client ->
|
profile,
|
||||||
|
numberOfEntries,
|
||||||
val entryGenerator = sequence {
|
size,
|
||||||
val random = Random(SecureRandom.getInstance("NativePRNGNonBlocking").nextLong())
|
randomValues
|
||||||
while (true) {
|
)
|
||||||
val key = JWO.bytesToHex(random.nextBytes(16))
|
|
||||||
val value = if(randomValues) {
|
|
||||||
random.nextBytes(size)
|
|
||||||
} else {
|
|
||||||
val byteValue = random.nextInt().toByte()
|
|
||||||
ByteArray(size) {_ -> byteValue}
|
|
||||||
}
|
|
||||||
yield(key to value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info {
|
|
||||||
"Starting insertion"
|
|
||||||
}
|
|
||||||
val entries = let {
|
|
||||||
val completionCounter = AtomicLong(0)
|
|
||||||
val completionQueue = LinkedBlockingQueue<Pair<String, ByteArray>>(numberOfEntries)
|
|
||||||
val start = Instant.now()
|
|
||||||
val semaphore = Semaphore(profile.maxConnections * 5)
|
|
||||||
val iterator = entryGenerator.take(numberOfEntries).iterator()
|
|
||||||
while (completionCounter.get() < numberOfEntries) {
|
|
||||||
if (iterator.hasNext()) {
|
|
||||||
val entry = iterator.next()
|
|
||||||
semaphore.acquire()
|
|
||||||
val future = client.put(entry.first, entry.second, CacheValueMetadata(null, null)).thenApply { entry }
|
|
||||||
future.whenComplete { result, ex ->
|
|
||||||
if (ex != null) {
|
|
||||||
log.error(ex.message, ex)
|
|
||||||
} else {
|
|
||||||
completionQueue.put(result)
|
|
||||||
}
|
|
||||||
semaphore.release()
|
|
||||||
val completed = completionCounter.incrementAndGet()
|
|
||||||
if(completed.mod(progressThreshold) == 0L) {
|
|
||||||
log.debug {
|
|
||||||
"Inserted $completed / $numberOfEntries"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Thread.sleep(Duration.of(500, ChronoUnit.MILLIS))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val inserted = completionQueue.toList()
|
|
||||||
val end = Instant.now()
|
|
||||||
log.info {
|
|
||||||
val elapsed = Duration.between(start, end).toMillis()
|
|
||||||
val opsPerSecond = String.format("%.2f", numberOfEntries.toDouble() / elapsed * 1000)
|
|
||||||
"Insertion rate: $opsPerSecond ops/s"
|
|
||||||
}
|
|
||||||
inserted
|
|
||||||
}
|
|
||||||
log.info {
|
|
||||||
"Inserted ${entries.size} entries"
|
|
||||||
}
|
|
||||||
log.info {
|
|
||||||
"Starting retrieval"
|
|
||||||
}
|
|
||||||
if (entries.isNotEmpty()) {
|
|
||||||
val completionCounter = AtomicLong(0)
|
|
||||||
val semaphore = Semaphore(profile.maxConnections * 5)
|
|
||||||
val start = Instant.now()
|
|
||||||
val it = entries.iterator()
|
|
||||||
while (completionCounter.get() < entries.size) {
|
|
||||||
if (it.hasNext()) {
|
|
||||||
val entry = it.next()
|
|
||||||
semaphore.acquire()
|
|
||||||
val future = client.get(entry.first).thenApply {
|
|
||||||
if (it == null) {
|
|
||||||
log.error {
|
|
||||||
"Missing entry for key '${entry.first}'"
|
|
||||||
}
|
|
||||||
} else if (!entry.second.contentEquals(it)) {
|
|
||||||
log.error {
|
|
||||||
"Retrieved a value different from what was inserted for key '${entry.first}'"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
future.whenComplete { _, _ ->
|
|
||||||
val completed = completionCounter.incrementAndGet()
|
|
||||||
if(completed.mod(progressThreshold) == 0L) {
|
|
||||||
log.debug {
|
|
||||||
"Retrieved $completed / ${entries.size}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
semaphore.release()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Thread.sleep(Duration.of(500, ChronoUnit.MILLIS))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val end = Instant.now()
|
|
||||||
log.info {
|
|
||||||
val elapsed = Duration.between(start, end).toMillis()
|
|
||||||
val opsPerSecond = String.format("%.2f", entries.size.toDouble() / elapsed * 1000)
|
|
||||||
"Retrieval rate: $opsPerSecond ops/s"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.error("Skipping retrieval benchmark as it was not possible to insert any entry in the cache")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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)
|
||||||
|
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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
|
||||||
@@ -15,6 +16,27 @@ import kotlin.random.Random
|
|||||||
class HealthCheckCommand : RbcsCommand() {
|
class HealthCheckCommand : RbcsCommand() {
|
||||||
companion object{
|
companion object{
|
||||||
private val log = createLogger<HealthCheckCommand>()
|
private val log = createLogger<HealthCheckCommand>()
|
||||||
|
|
||||||
|
fun execute(profile : Configuration.Profile) {
|
||||||
|
RemoteBuildCacheClient(profile).use { client ->
|
||||||
|
val random = Random(SecureRandom.getInstance("NativePRNGNonBlocking").nextLong())
|
||||||
|
val nonce = ByteArray(0xa0)
|
||||||
|
random.nextBytes(nonce)
|
||||||
|
client.healthCheck(nonce).thenApply { value ->
|
||||||
|
if(value == null) {
|
||||||
|
throw IllegalStateException("Empty response from server")
|
||||||
|
}
|
||||||
|
val offset = value.size - nonce.size
|
||||||
|
for(i in 0 until nonce.size) {
|
||||||
|
val a = nonce[i]
|
||||||
|
val b = value[offset + i]
|
||||||
|
if(a != b) {
|
||||||
|
throw IllegalStateException("Server nonce does not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandLine.Spec
|
@CommandLine.Spec
|
||||||
@@ -26,23 +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")
|
||||||
}
|
}
|
||||||
RemoteBuildCacheClient(profile).use { client ->
|
execute(profile)
|
||||||
val random = Random(SecureRandom.getInstance("NativePRNGNonBlocking").nextLong())
|
|
||||||
val nonce = ByteArray(0xa0)
|
|
||||||
random.nextBytes(nonce)
|
|
||||||
client.healthCheck(nonce).thenApply { value ->
|
|
||||||
if(value == null) {
|
|
||||||
throw IllegalStateException("Empty response from server")
|
|
||||||
}
|
|
||||||
val offset = value.size - nonce.size
|
|
||||||
for(i in 0 until nonce.size) {
|
|
||||||
val a = nonce[i]
|
|
||||||
val b = value[offset + i]
|
|
||||||
if(a != b) {
|
|
||||||
throw IllegalStateException("Server nonce does not match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.get()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -36,24 +36,21 @@ 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.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
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import javax.net.ssl.TrustManagerFactory
|
||||||
|
import javax.net.ssl.X509TrustManager
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import io.netty.util.concurrent.Future as NettyFuture
|
import io.netty.util.concurrent.Future as NettyFuture
|
||||||
|
|
||||||
@@ -63,62 +60,40 @@ class RemoteBuildCacheClient(private val profile: Configuration.Profile) : AutoC
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val group: NioEventLoopGroup
|
private val group: NioEventLoopGroup
|
||||||
private var 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 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?,
|
|
||||||
)
|
|
||||||
|
|
||||||
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 ->
|
||||||
(profile.authentication as? Configuration.Authentication.TlsClientAuthenticationCredentials)?.let { tlsClientAuthenticationCredentials ->
|
(profile.authentication as? Configuration.Authentication.TlsClientAuthenticationCredentials)?.let { tlsClientAuthenticationCredentials ->
|
||||||
builder.keyManager(
|
builder.apply {
|
||||||
tlsClientAuthenticationCredentials.key,
|
keyManager(
|
||||||
*tlsClientAuthenticationCredentials.certificateChain
|
tlsClientAuthenticationCredentials.key,
|
||||||
)
|
*tlsClientAuthenticationCredentials.certificateChain
|
||||||
|
)
|
||||||
|
profile.tlsTruststore?.let { trustStore ->
|
||||||
|
if(!trustStore.verifyServerCertificate) {
|
||||||
|
trustManager(object : X509TrustManager {
|
||||||
|
override fun checkClientTrusted(certChain: Array<out X509Certificate>, p1: String?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkServerTrusted(certChain: Array<out X509Certificate>, p1: String?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAcceptedIssuers() = null
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
trustStore.file?.let {
|
||||||
|
val ts = loadKeystore(it, trustStore.password)
|
||||||
|
val trustManagerFactory: TrustManagerFactory =
|
||||||
|
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||||
|
trustManagerFactory.init(ts)
|
||||||
|
trustManager(trustManagerFactory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
@@ -178,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()
|
@@ -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,9 +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 : 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" -> {
|
||||||
@@ -51,7 +52,7 @@ object Parser {
|
|||||||
.toList()
|
.toList()
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
authentication =
|
authentication =
|
||||||
RemoteBuildCacheClient.Configuration.Authentication.TlsClientAuthenticationCredentials(
|
Configuration.Authentication.TlsClientAuthenticationCredentials(
|
||||||
key,
|
key,
|
||||||
certChain
|
certChain
|
||||||
)
|
)
|
||||||
@@ -63,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
|
||||||
)
|
)
|
||||||
@@ -82,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()
|
||||||
@@ -90,24 +91,29 @@ 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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"tls-trust-store" -> {
|
||||||
|
val file = gchild.renderAttribute("file")
|
||||||
|
?.let(Path::of)
|
||||||
|
val password = gchild.renderAttribute("password")
|
||||||
|
val checkCertificateStatus = gchild.renderAttribute("check-certificate-status")
|
||||||
|
?.let(String::toBoolean) ?: false
|
||||||
|
val verifyServerCertificate = gchild.renderAttribute("verify-server-certificate")
|
||||||
|
?.let(String::toBoolean) ?: true
|
||||||
|
trustStore = Configuration.TrustStore(file, password, checkCertificateStatus, verifyServerCertificate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val maxConnections = child.renderAttribute("max-connections")
|
val maxConnections = child.renderAttribute("max-connections")
|
||||||
@@ -119,18 +125,19 @@ 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,
|
||||||
connectionTimeout,
|
connectionTimeout,
|
||||||
maxConnections,
|
maxConnections,
|
||||||
compressionEnabled,
|
compressionEnabled,
|
||||||
retryPolicy
|
retryPolicy,
|
||||||
|
trustStore
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return RemoteBuildCacheClient.Configuration(profiles)
|
return Configuration(profiles)
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,46 +15,239 @@
|
|||||||
<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: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:attribute name="file" type="xs:string" use="required">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>
|
||||||
|
Path to the truststore file
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="password" type="xs:string">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>
|
||||||
|
Truststore file password
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="check-certificate-status" type="xs:boolean">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>
|
||||||
|
Whether or not check the server certificate validity using CRL/OCSP
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="verify-server-certificate" type="xs:boolean" use="optional" default="true">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>
|
||||||
|
If false, the client will blindly trust the certificate provided by the server
|
||||||
|
</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
</xs:complexType>
|
||||||
</xs:schema>
|
</xs:schema>
|
||||||
|
@@ -129,7 +129,7 @@ class RetryTest {
|
|||||||
previousAttempt.first + testArgs.initialDelay * Math.pow(testArgs.exp, index.toDouble()) * 1e6
|
previousAttempt.first + testArgs.initialDelay * Math.pow(testArgs.exp, index.toDouble()) * 1e6
|
||||||
val actualTimestamp = timestamp
|
val actualTimestamp = timestamp
|
||||||
val err = Math.abs(expectedTimestamp - actualTimestamp) / expectedTimestamp
|
val err = Math.abs(expectedTimestamp - actualTimestamp) / expectedTimestamp
|
||||||
Assertions.assertTrue(err < 1e-2)
|
Assertions.assertTrue(err < 0.1)
|
||||||
}
|
}
|
||||||
if (index == attempts.size - 1 && index < testArgs.maxAttempt - 1) {
|
if (index == attempts.size - 1 && index < testArgs.maxAttempt - 1) {
|
||||||
/*
|
/*
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
key-store-password="password"
|
key-store-password="password"
|
||||||
key-alias="woggioni@c962475fa38"
|
key-alias="woggioni@c962475fa38"
|
||||||
key-password="key-password"/>
|
key-password="key-password"/>
|
||||||
|
<connection write-idle-timeout="PT60S" read-idle-timeout="PT60S" write-timeout="PT0S" read-timeout="PT0S" idle-timeout="PT30S" />
|
||||||
|
<tls-trust-store file="file.pfx" password="password" check-certificate-status="false" verify-server-certificate="true"/>
|
||||||
</profile>
|
</profile>
|
||||||
<profile name="profile2" base-url="https://rbcs2.example.com/">
|
<profile name="profile2" base-url="https://rbcs2.example.com/">
|
||||||
<basic-auth user="user" password="password"/>
|
<basic-auth user="user" password="password"/>
|
||||||
|
@@ -1,9 +1,26 @@
|
|||||||
package net.woggioni.rbcs.common
|
package net.woggioni.rbcs.common
|
||||||
|
|
||||||
import net.woggioni.jwo.JWO
|
import net.woggioni.jwo.JWO
|
||||||
|
import net.woggioni.jwo.Tuple2
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.InetAddress
|
||||||
|
import java.net.ServerSocket
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.security.KeyStore
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
import java.security.cert.CertPathValidator
|
||||||
|
import java.security.cert.CertPathValidatorException
|
||||||
|
import java.security.cert.CertificateException
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
import java.security.cert.PKIXParameters
|
||||||
|
import java.security.cert.PKIXRevocationChecker
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import java.util.EnumSet
|
||||||
|
import javax.net.ssl.TrustManagerFactory
|
||||||
|
import javax.net.ssl.X509TrustManager
|
||||||
|
|
||||||
object RBCS {
|
object RBCS {
|
||||||
fun String.toUrl() : URL = URL.of(URI(this), null)
|
fun String.toUrl() : URL = URL.of(URI(this), null)
|
||||||
@@ -58,4 +75,86 @@ object RBCS {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getFreePort(): Int {
|
||||||
|
var count = 0
|
||||||
|
while (count < 50) {
|
||||||
|
try {
|
||||||
|
ServerSocket(0, 50, InetAddress.getLocalHost()).use { serverSocket ->
|
||||||
|
val candidate = serverSocket.localPort
|
||||||
|
if (candidate > 0) {
|
||||||
|
return candidate
|
||||||
|
} else {
|
||||||
|
throw RuntimeException("Got invalid port number: $candidate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ignored: IOException) {
|
||||||
|
++count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw RuntimeException("Error trying to find an open port")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadKeystore(file: Path, password: String?): KeyStore {
|
||||||
|
val ext = JWO.splitExtension(file)
|
||||||
|
.map(Tuple2<String, String>::get_2)
|
||||||
|
.orElseThrow {
|
||||||
|
IllegalArgumentException(
|
||||||
|
"Keystore file '${file}' must have .jks, .p12, .pfx extension"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val keystore = when (ext.substring(1).lowercase()) {
|
||||||
|
"jks" -> KeyStore.getInstance("JKS")
|
||||||
|
"p12", "pfx" -> KeyStore.getInstance("PKCS12")
|
||||||
|
else -> throw IllegalArgumentException(
|
||||||
|
"Keystore file '${file}' must have .jks, .p12, .pfx extension"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Files.newInputStream(file).use {
|
||||||
|
keystore.load(it, password?.let(String::toCharArray))
|
||||||
|
}
|
||||||
|
return keystore
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTrustManager(trustStore: KeyStore?, certificateRevocationEnabled: Boolean): X509TrustManager {
|
||||||
|
return if (trustStore != null) {
|
||||||
|
val certificateFactory = CertificateFactory.getInstance("X.509")
|
||||||
|
val validator = CertPathValidator.getInstance("PKIX").apply {
|
||||||
|
val rc = revocationChecker as PKIXRevocationChecker
|
||||||
|
rc.options = EnumSet.of(
|
||||||
|
PKIXRevocationChecker.Option.NO_FALLBACK
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val params = PKIXParameters(trustStore).apply {
|
||||||
|
isRevocationEnabled = certificateRevocationEnabled
|
||||||
|
}
|
||||||
|
object : X509TrustManager {
|
||||||
|
override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String) {
|
||||||
|
val clientCertificateChain = certificateFactory.generateCertPath(chain.toList())
|
||||||
|
try {
|
||||||
|
validator.validate(clientCertificateChain, params)
|
||||||
|
} catch (ex: CertPathValidatorException) {
|
||||||
|
throw CertificateException(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String) {
|
||||||
|
throw NotImplementedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val acceptedIssuers = trustStore.aliases().asSequence()
|
||||||
|
.filter(trustStore::isCertificateEntry)
|
||||||
|
.map(trustStore::getCertificate)
|
||||||
|
.map { it as X509Certificate }
|
||||||
|
.toList()
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
|
override fun getAcceptedIssuers() = acceptedIssuers
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||||
|
trustManagerFactory.trustManagers.asSequence().filter { it is X509TrustManager }
|
||||||
|
.single() as X509TrustManager
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -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"
|
||||||
|
@@ -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"/>
|
||||||
|
@@ -35,13 +35,13 @@ import io.netty.handler.timeout.IdleStateHandler
|
|||||||
import io.netty.util.AttributeKey
|
import io.netty.util.AttributeKey
|
||||||
import io.netty.util.concurrent.DefaultEventExecutorGroup
|
import io.netty.util.concurrent.DefaultEventExecutorGroup
|
||||||
import io.netty.util.concurrent.EventExecutorGroup
|
import io.netty.util.concurrent.EventExecutorGroup
|
||||||
import net.woggioni.jwo.JWO
|
|
||||||
import net.woggioni.jwo.Tuple2
|
|
||||||
import net.woggioni.rbcs.api.AsyncCloseable
|
import net.woggioni.rbcs.api.AsyncCloseable
|
||||||
import net.woggioni.rbcs.api.Configuration
|
import net.woggioni.rbcs.api.Configuration
|
||||||
import net.woggioni.rbcs.api.exception.ConfigurationException
|
import net.woggioni.rbcs.api.exception.ConfigurationException
|
||||||
import net.woggioni.rbcs.common.PasswordSecurity.decodePasswordHash
|
import net.woggioni.rbcs.common.PasswordSecurity.decodePasswordHash
|
||||||
import net.woggioni.rbcs.common.PasswordSecurity.hashPassword
|
import net.woggioni.rbcs.common.PasswordSecurity.hashPassword
|
||||||
|
import net.woggioni.rbcs.common.RBCS.getTrustManager
|
||||||
|
import net.woggioni.rbcs.common.RBCS.loadKeystore
|
||||||
import net.woggioni.rbcs.common.RBCS.toUrl
|
import net.woggioni.rbcs.common.RBCS.toUrl
|
||||||
import net.woggioni.rbcs.common.Xml
|
import net.woggioni.rbcs.common.Xml
|
||||||
import net.woggioni.rbcs.common.createLogger
|
import net.woggioni.rbcs.common.createLogger
|
||||||
@@ -49,7 +49,6 @@ import net.woggioni.rbcs.common.debug
|
|||||||
import net.woggioni.rbcs.common.info
|
import net.woggioni.rbcs.common.info
|
||||||
import net.woggioni.rbcs.server.auth.AbstractNettyHttpAuthenticator
|
import net.woggioni.rbcs.server.auth.AbstractNettyHttpAuthenticator
|
||||||
import net.woggioni.rbcs.server.auth.Authorizer
|
import net.woggioni.rbcs.server.auth.Authorizer
|
||||||
import net.woggioni.rbcs.server.auth.ClientCertificateValidator
|
|
||||||
import net.woggioni.rbcs.server.auth.RoleAuthorizer
|
import net.woggioni.rbcs.server.auth.RoleAuthorizer
|
||||||
import net.woggioni.rbcs.server.configuration.Parser
|
import net.woggioni.rbcs.server.configuration.Parser
|
||||||
import net.woggioni.rbcs.server.configuration.Serializer
|
import net.woggioni.rbcs.server.configuration.Serializer
|
||||||
@@ -63,7 +62,6 @@ import java.io.OutputStream
|
|||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyStore
|
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@@ -87,7 +85,7 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
val userAttribute: AttributeKey<Configuration.User> = AttributeKey.valueOf("user")
|
val userAttribute: AttributeKey<Configuration.User> = AttributeKey.valueOf("user")
|
||||||
val groupAttribute: AttributeKey<Set<Configuration.Group>> = AttributeKey.valueOf("group")
|
val groupAttribute: AttributeKey<Set<Configuration.Group>> = AttributeKey.valueOf("group")
|
||||||
|
|
||||||
val DEFAULT_CONFIGURATION_URL by lazy { "classpath:net/woggioni/rbcs/server/rbcs-default.xml".toUrl() }
|
val DEFAULT_CONFIGURATION_URL by lazy { "jpms://net.woggioni.rbcs.server/net/woggioni/rbcs/server/rbcs-default.xml".toUrl() }
|
||||||
private const val SSL_HANDLER_NAME = "sslHandler"
|
private const val SSL_HANDLER_NAME = "sslHandler"
|
||||||
|
|
||||||
fun loadConfiguration(configurationFile: Path): Configuration {
|
fun loadConfiguration(configurationFile: Path): Configuration {
|
||||||
@@ -230,7 +228,7 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
val clientAuth = tls.trustStore?.let { trustStore ->
|
val clientAuth = tls.trustStore?.let { trustStore ->
|
||||||
val ts = loadKeystore(trustStore.file, trustStore.password)
|
val ts = loadKeystore(trustStore.file, trustStore.password)
|
||||||
trustManager(
|
trustManager(
|
||||||
ClientCertificateValidator.getTrustManager(ts, trustStore.isCheckCertificateStatus)
|
getTrustManager(ts, trustStore.isCheckCertificateStatus)
|
||||||
)
|
)
|
||||||
if (trustStore.isRequireClientCertificate) ClientAuth.REQUIRE
|
if (trustStore.isRequireClientCertificate) ClientAuth.REQUIRE
|
||||||
else ClientAuth.OPTIONAL
|
else ClientAuth.OPTIONAL
|
||||||
@@ -240,27 +238,6 @@ class RemoteBuildCacheServer(private val cfg: Configuration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadKeystore(file: Path, password: String?): KeyStore {
|
|
||||||
val ext = JWO.splitExtension(file)
|
|
||||||
.map(Tuple2<String, String>::get_2)
|
|
||||||
.orElseThrow {
|
|
||||||
IllegalArgumentException(
|
|
||||||
"Keystore file '${file}' must have .jks, .p12, .pfx extension"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val keystore = when (ext.substring(1).lowercase()) {
|
|
||||||
"jks" -> KeyStore.getInstance("JKS")
|
|
||||||
"p12", "pfx" -> KeyStore.getInstance("PKCS12")
|
|
||||||
else -> throw IllegalArgumentException(
|
|
||||||
"Keystore file '${file}' must have .jks, .p12, .pfx extension"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Files.newInputStream(file).use {
|
|
||||||
keystore.load(it, password?.let(String::toCharArray))
|
|
||||||
}
|
|
||||||
return keystore
|
|
||||||
}
|
|
||||||
|
|
||||||
private val log = createLogger<ServerInitializer>()
|
private val log = createLogger<ServerInitializer>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,90 +0,0 @@
|
|||||||
package net.woggioni.rbcs.server.auth
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext
|
|
||||||
import io.netty.channel.ChannelInboundHandlerAdapter
|
|
||||||
import io.netty.handler.ssl.SslHandler
|
|
||||||
import io.netty.handler.ssl.SslHandshakeCompletionEvent
|
|
||||||
import java.security.KeyStore
|
|
||||||
import java.security.cert.CertPathValidator
|
|
||||||
import java.security.cert.CertPathValidatorException
|
|
||||||
import java.security.cert.CertificateException
|
|
||||||
import java.security.cert.CertificateFactory
|
|
||||||
import java.security.cert.PKIXParameters
|
|
||||||
import java.security.cert.PKIXRevocationChecker
|
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.util.EnumSet
|
|
||||||
import javax.net.ssl.SSLSession
|
|
||||||
import javax.net.ssl.TrustManagerFactory
|
|
||||||
import javax.net.ssl.X509TrustManager
|
|
||||||
|
|
||||||
|
|
||||||
class ClientCertificateValidator private constructor(
|
|
||||||
private val sslHandler: SslHandler,
|
|
||||||
private val x509TrustManager: X509TrustManager
|
|
||||||
) : ChannelInboundHandlerAdapter() {
|
|
||||||
override fun userEventTriggered(ctx: ChannelHandlerContext, evt: Any) {
|
|
||||||
if (evt is SslHandshakeCompletionEvent) {
|
|
||||||
if (evt.isSuccess) {
|
|
||||||
val session: SSLSession = sslHandler.engine().session
|
|
||||||
val clientCertificateChain = session.peerCertificates as Array<X509Certificate>
|
|
||||||
val authType: String = clientCertificateChain[0].publicKey.algorithm
|
|
||||||
x509TrustManager.checkClientTrusted(clientCertificateChain, authType)
|
|
||||||
} else {
|
|
||||||
// Handle the failure, for example by closing the channel.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.userEventTriggered(ctx, evt)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun getTrustManager(trustStore: KeyStore?, certificateRevocationEnabled: Boolean): X509TrustManager {
|
|
||||||
return if (trustStore != null) {
|
|
||||||
val certificateFactory = CertificateFactory.getInstance("X.509")
|
|
||||||
val validator = CertPathValidator.getInstance("PKIX").apply {
|
|
||||||
val rc = revocationChecker as PKIXRevocationChecker
|
|
||||||
rc.options = EnumSet.of(
|
|
||||||
PKIXRevocationChecker.Option.NO_FALLBACK
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val params = PKIXParameters(trustStore).apply {
|
|
||||||
isRevocationEnabled = certificateRevocationEnabled
|
|
||||||
}
|
|
||||||
object : X509TrustManager {
|
|
||||||
override fun checkClientTrusted(chain: Array<out X509Certificate>, authType: String) {
|
|
||||||
val clientCertificateChain = certificateFactory.generateCertPath(chain.toList())
|
|
||||||
try {
|
|
||||||
validator.validate(clientCertificateChain, params)
|
|
||||||
} catch (ex: CertPathValidatorException) {
|
|
||||||
throw CertificateException(ex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun checkServerTrusted(chain: Array<out X509Certificate>, authType: String) {
|
|
||||||
throw NotImplementedError()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val acceptedIssuers = trustStore.aliases().asSequence()
|
|
||||||
.filter(trustStore::isCertificateEntry)
|
|
||||||
.map(trustStore::getCertificate)
|
|
||||||
.map { it as X509Certificate }
|
|
||||||
.toList()
|
|
||||||
.toTypedArray()
|
|
||||||
|
|
||||||
override fun getAcceptedIssuers() = acceptedIssuers
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
|
||||||
trustManagerFactory.trustManagers.asSequence().filter { it is X509TrustManager }
|
|
||||||
.single() as X509TrustManager
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun of(
|
|
||||||
sslHandler: SslHandler,
|
|
||||||
trustStore: KeyStore?,
|
|
||||||
certificateRevocationEnabled: Boolean
|
|
||||||
): ClientCertificateValidator {
|
|
||||||
return ClientCertificateValidator(sslHandler, getTrustManager(trustStore, certificateRevocationEnabled))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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"
|
||||||
|
|
||||||
|
@@ -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"
|
||||||
|
|
||||||
|
@@ -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()
|
||||||
|
@@ -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>
|
@@ -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>
|
@@ -1,30 +0,0 @@
|
|||||||
package net.woggioni.rbcs.server.test.utils;
|
|
||||||
|
|
||||||
import net.woggioni.jwo.JWO;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
|
|
||||||
public class NetworkUtils {
|
|
||||||
|
|
||||||
private static final int MAX_ATTEMPTS = 50;
|
|
||||||
|
|
||||||
public static int getFreePort() {
|
|
||||||
int count = 0;
|
|
||||||
while(count < MAX_ATTEMPTS) {
|
|
||||||
try (ServerSocket serverSocket = new ServerSocket(0, 50, InetAddress.getLocalHost())) {
|
|
||||||
final var candidate = serverSocket.getLocalPort();
|
|
||||||
if (candidate > 0) {
|
|
||||||
return candidate;
|
|
||||||
} else {
|
|
||||||
JWO.newThrowable(RuntimeException.class, "Got invalid port number: %d", candidate);
|
|
||||||
throw new RuntimeException("Error trying to find an open port");
|
|
||||||
}
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Error trying to find an open port");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,10 +2,10 @@ package net.woggioni.rbcs.server.test
|
|||||||
|
|
||||||
import net.woggioni.rbcs.api.Configuration
|
import net.woggioni.rbcs.api.Configuration
|
||||||
import net.woggioni.rbcs.api.Role
|
import net.woggioni.rbcs.api.Role
|
||||||
|
import net.woggioni.rbcs.common.RBCS.getFreePort
|
||||||
import net.woggioni.rbcs.common.Xml
|
import net.woggioni.rbcs.common.Xml
|
||||||
import net.woggioni.rbcs.server.cache.FileSystemCacheConfiguration
|
import net.woggioni.rbcs.server.cache.FileSystemCacheConfiguration
|
||||||
import net.woggioni.rbcs.server.configuration.Serializer
|
import net.woggioni.rbcs.server.configuration.Serializer
|
||||||
import net.woggioni.rbcs.server.test.utils.NetworkUtils
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.http.HttpRequest
|
import java.net.http.HttpRequest
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
@@ -33,7 +33,7 @@ abstract class AbstractBasicAuthServerTest : AbstractServerTest() {
|
|||||||
this.cacheDir = testDir.resolve("cache")
|
this.cacheDir = testDir.resolve("cache")
|
||||||
cfg = Configuration.of(
|
cfg = Configuration.of(
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
NetworkUtils.getFreePort(),
|
getFreePort(),
|
||||||
50,
|
50,
|
||||||
serverPath,
|
serverPath,
|
||||||
Configuration.EventExecutor(false),
|
Configuration.EventExecutor(false),
|
||||||
|
@@ -2,12 +2,12 @@ package net.woggioni.rbcs.server.test
|
|||||||
|
|
||||||
import net.woggioni.rbcs.api.Configuration
|
import net.woggioni.rbcs.api.Configuration
|
||||||
import net.woggioni.rbcs.api.Role
|
import net.woggioni.rbcs.api.Role
|
||||||
|
import net.woggioni.rbcs.common.RBCS.getFreePort
|
||||||
import net.woggioni.rbcs.common.Xml
|
import net.woggioni.rbcs.common.Xml
|
||||||
import net.woggioni.rbcs.server.cache.FileSystemCacheConfiguration
|
import net.woggioni.rbcs.server.cache.FileSystemCacheConfiguration
|
||||||
import net.woggioni.rbcs.server.configuration.Serializer
|
import net.woggioni.rbcs.server.configuration.Serializer
|
||||||
import net.woggioni.rbcs.server.test.utils.CertificateUtils
|
import net.woggioni.rbcs.server.test.utils.CertificateUtils
|
||||||
import net.woggioni.rbcs.server.test.utils.CertificateUtils.X509Credentials
|
import net.woggioni.rbcs.server.test.utils.CertificateUtils.X509Credentials
|
||||||
import net.woggioni.rbcs.server.test.utils.NetworkUtils
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.http.HttpClient
|
import java.net.http.HttpClient
|
||||||
@@ -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
|
||||||
@@ -138,7 +139,7 @@ abstract class AbstractTlsServerTest : AbstractServerTest() {
|
|||||||
createKeyStoreAndTrustStore()
|
createKeyStoreAndTrustStore()
|
||||||
cfg = Configuration(
|
cfg = Configuration(
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
NetworkUtils.getFreePort(),
|
getFreePort(),
|
||||||
100,
|
100,
|
||||||
serverPath,
|
serverPath,
|
||||||
Configuration.EventExecutor(false),
|
Configuration.EventExecutor(false),
|
||||||
|
@@ -2,10 +2,10 @@ package net.woggioni.rbcs.server.test
|
|||||||
|
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus
|
import io.netty.handler.codec.http.HttpResponseStatus
|
||||||
import net.woggioni.rbcs.api.Configuration
|
import net.woggioni.rbcs.api.Configuration
|
||||||
|
import net.woggioni.rbcs.common.RBCS.getFreePort
|
||||||
import net.woggioni.rbcs.common.Xml
|
import net.woggioni.rbcs.common.Xml
|
||||||
import net.woggioni.rbcs.server.cache.InMemoryCacheConfiguration
|
import net.woggioni.rbcs.server.cache.InMemoryCacheConfiguration
|
||||||
import net.woggioni.rbcs.server.configuration.Serializer
|
import net.woggioni.rbcs.server.configuration.Serializer
|
||||||
import net.woggioni.rbcs.server.test.utils.NetworkUtils
|
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.Order
|
import org.junit.jupiter.api.Order
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -33,7 +33,7 @@ class NoAuthServerTest : AbstractServerTest() {
|
|||||||
this.cacheDir = testDir.resolve("cache")
|
this.cacheDir = testDir.resolve("cache")
|
||||||
cfg = Configuration(
|
cfg = Configuration(
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
NetworkUtils.getFreePort(),
|
getFreePort(),
|
||||||
100,
|
100,
|
||||||
serverPath,
|
serverPath,
|
||||||
Configuration.EventExecutor(false),
|
Configuration.EventExecutor(false),
|
||||||
|
@@ -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> =
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
@@ -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"
|
||||||
|
@@ -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
|
||||||
|
@@ -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"
|
||||||
|
@@ -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"
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user