Pārlūkot izejas kodu

[git-bridge] Update jetty to v12 (#30221)

GitOrigin-RevId: 44b3f754b3dbced8a2ac47472e113ef4cae30c50
Miguel Serrano 7 mēneši atpakaļ
vecāks
revīzija
48e7f8042b
26 mainītis faili ar 308 papildinājumiem un 321 dzēšanām
  1. 16 9
      services/git-bridge/pom.xml
  2. 1 1
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/GitBridgeApp.java
  3. 0 60
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/jetty/NullLogger.java
  4. 1 1
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLReceivePackFactory.java
  5. 1 1
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java
  6. 1 1
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLUploadPackFactory.java
  7. 2 2
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServlet.java
  8. 3 3
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServletConfig.java
  9. 1 1
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/util/RepositoryObjectTreeWalker.java
  10. 24 20
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/CORSHandler.java
  11. 31 21
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/DiagnosticsHandler.java
  12. 15 16
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/FileHandler.java
  13. 19 16
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitBridgeServer.java
  14. 21 17
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitLfsHandler.java
  15. 20 17
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/HealthCheckHandler.java
  16. 3 3
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/Oauth2Filter.java
  17. 23 19
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackHandler.java
  18. 9 14
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProductionErrorHandler.java
  19. 13 12
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProjectDeletionHandler.java
  20. 41 28
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PrometheusHandler.java
  21. 15 13
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/StatusHandler.java
  22. 5 6
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Request.java
  23. 16 14
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockOAuthRequestHandler.java
  24. 17 16
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotRequestHandler.java
  25. 7 6
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotServer.java
  26. 3 4
      services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/PostbackThread.java

+ 16 - 9
services/git-bridge/pom.xml

@@ -16,10 +16,11 @@
         <fmt.plugin.version>2.23</fmt.plugin.version>
         <junit.version>4.13.2</junit.version>
         <jmock.junit4.version>2.13.1</jmock.junit4.version>
-        <jetty.servlet.version>9.4.57.v20241219</jetty.servlet.version>
+        <jetty.version>12.1.5</jetty.version>
+        <jakarta.servlet.version>6.1.0</jakarta.servlet.version>
         <gson.version>2.13.2</gson.version>
         <async.http.client.version>3.0.3</async.http.client.version>
-        <jgit.version>6.10.1.202505221210-r</jgit.version>
+        <jgit.version>7.5.0.202512021534-r</jgit.version>
         <sqlite.jdbc.version>3.41.2.2</sqlite.jdbc.version>
         <joda.time.version>2.9.9</joda.time.version>
         <google.oauth.client.version>1.39.0</google.oauth.client.version>
@@ -104,17 +105,23 @@
             <version>${jmock.junit4.version}</version>
             <scope>test</scope>
         </dependency>
-        <!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-servlet -->
-        <dependency>
-            <groupId>org.eclipse.jetty</groupId>
-            <artifactId>jetty-servlet</artifactId>
-            <version>${jetty.servlet.version}</version>
-        </dependency>
         <!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-server -->
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-server</artifactId>
-            <version>${jetty.servlet.version}</version>
+            <version>${jetty.version}</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.eclipse.jetty.ee10/jetty-ee10-servlet -->
+        <dependency>
+            <groupId>org.eclipse.jetty.ee10</groupId>
+            <artifactId>jetty-ee10-servlet</artifactId>
+            <version>${jetty.version}</version>
+        </dependency>
+        <!-- Jakarta Servlet API -->
+        <dependency>
+            <groupId>jakarta.servlet</groupId>
+            <artifactId>jakarta.servlet-api</artifactId>
+            <version>${jakarta.servlet.version}</version>
         </dependency>
         <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
         <dependency>

+ 1 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/GitBridgeApp.java

@@ -1,7 +1,7 @@
 package uk.ac.ic.wlgitbridge.application;
 
+import jakarta.servlet.ServletException;
 import java.io.IOException;
-import javax.servlet.ServletException;
 import uk.ac.ic.wlgitbridge.application.config.Config;
 import uk.ac.ic.wlgitbridge.application.exception.ArgsException;
 import uk.ac.ic.wlgitbridge.application.exception.ConfigFileException;

+ 0 - 60
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/application/jetty/NullLogger.java

@@ -1,60 +0,0 @@
-package uk.ac.ic.wlgitbridge.application.jetty;
-
-import org.eclipse.jetty.util.log.Logger;
-
-/*
- * Created by Winston on 03/11/14.
- */
-public class NullLogger implements Logger {
-
-  @Override
-  public String getName() {
-    return "null_logger";
-  }
-
-  @Override
-  public void warn(String s, Object... objects) {}
-
-  @Override
-  public void warn(Throwable throwable) {}
-
-  @Override
-  public void warn(String s, Throwable throwable) {}
-
-  @Override
-  public void info(String s, Object... objects) {}
-
-  @Override
-  public void info(Throwable throwable) {}
-
-  @Override
-  public void info(String s, Throwable throwable) {}
-
-  @Override
-  public boolean isDebugEnabled() {
-    return false;
-  }
-
-  @Override
-  public void setDebugEnabled(boolean b) {}
-
-  @Override
-  public void debug(String s, Object... objects) {}
-
-  @Override
-  public void debug(String s, long l) {}
-
-  @Override
-  public void debug(Throwable throwable) {}
-
-  @Override
-  public void debug(String s, Throwable throwable) {}
-
-  @Override
-  public Logger getLogger(String s) {
-    return this;
-  }
-
-  @Override
-  public void ignore(Throwable throwable) {}
-}

+ 1 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLReceivePackFactory.java

@@ -1,8 +1,8 @@
 package uk.ac.ic.wlgitbridge.git.handler;
 
 import com.google.api.client.auth.oauth2.Credential;
+import jakarta.servlet.http.HttpServletRequest;
 import java.util.Optional;
-import javax.servlet.http.HttpServletRequest;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.ReceivePack;
 import org.eclipse.jgit.transport.resolver.ReceivePackFactory;

+ 1 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLRepositoryResolver.java

@@ -1,9 +1,9 @@
 package uk.ac.ic.wlgitbridge.git.handler;
 
 import com.google.api.client.auth.oauth2.Credential;
+import jakarta.servlet.http.HttpServletRequest;
 import java.io.IOException;
 import java.util.Optional;
-import javax.servlet.http.HttpServletRequest;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.ServiceMayNotContinueException;

+ 1 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/handler/WLUploadPackFactory.java

@@ -1,6 +1,6 @@
 package uk.ac.ic.wlgitbridge.git.handler;
 
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.UploadPack;
 import org.eclipse.jgit.transport.resolver.UploadPackFactory;

+ 2 - 2
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServlet.java

@@ -1,7 +1,7 @@
 package uk.ac.ic.wlgitbridge.git.servlet;
 
-import javax.servlet.ServletException;
-import org.eclipse.jetty.servlet.ServletContextHandler;
+import jakarta.servlet.ServletException;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
 import org.eclipse.jgit.http.server.GitServlet;
 import uk.ac.ic.wlgitbridge.bridge.Bridge;
 import uk.ac.ic.wlgitbridge.bridge.repo.RepoStore;

+ 3 - 3
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/servlet/WLGitServletConfig.java

@@ -1,9 +1,9 @@
 package uk.ac.ic.wlgitbridge.git.servlet;
 
+import jakarta.servlet.ServletConfig;
+import jakarta.servlet.ServletContext;
 import java.util.Enumeration;
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
 
 /*
  * Created by Winston on 02/11/14.

+ 1 - 1
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/git/util/RepositoryObjectTreeWalker.java

@@ -64,7 +64,7 @@ public class RepositoryObjectTreeWalker {
       String path = treeWalk.getPathString();
 
       ObjectId objectId = treeWalk.getObjectId(0);
-      if (!repository.hasObject(objectId)) {
+      if (!repository.getObjectDatabase().has(objectId)) {
         throw new InvalidGitRepository();
       }
       ObjectLoader obj = repository.open(objectId);

+ 24 - 20
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/CORSHandler.java

@@ -1,14 +1,16 @@
 package uk.ac.ic.wlgitbridge.server;
 
-import java.io.IOException;
 import java.util.Set;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 import uk.ac.ic.wlgitbridge.util.Log;
 
-public class CORSHandler extends AbstractHandler {
+public class CORSHandler extends Handler.Abstract {
   private final Set<String> allowedCorsOrigins;
 
   public CORSHandler(String[] allowedCorsOrigins) {
@@ -16,32 +18,34 @@ public class CORSHandler extends AbstractHandler {
   }
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException {
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    HttpField originField = request.getHeaders().getField(HttpHeader.ORIGIN);
+    String origin = originField != null ? originField.getValue() : null;
 
-    String origin = request.getHeader("Origin");
     if (origin == null) {
-      return; // Not a CORS request
+      return false; // Not a CORS request
     }
 
     final boolean ok = allowedCorsOrigins.contains(origin);
     if (ok) {
-      response.setHeader("Access-Control-Allow-Origin", origin);
-      response.setHeader("Access-Control-Allow-Credentials", "true");
-      response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, PUT, POST, DELETE");
-      response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
-      response.setHeader("Access-Control-Max-Age", "86400"); // cache for 24h
+      response.getHeaders().put("Access-Control-Allow-Origin", origin);
+      response.getHeaders().put("Access-Control-Allow-Credentials", "true");
+      response.getHeaders().put("Access-Control-Allow-Methods", "GET, HEAD, PUT, POST, DELETE");
+      response.getHeaders().put("Access-Control-Allow-Headers", "Authorization, Content-Type");
+      response.getHeaders().put("Access-Control-Max-Age", "86400"); // cache for 24h
     }
-    String method = baseRequest.getMethod();
+    String method = request.getMethod();
     if ("OPTIONS".equals(method)) {
-      Log.debug("OPTIONS <- {}", target);
-      baseRequest.setHandled(true);
+      String path = Request.getPathInContext(request);
+      Log.debug("OPTIONS <- {}", path);
       if (ok) {
-        response.setStatus(200);
+        response.setStatus(HttpStatus.OK_200);
       } else {
-        response.setStatus(403);
+        response.setStatus(HttpStatus.FORBIDDEN_403);
       }
+      callback.succeeded();
+      return true;
     }
+    return false;
   }
 }

+ 31 - 21
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/DiagnosticsHandler.java

@@ -1,28 +1,28 @@
 package uk.ac.ic.wlgitbridge.server;
 
-import java.io.IOException;
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 import java.lang.management.ManagementFactory;
 import javax.management.JMException;
 import javax.management.ObjectName;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 import uk.ac.ic.wlgitbridge.util.Log;
 
-public class DiagnosticsHandler extends AbstractHandler {
+public class DiagnosticsHandler extends Handler.Abstract {
 
   public DiagnosticsHandler() {}
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException, ServletException {
-    String method = baseRequest.getMethod();
-    if (("GET".equals(method)) && target != null && target.matches("^/diags/?$")) {
-      baseRequest.setHandled(true);
-
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String method = request.getMethod();
+    String path = Request.getPathInContext(request);
+    if (("GET".equals(method)) && path != null && path.matches("^/diags/?$")) {
       Log.debug(method + " <- /diags");
 
       String detail;
@@ -33,18 +33,28 @@ public class DiagnosticsHandler extends AbstractHandler {
         summary = execute("vmNativeMemory", "summary");
       } catch (JMException e) {
         Log.error("Failed to get native memory detail: " + e.getMessage());
-        response.setStatus(500);
-        return;
+        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+        callback.succeeded();
+        return true;
       }
 
-      response.setContentType("text/plain");
-      response.setStatus(200);
+      response.getHeaders().put("Content-Type", "text/plain");
+      response.setStatus(HttpStatus.OK_200);
 
-      response.getWriter().write(summary);
-      response.getWriter().write("\n----------\n\n");
-      response.getWriter().write(detail);
-      response.getWriter().flush();
+      Writer writer =
+          new BufferedWriter(new OutputStreamWriter(Content.Sink.asOutputStream(response)));
+      try {
+        writer.write(summary);
+        writer.write("\n----------\n\n");
+        writer.write(detail);
+        writer.flush();
+      } finally {
+        writer.close();
+        callback.succeeded();
+      }
+      return true;
     }
+    return false;
   }
 
   public static String execute(String command, String... args) throws JMException {

+ 15 - 16
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/FileHandler.java

@@ -1,13 +1,12 @@
 package uk.ac.ic.wlgitbridge.server;
 
-import java.io.IOException;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Fields;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import uk.ac.ic.wlgitbridge.bridge.Bridge;
@@ -29,26 +28,26 @@ public class FileHandler extends ResourceHandler {
   }
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException, ServletException {
-    if (!"GET".equals(baseRequest.getMethod())) return;
-    LOG.debug("GET <- {}", baseRequest.getRequestURI());
-
-    Matcher docKeyMatcher = DOC_KEY_PATTERN.matcher(target);
-    if (!docKeyMatcher.matches()) return;
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    if (!"GET".equals(request.getMethod())) return false;
+    String path = Request.getPathInContext(request);
+    LOG.debug("GET <- {}", request.getHttpURI());
+
+    Matcher docKeyMatcher = DOC_KEY_PATTERN.matcher(path);
+    if (!docKeyMatcher.matches()) return false;
     String docKey = docKeyMatcher.group(1);
 
-    String apiKey = request.getParameter("key");
-    if (apiKey == null) return;
+    Fields parameters = Request.getParameters(request);
+    String apiKey = parameters != null ? parameters.getValue("key") : null;
+    if (apiKey == null) return false;
 
     try {
       bridge.checkPostbackKey(docKey, apiKey);
     } catch (InvalidPostbackKeyException e) {
       LOG.warn("INVALID POST BACK KEY: docKey={} apiKey={}", docKey, apiKey);
-      return;
+      return false;
     }
 
-    super.handle(target, baseRequest, request, response);
+    return super.handle(request, response, callback);
   }
 }

+ 19 - 16
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitBridgeServer.java

@@ -1,21 +1,23 @@
 package uk.ac.ic.wlgitbridge.server;
 
+import jakarta.servlet.DispatcherType;
+import jakarta.servlet.Filter;
+import jakarta.servlet.ServletException;
 import java.io.File;
 import java.net.BindException;
 import java.nio.file.Paths;
 import java.util.EnumSet;
-import javax.servlet.DispatcherType;
-import javax.servlet.Filter;
-import javax.servlet.ServletException;
+import org.eclipse.jetty.ee10.servlet.FilterHolder;
+import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
+import org.eclipse.jetty.ee10.servlet.ServletHolder;
+import org.eclipse.jetty.ee10.servlet.SessionHandler;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.*;
-import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.DefaultHandler;
+import org.eclipse.jetty.util.resource.ResourceFactory;
 import uk.ac.ic.wlgitbridge.application.config.Config;
-import uk.ac.ic.wlgitbridge.application.jetty.NullLogger;
 import uk.ac.ic.wlgitbridge.bridge.Bridge;
 import uk.ac.ic.wlgitbridge.bridge.db.DBStore;
 import uk.ac.ic.wlgitbridge.bridge.db.sqlite.SqliteDBStore;
@@ -48,7 +50,6 @@ public class GitBridgeServer {
   private String apiBaseURL;
 
   public GitBridgeServer(Config config) throws ServletException {
-    org.eclipse.jetty.util.log.Log.setLog(new NullLogger());
     this.port = config.getPort();
     this.rootGitDirectoryPath = config.getRootGitDirectory();
     RepoStore repoStore =
@@ -109,7 +110,7 @@ public class GitBridgeServer {
     connector.setIdleTimeout(config.getIdleTimeout());
     this.jettyServer.addConnector(connector);
 
-    HandlerCollection handlers = new HandlerList();
+    Handler.Sequence handlers = new Handler.Sequence();
     handlers.addHandler(new CORSHandler(config.getAllowedCorsOrigins()));
     handlers.addHandler(initApiHandler());
     handlers.addHandler(initBaseHandler());
@@ -120,7 +121,7 @@ public class GitBridgeServer {
   private Handler initBaseHandler() {
     ContextHandler base = new ContextHandler();
     base.setContextPath("/");
-    HandlerCollection handlers = new HandlerList();
+    Handler.Sequence handlers = new Handler.Sequence();
     handlers.addHandler(new StatusHandler(bridge));
     handlers.addHandler(new HealthCheckHandler(bridge));
     handlers.addHandler(new GitLfsHandler(bridge));
@@ -134,7 +135,7 @@ public class GitBridgeServer {
     ContextHandler api = new ContextHandler();
     api.setContextPath("/api");
 
-    HandlerCollection handlers = new HandlerList();
+    Handler.Sequence handlers = new Handler.Sequence();
     handlers.addHandler(initResourceHandler());
     handlers.addHandler(new PostbackHandler(bridge));
     handlers.addHandler(new ProjectDeletionHandler(bridge));
@@ -149,8 +150,8 @@ public class GitBridgeServer {
 
   private Handler initGitHandler(Config config, RepoStore repoStore, SnapshotApi snapshotApi)
       throws ServletException {
-    final ServletContextHandler servletContextHandler =
-        new ServletContextHandler(ServletContextHandler.SESSIONS);
+    final ServletContextHandler servletContextHandler = new ServletContextHandler();
+    servletContextHandler.setSessionHandler(new SessionHandler());
     if (config.getOauth2Server() != null) {
       Filter filter =
           new Oauth2Filter(snapshotApi, config.getOauth2Server(), config.isUserPasswordEnabled());
@@ -166,8 +167,10 @@ public class GitBridgeServer {
   }
 
   private Handler initResourceHandler() {
-    ResourceHandler resourceHandler = new FileHandler(bridge);
-    resourceHandler.setResourceBase(new File(rootGitDirectoryPath, ".wlgb/atts").getAbsolutePath());
+    FileHandler resourceHandler = new FileHandler(bridge);
+    File attDir = new File(rootGitDirectoryPath, ".wlgb/atts");
+    resourceHandler.setBaseResource(
+        ResourceFactory.of(resourceHandler).newResource(attDir.toPath()));
     return resourceHandler;
   }
 }

+ 21 - 17
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/GitLfsHandler.java

@@ -1,14 +1,16 @@
 package uk.ac.ic.wlgitbridge.server;
 
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 import uk.ac.ic.wlgitbridge.bridge.Bridge;
 import uk.ac.ic.wlgitbridge.util.Log;
 
-public class GitLfsHandler extends AbstractHandler {
+public class GitLfsHandler extends Handler.Abstract {
 
   private final Bridge bridge;
 
@@ -17,20 +19,22 @@ public class GitLfsHandler extends AbstractHandler {
   }
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException {
-    String method = baseRequest.getMethod();
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String method = request.getMethod();
+    String path = Request.getPathInContext(request);
     if (("POST".equals(method))
-        && target != null
-        && target.matches("^/[0-9a-z]+\\.git/info/lfs/objects/batch/?$")) {
+        && path != null
+        && path.matches("^/[0-9a-z]+\\.git/info/lfs/objects/batch/?$")) {
       Log.debug(method + " <- /<project>.git/info/lfs/objects/batch");
-      response.setContentType("application/vnd.git-lfs+json");
-      response.setStatus(422);
-      response
-          .getWriter()
-          .println("{\"message\": \"ERROR: Git LFS is not supported on Overleaf\"}");
-      baseRequest.setHandled(true);
+      response.getHeaders().put("Content-Type", "application/vnd.git-lfs+json");
+      response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY_422);
+      ByteBuffer responseBuffer =
+          ByteBuffer.wrap(
+              "{\"message\": \"ERROR: Git LFS is not supported on Overleaf\"}\n"
+                  .getBytes(StandardCharsets.UTF_8));
+      response.write(true, responseBuffer, callback);
+      return true;
     }
+    return false;
   }
 }

+ 20 - 17
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/HealthCheckHandler.java

@@ -1,14 +1,16 @@
 package uk.ac.ic.wlgitbridge.server;
 
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 import uk.ac.ic.wlgitbridge.bridge.Bridge;
 import uk.ac.ic.wlgitbridge.util.Log;
 
-public class HealthCheckHandler extends AbstractHandler {
+public class HealthCheckHandler extends Handler.Abstract {
 
   private final Bridge bridge;
 
@@ -17,23 +19,24 @@ public class HealthCheckHandler extends AbstractHandler {
   }
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException {
-    String method = baseRequest.getMethod();
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String method = request.getMethod();
+    String path = Request.getPathInContext(request);
     if (("GET".equals(method) || "HEAD".equals(method))
-        && target != null
-        && target.matches("^/health_check/?$")) {
+        && path != null
+        && path.matches("^/health_check/?$")) {
       Log.debug(method + " <- /health_check");
-      baseRequest.setHandled(true);
-      response.setContentType("text/plain");
+      response.getHeaders().put("Content-Type", "text/plain");
       if (bridge.healthCheck()) {
-        response.setStatus(200);
-        response.getWriter().println("ok");
+        response.setStatus(HttpStatus.OK_200);
+        response.write(true, ByteBuffer.wrap("ok\n".getBytes(StandardCharsets.UTF_8)), callback);
       } else {
-        response.setStatus(500);
-        response.getWriter().println("failed");
+        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
+        response.write(
+            true, ByteBuffer.wrap("failed\n".getBytes(StandardCharsets.UTF_8)), callback);
       }
+      return true;
     }
+    return false;
   }
 }

+ 3 - 3
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/Oauth2Filter.java

@@ -5,13 +5,13 @@ import com.google.api.client.http.GenericUrl;
 import com.google.api.client.http.HttpHeaders;
 import com.google.api.client.http.HttpRequest;
 import com.google.api.client.http.HttpResponse;
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
 import java.util.*;
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 import org.apache.commons.codec.binary.Base64;
 import uk.ac.ic.wlgitbridge.bridge.snapshot.SnapshotApi;
 import uk.ac.ic.wlgitbridge.util.Instance;

+ 23 - 19
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PostbackHandler.java

@@ -2,21 +2,24 @@ package uk.ac.ic.wlgitbridge.server;
 
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
+import jakarta.servlet.ServletException;
 import java.io.IOException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 import uk.ac.ic.wlgitbridge.bridge.Bridge;
 import uk.ac.ic.wlgitbridge.snapshot.push.exception.UnexpectedPostbackException;
 import uk.ac.ic.wlgitbridge.util.Log;
-import uk.ac.ic.wlgitbridge.util.Util;
 
 /*
  * Created by Winston on 16/11/14.
  */
-public class PostbackHandler extends AbstractHandler {
+public class PostbackHandler extends Handler.Abstract {
 
   private final Bridge bridge;
 
@@ -25,14 +28,13 @@ public class PostbackHandler extends AbstractHandler {
   }
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException, ServletException {
-    Log.debug("PostbackHandler: " + baseRequest.getMethod() + " <- " + baseRequest.getHttpURI());
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String target = Request.getPathInContext(request);
+    Log.debug("PostbackHandler: " + request.getMethod() + " <- " + request.getHttpURI());
     try {
       if (request.getMethod().equals("POST") && target.endsWith("postback")) {
-        response.setContentType("application/json");
-        String contents = Util.getContentsOfReader(request.getReader());
+        response.getHeaders().put("Content-Type", "application/json");
+        String contents = Content.Source.asString(request);
         String[] parts = target.split("/");
         if (parts.length < 4) {
           throw new ServletException();
@@ -46,16 +48,17 @@ public class PostbackHandler extends AbstractHandler {
         try {
           postbackContents.processPostback();
         } catch (UnexpectedPostbackException e) {
-          response.setStatus(HttpServletResponse.SC_CONFLICT);
+          response.setStatus(HttpStatus.CONFLICT_409);
           body.add("code", new JsonPrimitive("unexpectedPostback"));
-          response.getWriter().println(body);
-          baseRequest.setHandled(true);
-          return;
+          response.write(
+              true, ByteBuffer.wrap((body + "\n").getBytes(StandardCharsets.UTF_8)), callback);
+          return true;
         }
-        response.setStatus(HttpServletResponse.SC_OK);
+        response.setStatus(HttpStatus.OK_200);
         body.add("code", new JsonPrimitive("success"));
-        response.getWriter().println(body);
-        baseRequest.setHandled(true);
+        response.write(
+            true, ByteBuffer.wrap((body + "\n").getBytes(StandardCharsets.UTF_8)), callback);
+        return true;
       }
     } catch (IOException e) {
       Log.warn("IOException when handling postback to target: " + target, e);
@@ -67,5 +70,6 @@ public class PostbackHandler extends AbstractHandler {
       Log.warn("RuntimeException when handling postback to target: " + target, e);
       throw e;
     }
+    return false;
   }
 }

+ 9 - 14
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProductionErrorHandler.java

@@ -1,22 +1,17 @@
 package uk.ac.ic.wlgitbridge.server;
 
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
 import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.util.Callback;
 
 public class ProductionErrorHandler extends ErrorHandler {
   @Override
-  public void handle(
-      String target,
-      org.eclipse.jetty.server.Request baseRequest,
-      HttpServletRequest request,
-      HttpServletResponse response)
-      throws IOException {
-    response
-        .getWriter()
-        .append("{\"message\":\"HTTP error ")
-        .append(String.valueOf(response.getStatus()))
-        .append("\"}");
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String message = "{\"message\":\"HTTP error " + response.getStatus() + "\"}";
+    response.write(true, ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)), callback);
+    return true;
   }
 }

+ 13 - 12
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/ProjectDeletionHandler.java

@@ -1,15 +1,15 @@
 package uk.ac.ic.wlgitbridge.server;
 
-import java.io.IOException;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 import uk.ac.ic.wlgitbridge.bridge.Bridge;
 
-public class ProjectDeletionHandler extends AbstractHandler {
+public class ProjectDeletionHandler extends Handler.Abstract {
 
   private final Bridge bridge;
   private final Pattern routePattern = Pattern.compile("^/projects/([0-9a-f]{24})$");
@@ -19,17 +19,18 @@ public class ProjectDeletionHandler extends AbstractHandler {
   }
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException {
-    String method = baseRequest.getMethod();
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String method = request.getMethod();
+    String target = Request.getPathInContext(request);
     Matcher matcher = routePattern.matcher(target);
     if (method.equals("DELETE") && target != null && matcher.matches()) {
       String projectName = matcher.group(1);
-      response.setContentType("text/plain");
-      response.setStatus(204);
+      response.getHeaders().put("Content-Type", "text/plain");
+      response.setStatus(HttpStatus.NO_CONTENT_204);
       this.bridge.deleteProject(projectName);
-      baseRequest.setHandled(true);
+      callback.succeeded();
+      return true;
     }
+    return false;
   }
 }

+ 41 - 28
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/PrometheusHandler.java

@@ -4,44 +4,48 @@ import io.prometheus.client.CollectorRegistry;
 import io.prometheus.client.exporter.common.TextFormat;
 import io.prometheus.client.hotspot.DefaultExports;
 import java.io.BufferedWriter;
-import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.util.*;
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Fields;
 import uk.ac.ic.wlgitbridge.util.Log;
 
-public class PrometheusHandler extends AbstractHandler {
+public class PrometheusHandler extends Handler.Abstract {
 
   public PrometheusHandler() {
     DefaultExports.initialize();
   }
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException, ServletException {
-    String method = baseRequest.getMethod();
-    if (("GET".equals(method)) && target != null && target.matches("^/metrics/?$")) {
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String method = request.getMethod();
+    String path = Request.getPathInContext(request);
+    if (("GET".equals(method)) && path != null && path.matches("^/metrics/?$")) {
       Log.debug(method + " <- /metrics");
-      this.printMetrics(request, response);
-      baseRequest.setHandled(true);
+      this.printMetrics(request, response, callback);
+      return true;
     }
+    return false;
   }
 
-  private void printMetrics(HttpServletRequest request, HttpServletResponse response)
-      throws ServletException, IOException {
-    response.setStatus(200);
-    String contentType = TextFormat.chooseContentType(request.getHeader("Accept"));
-    response.setContentType(contentType);
+  private void printMetrics(Request request, Response response, Callback callback)
+      throws Exception {
+    response.setStatus(HttpStatus.OK_200);
+    HttpField acceptField = request.getHeaders().getField(HttpHeader.ACCEPT);
+    String accept = acceptField != null ? acceptField.getValue() : null;
+    String contentType = TextFormat.chooseContentType(accept);
+    response.getHeaders().put("Content-Type", contentType);
 
-    Writer writer = new BufferedWriter(response.getWriter());
+    Writer writer =
+        new BufferedWriter(new OutputStreamWriter(Content.Sink.asOutputStream(response)));
 
     try {
       TextFormat.writeFormat(
@@ -51,15 +55,24 @@ public class PrometheusHandler extends AbstractHandler {
       writer.flush();
     } finally {
       writer.close();
+      callback.succeeded();
     }
   }
 
-  private Set<String> parse(HttpServletRequest req) {
-    String[] includedParam = req.getParameterValues("name[]");
-    if (includedParam == null) {
-      return Collections.emptySet();
-    } else {
+  private Set<String> parse(Request req) {
+    try {
+      Fields parameters = Request.getParameters(req);
+      if (parameters == null) {
+        return Collections.emptySet();
+      }
+      List<String> values = parameters.getValues("name[]");
+      if (values == null || values.isEmpty()) {
+        return Collections.emptySet();
+      }
+      String[] includedParam = values.toArray(new String[0]);
       return new HashSet<String>(Arrays.asList(includedParam));
+    } catch (Exception e) {
+      return Collections.emptySet();
     }
   }
 }

+ 15 - 13
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/server/StatusHandler.java

@@ -1,14 +1,15 @@
 package uk.ac.ic.wlgitbridge.server;
 
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.nio.charset.StandardCharsets;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 import uk.ac.ic.wlgitbridge.bridge.Bridge;
 import uk.ac.ic.wlgitbridge.util.Log;
 
-public class StatusHandler extends AbstractHandler {
+public class StatusHandler extends Handler.Abstract {
 
   private final Bridge bridge;
 
@@ -17,18 +18,19 @@ public class StatusHandler extends AbstractHandler {
   }
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException {
-    String method = baseRequest.getMethod();
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String method = request.getMethod();
+    String target = Request.getPathInContext(request);
     if (("GET".equals(method) || "HEAD".equals(method))
         && target != null
         && target.matches("^/status/?$")) {
       Log.debug(method + " <- /status");
-      baseRequest.setHandled(true);
-      response.setContentType("text/plain");
-      response.setStatus(200);
-      response.getWriter().println("ok");
+      response.setStatus(HttpStatus.OK_200);
+      response.getHeaders().put("Content-Type", "text/plain");
+      response.write(
+          true, java.nio.ByteBuffer.wrap("ok\n".getBytes(StandardCharsets.UTF_8)), callback);
+      return true;
     }
+    return false;
   }
 }

+ 5 - 6
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/base/Request.java

@@ -8,8 +8,8 @@ import com.google.gson.JsonObject;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.concurrent.*;
-import javax.servlet.http.HttpServletResponse;
 import org.asynchttpclient.AsyncHttpClient;
+import org.eclipse.jetty.http.HttpStatus;
 import uk.ac.ic.wlgitbridge.snapshot.exception.FailedConnectionException;
 import uk.ac.ic.wlgitbridge.util.Instance;
 import uk.ac.ic.wlgitbridge.util.Log;
@@ -72,17 +72,16 @@ public abstract class Request<T extends Result> {
       if (cause instanceof HttpResponseException) {
         HttpResponseException httpCause = (HttpResponseException) cause;
         int sc = httpCause.getStatusCode();
-        if (sc == HttpServletResponse.SC_UNAUTHORIZED
-            || sc == HttpServletResponse.SC_FORBIDDEN) { // 401, 403
+        if (sc == HttpStatus.UNAUTHORIZED_401 || sc == HttpStatus.FORBIDDEN_403) {
           throw new ForbiddenException();
-        } else if (sc == 429) { // Too many requests
+        } else if (sc == HttpStatus.TOO_MANY_REQUESTS_429) {
           throw new MissingRepositoryException(
               Arrays.asList(
                   "Rate-limit exceeded. Please wait a while and try again.",
                   "",
                   "If this is unexpected, please contact us at support@overleaf.com, or",
                   "see https://www.overleaf.com/learn/how-to/Git_integration for more information."));
-        } else if (sc == HttpServletResponse.SC_CONFLICT) { // 409
+        } else if (sc == HttpStatus.CONFLICT_409) {
           try {
             JsonObject json = Instance.gson.fromJson(httpCause.getContent(), JsonObject.class);
             String code = json.get("code").getAsString();
@@ -105,7 +104,7 @@ public abstract class Request<T extends Result> {
               | NullPointerException _e) { // json parse errors
             throw new MissingRepositoryException(Arrays.asList("Conflict: 409"));
           }
-        } else if (sc == HttpServletResponse.SC_NOT_FOUND) { // 404
+        } else if (sc == HttpStatus.NOT_FOUND_404) {
           try {
             JsonObject json = Instance.gson.fromJson(httpCause.getContent(), JsonObject.class);
             String message = json.get("message").getAsString();

+ 16 - 14
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockOAuthRequestHandler.java

@@ -1,23 +1,25 @@
 package uk.ac.ic.wlgitbridge.snapshot.servermock.server;
 
-import java.io.IOException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 
-public class MockOAuthRequestHandler extends AbstractHandler {
+public class MockOAuthRequestHandler extends Handler.Abstract {
 
   @Override
-  public void handle(
-      String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
-      throws IOException {
-    String method = baseRequest.getMethod();
-    if (method.equals("GET") && target.equals("/oauth/token/info")) {
-      response.setContentType("application/json");
-      response.setStatus(HttpServletResponse.SC_OK);
-      response.getWriter().println("{}");
-      baseRequest.setHandled(true);
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String method = request.getMethod();
+    String path = Request.getPathInContext(request);
+    if (method.equals("GET") && path.equals("/oauth/token/info")) {
+      response.getHeaders().put("Content-Type", "application/json");
+      response.setStatus(HttpStatus.OK_200);
+      response.write(true, ByteBuffer.wrap("{}\n".getBytes(StandardCharsets.UTF_8)), callback);
+      return true;
     }
+    return false;
   }
 }

+ 17 - 16
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotRequestHandler.java

@@ -1,11 +1,12 @@
 package uk.ac.ic.wlgitbridge.snapshot.servermock.server;
 
-import java.io.IOException;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.Callback;
 import uk.ac.ic.wlgitbridge.snapshot.servermock.exception.InvalidAPICallException;
 import uk.ac.ic.wlgitbridge.snapshot.servermock.response.*;
 import uk.ac.ic.wlgitbridge.util.Log;
@@ -13,7 +14,7 @@ import uk.ac.ic.wlgitbridge.util.Log;
 /*
  * Created by Winston on 09/01/15.
  */
-public class MockSnapshotRequestHandler extends AbstractHandler {
+public class MockSnapshotRequestHandler extends Handler.Abstract {
 
   private final SnapshotResponseBuilder responseBuilder;
 
@@ -22,25 +23,25 @@ public class MockSnapshotRequestHandler extends AbstractHandler {
   }
 
   @Override
-  public void handle(
-      String target,
-      final Request baseRequest,
-      HttpServletRequest request,
-      HttpServletResponse response)
-      throws IOException, ServletException {
+  public boolean handle(Request request, Response response, Callback callback) throws Exception {
+    String path = Request.getPathInContext(request);
     boolean handled;
     try {
+      String requestBody = Content.Source.asString(request);
       final SnapshotResponse snapshotResponse =
-          responseBuilder.buildWithTarget(target, baseRequest.getMethod());
-      response.getWriter().println(snapshotResponse.respond());
-      new PostbackThread(baseRequest.getReader(), snapshotResponse.postback()).startIfNotNull();
+          responseBuilder.buildWithTarget(path, request.getMethod());
+      String responseText = snapshotResponse.respond() + "\n";
+      response.write(
+          true, ByteBuffer.wrap(responseText.getBytes(StandardCharsets.UTF_8)), callback);
+      new PostbackThread(requestBody, snapshotResponse.postback()).startIfNotNull();
       handled = true;
     } catch (InvalidAPICallException e) {
       handled = false;
     } catch (RuntimeException e) {
       Log.warn("Runtime exception when handling request", e);
+      callback.succeeded();
       handled = true;
     }
-    baseRequest.setHandled(handled);
+    return handled;
   }
 }

+ 7 - 6
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/MockSnapshotServer.java

@@ -1,11 +1,11 @@
 package uk.ac.ic.wlgitbridge.snapshot.servermock.server;
 
 import java.io.File;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.NetworkConnector;
 import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.eclipse.jetty.server.handler.HandlerList;
 import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.util.resource.ResourceFactory;
 import uk.ac.ic.wlgitbridge.snapshot.servermock.response.SnapshotResponseBuilder;
 import uk.ac.ic.wlgitbridge.snapshot.servermock.state.SnapshotAPIState;
 import uk.ac.ic.wlgitbridge.util.Log;
@@ -23,14 +23,14 @@ public class MockSnapshotServer {
     server = new Server(port);
     responseBuilder = new SnapshotResponseBuilder();
 
-    HandlerList handlers = new HandlerList();
+    Handler.Sequence handlers = new Handler.Sequence();
     handlers.addHandler(new MockOAuthRequestHandler());
     handlers.addHandler(getHandlerForResourceBase(resourceBase));
     server.setHandler(handlers);
   }
 
-  private HandlerCollection getHandlerForResourceBase(File resourceBase) {
-    HandlerCollection handlers = new HandlerCollection();
+  private Handler.Sequence getHandlerForResourceBase(File resourceBase) {
+    Handler.Sequence handlers = new Handler.Sequence();
     handlers.addHandler(new MockSnapshotRequestHandler(responseBuilder));
     handlers.addHandler(resourceHandlerWithBase(resourceBase));
     return handlers;
@@ -38,7 +38,8 @@ public class MockSnapshotServer {
 
   private ResourceHandler resourceHandlerWithBase(File resourceBase) {
     ResourceHandler resourceHandler = new ResourceHandler();
-    resourceHandler.setResourceBase(resourceBase.getAbsolutePath());
+    resourceHandler.setBaseResource(
+        ResourceFactory.of(resourceHandler).newResource(resourceBase.toPath()));
     return resourceHandler;
   }
 

+ 3 - 4
services/git-bridge/src/main/java/uk/ac/ic/wlgitbridge/snapshot/servermock/server/PostbackThread.java

@@ -4,7 +4,6 @@ import static org.asynchttpclient.Dsl.*;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonObject;
-import java.io.Reader;
 import java.util.concurrent.ExecutionException;
 import uk.ac.ic.wlgitbridge.util.Log;
 
@@ -16,9 +15,9 @@ public class PostbackThread extends Thread {
   private String url;
   private String postback;
 
-  public PostbackThread(Reader reader, String postback) {
-    if (postback != null) {
-      url = new Gson().fromJson(reader, JsonObject.class).get("postbackUrl").getAsString();
+  public PostbackThread(String requestBody, String postback) {
+    if (postback != null && requestBody != null && !requestBody.isEmpty()) {
+      url = new Gson().fromJson(requestBody, JsonObject.class).get("postbackUrl").getAsString();
       this.postback = postback;
     }
   }