Skip to content

Commit

Permalink
GH-55 Support for proxy repositories (Resolve #55)
Browse files Browse the repository at this point in the history
  • Loading branch information
dzikoysk committed May 21, 2020
1 parent 730576b commit 740cae0
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 36 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ $ docker pull dzikoysk/reposilite
List of available management commands

```bash
Reposilite 2.3.2 Commands:
Reposilite 2.3.4 Commands:
help - List available commands
status - Display metrics
tokens - List all generated tokens
Expand Down Expand Up @@ -79,3 +79,5 @@ To use generated token add a new server in your `~/m2/settings.xml`
<password>{token}</password>
</server>
```

#### FAQ
9 changes: 9 additions & 0 deletions src/main/java/org/panda_lang/reposilite/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public final class Configuration implements Serializable {
private String hostname;
private int port;
private List<String> repositories;
private List<String> proxied;
private boolean deployEnabled;
private boolean rewritePathsEnabled;
private boolean fullAuthEnabled;
Expand All @@ -40,6 +41,10 @@ public void setRepositories(List<String> repositories) {
this.repositories = repositories;
}

public void setProxied(List<String> proxied) {
this.proxied = proxied;
}

public void setDeployEnabled(boolean deployEnabled) {
this.deployEnabled = deployEnabled;
}
Expand All @@ -60,6 +65,10 @@ public int getPort() {
return port;
}

public List<String> getProxied() {
return proxied;
}

public List<String> getRepositories() {
return repositories;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

public final class ReposiliteConstants {

public static final String VERSION = "2.3.2";
public static final String VERSION = "2.3.4";

static final String GREETING_MESSAGE = ansi().bold().fg(Color.GREEN).a("Reposilite ").reset().a(ReposiliteConstants.VERSION).reset().toString();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,24 @@

package org.panda_lang.reposilite.repository;

import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import fi.iki.elonen.NanoHTTPD;
import fi.iki.elonen.NanoHTTPD.IHTTPSession;
import fi.iki.elonen.NanoHTTPD.Response;
import fi.iki.elonen.NanoHTTPD.Response.Status;
import org.panda_lang.reposilite.Configuration;
import org.panda_lang.reposilite.Reposilite;
import org.panda_lang.reposilite.ReposiliteController;
import org.panda_lang.reposilite.ReposiliteHttpServer;
import org.panda_lang.reposilite.Reposilite;
import org.panda_lang.reposilite.auth.Authenticator;
import org.panda_lang.reposilite.auth.Session;
import org.panda_lang.reposilite.metadata.MetadataService;
import org.panda_lang.reposilite.metadata.MetadataUtils;
import org.panda_lang.reposilite.utils.Result;
import org.panda_lang.utilities.commons.StringUtils;
import org.panda_lang.utilities.commons.text.ContentJoiner;

import java.io.File;
Expand All @@ -45,17 +50,52 @@ final class DownloadController implements ReposiliteController {
private final Authenticator authenticator;
private final MetadataService metadataService;
private final RepositoryService repositoryService;
private final HttpRequestFactory requestFactory;

public DownloadController(Reposilite reposilite) {
this.reposilite = reposilite;
this.configuration = reposilite.getConfiguration();
this.authenticator = reposilite.getAuthenticator();
this.metadataService = reposilite.getMetadataService();
this.repositoryService = reposilite.getRepositoryService();
this.requestFactory = configuration.getProxied().isEmpty() ? null : new NetHttpTransport().createRequestFactory();
}

@Override
public NanoHTTPD.Response serve(ReposiliteHttpServer server, NanoHTTPD.IHTTPSession httpSession) throws IOException {
public Response serve(ReposiliteHttpServer server, IHTTPSession httpSession) throws Exception {
Response response = serveLocal(httpSession);

if (response.getStatus().getRequestStatus() == Status.NOT_FOUND.getRequestStatus()) {
return serveProxied(httpSession);
}

return response;
}

public Response serveProxied(IHTTPSession httpSession) throws IOException {
String uri = httpSession.getUri();

for (String proxied : configuration.getProxied()) {
HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(proxied + uri));
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();

if (!response.isSuccessStatusCode()) {
continue;
}

return NanoHTTPD.newFixedLengthResponse(
Status.lookup(response.getStatusCode()),
response.getMediaType().toString(),
response.getContent(),
response.getContent().available()
);
}

return notFound(reposilite, "Artifact not found in local and remote repository");
}

public Response serveLocal(IHTTPSession httpSession) throws IOException {
if (configuration.isFullAuthEnabled()) {
Result<Session, Response> authResult = this.authenticator.authUri(httpSession);

Expand All @@ -64,7 +104,7 @@ public NanoHTTPD.Response serve(ReposiliteHttpServer server, NanoHTTPD.IHTTPSess
}
}

String[] path = normalizeUri(reposilite.getConfiguration(), httpSession.getUri()).split("/");
String[] path = RepositoryUtils.normalizeUri(configuration, httpSession.getUri()).split("/");

if (path.length == 0) {
return notFound(reposilite, "Unsupported request");
Expand Down Expand Up @@ -95,7 +135,7 @@ public NanoHTTPD.Response serve(ReposiliteHttpServer server, NanoHTTPD.IHTTPSess
return notFound(reposilite, "Metadata not found");
}

return NanoHTTPD.newFixedLengthResponse(Status.OK, "text/xml", metadataService.generateMetadata(repository, requestPath));
return NanoHTTPD.newFixedLengthResponse(Status.OK, "text/xml", result);
}

if (requestedFileName.contains("-SNAPSHOT")) {
Expand All @@ -121,7 +161,7 @@ public NanoHTTPD.Response serve(ReposiliteHttpServer server, NanoHTTPD.IHTTPSess
content = new FileInputStream(file);

String mimeType = Files.probeContentType(file.toPath());
NanoHTTPD.Response response = NanoHTTPD.newChunkedResponse(NanoHTTPD.Response.Status.OK, mimeType, content);
Response response = NanoHTTPD.newChunkedResponse(Response.Status.OK, mimeType, content);
response.addHeader("Content-Disposition", "attachment; filename=\"" + MetadataUtils.getLast(path) +"\"");
response.addHeader("Content-Length", String.valueOf(file.length()));

Expand All @@ -136,34 +176,8 @@ public NanoHTTPD.Response serve(ReposiliteHttpServer server, NanoHTTPD.IHTTPSess
}
}

private String normalizeUri(Configuration configuration, String uri) {
if (uri.startsWith("/")) {
uri = uri.substring(1);
}

if (uri.contains("..")) {
return StringUtils.EMPTY;
}

if (!configuration.isRewritePathsEnabled()) {
return uri;
}

if (StringUtils.countOccurrences(uri, "/") <= 1) {
return uri;
}

for (String repositoryName : configuration.getRepositories()) {
if (uri.startsWith(repositoryName)) {
return uri;
}
}

return configuration.getRepositories().get(0) + "/" + uri;
}

private NanoHTTPD.Response notFound(Reposilite reposilite, String message) {
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.NOT_FOUND, "text/html", reposilite.getFrontend().forMessage(message));
private Response notFound(Reposilite reposilite, String message) {
return NanoHTTPD.newFixedLengthResponse(Response.Status.NOT_FOUND, "text/html", reposilite.getFrontend().forMessage(message));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.panda_lang.reposilite.repository;

import org.panda_lang.reposilite.Configuration;
import org.panda_lang.utilities.commons.StringUtils;
import org.panda_lang.utilities.commons.text.ContentJoiner;

import java.io.File;
Expand All @@ -28,4 +30,30 @@ public static File toRequestedFile(Repository repository, String[] requestPath)
return new File(repository.getLocalPath() + File.separator + ContentJoiner.on(File.separator).join(requestPath));
}

protected static String normalizeUri(Configuration configuration, String uri) {
if (uri.startsWith("/")) {
uri = uri.substring(1);
}

if (uri.contains("..")) {
return StringUtils.EMPTY;
}

if (!configuration.isRewritePathsEnabled()) {
return uri;
}

if (StringUtils.countOccurrences(uri, "/") <= 1) {
return uri;
}

for (String repositoryName : configuration.getRepositories()) {
if (uri.startsWith(repositoryName)) {
return uri;
}
}

return configuration.getRepositories().get(0) + "/" + uri;
}

}
6 changes: 6 additions & 0 deletions src/main/resources/reposilite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ repositories:
- releases
- snapshots

# List of proxied repositories.
# Reposilite will search for an artifact in remote repositories listed below, if the requested artifact was not found.
# Note: URL cannot contains / at the end
proxied: []
# - https://repo.panda-lang.org

# Accept deployment connections
deployEnabled: true
# Allow to omit name of the main repository in request
Expand Down

0 comments on commit 740cae0

Please sign in to comment.