/*
  These applications are required by the SockJS protocol and QUnit tests
   */
  public void installTestApplications() {
    installApp(
        new JsonObject().putString("prefix", "/echo").putNumber("max_bytes_streaming", 4096),
        new Handler<SockJSSocket>() {
          public void handle(final SockJSSocket sock) {
            sock.dataHandler(
                new Handler<Buffer>() {
                  public void handle(Buffer buff) {
                    sock.writeBuffer(buff);
                  }
                });
          }
        });
    installApp(
        new JsonObject().putString("prefix", "/close").putNumber("max_bytes_streaming", 4096),
        new Handler<SockJSSocket>() {
          public void handle(final SockJSSocket sock) {
            sock.close();
          }
        });
    JsonArray disabled = new JsonArray();
    disabled.add(Transport.WEBSOCKET.toString());
    installApp(
        new JsonObject()
            .putString("prefix", "/disabled_websocket_echo")
            .putNumber("max_bytes_streaming", 4096)
            .putArray("disabled_transports", disabled),
        new Handler<SockJSSocket>() {
          public void handle(final SockJSSocket sock) {
            sock.dataHandler(
                new Handler<Buffer>() {
                  public void handle(Buffer buff) {
                    sock.writeBuffer(buff);
                  }
                });
          }
        });
    installApp(
        new JsonObject().putString("prefix", "/ticker").putNumber("max_bytes_streaming", 4096),
        new Handler<SockJSSocket>() {
          public void handle(final SockJSSocket sock) {
            final long timerID =
                vertx.setPeriodic(
                    1000,
                    new Handler<Long>() {
                      public void handle(Long id) {
                        sock.writeBuffer(new Buffer("tick!"));
                      }
                    });
            sock.endHandler(
                new SimpleHandler() {
                  public void handle() {
                    vertx.cancelTimer(timerID);
                  }
                });
          }
        });
    installApp(
        new JsonObject().putString("prefix", "/amplify").putNumber("max_bytes_streaming", 4096),
        new Handler<SockJSSocket>() {
          long timerID;

          public void handle(final SockJSSocket sock) {
            sock.dataHandler(
                new Handler<Buffer>() {
                  public void handle(Buffer data) {
                    String str = data.toString();
                    int n = Integer.valueOf(str);
                    if (n < 0 || n > 19) {
                      n = 1;
                    }
                    int num = (int) Math.pow(2, n);
                    Buffer buff = new Buffer(num);
                    for (int i = 0; i < num; i++) {
                      buff.appendByte((byte) 'x');
                    }
                    sock.writeBuffer(buff);
                  }
                });
          }
        });
    installApp(
        new JsonObject().putString("prefix", "/broadcast").putNumber("max_bytes_streaming", 4096),
        new Handler<SockJSSocket>() {
          final Set<String> connections = vertx.sharedData().getSet("conns");

          public void handle(final SockJSSocket sock) {
            connections.add(sock.writeHandlerID);
            sock.dataHandler(
                new Handler<Buffer>() {
                  public void handle(Buffer buffer) {
                    for (String actorID : connections) {
                      vertx.eventBus().publish(actorID, buffer);
                    }
                  }
                });
            sock.endHandler(
                new SimpleHandler() {
                  public void handle() {
                    connections.remove(sock.writeHandlerID);
                  }
                });
          }
        });
    installApp(
        new JsonObject()
            .putString("prefix", "/cookie_needed_echo")
            .putNumber("max_bytes_streaming", 4096)
            .putBoolean("insert_JSESSIONID", true),
        new Handler<SockJSSocket>() {
          public void handle(final SockJSSocket sock) {
            sock.dataHandler(
                new Handler<Buffer>() {
                  public void handle(Buffer buff) {
                    sock.writeBuffer(buff);
                  }
                });
          }
        });
  }
  public void installApp(JsonObject config, final Handler<SockJSSocket> sockHandler) {

    config = setDefaults(config);

    String prefix = config.getString("prefix");

    if (prefix == null || prefix.equals("") || prefix.endsWith("/")) {
      throw new IllegalArgumentException("Invalid prefix: " + prefix);
    }

    // Base handler for app

    rm.getWithRegEx(
        prefix + "\\/?",
        new Handler<HttpServerRequest>() {
          public void handle(HttpServerRequest req) {
            if (log.isTraceEnabled()) log.trace("Returning welcome response");
            req.response.headers().put("Content-Type", "text/plain; charset=UTF-8");
            req.response.end("Welcome to SockJS!\n");
          }
        });

    // Iframe handlers
    String iframeHTML =
        IFRAME_TEMPLATE.replace("{{ sockjs_url }}", config.getString("library_url"));
    Handler<HttpServerRequest> iframeHandler = createIFrameHandler(iframeHTML);

    // Request exactly for iframe.html
    rm.getWithRegEx(prefix + "\\/iframe\\.html", iframeHandler);

    // Versioned
    rm.getWithRegEx(prefix + "\\/iframe-[^\\/]*\\.html", iframeHandler);

    // Chunking test
    rm.postWithRegEx(prefix + "\\/chunking_test", createChunkingTestHandler());
    rm.optionsWithRegEx(
        prefix + "\\/chunking_test",
        BaseTransport.createCORSOptionsHandler(config, "OPTIONS, POST"));

    // Info
    rm.getWithRegEx(prefix + "\\/info", BaseTransport.createInfoHandler(config));
    rm.optionsWithRegEx(
        prefix + "\\/info", BaseTransport.createCORSOptionsHandler(config, "OPTIONS, GET"));

    // Transports

    Set<String> enabledTransports = new HashSet<>();
    enabledTransports.add(Transport.EVENT_SOURCE.toString());
    enabledTransports.add(Transport.HTML_FILE.toString());
    enabledTransports.add(Transport.JSON_P.toString());
    enabledTransports.add(Transport.WEBSOCKET.toString());
    enabledTransports.add(Transport.XHR.toString());
    for (Object tr : config.getArray("disabled_transports", new JsonArray())) {
      enabledTransports.remove(tr);
    }

    if (enabledTransports.contains(Transport.XHR.toString())) {
      new XhrTransport(vertx, rm, prefix, sessions, config, sockHandler);
    }
    if (enabledTransports.contains(Transport.EVENT_SOURCE.toString())) {
      new EventSourceTransport(vertx, rm, prefix, sessions, config, sockHandler);
    }
    if (enabledTransports.contains(Transport.HTML_FILE.toString())) {
      new HtmlFileTransport(vertx, rm, prefix, sessions, config, sockHandler);
    }
    if (enabledTransports.contains(Transport.JSON_P.toString())) {
      new JsonPTransport(vertx, rm, prefix, sessions, config, sockHandler);
    }
    if (enabledTransports.contains(Transport.WEBSOCKET.toString())) {
      new WebSocketTransport(vertx, wsMatcher, rm, prefix, sessions, config, sockHandler);
      new RawWebSocketTransport(vertx, wsMatcher, rm, prefix, sockHandler);
    }
    // Catch all for any other requests on this app

    rm.getWithRegEx(
        prefix + "\\/.+",
        new Handler<HttpServerRequest>() {
          public void handle(HttpServerRequest req) {
            if (log.isTraceEnabled())
              log.trace("Request: " + req.uri + " does not match, returning 404");
            req.response.statusCode = 404;
            req.response.end();
          }
        });
  }