mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Apply code review suggestions part 2
This commit is contained in:
parent
27b78b6217
commit
593c0b8bb5
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode;
|
||||
package bisq.daonode;
|
||||
|
||||
|
||||
import bisq.core.app.TorSetup;
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode;
|
||||
package bisq.daonode;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
@ -28,17 +28,17 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import bisq.daoNode.endpoints.ProofOfBurnApi;
|
||||
import bisq.daoNode.error.CustomExceptionMapper;
|
||||
import bisq.daoNode.error.StatusException;
|
||||
import bisq.daoNode.util.StaticFileHandler;
|
||||
import bisq.daonode.endpoints.ProofOfBurnApi;
|
||||
import bisq.daonode.error.CustomExceptionMapper;
|
||||
import bisq.daonode.error.StatusException;
|
||||
import bisq.daonode.util.StaticFileHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
|
||||
/**
|
||||
* Application to start and config the rest service.
|
||||
* This creates and rest service at BASE_URL for clients to connect and for users to browse the documentation.
|
||||
* This creates a rest service for clients to connect and for users to browse the documentation.
|
||||
* <p>
|
||||
* Swagger doc are available at <a href="http://localhost:8082/doc/v1/index.html">REST API documentation</a>
|
||||
*/
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode;
|
||||
package bisq.daonode;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -60,9 +60,9 @@ public class SwaggerResolution {
|
||||
Reader reader = new Reader(configuration);
|
||||
OpenAPI openAPI = reader.read(application.getClasses());
|
||||
swaggerJson = Json.pretty(openAPI);
|
||||
} catch (RuntimeException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
throw e;
|
||||
} catch (Exception exception) {
|
||||
log.error("", exception);
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode.dto;
|
||||
package bisq.daonode.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode.endpoints;
|
||||
package bisq.daonode.endpoints;
|
||||
|
||||
import bisq.core.dao.state.model.blockchain.Tx;
|
||||
|
||||
@ -31,9 +31,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
|
||||
import bisq.daoNode.DaoNode;
|
||||
import bisq.daoNode.DaoNodeRestApiApplication;
|
||||
import bisq.daoNode.dto.ProofOfBurnDto;
|
||||
import bisq.daonode.DaoNode;
|
||||
import bisq.daonode.DaoNodeRestApiApplication;
|
||||
import bisq.daonode.dto.ProofOfBurnDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
@ -53,7 +53,7 @@ import jakarta.ws.rs.core.MediaType;
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Proof of burn API")
|
||||
public class ProofOfBurnApi {
|
||||
public static final String DESC_BLOCK_HEIGHT = "The block height from which we request the proof of burn data";
|
||||
private static final String DESC_BLOCK_HEIGHT = "The block height from which we request the proof of burn data";
|
||||
private final DaoNode daoNode;
|
||||
|
||||
public ProofOfBurnApi(@Context Application application) {
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode.error;
|
||||
package bisq.daonode.error;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -30,7 +30,7 @@ import jakarta.ws.rs.ext.Provider;
|
||||
public class CustomExceptionMapper implements ExceptionMapper<Exception> {
|
||||
@Override
|
||||
public Response toResponse(Exception exception) {
|
||||
log.error(exception.getMessage(), exception);
|
||||
log.error("", exception);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorMessage(exception.getMessage()))
|
||||
.build();
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode.error;
|
||||
package bisq.daonode.error;
|
||||
|
||||
import lombok.Getter;
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode.error;
|
||||
package bisq.daonode.error;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
@ -15,7 +15,7 @@
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daoNode.util;
|
||||
package bisq.daonode.util;
|
||||
|
||||
import java.net.URI;
|
||||
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daonodeOld.service;
|
||||
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import bisq.daonodeOld.web.WebServer;
|
||||
import bisq.daonodeOld.web.jdk.JdkServer;
|
||||
|
||||
// Todo We should limit usage to localhost as its not intended at that stage to be used
|
||||
// as a public API, but rather be used by Bisq 2 bridge clients or BSQ explorer nodes,
|
||||
// both running in a localhost environment. As long that holds, we do not require a high
|
||||
// level of protection against malicious usage.
|
||||
|
||||
// TODO This JDK http server is a super simple implementation. We might use some other
|
||||
// lightweight REST server framework.
|
||||
@Slf4j
|
||||
public class DaoNodeService {
|
||||
private WebServer webServer;
|
||||
private DaoStateService daoStateService;
|
||||
|
||||
public DaoNodeService(DaoStateService daoStateService) {
|
||||
this.daoStateService = daoStateService;
|
||||
}
|
||||
|
||||
public void start(int port) {
|
||||
try {
|
||||
webServer = new JdkServer(port, daoStateService);
|
||||
webServer.start();
|
||||
} catch (Throwable t) {
|
||||
log.error(t.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
if (webServer != null) {
|
||||
webServer.stop(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daonodeOld.web;
|
||||
|
||||
public interface WebServer {
|
||||
|
||||
void start();
|
||||
|
||||
void stop(int delay);
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daonodeOld.web.jdk;
|
||||
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.daonodeOld.web.jdk.handler.ResourcePathElement.DAONODE;
|
||||
|
||||
|
||||
|
||||
import bisq.daonodeOld.web.WebServer;
|
||||
import bisq.daonodeOld.web.jdk.handler.RestHandler;
|
||||
import com.sun.net.httpserver.HttpContext;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
// https://dev.to/piczmar_0/framework-less-rest-api-in-java-1jbl
|
||||
|
||||
// https://doc.networknt.com/getting-started/light-rest-4j
|
||||
// https://github.com/piczmar/pure-java-rest-api
|
||||
// https://stackoverflow.com/questions/3732109/simple-http-server-in-java-using-only-java-se-api
|
||||
// https://www.programcreek.com/java-api-examples/index.php?api=com.sun.net.httpserver.HttpServer
|
||||
|
||||
/**
|
||||
* From https://stackoverflow.com/questions/3732109/simple-http-server-in-java-using-only-java-se-api
|
||||
*
|
||||
* Note that this is, in contrary to what some developers think, absolutely not forbidden
|
||||
* by the well known FAQ Why Developers Should Not Write Programs That Call 'sun' Packages.
|
||||
* That FAQ concerns the sun.* package (such as sun.misc.BASE64Encoder) for internal usage
|
||||
* by the Oracle JRE (which would thus kill your application when you run it on a different
|
||||
* JRE), not the com.sun.* package. Sun/Oracle also just develop software on top of the
|
||||
* Java SE API themselves like as every other company such as Apache and so on. Moreover,
|
||||
* this specific HttpServer must be present in every JDK so there is absolutely no means
|
||||
* of "portability" issue like as would happen with sun.* package. Using com.sun.* classes
|
||||
* is only discouraged (but not forbidden) when it concerns an implementation of a certain
|
||||
* Java API, such as GlassFish (Java EE impl), Mojarra (JSF impl), Jersey (JAX-RS impl), etc.
|
||||
*/
|
||||
@Slf4j
|
||||
public class JdkServer extends HttpServer implements WebServer {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
WebServer webServer = new JdkServer(8080, null);
|
||||
webServer.start();
|
||||
Thread.sleep(40000);
|
||||
webServer.stop(0);
|
||||
}
|
||||
|
||||
private final int port;
|
||||
private final DaoStateService daoStateService;
|
||||
|
||||
private HttpServer server;
|
||||
|
||||
public JdkServer(int port, DaoStateService daoStateService) {
|
||||
this.port = port;
|
||||
this.daoStateService = daoStateService;
|
||||
configure();
|
||||
}
|
||||
|
||||
private void configure() {
|
||||
try {
|
||||
this.server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||
// As use case is intended for a 1 client environment we can stick with a single thread.
|
||||
setExecutor(Utilities.getSingleThreadExecutor("DaoNode-API"));
|
||||
// Map all request URLs starting with "/daonode" to a single RestHandler.
|
||||
// The RestHandler will pass valid requests on to an appropriate handler.
|
||||
createContext("/" + DAONODE, new RestHandler(daoStateService));
|
||||
} catch (IOException ex) {
|
||||
log.error(ex.toString());
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(InetSocketAddress addr, int backlog) throws IOException {
|
||||
server.bind(addr, backlog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
server.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExecutor(Executor executor) {
|
||||
server.setExecutor(executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getExecutor() {
|
||||
return server.getExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpContext createContext(String path, HttpHandler handler) {
|
||||
return server.createContext(path, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpContext createContext(String path) {
|
||||
return server.createContext(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeContext(String path) throws IllegalArgumentException {
|
||||
server.removeContext(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeContext(HttpContext context) {
|
||||
server.removeContext(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return server.getAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop(int delay) {
|
||||
if (server != null) {
|
||||
server.stop(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daonodeOld.web.jdk.handler;
|
||||
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.dao.state.model.blockchain.Tx;
|
||||
|
||||
import bisq.common.util.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.daonodeOld.web.jdk.handler.HandlerUtil.toJson;
|
||||
import static bisq.daonodeOld.web.jdk.handler.HandlerUtil.wrapErrorResponse;
|
||||
import static bisq.daonodeOld.web.jdk.handler.HandlerUtil.wrapResponse;
|
||||
import static bisq.daonodeOld.web.jdk.handler.ResourcePathElement.BLOCKHEIGHT;
|
||||
|
||||
|
||||
|
||||
import bisq.daoNode.dto.ProofOfBurnDto;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Handles daonode/proofofburn requests. Request URLs must match:
|
||||
* http://localhost:<port>/daonode/proofofburn/blockheight/<blockheight-value>
|
||||
*
|
||||
* Example: http://localhost:8080/daonode/proofofburn/blockheight/731270
|
||||
*/
|
||||
@Slf4j
|
||||
public class GetProofOfBurnHandler implements HttpHandler {
|
||||
|
||||
private final DaoStateService daoStateService;
|
||||
private final RequestSpec requestSpec;
|
||||
|
||||
/**
|
||||
* A new handler instance must be used for each request. We do not want to parse
|
||||
* details from each request URI more than once; they are passed to this constructor
|
||||
* from the RestHandler via the RequestSpec argument.
|
||||
*
|
||||
* @param daoStateService DaoStateService singleton
|
||||
* @param requestSpec RESTful request details, including parsed URL parameters
|
||||
*/
|
||||
public GetProofOfBurnHandler(DaoStateService daoStateService, RequestSpec requestSpec) {
|
||||
this.daoStateService = daoStateService;
|
||||
this.requestSpec = requestSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange httpExchange) throws IOException {
|
||||
try {
|
||||
if (daoStateService == null) {
|
||||
log.warn("DAO Node daoStateService is null; OK during web server dev/test.");
|
||||
HandlerUtil.sendResponse(httpExchange, wrapResponse("[]"));
|
||||
} else {
|
||||
int blockHeight = requestSpec.getIntParam(BLOCKHEIGHT);
|
||||
log.info("Requesting POB for blockheight {}.", blockHeight);
|
||||
List<ProofOfBurnDto> list = getProofOfBurnDtoList(blockHeight);
|
||||
if (list != null) {
|
||||
HandlerUtil.sendResponse(httpExchange, toJson(list));
|
||||
} else {
|
||||
log.error("DAO Node Proof of Burn data for blockHeight {} is null.", blockHeight);
|
||||
HandlerUtil.sendResponse(500,
|
||||
httpExchange,
|
||||
wrapErrorResponse(toJson("DAO Node proof of burn data is null.")));
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
HandlerUtil.sendResponse(500,
|
||||
httpExchange,
|
||||
wrapErrorResponse(toJson(ex.getMessage())));
|
||||
}
|
||||
}
|
||||
|
||||
private List<ProofOfBurnDto> getProofOfBurnDtoList(int fromBlockHeight) {
|
||||
return daoStateService.getProofOfBurnTxs().stream()
|
||||
.filter(tx -> tx.getBlockHeight() >= fromBlockHeight)
|
||||
.map(tx -> new ProofOfBurnDto(tx.getId(),
|
||||
tx.getBurntBsq(),
|
||||
tx.getBlockHeight(),
|
||||
tx.getTime(),
|
||||
getHash(tx)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// We strip out the version bytes
|
||||
private String getHash(Tx tx) {
|
||||
byte[] opReturnData = tx.getLastTxOutput().getOpReturnData();
|
||||
if (opReturnData == null) {
|
||||
return "";
|
||||
}
|
||||
return Hex.encode(Arrays.copyOfRange(opReturnData, 2, 22));
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daonodeOld.web.jdk.handler;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
public class HandlerUtil {
|
||||
|
||||
public static void setDefaultResponseHeaders(HttpExchange httpExchange) {
|
||||
httpExchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||
}
|
||||
|
||||
public static void sendResponse(HttpExchange httpExchange, String response) throws IOException {
|
||||
sendResponse(200, httpExchange, response);
|
||||
}
|
||||
|
||||
public static void sendResponse(int status, HttpExchange httpExchange, String response) throws IOException {
|
||||
setDefaultResponseHeaders(httpExchange);
|
||||
|
||||
byte[] responseBytes = response.getBytes(UTF_8);
|
||||
httpExchange.sendResponseHeaders(status, responseBytes.length);
|
||||
OutputStream os = httpExchange.getResponseBody();
|
||||
os.write(responseBytes);
|
||||
os.close();
|
||||
}
|
||||
|
||||
// TODO make as function toWhat?
|
||||
public static String wrapResponse(String jsonData) {
|
||||
return format("{\"data\":%s}", jsonData);
|
||||
}
|
||||
|
||||
// TODO make as function toErrorWhat?
|
||||
public static String wrapErrorResponse(String jsonError) {
|
||||
return format("{\"error\":%s}", jsonError);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String toJson(Object object) {
|
||||
return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(object);
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daonodeOld.web.jdk.handler;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.arraycopy;
|
||||
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
/**
|
||||
* REST request URI parser to extract parameter names and values from an HttpExchange.
|
||||
*
|
||||
* Splits the HttpExchange's request URI into a String[] of pathElements identifying a
|
||||
* (REST) service name, a resource name, and any parameter/value pairs.
|
||||
*
|
||||
* This class is limited to URIs adhering a specific pattern:
|
||||
* pathElements[0] = service-name, e.g., daonode
|
||||
* pathElements[1] = resource-name, e.g., proofofburn
|
||||
* pathElements[2, 3...N, N+1] = param-name/value pairs.
|
||||
*
|
||||
* For example, request URL http://localhost:8080/daonode/proofofburn/blockheight/731270
|
||||
* identifies service-name "daonode", resource-name "proofofburn", and one parameter
|
||||
* "blockheight" with value 731270.
|
||||
*/
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class RequestSpec {
|
||||
|
||||
private final HttpExchange httpExchange;
|
||||
private final String method;
|
||||
private final URI requestURI;
|
||||
private final String[] pathElements;
|
||||
private final String serviceName;
|
||||
private final String resourceName;
|
||||
private final Map<String, String> parametersByName;
|
||||
|
||||
public RequestSpec(HttpExchange httpExchange) {
|
||||
this.httpExchange = httpExchange;
|
||||
this.method = httpExchange.getRequestMethod();
|
||||
this.requestURI = httpExchange.getRequestURI();
|
||||
this.pathElements = toPathElements.apply(requestURI);
|
||||
this.serviceName = pathElements[0];
|
||||
this.resourceName = pathElements[1];
|
||||
try {
|
||||
this.parametersByName = getParametersByName();
|
||||
} catch (URISyntaxException ex) {
|
||||
// OK to throw ex in this constructor?
|
||||
log.error(ex.toString());
|
||||
throw new IllegalArgumentException(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRequestingResource(String resourceName) {
|
||||
return this.resourceName.equalsIgnoreCase(resourceName);
|
||||
}
|
||||
|
||||
public String getStringParam(String paramName) {
|
||||
if (parametersByName.containsKey(paramName))
|
||||
return parametersByName.get(paramName);
|
||||
else
|
||||
throw new IllegalArgumentException(format("Parameter '%s' not found.", paramName));
|
||||
}
|
||||
|
||||
public int getIntParam(String paramName) {
|
||||
if (parametersByName.containsKey(paramName)) {
|
||||
var value = parametersByName.get(paramName);
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
format("Parameter '%s' value '%s' is not a number.",
|
||||
paramName,
|
||||
value));
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(format("Parameter '%s' not found.", paramName));
|
||||
}
|
||||
}
|
||||
|
||||
private final Function<URI, String[]> toPathElements = (uri) -> {
|
||||
String[] raw = uri.getPath().split("/");
|
||||
String[] elements = new String[raw.length - 1];
|
||||
arraycopy(raw, 1, elements, 0, elements.length);
|
||||
return elements;
|
||||
};
|
||||
|
||||
private Map<String, String> getParametersByName() throws URISyntaxException {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if (pathElements.length == 2)
|
||||
return params; // There are no parameter name/value pairs in url.
|
||||
|
||||
// All pathElements beyond index 1 should be param-name/value pairs, and
|
||||
// a param-value must follow each param-name.
|
||||
Predicate<Integer> paramValueExists = (i) -> (i + 1) < pathElements.length;
|
||||
for (int i = 2; i < pathElements.length; i++) {
|
||||
String name = pathElements[i];
|
||||
if (paramValueExists.test(i))
|
||||
params.put(name, pathElements[++i]);
|
||||
else
|
||||
throw new URISyntaxException(requestURI.getPath(),
|
||||
format("No value found for parameter with name '%s'.", name),
|
||||
-1);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daonodeOld.web.jdk.handler;
|
||||
|
||||
public class ResourcePathElement {
|
||||
public static String DAONODE = "daonode";
|
||||
public static final String BLOCKHEIGHT = "blockheight";
|
||||
public static final String PROOFOFBURN = "proofofburn";
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.daonodeOld.web.jdk.handler;
|
||||
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.daonodeOld.web.jdk.handler.HandlerUtil.sendResponse;
|
||||
import static bisq.daonodeOld.web.jdk.handler.HandlerUtil.toJson;
|
||||
import static bisq.daonodeOld.web.jdk.handler.HandlerUtil.wrapErrorResponse;
|
||||
import static bisq.daonodeOld.web.jdk.handler.ResourcePathElement.PROOFOFBURN;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
/**
|
||||
* All HTTP requests are routed to a singleton RestHandler, then passed on to
|
||||
* appropriate sub handler instances.
|
||||
*/
|
||||
@Slf4j
|
||||
public class RestHandler implements HttpHandler {
|
||||
|
||||
private final DaoStateService daoStateService;
|
||||
|
||||
public RestHandler(DaoStateService daoStateService) {
|
||||
this.daoStateService = daoStateService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange httpExchange) throws IOException {
|
||||
try {
|
||||
requireNonNull(httpExchange, "HttpExchange cannot be null.");
|
||||
|
||||
// Parse the request URI details here, and pass them to a new
|
||||
// sub handler instance.
|
||||
RequestSpec requestSpec = new RequestSpec(httpExchange);
|
||||
|
||||
// Atm we only use GET, no plans yet for allow POST for DaoNode API.
|
||||
if (!requestSpec.getMethod().equals("GET")) {
|
||||
sendResponse(405,
|
||||
httpExchange,
|
||||
wrapErrorResponse(toJson("Forbidden HTTP method " + requestSpec.getMethod())));
|
||||
} else if (requestSpec.isRequestingResource(PROOFOFBURN)) {
|
||||
new GetProofOfBurnHandler(daoStateService, requestSpec).handle(httpExchange);
|
||||
} else {
|
||||
sendResponse(404, httpExchange, wrapErrorResponse(toJson("Not Found")));
|
||||
}
|
||||
|
||||
} catch (RuntimeException ex) {
|
||||
sendResponse(500,
|
||||
httpExchange,
|
||||
wrapErrorResponse(toJson(ex.getMessage())));
|
||||
} finally {
|
||||
httpExchange.close();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user