From 86b52ca4b784b7b5527ee0d4923fae7efd919c5b Mon Sep 17 00:00:00 2001 From: Evgeny Chugunnyy Date: Thu, 26 Oct 2023 13:55:00 +0300 Subject: [PATCH 1/7] Updated documentation --- README.md | 24 +++++++++++++++--------- testing/Caddyfile | 3 +++ testing/http3.md | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 03d7af3..82bae80 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -## Maven http3 experiments +## Maven http3 experimental plugin + +### HTTP3 new experimental plugin + +- `mvn-resolver-transport-http3` uses Jetty HTTP3 client library and Maven Artifact Resolver API. Supports Maven 3.9+ ### Maven transport APIs @@ -7,20 +11,16 @@ ### HTTP1.x basic plugins -These are distilled versions of standard maven http 1.x plugins with minimized dependencies, +These are distilled versions of the standard maven http 1.x plugins with minimized dependencies, minor code changes and debug `stderr` "logging". They are useful as reference starting point for new maven extensions and for maven testing/debugging. - `testing/mvn-wagon-http-light` uses Maven Wagon transport. - `testing/mvn-resolver-transport-http` uses Maven Artifact Resolver transport. -### HTTP3 new experimental plugin - -- `mvn-resolver-transport-http3` uses Jetty HTTP3 client library and Maven Artifact Resolver API. - ### HTTP3 demo/testing -For building and testing http3 tools, see `http3.md` +For building and testing http3 tools, see `testing/http3.md` #### nghttp3 server @@ -37,11 +37,17 @@ For now `Caddy` http3 server is used for testing plugin. #### Testing plugin +See also: +* `testing/helloworld-src/README.md` +* `mvn-resolver-transport-http3/README.md` + ```shell cd testing caddy run -curl -kv --http3 https://localhost.org:7433/ -cd helloworld-src + +curl -kv --http3-only https://localhost:7443 + +cd testing/helloworld-src rm -rf $HOME/.m2/repository/commons-cli/commons-cli/1.4 time mvn clean package -Daether.connector.https.securityMode=insecure ``` diff --git a/testing/Caddyfile b/testing/Caddyfile index a604b94..8177bf4 100644 --- a/testing/Caddyfile +++ b/testing/Caddyfile @@ -12,4 +12,7 @@ format console level info } + #basicauth /* { + # demo $2a$14$q3UGLjB66ZBZmfk5ISa0/u1U6Hznq59M/8hQZ/ualOxiGfY8BLTQS + #} } diff --git a/testing/http3.md b/testing/http3.md index 5f524c8..d150caf 100644 --- a/testing/http3.md +++ b/testing/http3.md @@ -90,6 +90,9 @@ sudo ldconfig format console level info } + #basicauth /* { # basic auth; demo/demo + # demo $2a$14$q3UGLjB66ZBZmfk5ISa0/u1U6Hznq59M/8hQZ/ualOxiGfY8BLTQS + #} } ``` 4. Run Caddy server: @@ -107,8 +110,19 @@ nghttpx $CERT $CERT --backend=localhost,8080 --frontend="localhost,9443;quic" # http3 proxy server to https://repo.maven.apache.org: nghttpx $CERT $CERT -L INFO -k --host-rewrite --backend="repo.maven.apache.org,443;/;tls" --frontend="localhost,9443;quic" # Testing client: -curl -kv --http3 https://nghttp2.org:4433/ -curl -kv --http3 https://localhost.org:9433/ +curl -kv --http3-only https://nghttp2.org:4433/ +curl -kv --http3-only https://localhost.org:9433/ +curl -kv --http3-only "https://localhost:7443/" -u demo:demo # for caddy auth test +curl -kv --http3-only "https://localhost:7443/" -H 'Authorization: Basic ZGVtbzpkZW1v' +curl -kv --http3-only "https://demo:demo@localhost:7443/" time mvn clean package -Daether.connector.https.securityMode=insecure time mvn -X -e clean package -Dmaven.resolver.transport=native -Daether.connector.https.securityMode=insecure # for full logs ``` + + +### Docker test + +``` +docker run -v $PWD/Caddyfile.docker:/etc/caddy/Caddyfile -v $PWD/stunnel.pem:/etc/caddy/stunnel.pem -p 7443:7443/udp caddy +curl --http3-only -kv https://demo:demo@localhost:7443/maven2 +``` From ff97582577b90364489e76f4bb719e7e7f7b9626 Mon Sep 17 00:00:00 2001 From: Evgeny Chugunnyy Date: Thu, 26 Oct 2023 14:07:59 +0300 Subject: [PATCH 2/7] Reworked tests + auth testing. Tests now run with testcontainers caddy http3 server. --- mvn-resolver-transport-http3/pom.xml | 6 + .../transport/http3/HttpTransporter.java | 30 ++-- .../transport/http3/HttpTransporterTest.java | 113 ------------ .../transport/http3/MavenResolverIT.java | 157 +++++++++++++++++ .../src/test/resources/Caddyfile.auth.docker | 18 ++ .../src/test/resources/Caddyfile.docker | 15 ++ .../src/test/resources/stunnel.pem | 165 ++++++++++++++++++ 7 files changed, 377 insertions(+), 127 deletions(-) delete mode 100644 mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/HttpTransporterTest.java create mode 100644 mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java create mode 100644 mvn-resolver-transport-http3/src/test/resources/Caddyfile.auth.docker create mode 100644 mvn-resolver-transport-http3/src/test/resources/Caddyfile.docker create mode 100644 mvn-resolver-transport-http3/src/test/resources/stunnel.pem diff --git a/mvn-resolver-transport-http3/pom.xml b/mvn-resolver-transport-http3/pom.xml index b53fa80..b12151a 100644 --- a/mvn-resolver-transport-http3/pom.xml +++ b/mvn-resolver-transport-http3/pom.xml @@ -171,6 +171,12 @@ jetty-io ${jettyVersion} + + org.testcontainers + testcontainers + 1.18.3 + test + diff --git a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java index 4d1f6f6..eeb85fc 100644 --- a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java +++ b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java @@ -28,10 +28,7 @@ import org.eclipse.aether.transfer.NoTransporterException; import org.eclipse.aether.util.ConfigUtils; import org.eclipse.aether.util.FileUtils; -import org.eclipse.jetty.client.BasicAuthentication; -import org.eclipse.jetty.client.ContentResponse; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.HttpResponseException; +import org.eclipse.jetty.client.*; import org.eclipse.jetty.http.*; import org.eclipse.jetty.http3.client.HTTP3Client; import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3; @@ -197,7 +194,7 @@ final class HttpTransporter extends AbstractTransporter { if (this.baseUri.getUserInfo() != null) { this.authInfo = this.baseUri.getUserInfo().split(":"); - } else if (this.repoAuthContext.get(AuthenticationContext.USERNAME) != null) { + } else if (this.repoAuthContext != null && this.repoAuthContext.get(AuthenticationContext.USERNAME) != null) { final String password = this.repoAuthContext.get(AuthenticationContext.PASSWORD); this.authInfo = new String[] { this.repoAuthContext.get(AuthenticationContext.USERNAME), @@ -291,8 +288,7 @@ protected void implClose() { AuthenticationContext.close(proxyAuthContext); } - private ContentResponse makeRequest(HttpMethod method, TransportTask task) - throws ExecutionException, InterruptedException, TimeoutException, MalformedURLException { + private ContentResponse makeRequest(HttpMethod method, TransportTask task) throws MalformedURLException { final String url = new URL(this.baseUri.toURL(), task.getLocation().toString()).toString(); System.err.printf("Custom HttpTransporter.makeRequest() called! Method: %s; URL: %s%n", method.toString(), url); @@ -301,13 +297,19 @@ private ContentResponse makeRequest(HttpMethod method, TransportTask task) new BasicAuthentication.BasicResult(this.baseUri, this.authInfo[0], this.authInfo[1]) ); } - final ContentResponse response = this.client.newRequest(url).method(method).headers(httpFields -> { - System.err.printf("\tCustom HEADER HttpTransporter.makeRequest() called! fields: %d; URL: %s%n", httpFields.size(), url); - final Object token = this.state.getUserToken(); - if (token != null) { - httpFields.add(LocalState.USER_TOKEN, token.toString()); - } - }).send(); + final Request request = this.client.newRequest(url); + final ContentResponse response; + try { + response = request.method(method).headers(httpFields -> { + System.err.printf("\tCustom HEADER HttpTransporter.makeRequest() called! fields: %d; URL: %s%n", httpFields.size(), url); + final Object token = this.state.getUserToken(); + if (token != null) { + httpFields.add(LocalState.USER_TOKEN, token.toString()); + } + }).send(); + } catch (Exception ex) { + throw new HttpRequestException(ex.getMessage(), request); + } if (response.getStatus() >= 300) { throw new HttpResponseException(Integer.toString(response.getStatus()), response); } diff --git a/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/HttpTransporterTest.java b/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/HttpTransporterTest.java deleted file mode 100644 index c87c97c..0000000 --- a/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/HttpTransporterTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.artipie.aether.transport.http3; - -import org.eclipse.aether.DefaultRepositorySystemSession; -import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager; -import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.spi.connector.transport.GetTask; -import org.eclipse.aether.spi.connector.transport.TransportListener; -import org.eclipse.aether.spi.connector.transport.Transporter; -import org.eclipse.aether.transfer.TransferCancelledException; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.ContentResponse; -import org.eclipse.jetty.http3.client.HTTP3Client; -import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3; -import org.junit.Ignore; -import org.junit.Test; - -import java.net.URI; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; - -import static org.junit.Assert.*; - -public class HttpTransporterTest { - - @Test - public void testConnection_http3check() throws Exception { - final HTTP3Client h3Client = new HTTP3Client(); - final HttpClientTransportOverHTTP3 transport = new HttpClientTransportOverHTTP3(h3Client); - final HttpClient client = new HttpClient(transport); - client.start(); - final ContentResponse response = client.GET("https://http3check.net"); - assertEquals(200, response.getStatus()); - client.stop(); - } - - @Test - @Ignore("https://github.com/eclipse/jetty.project/issues/10390") - public void testConnection_nghttp2() throws Exception { - final HTTP3Client h3Client = new HTTP3Client(); - final HttpClientTransportOverHTTP3 transport = new HttpClientTransportOverHTTP3(h3Client); - final HttpClient client = new HttpClient(transport); - client.start(); - final ContentResponse response = client.GET("https://nghttp2.org:4433"); //https://http3check.net - assertEquals(200, response.getStatus()); - client.stop(); - } - - @Test - public void testConnection_localhost() throws Exception { - final HTTP3Client h3Client = new HTTP3Client(); - //h3Client.getQuicConfiguration().setSessionRecvWindow(64 * 1024 * 1024); - HttpClientTransportOverHTTP3 transport = new HttpClientTransportOverHTTP3(h3Client); - final HttpClient client = new HttpClient(transport); - client.start(); - h3Client.getClientConnector().getSslContextFactory().setTrustAll(true); - //9443 - nghttpx; 7443 - caddy - final ContentResponse response = client.GET("https://localhost:7443/"); - System.out.println(new String(response.getContent(), StandardCharsets.UTF_8)); - assertEquals(200, response.getStatus()); - client.stop(); - } - - @Test - public void testConnection_localhostPom() throws Exception { - final HTTP3Client h3Client = new HTTP3Client(); - //h3Client.getQuicConfiguration().setSessionRecvWindow(64 * 1024 * 1024); - HttpClientTransportOverHTTP3 transport = new HttpClientTransportOverHTTP3(h3Client); - final HttpClient client = new HttpClient(transport); - client.start(); - h3Client.getClientConnector().getSslContextFactory().setTrustAll(true); - //9443 - nghttpx; 7443 - caddy - final ContentResponse response = client.GET("https://localhost:7443/maven2/commons-cli/commons-cli/1.4/commons-cli-1.4.pom"); - System.out.println(new String(response.getContent(), StandardCharsets.UTF_8)); - assertEquals(200, response.getStatus()); - client.stop(); - client.destroy(); - } - - @Test - public void testTransporter() throws Exception { - final RepositorySystemSession session = newSession(); - final RemoteRepository repository = newRepo("https://localhost:7443/maven2"); - final HttpTransporterFactory factory = new HttpTransporterFactory(); - TransportListener listener = new TransportListener() { - @Override - public void transportStarted(long dataOffset, long dataLength) throws TransferCancelledException { - super.transportStarted(dataOffset, dataLength); - } - @Override - public void transportProgressed(ByteBuffer data) throws TransferCancelledException { - super.transportProgressed(data); - } - }; - final GetTask task = new GetTask(URI.create("commons-cli/commons-cli/1.4/commons-cli-1.4.pom")).setListener(listener); - try (final Transporter transporter = factory.newInstance(session, repository)) { - transporter.get(task); - } - assertNotEquals(null, task.getDataBytes()); - System.err.println(new String(task.getDataBytes(), StandardCharsets.UTF_8)); - assertTrue(task.getDataBytes().length > 0); - } - - private static DefaultRepositorySystemSession newSession() { - DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(); - session.setLocalRepositoryManager(new TestLocalRepositoryManager()); - return session; - } - - private RemoteRepository newRepo(final String url) { - return new RemoteRepository.Builder("test", "default", url).build(); - } -} diff --git a/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java b/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java new file mode 100644 index 0000000..bbe6f8b --- /dev/null +++ b/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java @@ -0,0 +1,157 @@ +package com.artipie.aether.transport.http3; + +import org.eclipse.aether.ConfigurationProperties; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.internal.test.util.TestLocalRepositoryManager; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.spi.connector.transport.GetTask; +import org.eclipse.aether.spi.connector.transport.TransportListener; +import org.eclipse.aether.spi.connector.transport.Transporter; +import org.eclipse.aether.transfer.TransferCancelledException; +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpRequestException; +import org.eclipse.jetty.client.HttpResponseException; +import org.eclipse.jetty.http3.client.HTTP3Client; +import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.testcontainers.containers.FixedHostPortGenericContainer; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.InternetProtocol; +import org.testcontainers.containers.wait.strategy.ShellStrategy; +import java.io.IOException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import static org.junit.Assert.*; + +/** + * Testing transport via containerized Caddy http3 server in proxy mode. + */ +public class MavenResolverIT { + + private static GenericContainer caddy; + private static GenericContainer caddyAuth; + + @Test + public void testTransporterAuth() throws Exception { + final byte[] data = testTransporter("https://demo:demo@localhost:7444/maven2"); + assertNotEquals(null, data); + System.err.println(new String(data, StandardCharsets.UTF_8)); + assertTrue(data.length > 0); + } + + @Test(expected = HttpResponseException.class) + public void testTransporterAuthFail() throws Exception { + final byte[] data = testTransporter("https://demo1:demo1@localhost:7444/maven2"); + assertNull(data); + } + + @Test(expected = HttpResponseException.class) + public void testTransporterAnonAuthFail() throws Exception { + testTransporter("https://localhost:7444/maven2"); + } + + @Test + public void testTransporterAnon() throws Exception { + final byte[] data = testTransporter("https://localhost:7443/maven2"); + assertNotEquals(null, data); + System.err.println(new String(data, StandardCharsets.UTF_8)); + assertTrue(data.length > 0); + } + + @Test + public void testAnonTransporterSuccess() throws Exception { + final byte[] data = testTransporter("https://demo:demo@localhost:7443/maven2"); + assertNotNull(data); + assertTrue(data.length > 0); + } + + @Test(expected = HttpRequestException.class) + public void testTransporterInvalidUrl() throws Exception { + testTransporter("https://localhost:7445/maven2"); + } + + private byte[] testTransporter(final String repo) throws Exception { + final RepositorySystemSession session = newSession(); + final RemoteRepository repository = newRepo(repo); + final HttpTransporterFactory factory = new HttpTransporterFactory(); + TransportListener listener = new TransportListener() { + @Override + public void transportStarted(long dataOffset, long dataLength) throws TransferCancelledException { + super.transportStarted(dataOffset, dataLength); + } + @Override + public void transportProgressed(ByteBuffer data) throws TransferCancelledException { + super.transportProgressed(data); + } + }; + final GetTask task = new GetTask(URI.create("commons-cli/commons-cli/1.4/commons-cli-1.4.pom")).setListener(listener); + try (final Transporter transporter = factory.newInstance(session, repository)) { + transporter.get(task); + } + return task.getDataBytes(); + } + + @Test + public void testJettyLocalhostConnection() throws Exception { + final HTTP3Client h3Client = new HTTP3Client(); + HttpClientTransportOverHTTP3 transport = new HttpClientTransportOverHTTP3(h3Client); + final HttpClient client = new HttpClient(transport); + client.start(); + h3Client.getClientConnector().getSslContextFactory().setTrustAll(true); + final ContentResponse response = client.GET("https://localhost:7443/"); + System.out.println(new String(response.getContent(), StandardCharsets.UTF_8)); + assertEquals(200, response.getStatus()); + client.stop(); + } + + @BeforeClass + public static void prepare() throws IOException, InterruptedException { + try { + caddy = new FixedHostPortGenericContainer<>("library/caddy:2.7.5") + .withReuse(false) + .withFileSystemBind("src/test/resources/Caddyfile.docker", "/etc/caddy/Caddyfile") + .withFileSystemBind("src/test/resources/stunnel.pem", "/etc/caddy/stunnel.pem") + .waitingFor(new ShellStrategy().withCommand("nc -u -z localhost 7443")) + .withFixedExposedPort(7443, 7443, InternetProtocol.UDP) + .withFixedExposedPort(8080, 8080, InternetProtocol.TCP) + .withAccessToHost(true); + + caddyAuth = new FixedHostPortGenericContainer<>("library/caddy:2.7.5") + .withReuse(false) + .withFileSystemBind("src/test/resources/Caddyfile.auth.docker", "/etc/caddy/Caddyfile") + .withFileSystemBind("src/test/resources/stunnel.pem", "/etc/caddy/stunnel.pem") + .waitingFor(new ShellStrategy().withCommand("nc -u -z localhost 7444")) + .withFixedExposedPort(7444, 7444, InternetProtocol.UDP) + .withAccessToHost(true); + caddy.start(); + caddyAuth.start(); + } + catch (Exception ex) { + System.err.println(caddy.getLogs()); + System.err.println(caddyAuth.getLogs()); + throw ex; + } + } + + @AfterClass + public static void finish() { + caddy.stop(); + caddyAuth.stop(); + } + + private static DefaultRepositorySystemSession newSession() { + DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(); + session.setLocalRepositoryManager(new TestLocalRepositoryManager()); + session.setConfigProperty(ConfigurationProperties.HTTPS_SECURITY_MODE, ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE); + return session; + } + + private RemoteRepository newRepo(final String url) { + return new RemoteRepository.Builder("test", "default", url).build(); + } +} diff --git a/mvn-resolver-transport-http3/src/test/resources/Caddyfile.auth.docker b/mvn-resolver-transport-http3/src/test/resources/Caddyfile.auth.docker new file mode 100644 index 0000000..d8d78d4 --- /dev/null +++ b/mvn-resolver-transport-http3/src/test/resources/Caddyfile.auth.docker @@ -0,0 +1,18 @@ +{ + https_port 7444 + http_port 8080 +} +:7444 { + tls /etc/caddy/stunnel.pem /etc/caddy/stunnel.pem + reverse_proxy https://repo.maven.apache.org:443 { + header_up Host {http.reverse_proxy.upstream.hostport} + } + log { + output stdout + format console + level info + } + basicauth /* { + demo $2a$14$q3UGLjB66ZBZmfk5ISa0/u1U6Hznq59M/8hQZ/ualOxiGfY8BLTQS + } +} diff --git a/mvn-resolver-transport-http3/src/test/resources/Caddyfile.docker b/mvn-resolver-transport-http3/src/test/resources/Caddyfile.docker new file mode 100644 index 0000000..dfcd5f0 --- /dev/null +++ b/mvn-resolver-transport-http3/src/test/resources/Caddyfile.docker @@ -0,0 +1,15 @@ +{ + https_port 7443 + http_port 8080 +} +:7443 { + tls /etc/caddy/stunnel.pem /etc/caddy/stunnel.pem + reverse_proxy https://repo.maven.apache.org:443 { + header_up Host {http.reverse_proxy.upstream.hostport} + } + log { + output stdout + format console + level info + } +} diff --git a/mvn-resolver-transport-http3/src/test/resources/stunnel.pem b/mvn-resolver-transport-http3/src/test/resources/stunnel.pem new file mode 100644 index 0000000..b7dd1e8 --- /dev/null +++ b/mvn-resolver-transport-http3/src/test/resources/stunnel.pem @@ -0,0 +1,165 @@ +extensions = x509v3 +[ x509v3 ] +subjectAltName = DNS:localhost +keyUsage = keyEncipherment,digitalSignature,keyAgreement +extendedKeyUsage = serverAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid +basicConstraints = CA:false +authorityInfoAccess = @issuer_info +crlDistributionPoints = @crl_info + +[ crl_ext ] +authorityKeyIdentifier = keyid:always +authorityInfoAccess = @issuer_info + +[ issuer_info ] +caIssuers;URI.0 = http://test.curl.se/ca/EdelCurlRoot.cer + +[ crl_info ] +URI.0 = http://test.curl.se/ca/EdelCurlRoot.crl + +[ req ] +default_bits = 12048 +distinguished_name = req_DN +default_md = sha256 +string_mask = utf8only +[ req_DN ] +countryName = "Country Name is Northern Nowhere" +countryName_value = NN +organizationName = "Organization Name" +organizationName_value = Edel Curl Arctic Illudium Research Cloud +commonName = "Common Name" +commonName_value = localhost + +[something] +# The key +# the certificate +# some dhparam +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrCrAD0Hb+Xs4V +3mHV45FvfNa7yiaOeL4mNdGmWfHVPFU+CSzsoNSvDjxaorWweFGVYoCAcchOn1lZ +k0ASsqnOss0Xi58n8+PPI3gG0gYjX5sg7EJ3Zq2kXoK0TZRy6hNkcvzLgyzXoYv1 +LkzTwYiyyJgZX++Y/GKAs2fMHyP8XzjNgm4tltk1k/4pomllwN9Fqz+sFxgAgEq3 +ybq4Xym7xKwWl8xXNBDJNmVsPtiJRcilQoR8Xs0a6PE+VbMhD9A2E/LEL7lzQfqH +qtxE1mSW5FpQ+Uqf4KLnafStWs86IOWnCeLP6BmhAK6ouyICNFyzz7UkTHa/renx +uNOGun2TAgMBAAECggEAH0BsKb5Ax7h90jwYRzL141d9isFkaxq/r46c2FbN24bT +EmstxKycP8ILoAnjxbMuQOvHC/D+RvNRqY7Aocn4Qdakp50wvuWOpc3Ww/RC/9qb +pxfUCyn9Jy/HlPcp3RdM5MknzG2S13Fid7F2gyh0+CmztMs1JZBT1S0ylXbJJfbY +1pdlHcf9oEbYo36vGd9rtJHAFzsFfwua0idl76XYuOnR3bpOkHl1B5cJ8jpOliPv +VTmzn0cIgAmk7IByHHqGQ0u30PFiElI9kEbkKWoxAM1hq1pFU58jQhvp0ZkjVENL +bSFB2B4DbyosxPlbUgvJCN4B7nclqzYqBdrrk6/ZLQKBgQC1lDrPSGIGXLwvkZYS +xc0wtaCC7u6m7zV8rzh5HGcEoVvtmya/VyoZR8KGIpSor8COIkZqFtan6C77C3MH +wClbu2Kn3FkGb76D5U2Xwl38zepzjn8Z5qXc3bZfccrsDY1gXPicgsmcKUY9xV5/ +T0RjESDKB+xxkJpCjia6klm2NQKBgQDxJNuqB6frDYKaj7mW/rvyHqkeT94J6eDY +BcMZVKeHRNWcBMOvJDChVmpsCjJPOWHhHOlAE755NxWn8wpgyiUcac3BiysMgvTT +pyH8UVWaP/DWYOfpuhtcLPkIjKnPijOvshpyWBxfXNIejiovoT6E3IXKOxr5g5yq +U/9a5+I9pwKBgDyJG4YpkoyedBrDxa2ihkL7+nRMZgH/c+yKmiA+aNXxWa2AcU2P +KLje5KpFcxw948s/AAy0aoH19Vu6uHHYDbHIah6eZouvy2s7kj/LC/yRRd2anyMq +cxeMTxXI4ScLaZu7wyKis8Y9OG61k0iMS7dfaXgRZjGCTPttWtoOmpwVAoGAd2k+ +EXuLDl15UBpd18S6wxGlw2nfVN9oxPBNhUyjTNusP38oe6EbJ7mIJ4oBEbQjoPrV +EjL0vkelxK4YdIeFSwWlqvLEVYS/wdNgg/auwhxpoW8JSHctNz7Z7v7g0/Hm2FkE +uZyiKCLptdHGWCGruNUOt27/U5F10e6YY6ayJL8CgYEAoE/ddHIlrKFzcZ0cu47R +ky4D4x32ceC2au4eGPLMDItPjceXe8I6zgB5P8/U5U3PRw5oWabpAf5lAcRwa12c +23xgy9O5QNlyps/rAdnVjwxqGwEd5E2LrZ2+JDflPRGunx2VO5v9CxXvsxu9F3Rj +tREtebHe2H7u5jNsIQArnRI= +-----END PRIVATE KEY----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 16717980999635 (0xf3475519fd3) + Signature Algorithm: sha256WithRSAEncryption + Issuer: + countryName = NN + organizationName = Edel Curl Arctic Illudium Research Cloud + commonName = Northern Nowhere Trust Anchor + Validity + Not Before: Dec 23 12:21:39 2022 GMT + Not After : Mar 11 12:21:39 2031 GMT + Subject: + countryName = NN + organizationName = Edel Curl Arctic Illudium Research Cloud + commonName = localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ab:0a:b0:03:d0:76:fe:5e:ce:15:de:61:d5:e3: + 91:6f:7c:d6:bb:ca:26:8e:78:be:26:35:d1:a6:59: + f1:d5:3c:55:3e:09:2c:ec:a0:d4:af:0e:3c:5a:a2: + b5:b0:78:51:95:62:80:80:71:c8:4e:9f:59:59:93: + 40:12:b2:a9:ce:b2:cd:17:8b:9f:27:f3:e3:cf:23: + 78:06:d2:06:23:5f:9b:20:ec:42:77:66:ad:a4:5e: + 82:b4:4d:94:72:ea:13:64:72:fc:cb:83:2c:d7:a1: + 8b:f5:2e:4c:d3:c1:88:b2:c8:98:19:5f:ef:98:fc: + 62:80:b3:67:cc:1f:23:fc:5f:38:cd:82:6e:2d:96: + d9:35:93:fe:29:a2:69:65:c0:df:45:ab:3f:ac:17: + 18:00:80:4a:b7:c9:ba:b8:5f:29:bb:c4:ac:16:97: + cc:57:34:10:c9:36:65:6c:3e:d8:89:45:c8:a5:42: + 84:7c:5e:cd:1a:e8:f1:3e:55:b3:21:0f:d0:36:13: + f2:c4:2f:b9:73:41:fa:87:aa:dc:44:d6:64:96:e4: + 5a:50:f9:4a:9f:e0:a2:e7:69:f4:ad:5a:cf:3a:20: + e5:a7:09:e2:cf:e8:19:a1:00:ae:a8:bb:22:02:34: + 5c:b3:cf:b5:24:4c:76:bf:ad:e9:f1:b8:d3:86:ba: + 7d:93 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:localhost + X509v3 Key Usage: + Digital Signature, Key Encipherment, Key Agreement + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Subject Key Identifier: + 9C:97:B0:3D:B3:50:B1:F6:D4:71:E2:EB:CB:80:EA:93:7C:98:CC:72 + X509v3 Authority Key Identifier: + 87:CB:B1:33:2E:C1:67:7E:71:E3:E5:2B:4C:4D:A4:B3:6E:D2:5B:A9 + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + CA Issuers - URI:http://test.curl.se/ca/EdelCurlRoot.cer + X509v3 CRL Distribution Points: + Full Name: + URI:http://test.curl.se/ca/EdelCurlRoot.crl + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 0b:8a:ed:6a:87:fa:71:15:88:25:58:85:1b:4a:09:bf:43:00: + 35:93:78:0d:72:14:30:51:e2:93:83:a1:da:1b:2f:a9:31:ae: + b7:c7:4c:72:c2:5e:32:24:f1:96:93:70:d5:3f:b5:85:80:13: + 75:32:cf:0e:f8:5d:c4:a2:29:84:43:2c:75:81:26:12:6a:a0: + cb:7b:57:c1:92:78:85:08:fa:64:50:c2:7b:83:02:4d:79:13: + bc:61:64:4d:b8:6b:d5:f1:84:6b:12:5c:69:90:ad:40:47:c0: + ed:dd:ea:8a:66:7e:87:85:19:aa:89:d3:3c:08:72:08:a1:4d: + 63:60:5b:9b:17:9e:00:12:a1:00:52:ca:78:01:88:18:c7:ed: + 5b:c7:e4:d9:eb:bd:3f:af:92:53:3f:fe:58:57:0d:fc:f4:7b: + 7b:a2:4f:e9:b9:5c:b5:a4:52:50:b4:56:5a:44:8e:d9:d0:ed: + de:8f:7e:ac:1c:58:76:5b:a8:79:c9:95:ab:85:1d:db:4c:13: + 82:4a:a5:41:1b:29:f5:d3:96:df:80:d1:1e:00:7d:ba:35:94: + 57:81:e1:08:2a:81:6b:1c:30:50:37:01:1a:0e:26:4f:6b:ed: + c9:50:17:37:2b:33:3f:68:fe:c6:f0:21:8c:e7:b2:79:55:f2: + 42:bd:2f:b0 +-----BEGIN CERTIFICATE----- +MIIERDCCAyygAwIBAgIGDzR1UZ/TMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNVBAYT +Ak5OMTEwLwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNo +IENsb3VkMSYwJAYDVQQDDB1Ob3J0aGVybiBOb3doZXJlIFRydXN0IEFuY2hvcjAe +Fw0yMjEyMjMxMjIxMzlaFw0zMTAzMTExMjIxMzlaMFQxCzAJBgNVBAYTAk5OMTEw +LwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNoIENsb3Vk +MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCrCrAD0Hb+Xs4V3mHV45FvfNa7yiaOeL4mNdGmWfHVPFU+CSzsoNSvDjxa +orWweFGVYoCAcchOn1lZk0ASsqnOss0Xi58n8+PPI3gG0gYjX5sg7EJ3Zq2kXoK0 +TZRy6hNkcvzLgyzXoYv1LkzTwYiyyJgZX++Y/GKAs2fMHyP8XzjNgm4tltk1k/4p +omllwN9Fqz+sFxgAgEq3ybq4Xym7xKwWl8xXNBDJNmVsPtiJRcilQoR8Xs0a6PE+ +VbMhD9A2E/LEL7lzQfqHqtxE1mSW5FpQ+Uqf4KLnafStWs86IOWnCeLP6BmhAK6o +uyICNFyzz7UkTHa/renxuNOGun2TAgMBAAGjggEGMIIBAjAUBgNVHREEDTALggls +b2NhbGhvc3QwCwYDVR0PBAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB0GA1Ud +DgQWBBScl7A9s1Cx9tRx4uvLgOqTfJjMcjAfBgNVHSMEGDAWgBSHy7EzLsFnfnHj +5StMTaSzbtJbqTAJBgNVHRMEAjAAMEMGCCsGAQUFBwEBBDcwNTAzBggrBgEFBQcw +AoYnaHR0cDovL3Rlc3QuY3VybC5zZS9jYS9FZGVsQ3VybFJvb3QuY2VyMDgGA1Ud +HwQxMC8wLaAroCmGJ2h0dHA6Ly90ZXN0LmN1cmwuc2UvY2EvRWRlbEN1cmxSb290 +LmNybDANBgkqhkiG9w0BAQsFAAOCAQEAC4rtaof6cRWIJViFG0oJv0MANZN4DXIU +MFHik4Oh2hsvqTGut8dMcsJeMiTxlpNw1T+1hYATdTLPDvhdxKIphEMsdYEmEmqg +y3tXwZJ4hQj6ZFDCe4MCTXkTvGFkTbhr1fGEaxJcaZCtQEfA7d3qimZ+h4UZqonT +PAhyCKFNY2BbmxeeABKhAFLKeAGIGMftW8fk2eu9P6+SUz/+WFcN/PR7e6JP6blc +taRSULRWWkSO2dDt3o9+rBxYdluoecmVq4Ud20wTgkqlQRsp9dOW34DRHgB9ujWU +V4HhCCqBaxwwUDcBGg4mT2vtyVAXNyszP2j+xvAhjOeyeVXyQr0vsA== +-----END CERTIFICATE----- From f434c5d9876e4ff3a335c6a746026df7d128889c Mon Sep 17 00:00:00 2001 From: Evgeny Chugunnyy Date: Fri, 27 Oct 2023 13:52:48 +0300 Subject: [PATCH 3/7] Code review fixes --- .../transport/http3/HttpTransporter.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java index eeb85fc..bdc96ed 100644 --- a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java +++ b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java @@ -234,25 +234,25 @@ public int classify(Throwable error) { @Override protected void implPeek(PeekTask task) throws Exception { - this.makeRequest(HttpMethod.HEAD, task); + this.makeRequest(HttpMethod.HEAD, task, null); } @Override protected void implGet(GetTask task) throws Exception { - ContentResponse response = this.makeRequest(HttpMethod.GET, task); - + ContentResponse response = this.makeRequest(HttpMethod.GET, task, null); final boolean resume = false; final File dataFile = task.getDataFile(); + final byte[] content = response.getContent(); if (dataFile == null) { - try (final InputStream is = new ByteArrayInputStream(response.getContent())) { - utilGet(task, is, true, response.getContent().length, resume); + try (final InputStream is = new ByteArrayInputStream(content)) { + utilGet(task, is, true, content.length, resume); extractChecksums(response, task); } } else { try (FileUtils.CollocatedTempFile tempFile = FileUtils.newTempFile(dataFile.toPath())) { task.setDataFile(tempFile.getPath().toFile()); - try (final InputStream is = new ByteArrayInputStream(response.getContent())) { - utilGet(task, is, true, response.getContent().length, resume); + try (final InputStream is = new ByteArrayInputStream(content)) { + utilGet(task, is, true, content.length, resume); } tempFile.move(); } finally { @@ -273,7 +273,9 @@ protected void implGet(GetTask task) throws Exception { @Override protected void implPut(PutTask task) throws Exception { - this.makeRequest(HttpMethod.PUT, task); + try (final InputStream stream = task.newInputStream()) { + this.makeRequest(HttpMethod.PUT, task, new InputStreamRequestContent(stream)); + } } @Override @@ -288,10 +290,9 @@ protected void implClose() { AuthenticationContext.close(proxyAuthContext); } - private ContentResponse makeRequest(HttpMethod method, TransportTask task) throws MalformedURLException { + private ContentResponse makeRequest(HttpMethod method, TransportTask task, Request.Content bodyContent) throws MalformedURLException { final String url = new URL(this.baseUri.toURL(), task.getLocation().toString()).toString(); System.err.printf("Custom HttpTransporter.makeRequest() called! Method: %s; URL: %s%n", method.toString(), url); - if (this.authInfo != null) { this.client.getAuthenticationStore().addAuthenticationResult( new BasicAuthentication.BasicResult(this.baseUri, this.authInfo[0], this.authInfo[1]) @@ -306,7 +307,7 @@ private ContentResponse makeRequest(HttpMethod method, TransportTask task) throw if (token != null) { httpFields.add(LocalState.USER_TOKEN, token.toString()); } - }).send(); + }).body(bodyContent).send(); } catch (Exception ex) { throw new HttpRequestException(ex.getMessage(), request); } From d53545e0e63c70ec02542718b3dd7331a747d2fd Mon Sep 17 00:00:00 2001 From: Evgeny Chugunnyy Date: Fri, 27 Oct 2023 15:50:14 +0300 Subject: [PATCH 4/7] Fixed pom.xml --- mvn-resolver-transport-http3/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvn-resolver-transport-http3/pom.xml b/mvn-resolver-transport-http3/pom.xml index b12151a..4958605 100644 --- a/mvn-resolver-transport-http3/pom.xml +++ b/mvn-resolver-transport-http3/pom.xml @@ -92,7 +92,7 @@ org.apache.maven.resolver maven-resolver-test-util - 1.9.16-SNAPSHOT + 1.9.16 test From a79e8282602e8a9c29d06e16af35ade65f757783 Mon Sep 17 00:00:00 2001 From: Evgeny Chugunnyy Date: Fri, 27 Oct 2023 15:51:36 +0300 Subject: [PATCH 5/7] .gitignore --- .gitignore | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba1a085 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +target/ + +*.idea +*.iml +.factorypath +.project +.settings/** +.classpath From f1925add69274946817434cee9a20b99c57c7d1f Mon Sep 17 00:00:00 2001 From: Evgeny Chugunnyy Date: Tue, 31 Oct 2023 16:52:52 +0300 Subject: [PATCH 6/7] Core review fixes --- .../transport/http3/HttpTransporter.java | 62 ++------- .../transport/http3/state/GlobalState.java | 129 ------------------ .../transport/http3/state/LocalState.java | 89 ------------ .../transport/http3/MavenResolverIT.java | 3 +- 4 files changed, 10 insertions(+), 273 deletions(-) delete mode 100644 mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/state/GlobalState.java delete mode 100644 mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/state/LocalState.java diff --git a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java index bdc96ed..3962c43 100644 --- a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java +++ b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java @@ -19,7 +19,6 @@ package com.artipie.aether.transport.http3; import com.artipie.aether.transport.http3.checksum.ChecksumExtractor; -import com.artipie.aether.transport.http3.state.LocalState; import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.AuthenticationContext; @@ -34,7 +33,6 @@ import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import java.io.*; import java.lang.reflect.Field; import java.net.MalformedURLException; @@ -43,10 +41,9 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.attribute.FileTime; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; -import java.util.regex.Pattern; import static java.util.Objects.requireNonNull; @@ -54,27 +51,6 @@ * A transporter for HTTP/HTTPS. */ final class HttpTransporter extends AbstractTransporter { - - static final String BIND_ADDRESS = "aether.connector.bind.address"; - - static final String SUPPORT_WEBDAV = "aether.connector.http.supportWebDav"; - - static final String PREEMPTIVE_PUT_AUTH = "aether.connector.http.preemptivePutAuth"; - - static final String USE_SYSTEM_PROPERTIES = "aether.connector.http.useSystemProperties"; - - static final String HTTP_RETRY_HANDLER_NAME = "aether.connector.http.retryHandler.name"; - - private static final String HTTP_RETRY_HANDLER_NAME_STANDARD = "standard"; - - private static final String HTTP_RETRY_HANDLER_NAME_DEFAULT = "default"; - - static final String HTTP_RETRY_HANDLER_REQUEST_SENT_ENABLED = - "aether.connector.http.retryHandler.requestSentEnabled"; - - private static final Pattern CONTENT_RANGE_PATTERN = - Pattern.compile("\\s*bytes\\s+([0-9]+)\\s*-\\s*([0-9]+)\\s*/.*"); - private static final Logger LOGGER = LoggerFactory.getLogger(HttpTransporter.class); private final Map checksumExtractors; @@ -87,17 +63,8 @@ final class HttpTransporter extends AbstractTransporter { private final HttpClient client; - private final LocalState state; - private String[] authInfo = null; - /* - - private final Map headers; - - private final boolean supportWebDav; - */ - HttpTransporter( Map checksumExtractors, RemoteRepository repository, @@ -187,8 +154,6 @@ final class HttpTransporter extends AbstractTransporter { throw new NoTransporterException(repository, e.getMessage(), e); } - this.state = new LocalState(session, repository); - this.repoAuthContext = AuthenticationContext.forRepository(session, repository); this.proxyAuthContext = AuthenticationContext.forProxy(session, repository); @@ -221,7 +186,6 @@ final class HttpTransporter extends AbstractTransporter { this.client.start(); h3Client.getClientConnector().getSslContextFactory() .setTrustAll(httpsSecurityMode.equals(ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE)); - //h3Client.getClientConnector().getSslContextFactory().setTrustAll(true); } @Override @@ -260,10 +224,12 @@ protected void implGet(GetTask task) throws Exception { } } if (task.getDataFile() != null) { - final String lastModifiedHeader = - response.getHeaders().get(HttpHeader.LAST_MODIFIED); + final String lastModifiedHeader = response.getHeaders().get(HttpHeader.LAST_MODIFIED); if (lastModifiedHeader != null) { - Date lastModified = new Date(Date.parse(lastModifiedHeader)); + final DateFormat lastModifiedFormat = new SimpleDateFormat( + "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US + ); + final Date lastModified = lastModifiedFormat.parse(lastModifiedHeader); Files.setLastModifiedTime( task.getDataFile().toPath(), FileTime.fromMillis(lastModified.getTime()) ); @@ -291,7 +257,7 @@ protected void implClose() { } private ContentResponse makeRequest(HttpMethod method, TransportTask task, Request.Content bodyContent) throws MalformedURLException { - final String url = new URL(this.baseUri.toURL(), task.getLocation().toString()).toString(); + final String url = this.baseUri.resolve(task.getLocation()).toString(); System.err.printf("Custom HttpTransporter.makeRequest() called! Method: %s; URL: %s%n", method.toString(), url); if (this.authInfo != null) { this.client.getAuthenticationStore().addAuthenticationResult( @@ -301,23 +267,13 @@ private ContentResponse makeRequest(HttpMethod method, TransportTask task, Reque final Request request = this.client.newRequest(url); final ContentResponse response; try { - response = request.method(method).headers(httpFields -> { - System.err.printf("\tCustom HEADER HttpTransporter.makeRequest() called! fields: %d; URL: %s%n", httpFields.size(), url); - final Object token = this.state.getUserToken(); - if (token != null) { - httpFields.add(LocalState.USER_TOKEN, token.toString()); - } - }).body(bodyContent).send(); + response = request.method(method).body(bodyContent).send(); } catch (Exception ex) { throw new HttpRequestException(ex.getMessage(), request); } if (response.getStatus() >= 300) { throw new HttpResponseException(Integer.toString(response.getStatus()), response); } - final HttpField field = response.getHeaders().getField(LocalState.USER_TOKEN); //TODO: add test on tokens!? - if (field != null && field.getValue() != null && !field.getValue().trim().isEmpty()) { - this.state.setUserToken(field.getValue()); - } return response; } diff --git a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/state/GlobalState.java b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/state/GlobalState.java deleted file mode 100644 index f10b36a..0000000 --- a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/state/GlobalState.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package com.artipie.aether.transport.http3.state; - -import org.eclipse.aether.RepositoryCache; -import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.util.ConfigUtils; - -import java.io.Closeable; -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * Container for HTTP-related state that can be shared across incarnations of the transporter to optimize the - * communication with servers. - */ -final class GlobalState implements Closeable { - - static class CompoundKey { - - private final Object[] keys; - - CompoundKey(Object... keys) { - this.keys = keys; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || !getClass().equals(obj.getClass())) { - return false; - } - CompoundKey that = (CompoundKey) obj; - return Arrays.equals(keys, that.keys); - } - - @Override - public int hashCode() { - int hash = 17; - hash = hash * 31 + Arrays.hashCode(keys); - return hash; - } - - @Override - public String toString() { - return Arrays.toString(keys); - } - } - - private static final String KEY = GlobalState.class.getName(); - - private static final String CONFIG_PROP_CACHE_STATE = "aether.connector.http.cacheState"; - - private final ConcurrentMap userTokens; - - private final ConcurrentMap expectContinues; - - public static GlobalState get(RepositorySystemSession session) { - GlobalState cache; - RepositoryCache repoCache = session.getCache(); - if (repoCache == null || !ConfigUtils.getBoolean(session, true, CONFIG_PROP_CACHE_STATE)) { - cache = null; - } else { - Object tmp = repoCache.get(session, KEY); - if (tmp instanceof GlobalState) { - cache = (GlobalState) tmp; - } else { - synchronized (GlobalState.class) { - tmp = repoCache.get(session, KEY); - if (tmp instanceof GlobalState) { - cache = (GlobalState) tmp; - } else { - cache = new GlobalState(); - repoCache.put(session, KEY, cache); - } - } - } - } - return cache; - } - - private GlobalState() { - userTokens = new ConcurrentHashMap<>(); - expectContinues = new ConcurrentHashMap<>(); - } - - @Override - public void close() { - } - - public Object getUserToken(CompoundKey key) { - return userTokens.get(key); - } - - public void setUserToken(CompoundKey key, Object userToken) { - if (userToken != null) { - userTokens.put(key, userToken); - } else { - userTokens.remove(key); - } - } - - public Boolean getExpectContinue(CompoundKey key) { - return expectContinues.get(key); - } - - public void setExpectContinue(CompoundKey key, boolean enabled) { - expectContinues.put(key, enabled); - } -} diff --git a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/state/LocalState.java b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/state/LocalState.java deleted file mode 100644 index 04a7db4..0000000 --- a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/state/LocalState.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package com.artipie.aether.transport.http3.state; - -import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.repository.RemoteRepository; - -import java.io.Closeable; -import java.io.IOException; - -/** - * Container for HTTP-related state that can be shared across invocations of the transporter to optimize the - * communication with server. - */ -public final class LocalState implements Closeable { - - public static final String USER_TOKEN = "http.user-token"; - - private final GlobalState global; - - private final GlobalState.CompoundKey userTokenKey; - - private volatile Object userToken; - - private final GlobalState.CompoundKey expectContinueKey; - - private volatile Boolean expectContinue; - - public LocalState(RepositorySystemSession session, RemoteRepository repo) { - global = GlobalState.get(session); - userToken = this; - if (global == null) { - userTokenKey = null; - expectContinueKey = null; - } else { - userTokenKey = new GlobalState.CompoundKey(repo.getId(), repo.getUrl(), repo.getAuthentication(), repo.getProxy()); - expectContinueKey = new GlobalState.CompoundKey(repo.getUrl(), repo.getProxy()); - } - } - - public Object getUserToken() { - if (userToken == this) { - userToken = (global != null) ? global.getUserToken(userTokenKey) : null; - } - return userToken; - } - - public void setUserToken(Object userToken) { - this.userToken = userToken; - if (global != null) { - global.setUserToken(userTokenKey, userToken); - } - } - - public boolean isExpectContinue() { - if (expectContinue == null) { - expectContinue = - !Boolean.FALSE.equals((global != null) ? global.getExpectContinue(expectContinueKey) : null); - } - return expectContinue; - } - - public void setExpectContinue(boolean enabled) { - expectContinue = enabled; - if (global != null) { - global.setExpectContinue(expectContinueKey, enabled); - } - } - - @Override - public void close() throws IOException { - } -} diff --git a/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java b/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java index bbe6f8b..a9a22a4 100644 --- a/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java +++ b/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java @@ -22,7 +22,6 @@ import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.InternetProtocol; import org.testcontainers.containers.wait.strategy.ShellStrategy; -import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -110,7 +109,7 @@ public void testJettyLocalhostConnection() throws Exception { } @BeforeClass - public static void prepare() throws IOException, InterruptedException { + public static void prepare() { try { caddy = new FixedHostPortGenericContainer<>("library/caddy:2.7.5") .withReuse(false) From 9eafe0a29d057e9e117b6cae52708fa4a26c5c73 Mon Sep 17 00:00:00 2001 From: Evgeny Chugunnyy Date: Thu, 2 Nov 2023 08:54:35 +0300 Subject: [PATCH 7/7] Code review fixes. Also improved tests checks. --- .../transport/http3/HttpTransporter.java | 149 ++++---- .../transport/http3/MavenResolverIT.java | 85 ++--- .../{Caddyfile.docker => Caddyfile.proxy} | 0 ...yfile.auth.docker => Caddyfile.proxy.auth} | 0 .../src/test/resources/commons-cli-1.4.pom | 333 ++++++++++++++++++ 5 files changed, 457 insertions(+), 110 deletions(-) rename mvn-resolver-transport-http3/src/test/resources/{Caddyfile.docker => Caddyfile.proxy} (100%) rename mvn-resolver-transport-http3/src/test/resources/{Caddyfile.auth.docker => Caddyfile.proxy.auth} (100%) create mode 100644 mvn-resolver-transport-http3/src/test/resources/commons-cli-1.4.pom diff --git a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java index 3962c43..07ebcd0 100644 --- a/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java +++ b/mvn-resolver-transport-http3/src/main/java/com/artipie/aether/transport/http3/HttpTransporter.java @@ -38,7 +38,6 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.nio.file.Files; import java.nio.file.attribute.FileTime; import java.text.DateFormat; @@ -70,75 +69,8 @@ final class HttpTransporter extends AbstractTransporter { RemoteRepository repository, RepositorySystemSession session) throws Exception { - System.err.println("Custom HttpTransporter created!!!"); - // Checking http3 support is available (loaded) - PreEncodedHttpField f = new PreEncodedHttpField("Host", "localhost"); - for (final HttpVersion v: HttpVersion.values()) { - int len = 0; - try { - len = f.getEncodedLength(v); - } catch (Exception ex) { - len = -1; - } - System.err.println("\tCustom HttpTransporter PreEncodedHttpField v=" + v + "; len=" + len); - } - - // TODO: Force http3 initialization (HACK!) - final ServiceLoader load = ServiceLoader.load(HttpFieldPreEncoder.class,PreEncodedHttpField.class.getClassLoader()); - /*ServiceLoader load = null; - ClassLoader saveCl = Thread.currentThread().getContextClassLoader(); - try { - Thread.currentThread().setContextClassLoader(PreEncodedHttpField.class.getClassLoader()); - load = ServiceLoader.load(HttpFieldPreEncoder.class); - }finally { - Thread.currentThread().setContextClassLoader(saveCl); - }*/ - - /*System.err.println("\tCustom HttpTransporter ServiceLoader=" + load); - Stream> providerStream = TypeUtil.serviceProviderStream(load); - System.err.println("\tCustom HttpTransporter Stream> = " + providerStream); - ArrayList calls = new ArrayList<>(); - providerStream.forEach((provider) -> { - try { - calls.add(calls.size()); - HttpFieldPreEncoder encoder = (HttpFieldPreEncoder)provider.get(); - HttpVersion v = encoder.getHttpVersion(); - System.err.println("\tCustom HttpTransporter HttpFieldPreEncoder: encoder=" + encoder + "; ver=" + v); - } catch (RuntimeException | Error var3) { - System.err.println("\tCustom HttpTransporter Error processing encoder: " + provider.get()); - } - }); - System.err.println("\tCustom HttpTransporter providerStream calls=" + calls.size());*/ - - HashMap encoders = new HashMap<>(); - for (HttpFieldPreEncoder val: load) { - System.err.println("\tCustom HttpTransporter HttpFieldPreEncoder val=" + val); - encoders.put(val.getHttpVersion(), val); - } - - Field ff = PreEncodedHttpField.class.getDeclaredField("__encoders"); - ff.setAccessible(true); - String fldDescr = ff.get(null).toString(); - System.err.println("\tCustom HttpTransporter __encoders BEFORE: " + fldDescr + "; this=" + this); - @SuppressWarnings("unchecked") EnumMap obj = (EnumMap)ff.get(null); - if (encoders.containsKey(HttpVersion.HTTP_3) && !obj.containsKey(HttpVersion.HTTP_3)) { - System.err.println("\tCustom HttpTransporter adding to __encoders: " + obj + "; this=" + this); - obj.put(HttpVersion.HTTP_3, encoders.get(HttpVersion.HTTP_3)); - } - System.err.println("\tCustom HttpTransporter __encoders AFTER: " + obj + "; this=" + this); - - // Rechecking http3 support is available (loaded) - f = new PreEncodedHttpField("Host", "localhost"); - for (final HttpVersion v: HttpVersion.values()) { - int len = 0; - try { - len = f.getEncodedLength(v); - } catch (Exception ex) { - len = -1; - } - System.err.println("\tCustom HttpTransporter PreEncodedHttpField v=" + v + "; len=" + len); - } + forceLoadHttp3Support(); if (!"http".equalsIgnoreCase(repository.getProtocol()) && !"https".equalsIgnoreCase(repository.getProtocol())) { throw new NoTransporterException(repository); @@ -286,4 +218,83 @@ private void extractChecksums(ContentResponse response, GetTask task) { } } } + + /** + * TOOD: For unknown reason when running inside Maven, HttpFieldPreEncoder for HTTP3 is missing. + * It is not available in Jetty static initializer when that library is loaded by Maven. + * However, it is available the moment later. + * Here I use reflection to force-register missing HttpFieldPreEncoder for HTTP3. + * @throws NoSuchFieldException + * @throws IllegalAccessException + */ + private void forceLoadHttp3Support() throws NoSuchFieldException, IllegalAccessException { + System.err.println("Custom HttpTransporter.forceLoadHttp3Support() called!"); + // Checking http3 support is available (loaded) + PreEncodedHttpField f = new PreEncodedHttpField("Host", "localhost"); + for (final HttpVersion v: HttpVersion.values()) { + int len = 0; + try { + len = f.getEncodedLength(v); + } catch (Exception ex) { + len = -1; + } + System.err.println("\tCustom HttpTransporter PreEncodedHttpField v=" + v + "; len=" + len); + } + + // TODO: Force http3 initialization (HACK!) + final ServiceLoader load = ServiceLoader.load(HttpFieldPreEncoder.class,PreEncodedHttpField.class.getClassLoader()); + /*ServiceLoader load = null; + ClassLoader saveCl = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(PreEncodedHttpField.class.getClassLoader()); + load = ServiceLoader.load(HttpFieldPreEncoder.class); + }finally { + Thread.currentThread().setContextClassLoader(saveCl); + }*/ + + /*System.err.println("\tCustom HttpTransporter ServiceLoader=" + load); + Stream> providerStream = TypeUtil.serviceProviderStream(load); + System.err.println("\tCustom HttpTransporter Stream> = " + providerStream); + ArrayList calls = new ArrayList<>(); + providerStream.forEach((provider) -> { + try { + calls.add(calls.size()); + HttpFieldPreEncoder encoder = (HttpFieldPreEncoder)provider.get(); + HttpVersion v = encoder.getHttpVersion(); + System.err.println("\tCustom HttpTransporter HttpFieldPreEncoder: encoder=" + encoder + "; ver=" + v); + } catch (RuntimeException | Error var3) { + System.err.println("\tCustom HttpTransporter Error processing encoder: " + provider.get()); + } + }); + System.err.println("\tCustom HttpTransporter providerStream calls=" + calls.size());*/ + + HashMap encoders = new HashMap<>(); + for (HttpFieldPreEncoder val: load) { + System.err.println("\tCustom HttpTransporter HttpFieldPreEncoder val=" + val); + encoders.put(val.getHttpVersion(), val); + } + + Field ff = PreEncodedHttpField.class.getDeclaredField("__encoders"); + ff.setAccessible(true); + String fldDescr = ff.get(null).toString(); + System.err.println("\tCustom HttpTransporter __encoders BEFORE: " + fldDescr + "; this=" + this); + @SuppressWarnings("unchecked") EnumMap obj = (EnumMap)ff.get(null); + if (encoders.containsKey(HttpVersion.HTTP_3) && !obj.containsKey(HttpVersion.HTTP_3)) { + System.err.println("\tCustom HttpTransporter adding to __encoders: " + obj + "; this=" + this); + obj.put(HttpVersion.HTTP_3, encoders.get(HttpVersion.HTTP_3)); + } + System.err.println("\tCustom HttpTransporter __encoders AFTER: " + obj + "; this=" + this); + + // Rechecking http3 support is available (loaded) + f = new PreEncodedHttpField("Host", "localhost"); + for (final HttpVersion v: HttpVersion.values()) { + int len = 0; + try { + len = f.getEncodedLength(v); + } catch (Exception ex) { + len = -1; + } + System.err.println("\tCustom HttpTransporter PreEncodedHttpField v=" + v + "; len=" + len); + } + } } diff --git a/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java b/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java index a9a22a4..1e63e0d 100644 --- a/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java +++ b/mvn-resolver-transport-http3/src/test/java/com/artipie/aether/transport/http3/MavenResolverIT.java @@ -8,7 +8,6 @@ import org.eclipse.aether.spi.connector.transport.GetTask; import org.eclipse.aether.spi.connector.transport.TransportListener; import org.eclipse.aether.spi.connector.transport.Transporter; -import org.eclipse.aether.transfer.TransferCancelledException; import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpRequestException; @@ -23,7 +22,6 @@ import org.testcontainers.containers.InternetProtocol; import org.testcontainers.containers.wait.strategy.ShellStrategy; import java.net.URI; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import static org.junit.Assert.*; @@ -32,63 +30,68 @@ */ public class MavenResolverIT { - private static GenericContainer caddy; - private static GenericContainer caddyAuth; + private static final String REMOTE_PATH = "commons-cli/commons-cli/1.4/commons-cli-1.4.pom"; + private static final String LOCAL_PATH = "commons-cli-1.4.pom"; + + private static GenericContainer caddyProxy; + private static GenericContainer caddyProxyAuth; @Test public void testTransporterAuth() throws Exception { final byte[] data = testTransporter("https://demo:demo@localhost:7444/maven2"); assertNotEquals(null, data); - System.err.println(new String(data, StandardCharsets.UTF_8)); - assertTrue(data.length > 0); + final byte[] local = getClass().getClassLoader().getResourceAsStream(LOCAL_PATH).readAllBytes(); + assertArrayEquals(local, data); } - @Test(expected = HttpResponseException.class) - public void testTransporterAuthFail() throws Exception { - final byte[] data = testTransporter("https://demo1:demo1@localhost:7444/maven2"); - assertNull(data); + @Test + public void testTransporterAuthFail() { + HttpResponseException exception = assertThrows( + "Invalid exception thrown", HttpResponseException.class, + () -> testTransporter("https://demo1:demo1@localhost:7444/maven2") + ); + assertEquals("401", exception.getMessage()); } - @Test(expected = HttpResponseException.class) - public void testTransporterAnonAuthFail() throws Exception { - testTransporter("https://localhost:7444/maven2"); + @Test + public void testTransporterAnonAuthFail() { + HttpResponseException exception = assertThrows( + "Invalid exception thrown", HttpResponseException.class, + () -> testTransporter("https://localhost:7444/maven2") + ); + assertEquals("401", exception.getMessage()); } @Test public void testTransporterAnon() throws Exception { final byte[] data = testTransporter("https://localhost:7443/maven2"); assertNotEquals(null, data); - System.err.println(new String(data, StandardCharsets.UTF_8)); - assertTrue(data.length > 0); - } + final byte[] local = getClass().getClassLoader().getResourceAsStream(LOCAL_PATH).readAllBytes(); + assertArrayEquals(local, data); } @Test public void testAnonTransporterSuccess() throws Exception { final byte[] data = testTransporter("https://demo:demo@localhost:7443/maven2"); assertNotNull(data); - assertTrue(data.length > 0); + final byte[] local = getClass().getClassLoader().getResourceAsStream(LOCAL_PATH).readAllBytes(); + assertArrayEquals(local, data); } - @Test(expected = HttpRequestException.class) - public void testTransporterInvalidUrl() throws Exception { - testTransporter("https://localhost:7445/maven2"); + @Test() + public void testTransporterInvalidUrl() { + HttpRequestException exception = assertThrows( + "Invalid exception thrown", HttpRequestException.class, + () -> testTransporter("https://localhost:7445/maven2") + ); + assertEquals("java.net.SocketTimeoutException: connect timeout", exception.getMessage()); } private byte[] testTransporter(final String repo) throws Exception { final RepositorySystemSession session = newSession(); final RemoteRepository repository = newRepo(repo); final HttpTransporterFactory factory = new HttpTransporterFactory(); - TransportListener listener = new TransportListener() { - @Override - public void transportStarted(long dataOffset, long dataLength) throws TransferCancelledException { - super.transportStarted(dataOffset, dataLength); - } - @Override - public void transportProgressed(ByteBuffer data) throws TransferCancelledException { - super.transportProgressed(data); - } - }; - final GetTask task = new GetTask(URI.create("commons-cli/commons-cli/1.4/commons-cli-1.4.pom")).setListener(listener); + final GetTask task = new GetTask(URI.create(REMOTE_PATH)) + .setListener(new TransportListener() {}); try (final Transporter transporter = factory.newInstance(session, repository)) { transporter.get(task); } @@ -111,36 +114,36 @@ public void testJettyLocalhostConnection() throws Exception { @BeforeClass public static void prepare() { try { - caddy = new FixedHostPortGenericContainer<>("library/caddy:2.7.5") + caddyProxy = new FixedHostPortGenericContainer<>("library/caddy:2.7.5") .withReuse(false) - .withFileSystemBind("src/test/resources/Caddyfile.docker", "/etc/caddy/Caddyfile") + .withFileSystemBind("src/test/resources/Caddyfile.proxy", "/etc/caddy/Caddyfile") .withFileSystemBind("src/test/resources/stunnel.pem", "/etc/caddy/stunnel.pem") .waitingFor(new ShellStrategy().withCommand("nc -u -z localhost 7443")) .withFixedExposedPort(7443, 7443, InternetProtocol.UDP) .withFixedExposedPort(8080, 8080, InternetProtocol.TCP) .withAccessToHost(true); - caddyAuth = new FixedHostPortGenericContainer<>("library/caddy:2.7.5") + caddyProxyAuth = new FixedHostPortGenericContainer<>("library/caddy:2.7.5") .withReuse(false) - .withFileSystemBind("src/test/resources/Caddyfile.auth.docker", "/etc/caddy/Caddyfile") + .withFileSystemBind("src/test/resources/Caddyfile.proxy.auth", "/etc/caddy/Caddyfile") .withFileSystemBind("src/test/resources/stunnel.pem", "/etc/caddy/stunnel.pem") .waitingFor(new ShellStrategy().withCommand("nc -u -z localhost 7444")) .withFixedExposedPort(7444, 7444, InternetProtocol.UDP) .withAccessToHost(true); - caddy.start(); - caddyAuth.start(); + caddyProxy.start(); + caddyProxyAuth.start(); } catch (Exception ex) { - System.err.println(caddy.getLogs()); - System.err.println(caddyAuth.getLogs()); + System.err.println(caddyProxy.getLogs()); + System.err.println(caddyProxyAuth.getLogs()); throw ex; } } @AfterClass public static void finish() { - caddy.stop(); - caddyAuth.stop(); + caddyProxy.stop(); + caddyProxyAuth.stop(); } private static DefaultRepositorySystemSession newSession() { diff --git a/mvn-resolver-transport-http3/src/test/resources/Caddyfile.docker b/mvn-resolver-transport-http3/src/test/resources/Caddyfile.proxy similarity index 100% rename from mvn-resolver-transport-http3/src/test/resources/Caddyfile.docker rename to mvn-resolver-transport-http3/src/test/resources/Caddyfile.proxy diff --git a/mvn-resolver-transport-http3/src/test/resources/Caddyfile.auth.docker b/mvn-resolver-transport-http3/src/test/resources/Caddyfile.proxy.auth similarity index 100% rename from mvn-resolver-transport-http3/src/test/resources/Caddyfile.auth.docker rename to mvn-resolver-transport-http3/src/test/resources/Caddyfile.proxy.auth diff --git a/mvn-resolver-transport-http3/src/test/resources/commons-cli-1.4.pom b/mvn-resolver-transport-http3/src/test/resources/commons-cli-1.4.pom new file mode 100644 index 0000000..1a65965 --- /dev/null +++ b/mvn-resolver-transport-http3/src/test/resources/commons-cli-1.4.pom @@ -0,0 +1,333 @@ + + + + + org.apache.commons + commons-parent + 42 + + 4.0.0 + commons-cli + commons-cli + 1.4 + Apache Commons CLI + + 2002 + + Apache Commons CLI provides a simple API for presenting, processing and validating a command line interface. + + + http://commons.apache.org/proper/commons-cli/ + + + jira + http://issues.apache.org/jira/browse/CLI + + + + scm:svn:http://svn.apache.org/repos/asf/commons/proper/cli/trunk/ + scm:svn:https://svn.apache.org/repos/asf/commons/proper/cli/trunk/ + http://svn.apache.org/viewvc/commons/proper/cli/trunk/ + + + + + James Strachan + jstrachan + jstrachan@apache.org + SpiritSoft, Inc. + + + Bob McWhirter + bob + bob@werken.com + Werken + + contributed ideas and code from werken.opt + + + + John Keyes + jkeyes + jbjk@mac.com + integral Source + + contributed ideas and code from Optz + + + + Rob Oxspring + roxspring + roxspring@imapmail.org + Indigo Stone + + designed CLI2 + + + + Emmanuel Bourg + ebourg + ebourg@apache.org + Ariane Software + + + Thomas Neidhart + tn + tn@apache.org + + + + + + Beluga Behr + + + Peter Donald + + contributed ideas and code from Avalon Excalibur's cli package + + + + Brian Egge + + made the 1.1 release happen + + + + Duncan Jones + + supplied patches + + + + Berin Loritsch + bloritsch@apache.org + + helped in the Avalon CLI merge + + + + Peter Maddocks + peter_maddocks@hp.com + Hewlett-Packard + + supplied patch + + + + Alexandru Mocanu + + supplied patch + + + + Andrew Shirley + + lots of fixes for 1.1 + + + + Greg Thomas + + + Slawek Zachcial + + unit tests + + + + + + + + junit + junit + 4.12 + test + + + + + 1.5 + 1.5 + cli + 1.4 + commons-cli-${commons.release.version} + org.apache.commons.cli + CLI + 12310463 + + RC1 + + site-content + utf-8 + + + + + + maven-assembly-plugin + + + src/assembly/bin.xml + src/assembly/src.xml + + gnu + + + + + + + org.apache.rat + apache-rat-plugin + + + src/site/resources/.htaccess + + + + + org.apache.maven.plugins + maven-scm-publish-plugin + + + javadocs** + + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + http://download.oracle.com/javase/6/docs/api + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.15 + + ${basedir}/src/conf/checkstyle.xml + false + ${basedir}/src/conf/checkstyle-suppressions.xml + + + + + checkstyle + + + + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.1 + + Normal + Default + ${basedir}/src/conf/findbugs-exclude-filter.xml + + + + maven-pmd-plugin + 3.5 + + ${maven.compiler.target} + + + + + + + + apache.website + Apache Commons Site + scm:svn:${commons.scmPubUrl} + + + + + + rc + + + + apache.website + Apache Commons Release Candidate Staging Site + ${commons.deployment.protocol}://people.apache.org/www/people.apache.org/builds/commons/${commons.componentid}/${commons.release.version}/${commons.rc.version}/site + + + + + setup-checkout + + + site-content + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + prepare-checkout + pre-site + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + +