Middleware

The Middleware API allows developers to execute code before a request is processed. Based on the incoming request, you can run custom logic, return custom streams and files, modify responses, rewrite, redirect, add headers, and more. All that before returning a response.

Middleware Use Cases

Using Middleware in a plain Java Application

Since 1.3.0 you can also use Middleware in plain Java applications, to obtain a reference to the API use IMiddlewareService.findServiceReference().

API

Resource handlers for custom schemes must be added before any browser is initialized. Schemes http and https support the addition of handlers at any moment of the program execution.

Here’s an example of how you could set up a resource handler using a bundle activator and OSGi:

import com.equo.middleware.api.IMiddlewareService;

public class ContributionComponent {

	private IMiddlewareService middlewareService = IMiddlewareService.findServiceReference();

	public void configureSchemes() {
		middlewareService.addResourceHandler("customscheme", "", (request, headers) -> {
			// Modify response headers if needed
			// Build response for the given request
			// Return readable InputStream containing the response data
		});
		...
	}

	...

}

In the example above we can see all that’s needed to add a resource handler to your application. A resource handler is used by the middleware implementation to know which requests to intercept, it’s defined by the parameters of the addResourceHandler method. The first parameter of the API is the scheme of the URL you’ll be intercepting requests from. The second parameter is the domain of that URL, if empty or NULL your resource handler will be used for all domains in the scheme. The third parameter is an instance of an IResponseHandler, it will be used for every request done to the resource handler.

The handler gets called for every resource that matches the scheme and domain and the InputStream is used directly. Concurrent requests that match the handler need to return different InputStream instances or the data might get corrupted.

Scheme

You can use existing schemes such as http and https and you can also pass arbitrary schemes like customscheme used in the examples in this documentation page.

Domain

You can also intercept requests to arbitrary domains, if you wish to intercept requests to all domains in the defined scheme you can pass an empty string or a null reference in this parameter.

IResponseHandler

The IResponseHandler that you pass to the resource handler will be called in every request intercepted by the middleware. It can be defined with a lambda, but it has two methods: shouldProcessRequest(Request request) : boolean and getResponseData(Request request, Map<String, String> headers) : InputStream. The middleware first asks the response handler if it should handle the request (by default it handles all requests), and only if it returns true we try to get the response data for the request.

Request

The Request object represents a Request made by the browser, it contains the request headers, its target URL and its HTTP method. Since Middleware 1.4.0, using Chromium >= 116.0.14, it also contains the information of the Frame in which the request was generated.

Custom Streams and Files

You can read your responses directly from the filesystem or use resources that you bundled directly in your jars.

import com.equo.middleware.api.IMiddlewareService;

public class ContributionComponent {

	private IMiddlewareService middlewareService = IMiddlewareService.findServiceReference();

	public void configureSchemes() {
		middlewareService.addResourceHandler("customscheme", "", (request, headers) -> {
			File htmlResource = new File("path/to/a/resource.html");
			if (htmlResource.exists()) {
				return new FileInputStream(htmlResource);
			}
			// If the file doesn't exist fall back to a default resource contained in our classpath.
			return getClass().getClassLoader().getResourceAsStream("default.html");
		});
		...
	}

	...

}

Dynamic Content

You can also create your responses on demand depending on the Request.

import com.equo.middleware.api.IMiddlewareService;
import com.equo.middleware.api.handler.IResponseConstants;

public class ContributionComponent {

	private IMiddlewareService middlewareService = IMiddlewareService.findServiceReference();

	public void configureSchemes() {
		middlewareService.addResourceHandler("customscheme", "", (request, headers) -> {
			headers.put(IResponseConstants.CONTENT_TYPE_HEADER, "text/html");
			StringBuilder sb = new StringBuilder("<!doctype html><html><body>");
			if (request.getUrl().endsWith("welcome")) {
				sb.append("<div> An awesome welcome page! </div>");
			} else {
				sb.append("This page is not implemented yet.");
			}
			sb.append("</body></html>");
			return new ByteArrayInputStream(sb.toString().getBytes());
		});
		...
	}

	...

}

Blocking requests

You can block requests by default if you want complete control over the requests done in the browser.

import com.equo.middleware.api.IMiddlewareService;

public class ContributionComponent {

	private IMiddlewareService middlewareService = IMiddlewareService.findServiceReference();

	public void setupMiddleware() {
		middlewareService.blockByDefault(true);
		middlewareService.addAllowed("domain.com", "www.domain.com", "sub.domain.com");
		...
	}

	...

}

Request filtering

You can define request filters to pre-process outgoing requests.

import com.equo.middleware.api.IMiddlewareService;

public class ContributionComponent {

	private IMiddlewareService middlewareService = IMiddlewareService.findServiceReference();

	public void setupMiddlewareFilters() {
		middlewareService.addRequestFilter("http", "domain.com", (mutableRequest) -> {
			mutableRequest.setUrl("http://another-domain.com");
			mutableRequest.setMethod("POST");
			mutableRequest.getHeaderMap().put("Some-Header", "some-value");
		});
		...
	}

	...

}
Only available in version >= 1.4.0
Only supported in Chromium version >= 116.0.14.

Shemes and domains

The scheme and domain need to match exactly with the requests' that will be filtered. You may also use custom schemes that were added by the Middleware.

IRequestFilter

The IRequestFilter that you pass to the request filter will be called in every request intercepted by the middleware. It can be defined with a lambda. Modifying a request URL will be treated as a redirect.

MutableRequest

A Request that supports modifications to its URL, Method and header map. You will receive an instance created by the Middleware in your request filter.

Custom Status Code

You can set a custom status code in the response to the browser, by setting IResponseConstants.STATUS_CODE in the headers map.

By default the response status code is 200 for valid input stream, 404 for null input stream, and 500 in case of any exception thrown.

import com.equo.middleware.api.IMiddlewareService;
import com.equo.middleware.api.handler.IResponseConstants;

public class ContributionComponent {

	private IMiddlewareService middlewareService = IMiddlewareService.findServiceReference();

	public void configureSchemes() {
		middlewareService.addResourceHandler("customscheme", "", (request, headers) -> {
			headers.put(IResponseConstants.CONTENT_TYPE_HEADER, "text/html");
			StringBuilder sb = new StringBuilder("<!doctype html><html><body>");
			if (request.getUrl().endsWith("welcome")) {
				sb.append("<div> Your user was created succesfully! </div>");
				headers.put(IResponseConstants.STATUS_CODE, "201");
			} else {
				sb.append("This page is not implemented yet.");
				headers.put(IResponseConstants.STATUS_CODE, "404");
			}
			sb.append("</body></html>");
			return new ByteArrayInputStream(sb.toString().getBytes());
		});
		...
	}
	...
}

Custom Charset Encoding

You have the flexibility to define the character set that will be used in the HTTP response using the following approach:

import com.equo.middleware.api.IMiddlewareService;
import com.equo.middleware.api.handler.IResponseConstants;

public class ContributionComponent {

	private IMiddlewareService middlewareService = IMiddlewareService.findServiceReference();

	public void configureSchemes() {
		middlewareService.addResourceHandler(resourceURL.getProtocol(), resourceURL.getHost(),
		(request, responseHeaders) -> {
			responseHeaders.put(IResponseConstants.CONTENT_TYPE_HEADER,
				"text/html; charset=UTF-8");
			try {
				return new FileInputStream(Paths.get(path).toFile());
			} catch (Exception e) {
				fail();
			}

			return null;
		});
		...
	}
	...
}

This allows you to customize the character encoding to suit your specific requirements. This feature enhances the versatility of the framework, empowering you to tailor the response content as per your needs.

By default the charset is the default character encoding of the JVM (can be set with the file.encoding system property). You can use Chromium’s default by setting the property middleware.default_encoding=CHROMIUM.

Only available in version >= 1.3.4
Only supported in Chromium version >= 106.0.16.