Cleaning repo

This commit is contained in:
Alexis Delhaie
2020-09-16 18:49:40 +02:00
commit af3203a1e2
25 changed files with 2131 additions and 0 deletions

6
src/META-INF/MANIFEST.MF Normal file
View File

@@ -0,0 +1,6 @@
Manifest-Version: 1.0
Main-Class: ovh.alexisdelhaie.endpoint.Main
Class-Path: src.zip javafx-swt.jar javafx.web.jar javafx.base.jar javafx
.fxml.jar javafx.media.jar javafx.swing.jar javafx.controls.jar javafx.
graphics.jar

View File

@@ -0,0 +1,413 @@
package ovh.alexisdelhaie.endpoint;
import javafx.application.Platform;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
import ovh.alexisdelhaie.endpoint.http.HttpClient;
import ovh.alexisdelhaie.endpoint.http.Request;
import ovh.alexisdelhaie.endpoint.http.RequestBuilder;
import ovh.alexisdelhaie.endpoint.http.Response;
import ovh.alexisdelhaie.endpoint.impl.EditCell;
import ovh.alexisdelhaie.endpoint.model.Param;
import java.io.IOException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Function;
public class Controller implements Initializable {
private enum StatusColor {
INFORMATION("#53baf5"),
SUCCESS("#7ccf16"),
REDIRECTION("#b153f5"),
ERROR_CLIENT("#f5ca53"),
ERROR_SERVER("#f55353"),
DEFAULT("BLACK");
private final String hex;
StatusColor(String s) {
hex = s;
}
public String getHex() {
return hex;
}
}
private enum RequestTab {
PARAMS(0),
AUTHORIZATION(1),
HEADERS(2),
BODY(3),
RESPONSE(4);
private final int index;
RequestTab (int i) { index = i; }
public int getIndex() { return index; }
}
@FXML
private ChoiceBox<String> httpMethod;
@FXML
private TabPane tabs;
@FXML
private TextField requestInput;
@FXML
private Pane runningIndicatorPane;
private Stage primaryStage;
private HashMap<Integer, String> requests;
@Override
public void initialize(URL location, ResourceBundle resources) {
requests = new HashMap<>();
String[] method = { "GET", "POST", "HEAD", "PUT", "DELETE" };
httpMethod.setItems(FXCollections.observableArrayList(method));
httpMethod.setValue(method[0]);
tabs.getSelectionModel().selectedItemProperty().addListener(
(ov, t, t1) -> requestInput.setText((t1 != null) ? requests.get(t1.hashCode()) : "")
);
createNewTab();
}
private Tab newTab() {
SplitPane sp = new SplitPane();
Tab params = new Tab("Params", createParamTable());
Tab auth = new Tab("Authorization");
Tab headers = new Tab("Headers", createParamTable());
Tab body = new Tab("Body", new TextArea());
Tab response = new Tab("Response", createResponseTab());
TabPane options = new TabPane();
TextArea ta = new TextArea();
options.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
options.getTabs().addAll(params, auth, headers, body, response);
sp.getItems().add(options);
sp.getItems().add(ta);
sp.setOrientation(Orientation.VERTICAL);
return new Tab("untitled", sp);
}
@SuppressWarnings("unchecked")
private ScrollPane createResponseTab() {
ScrollPane sp = new ScrollPane();
Pane p = new Pane();
sp.setContent(p);
try {
Parent xml = FXMLLoader.load(getClass().getResource("responsetab.fxml"));
p.getChildren().add(xml);
TableView<Param> headers = (TableView<Param>) p.lookup("#headers");
headers.getColumns().get(0).setCellValueFactory(new PropertyValueFactory<>("name"));
headers.getColumns().get(1).setCellValueFactory(new PropertyValueFactory<>("value"));
} catch (IOException e) {
System.err.println(e.getMessage());
}
return sp;
}
private TableView<Param> createParamTable() {
TableView<Param> paramsTable = new TableView<>();
TableColumn<Param, String> name = createColumn("Name", Param::name);
TableColumn<Param, String> value = createColumn("Value", Param::value);
name.prefWidthProperty().bind(paramsTable.widthProperty().multiply(0.5));
value.prefWidthProperty().bind(paramsTable.widthProperty().multiply(0.5));
name.setOnEditCommit(t -> {
TableView<Param> tv = t.getTableView();
t.getRowValue().name().set(t.getNewValue());
manageEmptyCells(t, tv);
});
value.setOnEditCommit(t -> {
TableView<Param> tv = t.getTableView();
t.getRowValue().value().set(t.getNewValue());
manageEmptyCells(t, tv);
});
paramsTable.getColumns().add(name);
paramsTable.getColumns().add(value);
paramsTable.setEditable(true);
paramsTable.getItems().add(new Param());
return paramsTable;
}
private void manageEmptyCells(TableColumn.CellEditEvent<Param, String> t, TableView<Param> tv) {
if (t.getRowValue().isEmpty() && tv.getItems().size() > 1) {
tv.getItems().remove(t.getRowValue());
}
if (!tv.getItems().get(tv.getItems().size() - 1).isEmpty()) {
tv.getItems().add(new Param());
}
requestInput.setText(URLGenerator.processNewUrl(getParamsMap(), requestInput.getText()));
}
private TableColumn<Param, String> createColumn(String title, Function<Param, StringProperty> property) {
TableColumn<Param, String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setCellFactory(column -> EditCell.createStringEditCell());
return col ;
}
@FXML
private void start() {
runningIndicatorPane.setVisible(true);
new Thread(() -> {
final String method = httpMethod.getValue();
Optional<TextArea> textArea = getCurrentTextArea();
Optional<Response> response = Optional.empty();
if (textArea.isPresent()) {
try {
Request r = new RequestBuilder(requestInput.getText())
.setCustomHeaders(getCustomHeaders())
.build();
HttpClient hc = new HttpClient();
switch (method) {
case "GET" -> response = hc.get(r);
case "POST" -> response = hc.post(r, getBody());
case "PUT" -> response = hc.put(r, getBody());
case "DELETE" -> response = hc.delete(r);
case "HEAD" -> response = hc.head(r);
}
if (response.isPresent()) {
final Response res = response.get();
Platform.runLater(() -> {
textArea.get().setStyle(null);
textArea.get().setText(res.getBody());
updateResponseTab(res);
setSelectedTab(RequestTab.RESPONSE);
});
}
} catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
System.err.println(e.getMessage());
e.printStackTrace();
textArea.ifPresent(area -> Platform.runLater(() -> {
resetResponseTab();
area.setStyle("-fx-text-fill: red");
area.setText("Somethings went wrong: " + e.getMessage());
}));
} finally {
Platform.runLater(() -> {
runningIndicatorPane.setVisible(false);
});
}
} else {
Platform.runLater(() -> {
runningIndicatorPane.setVisible(false);
});
}
}).start();
}
@SuppressWarnings("unchecked")
private void updateResponseTab(Response res) {
Optional<Tab> responseTab = getCurrentResponseTab();
if (responseTab.isPresent()) {
Label status = (Label) responseTab.get().getContent().lookup("#status");
status.setText(String.format("%s %s", res.getStatusCode(), res.getStatus()));
if (res.getStatusCode() >= 100 && res.getStatusCode() < 200) {
status.setTextFill(Color.web(StatusColor.INFORMATION.getHex()));
} else if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
status.setTextFill(Color.web(StatusColor.SUCCESS.getHex()));
} else if (res.getStatusCode() >= 300 && res.getStatusCode() < 400) {
status.setTextFill(Color.web(StatusColor.REDIRECTION.getHex()));
} else if (res.getStatusCode() >= 400 && res.getStatusCode() < 500) {
status.setTextFill(Color.web(StatusColor.ERROR_CLIENT.getHex()));
} else if (res.getStatusCode() >= 500) {
status.setTextFill(Color.web(StatusColor.ERROR_SERVER.getHex()));
}
Label time = (Label) responseTab.get().getContent().lookup("#time");
time.setText(String.format("%s ms", res.getTime()));
TextArea raw = (TextArea) responseTab.get().getContent().lookup("#raw");
raw.setText(res.getRawResponse());
TextArea request = (TextArea) responseTab.get().getContent().lookup("#request");
request.setText(res.getRequest().getRawRequest());
TableView<Param> headers = (TableView<Param>) responseTab.get().getContent().lookup("#headers");
headers.getItems().clear();
for (Map.Entry<String, String> entry : res.getHeaders().entrySet()) {
headers.getItems().add(new Param(entry.getKey(), entry.getValue()));
}
}
}
private void setSelectedTab(RequestTab rt) {
Optional<TabPane> options = getRequestOptionsTab();
options.ifPresent(tabPane -> tabPane.getSelectionModel().select(rt.getIndex()));
}
@SuppressWarnings("unchecked")
private void resetResponseTab() {
Optional<Tab> responseTab = getCurrentResponseTab();
if (responseTab.isPresent()) {
Label status = (Label) responseTab.get().getContent().lookup("#status");
status.setTextFill(Color.web(StatusColor.DEFAULT.getHex()));
status.setText("...");
Label time = (Label) responseTab.get().getContent().lookup("#time");
time.setText("... ms");
TextArea raw = (TextArea) responseTab.get().getContent().lookup("#raw");
raw.setText("");
TableView<Param> headers = (TableView<Param>) responseTab.get().getContent().lookup("#headers");
headers.getItems().clear();
}
}
private HashMap<String, String> getCustomHeaders() {
HashMap<String, String> result = new HashMap<>();
Optional<TabPane> tabs = getRequestOptionsTab();
if (tabs.isPresent()) {
Node n = tabs.get().getTabs().get(RequestTab.HEADERS.getIndex()).getContent();
toHashMap(result, n, true);
}
return result;
}
private String getBody() {
Optional<TabPane> tabs = getRequestOptionsTab();
if (tabs.isPresent()) {
Node n = tabs.get().getTabs().get(RequestTab.BODY.getIndex()).getContent();
if (n instanceof TextArea) {
return ((TextArea) n).getText();
}
}
return "";
}
private HashMap<String, String> getParamsMap() {
HashMap<String, String> result = new HashMap<>();
Optional<TabPane> tabs = getRequestOptionsTab();
if (tabs.isPresent()) {
Node n = tabs.get().getTabs().get(RequestTab.PARAMS.getIndex()).getContent();
toHashMap(result, n, false);
}
return result;
}
@SuppressWarnings("unchecked")
private void toHashMap(HashMap<String, String> result, Node n, boolean lowering) {
if (n instanceof TableView) {
TableView<Param> tv = (TableView<Param>) n;
for (Param p : tv.getItems()) {
if (!p.isEmpty()) {
String key = (lowering) ? p.getName().toLowerCase() : p.getName();
result.put(key, p.getValue());
}
}
}
}
private Optional<TabPane> getRequestOptionsTab() {
Optional<SplitPane> requestTab = getCurrentRequestTab();
if (requestTab.isPresent()) {
Node n = requestTab.get().getItems().get(0);
if (n instanceof TabPane) {
return Optional.of((TabPane) n);
}
}
return Optional.empty();
}
private Optional<SplitPane> getCurrentRequestTab() {
Node n = tabs.getSelectionModel().getSelectedItem().getContent();
if (n instanceof SplitPane) {
return Optional.of((SplitPane) n);
}
return Optional.empty();
}
private Optional<TextArea> getCurrentTextArea() {
Optional<SplitPane> requestTab = getCurrentRequestTab();
if (requestTab.isPresent()) {
Node n = requestTab.get().getItems().get(1);//
if (n instanceof TextArea) {
return Optional.of((TextArea) n);
}
}
return Optional.empty();
}
private Optional<Tab> getCurrentResponseTab() {
Optional<TabPane> requestTab = getRequestOptionsTab();
if (requestTab.isPresent()) {
Tab n = requestTab.get().getTabs().get(RequestTab.RESPONSE.getIndex());
return Optional.of(n);
}
return Optional.empty();
}
@FXML
private void createNewTab() {
new Thread(() -> {
Tab t = newTab();
requests.put(t.hashCode(), "");
Platform.runLater(() -> {
tabs.getTabs().add(t);
tabs.getSelectionModel().select(t);
});
}).start();
}
public void setStageAndSetupListeners(Stage s) {
primaryStage = s;
}
@FXML
private void showAboutDialog() {
try {
Stage dialog = new Stage();
Parent xml = FXMLLoader.load(getClass().getResource("about.fxml"));
dialog.initOwner(primaryStage);
dialog.setScene(new Scene(xml, 677, 365));
dialog.setMaxHeight(365);
dialog.setMinHeight(365);
dialog.setMaxWidth(707);
dialog.setMinWidth(707);
dialog.setResizable(false);
dialog.setTitle("About EndPoint");
dialog.getIcons().add( new Image(
Controller.class.getResourceAsStream( "icon.png" )));
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.showAndWait();
} catch (IOException e) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Cannot initialize About");
alert.setHeaderText("There was an error while initializing this dialog");
alert.setContentText(e.getMessage());
alert.showAndWait();
}
}
@FXML
private void requestInputOnKeyPressed() {
Tab tab = tabs.getSelectionModel().getSelectedItem();
requests.put(tab.hashCode(), requestInput.getText());
}
}

View File

@@ -0,0 +1,32 @@
package ovh.alexisdelhaie.endpoint;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("mainwindow.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
controller.setStageAndSetupListeners(primaryStage);
primaryStage.setTitle("EndPoint");
primaryStage.setScene(new Scene(root, 1067, 644));
primaryStage.setMinWidth(1067);
primaryStage.setMinHeight(644);
primaryStage.setMaximized(true);
primaryStage.getIcons().add( new Image(
Main.class.getResourceAsStream( "icon.png" )));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

View File

@@ -0,0 +1,99 @@
package ovh.alexisdelhaie.endpoint;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class URLGenerator {
public static final String HTTP_START = "http://";
public static final String HTTPS_START = "https://";
public static final String[][] SPECIAL_CHARS = new String[][] {
{ " ", "%20" }, { "<", "%3C" }, { ">", "%3E" },
{ "#", "%23" }, { "{", "%7B" },
{ "}", "%7D" }, { "|", "%7C" }, { "\\", "%5C" },
{ "^", "%5E" }, { "~", "%7E" }, { "[", "%5B" },
{ "]", "%5D" }, { "`", "%60" }, { ";", "%3B" },
{ "/", "%2F" }, { "?", "%3F" }, { ":", "%3A" },
{ "@", "%40" }, { "=", "%3D" }, { "&", "%26" },
{ "$", "%24" }, { "\r", "%0D" }, { "\n", "%0A" }
};
static public String processNewUrl(HashMap<String, String> p, String url) {
try {
if (!url.startsWith(HTTP_START) && !url.startsWith(HTTPS_START)) {
url = HTTP_START + url;
}
String newUrl = (url.startsWith(HTTPS_START)) ? HTTPS_START : HTTP_START;
URL u = new URL(url);
newUrl += u.getHost();
if (u.getPort() != -1) {
newUrl += ":" + u.getPort();
}
newUrl += (u.getPath().isBlank()) ? "/" : u.getPath();
newUrl += generateParamsPart(p);
return newUrl;
} catch (MalformedURLException e) {
System.err.println(e.getMessage());
}
return url;
}
public static Map<String, String> getSpecialCharRef() {
return Stream.of(SPECIAL_CHARS)
.collect(Collectors.toMap(data -> (String) data[0], data -> (String) data[1]));
}
static public String generateParamsPartWithEncoding(HashMap<String, String> p) {
String result = "?";
for (Map.Entry<String, String> entry : p.entrySet()) {
result = new StringBuilder(result)
.append(entry.getKey())
.append("=")
.append(entry.getValue())
.append("&").toString();
}
if (result.equals("?")) {
result = "";
} else if (result.endsWith("&")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
static public String generateParamsPart(HashMap<String, String> p) {
Map<String, String> specialCharRef = getSpecialCharRef();
String result = "?";
for (Map.Entry<String, String> entry : p.entrySet()) {
String key = escapeText(entry.getKey(), specialCharRef);
String value = escapeText(entry.getValue(), specialCharRef);
result = new StringBuilder(result)
.append(key)
.append("=")
.append(value)
.append("&").toString();
}
if (result.equals("?")) {
result = "";
} else if (result.endsWith("&")) {
result = result.substring(0, result.length() - 1);
}
return result;
}
static public String escapeText(String s, Map<String, String> scr) {
String result = s.replace("%", "%25");
for (Map.Entry<String, String> entry : scr.entrySet()) {
if (result.contains(entry.getKey())) {
result = result.replace(entry.getKey(), entry.getValue());
}
}
return result;
}
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="365.0" prefWidth="707.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label layoutX="25.0" layoutY="183.0" text="Made with Java 14.0.2 (GUI with JavaFX 15)">
<font>
<Font size="13.0" />
</font></Label>
<Label layoutX="25.0" layoutY="147.0" text="Author: Alexis Delhaie (@alexlegarnd)">
<font>
<Font size="13.0" />
</font></Label>
<ImageView fitHeight="111.0" fitWidth="707.0" pickOnBounds="true" preserveRatio="true" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<image>
<Image url="@banner.png" />
</image></ImageView>
<Label layoutX="25.0" layoutY="220.0" text="Installer made with Delphi 10.3 Community">
<font>
<Font size="13.0" />
</font>
</Label>
<Label layoutX="25.0" layoutY="257.0" text="Installer Bootstrap made with Python 3">
<font>
<Font size="13.0" />
</font>
</Label>
<Label layoutX="422.0" layoutY="158.0" text="EndPoint">
<font>
<Font size="48.0" />
</font>
</Label>
<Label layoutX="444.0" layoutY="230.0" text="Version 0.1 (Not finished)">
<font>
<Font size="13.0" />
</font>
</Label>
</children>
</AnchorPane>

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>
<AnchorPane prefHeight="556.0" prefWidth="410.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ovh.alexisdelhaie.endpoint.Configuration">
<children>
<Label layoutX="12.0" layoutY="11.0" text="SSL">
<font>
<Font name="System Bold" size="12.0" />
</font>
</Label>
<Label layoutX="13.0" layoutY="35.0" text="Allow invalid SSL certificate" />
<CheckBox layoutX="381.0" layoutY="31.0" mnemonicParsing="false" />
<Line endX="100.0" layoutX="271.0" layoutY="48.0" startX="-100.0" stroke="#bfbfbf" strokeLineCap="ROUND" />
</children>
</AnchorPane>

View File

@@ -0,0 +1,135 @@
package ovh.alexisdelhaie.endpoint.http;
import javax.net.ssl.*;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
public class HttpClient {
public final static String CRLF = "\r\n";
public final static int DEFAULT_TIMEOUT = 10000;
private boolean allowInvalidSsl;
public HttpClient() { this(false); }
public HttpClient(boolean allowInvalidSsl) { this.allowInvalidSsl = allowInvalidSsl; }
public Optional<Response> get(Request r) throws IOException, KeyManagementException, NoSuchAlgorithmException {
return process("GET", r, "");
}
public Optional<Response> post(Request r, String body) throws IOException, KeyManagementException, NoSuchAlgorithmException {
if (!r.getCustomHeaders().containsKey("content-length")) {
r.getCustomHeaders().put("content-length", String.valueOf(body.length()));
}
return process("POST", r, body);
}
public Optional<Response> put(Request r, String body) throws IOException, KeyManagementException, NoSuchAlgorithmException {
if (!r.getCustomHeaders().containsKey("content-length")) {
r.getCustomHeaders().put("content-length", String.valueOf(body.length()));
}
return process("PUT", r, body);
}
public Optional<Response> delete(Request r) throws IOException, KeyManagementException, NoSuchAlgorithmException {
return process("DELETE", r, "");
}
public Optional<Response> head(Request r) throws IOException, KeyManagementException, NoSuchAlgorithmException {
return process("HEAD", r, "");
}
private Optional<Response> process(String method, Request r, String body) throws IOException, NoSuchAlgorithmException, KeyManagementException {
String headers = buildHeaders(method, r);
Socket s = (r.getScheme().equals("https")) ?
buildSSLSocket(resolve(r.getHost()).getHostAddress(), r.getPort())
: buildSocket(resolve(r.getHost()).getHostAddress(), r.getPort());
String request = (method.equals("POST") || method.equals("PUT")) ?
new StringBuilder(headers).append(body).toString() : headers;
if (s.isConnected()) {
Instant start = Instant.now();
try {
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
bos.write(request.getBytes(StandardCharsets.UTF_8));
bos.flush();
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
byte[] b = bis.readAllBytes();
Instant end = Instant.now();
long time = end.toEpochMilli() - start.toEpochMilli();
r.setRawRequest(request);
return Optional.of(new Response(b, time, r));
} finally {
s.close();
}
}
return Optional.empty();
}
private InetAddress resolve(String host) throws UnknownHostException {
InetAddress[] addresses = InetAddress.getAllByName(host);
return addresses[0];
}
private Socket buildSocket(String host, int port) throws IOException {
Socket s = new Socket(host, port);
s.setKeepAlive(false);
s.setSoTimeout(DEFAULT_TIMEOUT);
return s;
}
private Socket buildSSLSocket(String host, int port) throws IOException, KeyManagementException, NoSuchAlgorithmException {
SSLSocketFactory factory;
if(!allowInvalidSsl) {
factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
} else {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {return null;}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType){}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType){}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
factory = sc.getSocketFactory();
}
Socket s = factory.createSocket(host, port);
((SSLSocket)s).setEnabledProtocols(new String[] { "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" });
s.setKeepAlive(false);
s.setSoTimeout(DEFAULT_TIMEOUT);
((SSLSocket)s).startHandshake();
return s;
}
private String buildHeaders(String method, Request r) {
Map<String, String> custom = r.getCustomHeaders();
String path = (r.getParams().isEmpty()) ?
r.getPath() : r.getPathWithParams();
final StringBuilder sb = new StringBuilder(method).append(" ").append(path)
.append(" HTTP/1.1").append(CRLF);
sb.append("host: ").append(r.getHost()).append(":").append(r.getPort()).append(CRLF);
sb.append("connection: close").append(CRLF);
if (!custom.containsKey("accept")) {
sb.append("accept: */*").append(CRLF);
}
if (!custom.containsKey("user-agent")) {
sb.append("user-agent: endpoint/1.0").append(CRLF);
}
for (Map.Entry<String, String> h : custom.entrySet()) {
sb.append(h.getKey()).append(": ").append(h.getValue()).append(CRLF);
}
return sb.append(CRLF).toString();
}
}

View File

@@ -0,0 +1,102 @@
package ovh.alexisdelhaie.endpoint.http;
import ovh.alexisdelhaie.endpoint.URLGenerator;
import java.util.HashMap;
import java.util.Objects;
public class Request {
private String host;
private String scheme;
private String path;
private int port;
private HashMap<String, String> params;
private HashMap<String, String> customHeaders;
private String body;
private String rawRequest;
Request(String host, String scheme, String path, int port,
HashMap<String, String> params, HashMap<String, String> customHeaders, String body) {
this.host = host;
this.scheme = scheme;
this.path = path;
this.port = port;
this.params = params;
this.customHeaders = customHeaders;
this.body = body;
}
public String getHost() {
return host;
}
public String getScheme() {
return scheme;
}
public String getPath() {
return path;
}
public String getPathWithParams() {
return String.format("%s%s", path, URLGenerator.generateParamsPartWithEncoding(params));
}
public int getPort() {
return port;
}
public HashMap<String, String> getParams() {
return params;
}
public HashMap<String, String> getCustomHeaders() {
return customHeaders;
}
public String getBody() {
return body;
}
public String getRawRequest() {
return rawRequest;
}
void setRawRequest(String r) {
rawRequest = r;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Request request = (Request) o;
return getPort() == request.getPort() &&
Objects.equals(getHost(), request.getHost()) &&
Objects.equals(getScheme(), request.getScheme()) &&
Objects.equals(getPath(), request.getPath()) &&
Objects.equals(getParams(), request.getParams()) &&
Objects.equals(getCustomHeaders(), request.getCustomHeaders()) &&
Objects.equals(getBody(), request.getBody());
}
@Override
public int hashCode() {
return Objects.hash(getHost(), getScheme(), getPath(), getPort(), getParams(), getCustomHeaders(), getBody());
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Request{");
sb.append("scheme='").append(scheme).append('\'');
sb.append(", host='").append(host).append('\'');
sb.append(", port=").append(port);
sb.append(", path='").append(path).append('\'');
sb.append(", params=").append(params);
sb.append(", customHeaders=").append(customHeaders);
sb.append(", body='").append(body).append('\'');
sb.append('}');
return sb.toString();
}
}

View File

@@ -0,0 +1,94 @@
package ovh.alexisdelhaie.endpoint.http;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class RequestBuilder {
public static int HTTP_PORT = 80;
public static int HTTPS_PORT = 443;
public static String HTTP_SCHEME = "http";
public static String HTTPS_SCHEME = "https";
private String host;
private String scheme;
private String path;
private int port;
private HashMap<String, String> params;
private HashMap<String, String> customHeaders;
private String body;
public RequestBuilder (String url) throws MalformedURLException {
params = new HashMap<>();
customHeaders = new HashMap<>();
scheme = (url.toLowerCase().startsWith("https://")) ? HTTPS_SCHEME : HTTP_SCHEME;
url = (!url.toLowerCase().startsWith("https://")
&& !url.toLowerCase().startsWith("http://")) ? "http://" + url : url;
URL u = new URL(url);
host = u.getHost();
path = (u.getPath().isBlank()) ? "/" : u.getPath();
port = u.getPort();
if (port == -1) {
port = (scheme.equals(HTTPS_SCHEME)) ? HTTPS_PORT : HTTP_PORT;
}
parseParams(url);
}
private void parseParams(String url) {
String[] p = url.split("[?]");
if (p.length == 2) {
String[] couples = p[1].split("[&]");
for (String c: couples) {
String[] v = c.split("[=]");
if (v.length == 2) {
params.put(v[0], v[1]);
}
}
}
}
public RequestBuilder setHost(String host) {
this.host = host;
return this;
}
public RequestBuilder setScheme(String scheme) {
this.scheme = scheme;
return this;
}
public RequestBuilder setPort(int port) {
this.port = port;
return this;
}
public RequestBuilder setBody(String body) {
this.body = body;
return this;
}
public RequestBuilder setPath(String path) {
this.path = path;
return this;
}
public RequestBuilder setCustomHeaders(HashMap<String, String> custom) {
this.customHeaders.clear();
for (Map.Entry<String, String> entry : custom.entrySet()) {
this.customHeaders.put(entry.getKey().toLowerCase(), entry.getValue());
}
return this;
}
public RequestBuilder addCustomHeader(String key, String value) {
this.customHeaders.put(key.toLowerCase(), value);
return this;
}
public Request build() {
return new Request(host, scheme, path, port, params, customHeaders, body);
}
}

View File

@@ -0,0 +1,130 @@
package ovh.alexisdelhaie.endpoint.http;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Response {
public final static String CRLF = "\r\n";
public final static String DOUBLE_CRLF = "\r\n\r\n";
private HashMap<String, String> headers;
private String rawHeaders;
private String rawResponse;
private String body;
private int statusCode;
private String status;
private long time;
private Request request;
Response(byte[] res, long time, Request r) throws UnsupportedEncodingException {
headers = new HashMap<>();
rawResponse = new String(res, StandardCharsets.UTF_8);
int crlf = rawResponse.indexOf(DOUBLE_CRLF);
String h = rawResponse.substring(0, crlf);
rawHeaders = h;
parseHeaders();
rawResponse = new String(res, getEncoding());
body = rawResponse.substring(crlf + DOUBLE_CRLF.length());
this.time = time;
request = r;
}
private void parseHeaders() {
String[] hh = rawHeaders.split(CRLF);
parseStatus(hh[0]);
for (String c : hh) {
String[] entry = c.split(":\\s");
if (entry.length == 2) {
headers.put(entry[0].toLowerCase(), entry[1]);
}
}
}
private void parseStatus(String l) {
Pattern p = Pattern.compile("(HTTP/1.1)\\s([0-9]{3})\\s(.+)");
Matcher m = p.matcher(l);
if (m.matches()) {
statusCode = Integer.parseInt(m.group(2));
status = m.group(3);
}
}
private String getEncoding() {
Pattern p = Pattern.compile("(.+);\\s(.+)=(.+)");
if (headers.containsKey("content-type")) {
String value = headers.get("content-type");
Matcher m = p.matcher(value);
if (m.matches()) {
return m.group(3);
}
}
return StandardCharsets.UTF_8.toString();
}
public HashMap<String, String> getHeaders() {
return headers;
}
public String getRawHeaders() {
return rawHeaders;
}
public String getRawResponse() {
return rawResponse;
}
public String getBody() {
return body;
}
public int getStatusCode() {
return statusCode;
}
public String getStatus() {
return status;
}
public long getTime() {
return time;
}
public Request getRequest() { return request; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Response)) return false;
Response response = (Response) o;
return statusCode == response.statusCode &&
time == response.time &&
headers.equals(response.headers) &&
rawHeaders.equals(response.rawHeaders) &&
rawResponse.equals(response.rawResponse) &&
body.equals(response.body) &&
status.equals(response.status) &&
request.equals(response.request);
}
@Override
public int hashCode() {
return Objects.hash(headers, rawHeaders, rawResponse, body, statusCode, status, time, request);
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Response{");
sb.append("headers=").append(headers);
sb.append(", body='").append(body).append('\'');
sb.append(", statusCode=").append(statusCode);
sb.append(", status='").append(status).append('\'');
sb.append(", time=").append(time);
sb.append('}');
return sb.toString();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,135 @@
/*
Stolen code from Github : https://gist.github.com/james-d/be5bbd6255a4640a5357#file-editcell-java-L109
*/
package ovh.alexisdelhaie.endpoint.impl;
import javafx.event.Event;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;
public class EditCell<S, T> extends TableCell<S, T> {
// Text field for editing
// TODO: allow this to be a plugable control.
private final TextField textField = new TextField();
// Converter for converting the text in the text field to the user type, and vice-versa:
private final StringConverter<T> converter ;
public EditCell(StringConverter<T> converter) {
this.converter = converter ;
itemProperty().addListener((obx, oldItem, newItem) -> {
if (newItem == null) {
setText(null);
} else {
setText(converter.toString(newItem));
}
});
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
textField.setOnAction(evt -> {
commitEdit(this.converter.fromString(textField.getText()));
});
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused) {
commitEdit(this.converter.fromString(textField.getText()));
}
});
textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ESCAPE) {
textField.setText(converter.toString(getItem()));
cancelEdit();
event.consume();
} else if (event.getCode() == KeyCode.RIGHT) {
getTableView().getSelectionModel().selectRightCell();
event.consume();
} else if (event.getCode() == KeyCode.LEFT) {
getTableView().getSelectionModel().selectLeftCell();
event.consume();
} else if (event.getCode() == KeyCode.UP) {
getTableView().getSelectionModel().selectAboveCell();
event.consume();
} else if (event.getCode() == KeyCode.DOWN) {
getTableView().getSelectionModel().selectBelowCell();
event.consume();
}
});
}
/**
* Convenience converter that does nothing (converts Strings to themselves and vice-versa...).
*/
public static final StringConverter<String> IDENTITY_CONVERTER = new StringConverter<String>() {
@Override
public String toString(String object) {
return object;
}
@Override
public String fromString(String string) {
return string;
}
};
/**
* Convenience method for creating an EditCell for a String value.
* @return
*/
public static <S> EditCell<S, String> createStringEditCell() {
return new EditCell<S, String>(IDENTITY_CONVERTER);
}
// set the text of the text field and display the graphic
@Override
public void startEdit() {
super.startEdit();
textField.setText(converter.toString(getItem()));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
}
// revert to text display
@Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
// commits the edit. Update property if possible and revert to text display
@Override
public void commitEdit(T item) {
// This block is necessary to support commit on losing focus, because the baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (! isEditing() && ! item.equals(getItem())) {
TableView<S> table = getTableView();
if (table != null) {
TableColumn<S, T> column = getTableColumn();
CellEditEvent<S, T> event = new CellEditEvent<>(table,
new TablePosition<S,T>(table, getIndex(), column),
TableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
super.commitEdit(item);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ovh.alexisdelhaie.endpoint.Controller">
<TextField fx:id="requestInput" layoutX="109.0" layoutY="39.0" onKeyTyped="#requestInputOnKeyPressed" prefHeight="25.0" prefWidth="861.0" AnchorPane.leftAnchor="109.0" AnchorPane.rightAnchor="96.99999999999989" AnchorPane.topAnchor="39.0" />
<Button layoutX="979.0" layoutY="39.0" mnemonicParsing="false" onMouseClicked="#start" prefHeight="25.0" prefWidth="73.0" text="Send" AnchorPane.rightAnchor="16.0" AnchorPane.topAnchor="39.0" />
<ChoiceBox id="httpMethod" fx:id="httpMethod" layoutX="14.0" layoutY="39.0" prefHeight="25.0" prefWidth="85.0" AnchorPane.leftAnchor="14.0" AnchorPane.topAnchor="39.0" />
<Label layoutX="109.0" layoutY="21.0" text="Request URL" />
<TabPane fx:id="tabs" layoutX="-1.0" layoutY="104.0" prefHeight="540.0" prefWidth="1067.0" tabClosingPolicy="ALL_TABS" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="104.0">
</TabPane>
<Button layoutX="1022.0" layoutY="71.0" mnemonicParsing="false" onMouseClicked="#createNewTab" prefHeight="25.0" prefWidth="31.0" text="+" AnchorPane.rightAnchor="16.0" AnchorPane.topAnchor="71.0" />
<Button layoutX="1000.0" layoutY="71.0" mnemonicParsing="false" onMouseClicked="#showAboutDialog" prefHeight="25.0" prefWidth="31.0" text="?" AnchorPane.rightAnchor="58.0" AnchorPane.topAnchor="71.0" />
<Pane fx:id="runningIndicatorPane" layoutX="109.0" layoutY="74.0" prefHeight="17.0" prefWidth="200.0" visible="false">
<children>
<ProgressIndicator layoutX="2.0" layoutY="1.0" prefHeight="17.0" prefWidth="18.0" />
<Label layoutX="23.0" layoutY="1.0" prefHeight="17.0" prefWidth="133.0" text="Running..." />
</children>
</Pane>
</AnchorPane>

View File

@@ -0,0 +1,49 @@
package ovh.alexisdelhaie.endpoint.model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Param {
private final StringProperty _name = new SimpleStringProperty();
private final StringProperty _value = new SimpleStringProperty();
public Param() {
_name.set("");
_value.set("");
}
public Param(String n, String v) {
_name.set(n);
_value.set(v);
}
public String getName() {
return _name.get();
}
public StringProperty name() {
return _name;
}
public String getValue() {
return _value.get();
}
public StringProperty value() {
return _value;
}
public boolean isEmpty() {
return (_name.get().isBlank() && _value.get().isBlank());
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Param{");
sb.append("_name=").append(_name.get());
sb.append(", _value=").append(_value.get());
sb.append('}');
return sb.toString();
}
}

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="890.0" prefWidth="1149.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="295.0" minWidth="0.0" prefWidth="19.33333381017049" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="344.3333333333333" minWidth="10.0" prefWidth="55.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="648.6666781107585" minWidth="10.0" prefWidth="359.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="668.0" minWidth="5.333333333333314" prefWidth="17.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="678.0" minWidth="10.0" prefWidth="674.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="632.6666361490886" minWidth="10.0" prefWidth="23.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="128.00000762939453" minHeight="0.0" prefHeight="1.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="128.00000762939453" minHeight="0.0" prefHeight="16.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="201.0" minHeight="0.0" prefHeight="17.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="636.0" minHeight="10.0" prefHeight="461.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="188.0" minHeight="8.0" prefHeight="23.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="333.0" minHeight="10.0" prefHeight="326.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Time" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="Status" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label fx:id="status" text="..." GridPane.columnIndex="2" GridPane.rowIndex="1">
<font>
<Font name="System Bold" size="12.0" />
</font></Label>
<Label fx:id="time" text="... ms" GridPane.columnIndex="2" GridPane.rowIndex="2">
<font>
<Font name="System Bold" size="12.0" />
</font></Label>
<TableView fx:id="headers" prefHeight="321.0" prefWidth="599.0" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="3">
<columns>
<TableColumn prefWidth="75.0" text="Key" />
<TableColumn prefWidth="75.0" text="Value" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<TextArea fx:id="raw" editable="false" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="4" GridPane.rowIndex="3">
<font>
<Font name="Consolas" size="12.0" />
</font>
</TextArea>
<Label text="Raw" GridPane.columnIndex="4" GridPane.rowIndex="2" />
<TextArea fx:id="request" editable="false" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="4" GridPane.rowIndex="5">
<font>
<Font name="Consolas" size="12.0" />
</font>
</TextArea>
<Label text="Sended request" GridPane.columnIndex="4" GridPane.rowIndex="4" />
</children>
</GridPane>