Skip to content
This repository has been archived by the owner on Feb 2, 2021. It is now read-only.

Commit

Permalink
Server supports JSON, XML, HTML formats for status responses now as w…
Browse files Browse the repository at this point in the history
…ell as protobuf. Resolves issue #74
  • Loading branch information
mikehearn committed Dec 11, 2014
1 parent 4a8ad58 commit 2a23f88
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 20 deletions.
5 changes: 5 additions & 0 deletions server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,10 @@
<artifactId>postgresql</artifactId>
<version>9.1-901.jdbc4</version>
</dependency>
<dependency>
<groupId>com.googlecode.protobuf-java-format</groupId>
<artifactId>protobuf-java-format</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>
1 change: 1 addition & 0 deletions server/server.iml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<orderEntry type="library" name="Maven: org.javatuples:javatuples:1.2" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:2.0.1" level="project" />
<orderEntry type="library" name="Maven: postgresql:postgresql:9.1-901.jdbc4" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.protobuf-java-format:protobuf-java-format:1.2" level="project" />
<orderEntry type="library" name="Maven: org.bitcoinj:bitcoinj-core:0.13-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: net.jcip:jcip-annotations:1.0" level="project" />
<orderEntry type="library" name="Maven: com.lambdaworks:scrypt:1.4.0" level="project" />
Expand Down
73 changes: 53 additions & 20 deletions server/src/main/java/lighthouse/server/ProjectHandler.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package lighthouse.server;

import org.bitcoinj.core.Sha256Hash;
import com.google.common.base.Charsets;
import com.google.common.base.Splitter;
import com.google.common.base.Throwables;
import com.google.common.io.ByteStreams;
import com.google.protobuf.ByteString;
import com.googlecode.protobuf.format.HtmlFormat;
import com.googlecode.protobuf.format.JsonFormat;
import com.googlecode.protobuf.format.XmlFormat;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import javafx.collections.ObservableMap;
Expand All @@ -14,11 +17,13 @@
import lighthouse.protocol.LHUtils;
import lighthouse.protocol.Project;
import lighthouse.threading.AffinityExecutor;
import org.bitcoinj.core.Sha256Hash;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URLDecoder;
import java.security.SignatureException;
import java.time.Instant;
Expand Down Expand Up @@ -53,6 +58,13 @@ public PledgeGroup(ObservableSet<LHProtos.Pledge> open, ObservableSet<LHProtos.P
}
private final Map<Project, PledgeGroup> pledges = new HashMap<>();

enum StatusFormat {
PBUF,
JSON,
HTML,
XML
}

public ProjectHandler(LighthouseBackend backend) {
this.backend = backend;
// This might change in future so alias it to keep assertions simple.
Expand Down Expand Up @@ -97,21 +109,32 @@ private void realHandle(HttpExchange httpExchange) throws Exception {
// Uninterruptibles.sleepUninterruptibly(3, TimeUnit.SECONDS);

final String method = httpExchange.getRequestMethod();
final String path = httpExchange.getRequestURI().toString();
URI uri = httpExchange.getRequestURI();
String path = uri.toString();
log.info("REQ: {} {}", method, path);
if (!path.startsWith(LHUtils.HTTP_PATH_PREFIX + LHUtils.HTTP_PROJECT_PATH)) {
sendError(httpExchange, HTTP_NOT_FOUND);
return;
}
Project project = backend.getProjectFromURL(httpExchange.getRequestURI());
StatusFormat format = StatusFormat.PBUF;
if (method.equals("GET") && (path.endsWith(".json") || path.endsWith(".xml") || path.endsWith(".html"))) {
try {
format = StatusFormat.valueOf(path.substring(path.lastIndexOf(".") + 1).toUpperCase());
} catch (IllegalArgumentException e) {
log.error("Could not figure out format from extension, defaulting to protobuf");
}
path = path.substring(0, path.lastIndexOf("."));
uri = new URI(uri.getScheme(), uri.getHost(), path, uri.getRawFragment());
}
Project project = backend.getProjectFromURL(uri);
if (project == null) {
log.warn("Project URL did not match any known project", httpExchange.getRequestURI());
log.warn("Project URL did not match any known project", uri);
sendError(httpExchange, HTTP_NOT_FOUND);
return;
}
switch (method) {
case "POST": pledgeUpload(httpExchange, project); break;
case "GET": statusDownload(httpExchange, project); break;
case "GET": statusDownload(httpExchange, project, format); break;
default: sendError(httpExchange, HTTP_BAD_METHOD); break;
}
}
Expand All @@ -127,27 +150,31 @@ private PledgeGroup getPledgesFor(Project project) {
return result;
}

private void statusDownload(HttpExchange httpExchange, Project project) throws IOException, SignatureException {
private void statusDownload(HttpExchange httpExchange, Project project, StatusFormat format) throws IOException, SignatureException {
LHProtos.ProjectStatus.Builder status = LHProtos.ProjectStatus.newBuilder();
status.setId(project.getID());
status.setTimestamp(Instant.now().getEpochSecond());
status.setValuePledgedSoFar(Database.getInstance().getPledgedValue(project));

boolean authenticated = false;
Map<String, String> params;
String queryParams = httpExchange.getRequestURI().getRawQuery();
if (queryParams != null && !queryParams.isEmpty()) {
// Why doesn't the URI API have this? That's stupid.
Map<String, String> params = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(queryParams);
String signature = params.get("sig");
String message = params.get("msg");
if (signature != null && message != null) {
signature = URLDecoder.decode(signature, "UTF-8");
message = URLDecoder.decode(message, "UTF-8");
log.info("Attempting to authenticate project owner");
project.authenticateOwner(message, signature); // throws SignatureException
log.info("... authenticated OK");
authenticated = true;
}
params = Splitter.on('&').trimResults().withKeyValueSeparator('=').split(queryParams);
} else {
params = new HashMap<>();
}

boolean authenticated = false;
String signature = params.get("sig");
String message = params.get("msg");
if (signature != null && message != null) {
signature = URLDecoder.decode(signature, "UTF-8");
message = URLDecoder.decode(message, "UTF-8");
log.info("Attempting to authenticate project owner");
project.authenticateOwner(message, signature); // throws SignatureException
log.info("... authenticated OK");
authenticated = true;
}

long totalPledged = 0;
Expand Down Expand Up @@ -182,8 +209,14 @@ private void statusDownload(HttpExchange httpExchange, Project project) throws I

status.setValuePledgedSoFar(totalPledged);
final LHProtos.ProjectStatus proto = status.build();
log.info("Replying with status: {}", proto);
byte[] bits = proto.toByteArray();
log.info("Replying using {} with status: {}", format, proto);
byte[] bits = null;
switch (format) {
case PBUF: bits = proto.toByteArray(); break;
case JSON: bits = JsonFormat.printToString(proto).getBytes(Charsets.UTF_8); break;
case HTML: bits = HtmlFormat.printToString(proto).getBytes(Charsets.UTF_8); break;
case XML: bits = XmlFormat.printToString(proto).getBytes(Charsets.UTF_8); break;
}
httpExchange.sendResponseHeaders(HTTP_OK, bits.length);
httpExchange.getResponseBody().write(bits);
httpExchange.close();
Expand Down

0 comments on commit 2a23f88

Please sign in to comment.