Commit 7d983ec0 authored by Martti Käärik's avatar Martti Käärik
Browse files

TRI changes + added connection reference to endpoint configuration and mapping.

parent 462684e2
Loading
Loading
Loading
Loading
Loading
+71 −43
Original line number Diff line number Diff line
@@ -79,20 +79,23 @@ public class HttpSystemAdapter implements SystemAdapter {
	public HttpSystemAdapter(Validator validator, Reporter reporter) {
		this.validator = validator;
		this.reporter = reporter;
		this.endpoints.add(new HttpEndpoint(defaultSut, defaultGate, "https://example.com", 0));
		this.endpoints.add(new HttpEndpoint("", defaultSut, defaultGate, "https://example.com", "/", 0));
	}

	public void setBaseUri(String baseUri) {
		getEndpoint(defaultSut, defaultGate).address = baseUri;
		getEndpoint(null, defaultSut, defaultGate).address = baseUri;
		getEndpoint(null, defaultSut, defaultGate).baseUri = "";
	}

	public void setEndpoint(String componentName, String gateName, String baseUri, int serverPort) {
		this.endpoints.add(new HttpEndpoint(componentName, gateName, baseUri, serverPort));
	public void setEndpoint(String connectionName, String componentName, String gateName, String address, String baseUri, int serverPort) {
		this.endpoints.add(new HttpEndpoint(connectionName, componentName, gateName, address, baseUri, serverPort));
	}

	private HttpEndpoint getEndpoint(String componentName, String gateName) {
	private HttpEndpoint getEndpoint(String connectionName, String componentName, String gateName) {
		if (connectionName == null)
			connectionName = "";
		for (HttpEndpoint endpoint : this.endpoints) {
			if (endpoint.componentName.equals(componentName) && endpoint.gateName == gateName)
			if (endpoint.connectionName.equals(connectionName) && endpoint.componentName.equals(componentName) && endpoint.gateName == gateName)
				return endpoint;
		}
		return null;
@@ -111,19 +114,23 @@ public class HttpSystemAdapter implements SystemAdapter {
	}

	@Override
	public void configure(Connection[] connections) {
	public void configure(Connection[] connections, NamedElement component) {

		for (Connection connection : connections) {
			for (GateReference gate : connection.getEndPoints()) {
				if (gate.getComponentRole() != ComponentInstanceRole.Tester)
					continue;
				HttpEndpoint endpoint = this.getEndpoint(gate.getComponent().getName(), gate.getGate().getName());
				if (endpoint == null || this.servers.containsKey(endpoint))
				if (!gate.getComponent().equals(component))
					continue;
				HttpEndpoint endpoint = this.getEndpoint(connection.getName(), gate.getComponent().getName(), gate.getGate().getName());
				if (endpoint == null)
					continue;
				if (this.servers.containsKey(endpoint))
					throw new RuntimeException("Invalid endpoint configuration (duplicates).");
				int serverPort = endpoint.port;
				try {
					HttpServer server = HttpServer.create(new InetSocketAddress(serverPort), 0);
					server.createContext(serverBaseUri, request -> this.handleRequest(request, endpoint, connection));
					server.createContext(endpoint.baseUri, request -> this.handleRequest(request, endpoint, connection));
					server.setExecutor(null);
					server.start();
					this.servers.put(endpoint, server);
@@ -158,20 +165,20 @@ public class HttpSystemAdapter implements SystemAdapter {
			server.stop(5);
	}

	private void handleRequest(HttpExchange request, HttpEndpoint source, Connection connection) throws IOException {
	private void handleRequest(HttpExchange request, HttpEndpoint target, Connection connection) throws IOException {
		synchronized (unhandledInputs) {
			unhandledInputs.add(new HttpInput(null, request, source, connection));
			unhandledInputs.add(new HttpInput(null, request, target, connection));
			unhandledInputs.notifyAll();
		}
	}

	private void handleResponse(HttpResponse<String> response, Throwable t, HttpEndpoint source,
	private void handleResponse(HttpResponse<String> response, Throwable t, HttpEndpoint target,
			Connection connection) {
		if (t != null)
			handleError(t);
		else {
			synchronized (unhandledInputs) {
				unhandledInputs.add(new HttpInput(response, null, source, connection));
				unhandledInputs.add(new HttpInput(response, null, target, connection));
				unhandledInputs.notifyAll();
			}
		}
@@ -179,7 +186,7 @@ public class HttpSystemAdapter implements SystemAdapter {

	private void handleError(Throwable t) {
		t.printStackTrace();
		reporter.runtimeError(t);
		reporter.runtimeError(null, t);
		throw new RuntimeException(t);
	}

@@ -211,16 +218,18 @@ public class HttpSystemAdapter implements SystemAdapter {
		if (!(data instanceof HttpRequestData) && !(data instanceof HttpResponseData))
			throw new RuntimeException("Request/response data in unsupported format");

		GateReference sourceGate = null;
		GateReference target = null;
		for (GateReference gate : connection.getEndPoints())
			if (!gate.getComponent().equals(source)) {
				target = gate;
				break;
			} else {
				sourceGate = gate;
			}
		HttpEndpoint targetEndpoint = this.getEndpoint(target.getComponent().getName(), target.getGate().getName());
		HttpEndpoint targetEndpoint = this.getEndpoint(connection.getName(), target.getComponent().getName(), target.getGate().getName());
		if (targetEndpoint == null)
			// Assume default
			targetEndpoint = this.getEndpoint(defaultSut, defaultGate);
			targetEndpoint = this.getEndpoint(null, defaultSut, defaultGate);

		boolean isRequest = data instanceof HttpRequestData;
		try {
@@ -273,20 +282,22 @@ public class HttpSystemAdapter implements SystemAdapter {
				requestBuilder.uri(uri);

				// TODO logging
				reporter.comment("Outgoing (headers): " + httpData.method.toString() + " | " + uri);
				reporter.comment(source.getName(), "Outgoing (headers): " + httpData.method.toString() + " | " + uri);
				
				byte[] body = encodeBody(httpData.body);
				reporter.comment(source.getName(), "Outgoing (body): " + mapper.writeValueAsString(httpData.body));
				requestBuilder.method(httpData.method.toString(), HttpRequest.BodyPublishers.ofByteArray(body));

				if (httpData.headers != null)
					for (HttpHeader header : httpData.headers)
						requestBuilder.header(header.name, header.value);

				CompletableFuture<HttpResponse<String>> responseFuture = client.sendAsync(requestBuilder.build(),
						HttpResponse.BodyHandlers.ofString(UTF_8));

				HttpEndpoint endpoint = targetEndpoint;
				HttpEndpoint sourceEndpoint = this.getEndpoint(connection.getName(), sourceGate.getComponent().getName(), sourceGate.getGate().getName());
				responseFuture
						.whenCompleteAsync((response, t) -> this.handleResponse(response, t, endpoint, connection));
						.whenCompleteAsync((response, t) -> this.handleResponse(response, t, sourceEndpoint, connection));

			} else {

@@ -306,6 +317,7 @@ public class HttpSystemAdapter implements SystemAdapter {

				Headers headers = unhandledExchange.getResponseHeaders();
				this.applyDefaults(headers);
				if (httpData.headers != null)
					for (HttpHeader header : httpData.headers)
						headers.add(header.name, header.value);

@@ -327,7 +339,8 @@ public class HttpSystemAdapter implements SystemAdapter {
				int length = body.length;

				// TODO logging
				reporter.comment("Outgoing (headers): " + httpData.status + " | " + length);
				reporter.comment(source.getName(), "Outgoing (headers): " + httpData.status + " | length=" + length);
				reporter.comment(source.getName(), "Outgoing (body): " + mapper.writeValueAsString(httpData.body));
				
				unhandledExchange.sendResponseHeaders(httpData.status, length);
				
@@ -351,7 +364,7 @@ public class HttpSystemAdapter implements SystemAdapter {
			do {
				for (HttpInput i : unhandledInputs)
					if (i.connection.equals(connection))
						if (!i.source.componentName.equals(target.getName())) {
						if (i.target.componentName.equals(target.getName())) {
							input = i;
							break;
						}
@@ -371,10 +384,13 @@ public class HttpSystemAdapter implements SystemAdapter {
				String body = response.body();

				// TODO logging
				reporter.comment("Incoming: " + response.statusCode() + " | " + body);
				reporter.comment(target.getName(), "Incoming: " + response.statusCode() + " | " + body);

				try {
					
					// This is not provided by HttpResponse
					expectedHttpData.statusMessage = null;

					HttpResponseData receivedHttpData = new HttpResponseData();
					receivedHttpData.status = response.statusCode();
					Map<String, List<String>> headers = response.headers().map();
@@ -411,14 +427,21 @@ public class HttpSystemAdapter implements SystemAdapter {

				HttpExchange exchange = input.exchange;

				if (input.body == null)
					try (InputStream is = exchange.getRequestBody()) {
	
						// TODO other encodings
						Charset cs = StandardCharsets.UTF_8;
					String body = new String(is.readAllBytes(), cs);
						input.body = new String(is.readAllBytes(), cs);

						// TODO logging
					reporter.comment("Incoming: " + exchange.getRequestMethod() + " | " + body);
						reporter.comment(target.getName(), "Incoming: " + exchange.getRequestMethod() + " | " + input.body);
	
					} catch (IOException e) {
						handleError(e);
					}
				
				try {

					HttpRequestData receivedHttpData = new HttpRequestData();
					receivedHttpData.uri = exchange.getRequestURI().toString();
@@ -434,14 +457,14 @@ public class HttpSystemAdapter implements SystemAdapter {
					boolean isReceived = false;
					if (expected != null) {
						if (expectedHttpData.body != null) {
							receivedHttpData.body = decodeBody(body, expectedHttpData.body.getClass());
							receivedHttpData.body = decodeBody(input.body, expectedHttpData.body.getClass());
						}
						if (this.validator.matches(expected, received)) {
							isReceived = true;
						}

					} else {
						receivedHttpData.body = body;
						receivedHttpData.body = input.body;
						isReceived = true;
					}

@@ -467,7 +490,6 @@ public class HttpSystemAdapter implements SystemAdapter {
	}

	protected byte[] encodeBody(Object body) throws IOException {
		reporter.comment("Outgoing (body): " + mapper.writeValueAsString(body));
		return mapper.writeValueAsBytes(body);
	}

@@ -575,30 +597,36 @@ public class HttpSystemAdapter implements SystemAdapter {
	class HttpInput {
		HttpResponse<String> response;
		HttpExchange exchange;
		HttpEndpoint source;
		HttpEndpoint target;
		Connection connection;
		
		public HttpInput(HttpResponse<String> response, HttpExchange exchange, HttpEndpoint source,
		String body;

		public HttpInput(HttpResponse<String> response, HttpExchange exchange, HttpEndpoint target,
				Connection connection) {
			this.response = response;
			this.exchange = exchange;
			this.source = source;
			this.target = target;
			this.connection = connection;
		}

	}

	class HttpEndpoint {
		String connectionName;
		String componentName;
		String gateName;
		String address;
		String baseUri;
		int port;

		public HttpEndpoint(String componentName, String gateName, String address, int port) {
		public HttpEndpoint(String connectionName, String componentName, String gateName, String address, String baseUri, int port) {
			super();
			this.connectionName = connectionName;
			this.componentName = componentName;
			this.gateName = gateName;
			this.address = address;
			this.baseUri = baseUri;
			this.port = port;
		}
	}