// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.remote.server;

//import static com.google.common.net.MediaType.JAVASCRIPT_UTF_8;

import static com.google.common.net.MediaType.JSON_UTF_8;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
import static java.net.HttpURLConnection.HTTP_OK;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.openqa.selenium.remote.ErrorCodes.UNKNOWN_COMMAND;

import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.gson.GsonBuilder;

import org.openqa.selenium.remote.SessionId;
import org.openqa.selenium.remote.http.HttpRequest;
import org.openqa.selenium.remote.http.HttpResponse;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;


class AllHandlers {

  private final ActiveSessions allSessions;
  private final DriverSessions legacySessions;

  public AllHandlers(ActiveSessions allSessions, DriverSessions legacySessions) {
    this.allSessions = allSessions;
    this.legacySessions = legacySessions;
  }

  public CommandHandler match(HttpServletRequest req) {
    String path = Strings.isNullOrEmpty(req.getPathInfo()) ? "/" : req.getPathInfo();

    // All commands that take a session id expect that as the path fragment immediately after "/session".
    SessionId id = null;
    List<String> fragments = Splitter.on('/').limit(4).splitToList(path);
    if (fragments.size() > 2) {
      if ("session".equals(fragments.get(1))) {
        id = new SessionId(fragments.get(2));
      }
    }

    if (id != null) {
      ActiveSession session = allSessions.get(id);
      if (session != null) {
        return session;
      }
    }

    if ("POST".equalsIgnoreCase(req.getMethod()) && "/session".equals(path)) {
      return new BeginSession(allSessions, legacySessions);
    }

    if ("GET".equalsIgnoreCase(req.getMethod()) && "/status".equals(path)) {
      return new StatusHandler();
    }

    return new NoHandler();
  }

  private static class NoHandler implements CommandHandler {

    @Override
    public void execute(HttpRequest req, HttpResponse resp) throws IOException {
      // We're not using ImmutableMap for the outer map because it disallows null values.
      Map<String, Object> responseMap = new HashMap<>();
      responseMap.put("sessionId", null);
      responseMap.put("status", UNKNOWN_COMMAND);
      responseMap.put("value", ImmutableMap.of(
          "error", "unknown command",
          "message", String.format(
              "Unable to find command matching %s to %s",
              req.getMethod(),
              req.getUri()),
          "stacktrace", ""));
      responseMap = Collections.unmodifiableMap(responseMap);

      byte[] payload = new GsonBuilder().serializeNulls().create().toJson(responseMap)
          .getBytes(UTF_8);

      resp.setStatus(HTTP_NOT_FOUND);
      resp.setHeader("Content-Type", JSON_UTF_8.toString());
      resp.setHeader("Content-Length", String.valueOf(payload.length));

      resp.setContent(payload);
    }
  }

  private static class StatusHandler implements CommandHandler {

    @Override
    public void execute(HttpRequest req, HttpResponse resp) throws IOException {
      // Write out a minimal W3C status response.
      byte[] payload = new GsonBuilder().create().toJson(ImmutableMap.of(
          "ready", true,
          "message", "Server is running"
      )).getBytes(UTF_8);

      resp.setStatus(HTTP_OK);
      resp.setHeader("Content-Type", JSON_UTF_8.toString());
      resp.setHeader("Content-Length", String.valueOf(payload.length));

      resp.setContent(payload);
    }
  }
}

@@ -1124,24 +1124,93 @@
 sets.UTF_8;%0A
+import static org.openqa.selenium.remote.ErrorCodes.NO_SUCH_SESSION;%0A
 import stati
@@ -2617,17 +2617,17 @@
 session 
-!
+=
 = null) 
@@ -2647,16 +2647,63 @@
 urn 
+new NoSessionHandler(id);%0A      %7D%0A      return 
 session;
 %0A   
@@ -2698,24 +2698,16 @@
 session;
-%0A      %7D
 %0A    %7D%0A%0A
@@ -4059,24 +4059,1167 @@
 %0A    %7D%0A  %7D%0A%0A
+  private static class NoSessionHandler implements CommandHandler %7B%0A%0A    private final SessionId sessionId;%0A%0A    public NoSessionHandler(SessionId sessionId) %7B%0A      this.sessionId = sessionId;%0A    %7D%0A%0A    @Override%0A    public void execute(HttpRequest req, HttpResponse resp) throws IOException %7B%0A      // We're not using ImmutableMap for the outer map because it disallows null values.%0A      Map%3CString, Object%3E responseMap = new HashMap%3C%3E();%0A      responseMap.put(%22sessionId%22, sessionId.toString());%0A      responseMap.put(%22status%22, NO_SUCH_SESSION);%0A      responseMap.put(%22value%22, ImmutableMap.of(%0A          %22error%22, %22invalid session id%22,%0A          %22message%22, String.format(%22No active session with ID %25s%22, sessionId),%0A          %22stacktrace%22, %22%22));%0A      responseMap = Collections.unmodifiableMap(responseMap);%0A%0A      byte%5B%5D payload = new GsonBuilder().serializeNulls().create().toJson(responseMap)%0A          .getBytes(UTF_8);%0A%0A      resp.setStatus(HTTP_NOT_FOUND);%0A      resp.setHeader(%22Content-Type%22, JSON_UTF_8.toString());%0A      resp.setHeader(%22Content-Length%22, String.valueOf(payload.length));%0A%0A      resp.setContent(payload);%0A    %7D%0A  %7D%0A%0A
   private st

Use correct error when session does not exist.