Class WebFramework

java.lang.Object
com.renomad.minum.web.WebFramework

public final class WebFramework extends Object
This class is responsible for the HTTP handling after socket connection.

The public methods are for registering endpoints - code that will be run for a given combination of HTTP method and path. See documentation for the methods in this class.

  • Method Details

    • getSuffixToMimeMappings

      public Map<String,String> getSuffixToMimeMappings()
    • registerPath

      public void registerPath(RequestLine.Method method, String pathName, ThrowingFunction<IRequest,IResponse> webHandler)
      Add a new handler in the web application for a combination of a RequestLine.Method, a path, and then provide the code to handle a request.
      Note that the path text expected is *after* the first forward slash, so for example with http://foo.com/mypath, provide "mypath" as the path.
    • registerPartialPath

      public void registerPartialPath(RequestLine.Method method, String pathName, ThrowingFunction<IRequest,IResponse> webHandler)
      Similar to registerPath(RequestLine.Method, String, ThrowingFunction) except that the paths registered here may be partially matched.

      For example, if you register .well-known/acme-challenge then it can match a client request for .well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX

      Be careful here, be thoughtful - partial paths will match a lot, and may overlap with other URL's for your app, such as endpoints and static files.

    • registerPreHandler

      public void registerPreHandler(ThrowingFunction<PreHandlerInputs,IResponse> preHandler)
      Sets a handler to process all requests across the board.

      This is an unusual method. Setting a handler here allows the user to run code of his choosing before the regular business code is run. Note that by defining this value, the ordinary call to endpoint.apply(request) will not be run.

      Here is an example

      
      
            webFramework.registerPreHandler(preHandlerInputs -> preHandlerCode(preHandlerInputs, auth, context));
      
            ...
      
            private IResponse preHandlerCode(PreHandlerInputs preHandlerInputs, AuthUtils auth, Context context) throws Exception {
                int secureServerPort = context.getConstants().secureServerPort;
                Request request = preHandlerInputs.clientRequest();
                ThrowingFunction<IRequest, IResponse> endpoint = preHandlerInputs.endpoint();
                ISocketWrapper sw = preHandlerInputs.sw();
      
                // log all requests
                logger.logTrace(() -> String.format("Request: %s by %s",
                    request.requestLine().getRawValue(),
                    request.remoteRequester())
                );
      
                // redirect to https if they are on the plain-text connection and the path is "login"
      
                // get the path from the request line
                String path = request.getRequestLine().getPathDetails().getIsolatedPath();
      
                // redirect to https on the configured secure port if they are on the plain-text connection and the path contains "login"
                if (path.contains("login") &&
                    sw.getServerType().equals(HttpServerType.PLAIN_TEXT_HTTP)) {
                    return Response.redirectTo("https://%s:%d/%s".formatted(sw.getHostName(), secureServerPort, path));
                }
      
                // adjust behavior if non-authenticated and path includes "secure/"
                if (path.contains("secure/")) {
                    AuthResult authResult = auth.processAuth(request);
                    if (authResult.isAuthenticated()) {
                        return endpoint.apply(request);
                    } else {
                        return Response.buildLeanResponse(CODE_403_FORBIDDEN);
                    }
                }
      
                // if the path does not include /secure, just move the request along unchanged.
                return endpoint.apply(request);
            }
       
    • registerLastMinuteHandler

      public void registerLastMinuteHandler(ThrowingFunction<LastMinuteHandlerInputs,IResponse> lastMinuteHandler)
      Sets a handler to be executed after running the ordinary handler, just before sending the response.

      This is an unusual method, so please be aware of its proper use. Its purpose is to allow the user to inject code to run after ordinary code, across all requests.

      For example, if the system would have returned a 404 NOT FOUND response, code can handle that situation in a switch case and adjust the response according to your programming.

      Here is an example

      
      
      
            webFramework.registerLastMinuteHandler(TheRegister::lastMinuteHandlerCode);
      
       ...
      
           private static IResponse lastMinuteHandlerCode(LastMinuteHandlerInputs inputs) {
               switch (inputs.response().statusCode()) {
                   case CODE_404_NOT_FOUND -> {
                       return Response.buildResponse(
                               CODE_404_NOT_FOUND,
                               Map.of("Content-Type", "text/html; charset=UTF-8"),
                               "<p>No document was found</p>"));
                   }
                   case CODE_500_INTERNAL_SERVER_ERROR -> {
                       return Response.buildResponse(
                               CODE_500_INTERNAL_SERVER_ERROR,
                               Map.of("Content-Type", "text/html; charset=UTF-8"),
                               "<p>Server error occurred.</p>" ));
                   }
                   default -> {
                       return inputs.response();
                   }
               }
           }
       
       
      Parameters:
      lastMinuteHandler - a function that will take a request and return a response, exactly like we use in the other registration methods for this class.
    • addMimeForSuffix

      public void addMimeForSuffix(String suffix, String mimeType)
      This allows users to add extra mappings between file suffixes and mime types, in case a user needs one that was not provided.

      This is made available through the web framework.

      Example:

       webFramework.addMimeForSuffix().put("foo","text/foo")