Java tutorial
/** * Copyright (C) 2011-2018 Red Hat, Inc. (https://github.com/Commonjava/indy) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.commonjava.indy.httprox; import groovy.text.GStringTemplateEngine; import org.apache.commons.io.IOUtils; import org.apache.http.HttpHost; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.routing.HttpRoutePlanner; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.commonjava.cdi.util.weft.PoolWeftExecutorService; import org.commonjava.cdi.util.weft.WeftExecutorService; import org.commonjava.indy.bind.jaxrs.MDCManager; import org.commonjava.indy.boot.BootOptions; import org.commonjava.indy.conf.DefaultIndyConfiguration; import org.commonjava.indy.content.ContentDigester; import org.commonjava.indy.content.ContentGenerator; import org.commonjava.indy.content.ContentManager; import org.commonjava.indy.content.DirectContentAccess; import org.commonjava.indy.content.DownloadManager; import org.commonjava.indy.core.content.DefaultContentDigester; import org.commonjava.indy.core.content.DefaultContentManager; import org.commonjava.indy.core.content.DefaultDirectContentAccess; import org.commonjava.indy.core.content.DefaultDownloadManager; import org.commonjava.indy.core.ctl.ContentController; import org.commonjava.indy.core.inject.ExpiringMemoryNotFoundCache; import org.commonjava.indy.httprox.conf.HttproxConfig; import org.commonjava.indy.httprox.handler.ProxyAcceptHandler; import org.commonjava.indy.httprox.keycloak.KeycloakProxyAuthenticator; import org.commonjava.indy.mem.data.MemoryStoreDataManager; import org.commonjava.indy.model.core.GenericPackageTypeDescriptor; import org.commonjava.indy.model.core.RemoteRepository; import org.commonjava.indy.model.core.StoreKey; import org.commonjava.indy.model.core.StoreType; import org.commonjava.indy.model.core.io.IndyObjectMapper; import org.commonjava.indy.subsys.datafile.DataFileManager; import org.commonjava.indy.subsys.datafile.change.DataFileEventManager; import org.commonjava.indy.subsys.infinispan.CacheHandle; import org.commonjava.indy.subsys.infinispan.CacheProducer; import org.commonjava.indy.subsys.keycloak.conf.KeycloakConfig; import org.commonjava.indy.subsys.template.ScriptEngine; import org.commonjava.indy.subsys.template.TemplatingEngine; import org.commonjava.indy.test.fixture.core.MockContentAdvisor; import org.commonjava.indy.test.fixture.core.MockInstance; import org.commonjava.indy.util.MimeTyper; import org.commonjava.atlas.maven.ident.ref.ProjectVersionRef; import org.commonjava.maven.galley.auth.MemoryPasswordManager; import org.commonjava.maven.galley.io.SpecialPathManagerImpl; import org.commonjava.maven.galley.io.checksum.TransferMetadata; import org.commonjava.maven.galley.maven.parse.PomPeek; import org.commonjava.maven.galley.nfc.MemoryNotFoundCache; import org.commonjava.maven.galley.spi.transport.TransportManager; import org.commonjava.maven.galley.testing.core.CoreFixture; import org.commonjava.maven.galley.transport.TransportManagerImpl; import org.commonjava.maven.galley.transport.htcli.HttpClientTransport; import org.commonjava.maven.galley.transport.htcli.HttpImpl; import org.commonjava.maven.galley.transport.htcli.util.HttpUtil; import org.commonjava.test.http.expect.ExpectationServer; import org.infinispan.Cache; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.manager.DefaultCacheManager; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.InputStream; import java.util.Collections; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import static org.commonjava.indy.model.core.GenericPackageTypeDescriptor.GENERIC_PKG_KEY; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; public class HttpProxyTest { private static final String USER = "user"; private static final String PASS = "password"; private static final String HOST = "127.0.0.1"; protected final Logger logger = LoggerFactory.getLogger(getClass()); @Rule public ExpectationServer server = new ExpectationServer(); @Rule public TemporaryFolder temp = new TemporaryFolder(); @Rule public CoreFixture core = new CoreFixture(false, temp); private int proxyPort; private HttpProxy proxy; private MemoryStoreDataManager storeManager; private static DefaultCacheManager cacheManager; private static Cache<String, TransferMetadata> contentMetadata; @BeforeClass public static void setupClass() { cacheManager = new DefaultCacheManager(new ConfigurationBuilder().simpleCache(true).build()); contentMetadata = cacheManager.getCache("content-metadata", true); } @Before public void setup() throws Exception { contentMetadata.clear(); core.initGalley(); final TransportManager transports = new TransportManagerImpl( new HttpClientTransport(new HttpImpl(new MemoryPasswordManager()))); core.withTransportManager(transports); core.initMissingComponents(); final HttproxConfig config = new HttproxConfig(); config.setEnabled(true); proxyPort = config.getPort(); final BootOptions bootOpts = new BootOptions(); bootOpts.setBind(HOST); storeManager = new MemoryStoreDataManager(true); final IndyObjectMapper mapper = new IndyObjectMapper(true); final DefaultIndyConfiguration indyConfig = new DefaultIndyConfiguration(); indyConfig.setNotFoundCacheTimeoutSeconds(1); final ExpiringMemoryNotFoundCache nfc = new ExpiringMemoryNotFoundCache(indyConfig); WeftExecutorService rescanService = new PoolWeftExecutorService("test-rescan-executor", (ThreadPoolExecutor) Executors.newCachedThreadPool(), 2, 10f, false, null, null); final DownloadManager downloadManager = new DefaultDownloadManager(storeManager, core.getTransferManager(), core.getLocationExpander(), new MockInstance<>(new MockContentAdvisor()), nfc, rescanService); WeftExecutorService contentAccessService = new PoolWeftExecutorService("test-content-access-executor", (ThreadPoolExecutor) Executors.newCachedThreadPool(), 2, 10f, false, null, null); DirectContentAccess dca = new DefaultDirectContentAccess(downloadManager, contentAccessService); ContentDigester contentDigester = new DefaultContentDigester(dca, new CacheHandle<>("content-metadata", contentMetadata)); final ContentManager contentManager = new DefaultContentManager(storeManager, downloadManager, mapper, new SpecialPathManagerImpl(), new MemoryNotFoundCache(), contentDigester, Collections.emptySet()); DataFileManager dfm = new DataFileManager(temp.newFolder(), new DataFileEventManager()); final TemplatingEngine templates = new TemplatingEngine(new GStringTemplateEngine(), dfm); final ContentController contentController = new ContentController(storeManager, contentManager, templates, mapper, new MimeTyper()); KeycloakConfig kcConfig = new KeycloakConfig(); kcConfig.setEnabled(false); final KeycloakProxyAuthenticator auth = new KeycloakProxyAuthenticator(kcConfig, config); ScriptEngine scriptEngine = new ScriptEngine(dfm); proxy = new HttpProxy(config, bootOpts, new ProxyAcceptHandler(config, storeManager, contentController, auth, core.getCache(), scriptEngine, new MDCManager(), null, null, new CacheProducer(null, cacheManager, null))); proxy.start(); } @After public void teardown() { if (proxy != null) { proxy.stop(); } } @Test public void proxySimplePomAndAutoCreateRemoteRepo() throws Exception { final String testRepo = "test"; final PomRef pom = loadPom("simple.pom", Collections.emptyMap()); final String url = server.formatUrl(testRepo, pom.path); server.expect(url, 200, pom.pom); final HttpGet get = new HttpGet(url); final CloseableHttpClient client = proxiedHttp(); CloseableHttpResponse response = null; InputStream stream = null; try { response = client.execute(get, proxyContext(USER, PASS)); stream = response.getEntity().getContent(); final String resultingPom = IOUtils.toString(stream); assertThat(resultingPom, notNullValue()); assertThat(resultingPom, equalTo(pom.pom)); } finally { IOUtils.closeQuietly(stream); HttpUtil.cleanupResources(client, get, response); } final RemoteRepository remoteRepo = (RemoteRepository) storeManager.getArtifactStore( new StoreKey(GENERIC_PKG_KEY, StoreType.remote, "httprox_127-0-0-1_" + server.getPort())); assertThat(remoteRepo, notNullValue()); assertThat(remoteRepo.getUrl(), equalTo(server.getBaseUri())); } @Test public void proxy404() throws Exception { final String testRepo = "test"; final PomRef pom = loadPom("simple.pom", Collections.emptyMap()); final String url = server.formatUrl(testRepo, pom.path); final HttpGet get = new HttpGet(url); final CloseableHttpClient client = proxiedHttp(); CloseableHttpResponse response = null; final InputStream stream = null; try { response = client.execute(get, proxyContext(USER, PASS)); assertThat(response.getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_NOT_FOUND)); } finally { IOUtils.closeQuietly(stream); HttpUtil.cleanupResources(client, get, response); } final RemoteRepository remoteRepo = (RemoteRepository) storeManager.getArtifactStore( new StoreKey(GENERIC_PKG_KEY, StoreType.remote, "httprox_127-0-0-1_" + server.getPort())); assertThat(remoteRepo, notNullValue()); assertThat(remoteRepo.getUrl(), equalTo(server.getBaseUri())); } @Test @Ignore("return upstream 5xx class errors as 404 to keep Maven & co happy") public void proxy500As502() throws Exception { final String testRepo = "test"; final PomRef pom = loadPom("simple.pom", Collections.emptyMap()); final String url = server.formatUrl(testRepo, pom.path); server.registerException(new File("/" + testRepo, pom.path).getPath(), "Expected exception", 500); final HttpGet get = new HttpGet(url); final CloseableHttpClient client = proxiedHttp(); CloseableHttpResponse response = null; final InputStream stream = null; try { response = client.execute(get, proxyContext(USER, PASS)); assertThat(response.getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_BAD_GATEWAY)); } finally { IOUtils.closeQuietly(stream); HttpUtil.cleanupResources(client, get, response); } final RemoteRepository remoteRepo = (RemoteRepository) storeManager .getArtifactStore(new StoreKey(GENERIC_PKG_KEY, StoreType.remote, "httprox_127-0-0-1")); assertThat(remoteRepo, notNullValue()); assertThat(remoteRepo.getUrl(), equalTo(server.getBaseUri())); } protected HttpClientContext proxyContext(final String user, final String pass) { final CredentialsProvider creds = new BasicCredentialsProvider(); creds.setCredentials(new AuthScope(HOST, proxyPort), new UsernamePasswordCredentials(user, pass)); final HttpClientContext ctx = HttpClientContext.create(); ctx.setCredentialsProvider(creds); return ctx; } protected CloseableHttpClient proxiedHttp() throws Exception { final HttpRoutePlanner planner = new DefaultProxyRoutePlanner(new HttpHost(HOST, proxyPort)); return HttpClients.custom().setRoutePlanner(planner).build(); } protected PomRef loadPom(final String name, final Map<String, String> substitutions) { try { final String resource = name.endsWith(".pom") ? name : name + ".pom"; logger.info("Loading POM: {}", resource); final InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); String pom = IOUtils.toString(stream); IOUtils.closeQuietly(stream); for (final Map.Entry<String, String> entry : substitutions.entrySet()) { pom = pom.replace("@" + entry.getKey() + "@", entry.getValue()); } final PomPeek peek = new PomPeek(pom, false); final ProjectVersionRef gav = peek.getKey(); final String path = String.format("%s/%s/%s/%s-%s.pom", gav.getGroupId().replace('.', '/'), gav.getArtifactId(), gav.getVersionString(), gav.getArtifactId(), gav.getVersionString()); return new PomRef(pom, path); } catch (final Exception e) { e.printStackTrace(); fail("Failed to read POM from: " + name); } return null; } protected static final class PomRef { PomRef(final String pom, final String path) { this.pom = pom; this.path = path; } protected final String pom; protected final String path; } }