Skip to content

Handle non-root path requests #232

Description

@ComputerDaddyGuy

Hello,

For my use case, I would need java-httpbin to support handling requests on non-root paths, not only on the root path.

For example, for the /headers endpoint, we are currently forced to call http://127.0.0.1:8080/headers, whereas in my case I would need to call http://127.0.0.1:8080/some/other/path/headers (which returns 501 Not Implemented).

I thought of three possible options. Please let me know your thoughts, or if there is another option I may have missed.

Thank you!


Option 1: Custom HttpBinHandler

As mentioned in #15, I could indeed implement my own HttpBinHandler, but it would mostly be copy-and-paste work.

Since I would need to keep my implementation in sync with future changes in java-httpbin, this solution is neither ideal nor future-proof.


Option 2: Modify HttpBinHandler to support non-root paths

Modify HttpBinHandler so that it also handles non-root paths by default.

Not sure it is a valid approach as it seems to be a breaking change.

// HttpBinHandler
if (method.equals("GET") && uri.equals("/headers")) // Current implementation
if (method.equals("GET") && uri.endsWith("/headers")) // Proposed implementation: "endsWith" instead of "equals"

Option 3: Refactor HttpBinHandler to make it easier to extend/override

Instead of having a large if / else if / else block in a single method, perhaps a List<HttpBinRoute> could be introduced and iterated until a matching route is found.

This would make it easy to:

  • Extend existing routes to adjust matching logic or behavior
  • Add custom routes and behavior that are not provided by java-httpbin

It requires a bit more work, but it is also the most flexible approach. I would be glad to help 😊.

/** 
  * HttpBinHandler
  */

private List<HttpBinRoute> customRoutes = new ArrayList<>(); // mutable with some addCustomRoute(HttpBinRoute) method
private List<HttpBinRoute> defaultRoutes = /* default routes */;

private void handleHelper(Request baseRequest, HttpServletRequest request,
        HttpServletResponse servletResponse, InputStream is,
        OutputStream os) throws IOException {
    if(!handleRoutes(customRoutes, baseRequest, request, servletResponse, is, os)) { // Try first to handle request with custom routes...
        handleRoutes(defaultRoutes , baseRequest, request, servletResponse, is, os); // ... fallback to default routes otherwise
    }
}

private boolean handleRoutes(List<HttpBinRoute> routes, Request baseRequest, HttpServletRequest request,
        HttpServletResponse servletResponse, InputStream is,
        OutputStream os) throws IOException {
    // try
    for(var route : routes) {
        if(route.matches(request)) {
            route.handle(baseRequest, request, servletResponse, is, os);
            return true;
        }
    }
    // catch JSONException
    return false;
}

// ------------------------------------------------------------------------------

/** 
  * New interface
  */
public interface HttpBinRoute {

    boolean matches(Request request);

    void handle(Request baseRequest, HttpServletRequest request, 
            HttpServletResponse servletResponse, InputStream is,
            OutputStream os) throws IOException;
}

/** 
  * Default implementation from java-httpbin.
  * One implementation for each route handled by java-httpbin: HeadersRoute, GetRoute, PostRoute, IpRoute, etc.
  */
public class HeadersRoute implements HttpBinRoute {

    public boolean matches(Request request) {
        return request.getMethod().equals("GET") && request.getRequestURI().equals("/headers");
    }

    public void handle(...) throw IOException, JSONException {
        // default implementation from HttpBinHandler
    }
}

// ------------------------------------------------------------------------------

/**
  * Custom implementation that only overrides the "matches" method.
  */
public class MyCustomHeadersRoute extends HeadersRoute {

    @Override
    public boolean matches(Request request) {
        return request.getMethod().equals("GET") && request.getRequestURI().endsWith("/headers");
    }

}

var handler = new HttpBinHandler();
handler.addCustomRoute(new MyCustomHeadersRoute());
var httpBin = new HttpBin( uri, handler );
httpBin.start();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions