content: import io.muserver.*;
  import io.muserver.handlers.ResourceHandlerBuilder;
  import io.muserver.rest.CORSConfigBuilder;
  import io.muserver.rest.RestHandlerBuilder;
  import org.example.petstore.resource.PetResource;
  import org.example.petstore.resource.PetStoreResource;
  import org.example.petstore.resource.UserResource;
  import org.example.petstore.resource.VehicleResource;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import scaffolding.StringUtils;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.util.concurrent.Executors;
  import java.util.concurrent.ScheduledExecutorService;
  import java.util.concurrent.TimeUnit;
  
  import static io.muserver.ContextHandlerBuilder.context;
  import static io.muserver.Http2ConfigBuilder.http2EnabledIfAvailable;
  import static io.muserver.MuServerBuilder.muServer;
  import static io.muserver.WebSocketHandlerBuilder.webSocketHandler;
  import static io.muserver.handlers.AsyncFileProviderTest.BIG_FILE_DIR;
  import static io.muserver.handlers.ResourceHandlerBuilder.fileOrClasspath;
  
  public class RunLocal {
      private static final Logger log = LoggerFactory.getLogger(RunLocal.class);
      private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
  
      public static void main(String[] args) {
  
          MuServerBuilder builder = muServer()
              .withHttpPort(18080)
              .withHttpsPort(18443)
              .withHttp2Config(http2EnabledIfAvailable())
              .withMaxRequestSize(Long.MAX_VALUE)
              .withRateLimiter(request -> RateLimit.builder()
                  .withBucket(request.remoteAddress())
                  .withRate(10000).withWindow(1, TimeUnit.SECONDS)
                  .build())
              .addHandler(ResourceHandlerBuilder.fileHandler(BIG_FILE_DIR))
              .addHandler(fileOrClasspath("src/test/resources/sample-static", "/sample-static"))
              .addHandler(Method.GET, "/connections", (request, response, pathParams) -> {
                  response.contentType("text/plain;charset=utf8");
                  PrintWriter writer = response.writer();
                  for (HttpConnection connection : request.server().activeConnections()) {
                      writer.println(connection.protocol() + " " + " " + connection.httpsProtocol() + " from " + connection.remoteAddress());
                      for (MuRequest activeRequest : connection.activeRequests()) {
                          writer.println(" |-- " + activeRequest);
                      }
                  }
                  writer.println("-------------------------");
                  writer.close();
              })
              .addHandler(
                  context("files").addHandler(
                      fileOrClasspath("non-existant-path", "/sample-static")
                          .withDefaultFile(null)
                          .withDirectoryListing(true)
                  )
              )
              .addHandler(Method.GET, "/api", (request, response, pathParams) -> {
                  response.contentType(ContentTypes.APPLICATION_JSON);
                  response.write("{ \"hello\": \"world                    this is something           to be gzipped\" }");
              })
              .addHandler(Method.GET, "/lotsa-chunks", (request, response, pathParams) -> {
                  response.contentType("text/plain;charset=utf-8");
                  String thing = StringUtils.randomAsciiStringOfLength(1000) + "\n\n";
                  for (int i = 0; i < 1000000; i++) {
                      response.sendChunk(thing);
                  }
              })
              .addHandler(
                  RestHandlerBuilder.restHandler(
                          new PetResource(), new PetStoreResource(), new UserResource(), new VehicleResource()
                      )
                      .withOpenApiJsonUrl("/openapi.json")
                      .withOpenApiHtmlUrl("/api.html")
                      .withCORS(CORSConfigBuilder.corsConfig()
                          .withAllOriginsAllowed()
                          .withAllowedHeaders("Content-Type")
                          .withExposedHeaders("Content-Type"))
              )
              .addHandler(Method.POST, "/upload", (request, response, pathParams) -> {
                  UploadedFile file = request.uploadedFile("theFile");
                  if (file == null) {
                      response.write("No file uploaded");
                  } else {
                      response.contentType(file.contentType());
                      response.headers().set(HeaderNames.CONTENT_LENGTH, file.size());
                      boolean ticked = request.form().getBoolean("ticked");
                      log.info("Form parameters: " + request.form().get("blah") + " - " + ticked);
                      log.info("Going to send " + file.size() + " bytes as " + file.contentType());
                      try (InputStream fileStream = file.asStream();
                           OutputStream out = response.outputStream()) {
                          Mutils.copy(fileStream, out, 8192);
                      }
                  }
              })
              .addHandler(webSocketHandler()
                  .withPath("/websocket-echo")
                  .withWebSocketFactory((request, responseHeaders) -> new BaseWebSocket() {
                      public void onText(String message, boolean isLast, DoneCallback onComplete) throws IOException {
                          session().sendText(message, onComplete);
                          if (message.equalsIgnoreCase("close")) {
                              session().close(1000, "Finished");
                          }
                      }
                  }))
              .addHandler(Method.GET, "/stream", (request, response, pathParams) -> {
                  response.contentType(ContentTypes.TEXT_PLAIN);
                  for (int i = 0; i < Integer.MAX_VALUE; i++) {
                      if ((i % 100) == 0) {
                          response.sendChunk("*");
                      } else {
                          response.sendChunk(".");
                      }
                      Thread.sleep(10);
                  }
              })
              .addHandler(Method.GET, "/streamer", (request, response, pathParams) -> {
                  int startValue = request.headers().getInt(HeaderNames.LAST_EVENT_ID, 1);
                  log.info("Starting event stream at " + startValue);
                  SsePublisher ssePublisher = SsePublisher.start(request, response);
                  new Thread(() -> {
                      try {
                          for (int i = startValue; i < startValue + 10; i++) {
                              ssePublisher.send("This is message " + i, null, String.valueOf(i));
                              Thread.sleep(1000);
                          }
                      } catch (Exception e) {
                          // the user has probably disconnected; stop publishing
                          log.info("Error while publishing to event stream", e);
                      } finally {
                          ssePublisher.close();
                      }
                  }).start();
              })
              .addHandler(Method.GET, "/stats", (request, response, pathParams) -> {
                  MuStats stats = request.server().stats();
                  response.contentType(ContentTypes.TEXT_PLAIN);
                  response.sendChunk(stats.toString());
                  response.sendChunk("\n\n");
                  for (MuRequest muRequest : stats.activeRequests()) {
                      response.sendChunk(muRequest + "\r\n");
                  }
              })
              .addHandler(Method.GET, "/sleep", (req, resp, pp) -> {
                  AsyncHandle asyncHandle = req.handleAsync();
                  executor.schedule((Runnable) asyncHandle::complete,
                      req.query().getLong("millis", 5000), TimeUnit.MILLISECONDS);
                  asyncHandle.addResponseCompleteHandler(info -> log.info("Woke up: " + info));
              })
              .addHandler(Method.GET, "/throw-exception", (request, response, pathParams) -> { throw new RuntimeException("A runtime exception"); })
              .withExceptionHandler((request, response, cause) -> {
                  if (response.hasStartedSendingData()) return false;
                  response.writer().write("Oops, something went wrong: " + cause);
                  return true;
              });
          log.info("Builder: " + builder);
          MuServer server = builder.start();
  
  
          log.info("Started at " + server.httpUri() + " and " + server.httpsUri());
          log.info("REST docs available at " + server.httpUri().resolve("/api.html") + " and OpenAPI JSON at " + server.httpUri().resolve("/openapi.json"));
          log.info("Download stuff at " + server.uri().resolve("/files"));
  
  
          Runtime.getRuntime().addShutdownHook(new Thread(server::stop));
      }
  
  }
  