public RelayCookie resolve(String serverID, String id) throws IOException {
    if (!Util.isQuickConnectId(serverID)) {
      throw new IllegalArgumentException("serverID isn't a Quick Connect ID");
    }

    RelayManager relayManager = (RelayManager) RelayHandler.getDefault();
    RelayCookie cookie = relayManager.get(serverID);
    if (cookie == null) {
      cookie = new RelayCookie.Builder().serverID(serverID).id(id).build();
      relayManager.put(serverID, cookie);
    }

    HttpUrl serverUrl = HttpUrl.parse("http://global.quickconnect.to/Serv.php");
    ServerInfoJson infoJson = getServerInfo(serverUrl, serverID, id);

    // ping DSM directly
    HttpUrl resolvedUrl = pingDSM(infoJson);
    if (resolvedUrl != null) {
      cookie = cookie.newBuilder().resolvedUrl(resolvedUrl).build();
      return cookie;
    }

    // ping DSM through tunnel
    resolvedUrl = pingTunnel(infoJson.service);
    if (resolvedUrl != null) {
      cookie = cookie.newBuilder().resolvedUrl(resolvedUrl).build();
      return cookie;
    }

    // request tunnel
    infoJson = requestTunnel(infoJson, serverID, id);
    if (infoJson != null) {
      resolvedUrl =
          requestUrl
              .newBuilder()
              .host(infoJson.service.relay_ip)
              .port(infoJson.service.relay_port)
              .build();
      cookie = cookie.newBuilder().resolvedUrl(resolvedUrl).build();
      return cookie;
    }

    throw new IOException("No valid url resolved");
  }
  public ServerInfoJson requestTunnel(ServerInfoJson infoJson, String serverID, String id)
      throws IOException {
    if (infoJson == null || infoJson.env == null || Util.isEmpty(infoJson.env.control_host)) {
      return null;
    }

    // set timeout to 30 seconds
    OkHttpClient client =
        defaultClient
            .newBuilder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build();

    final String server = infoJson.env.control_host;

    JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("command", "request_tunnel");
    jsonObject.addProperty("version", 1);
    jsonObject.addProperty("serverID", serverID);
    jsonObject.addProperty("id", id);

    RequestBody requestBody =
        RequestBody.create(MediaType.parse("text/plain"), gson.toJson(jsonObject));
    Request request =
        new Request.Builder()
            .url(HttpUrl.parse("http://" + server + "/Serv.php"))
            .post(requestBody)
            .build();
    Response response = client.newCall(request).execute();
    JsonReader reader = null;
    try {
      InputStream in = response.body().byteStream();
      reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
      return gson.fromJson(reader, ServerInfoJson.class);
    } finally {
      if (reader != null) {
        reader.close();
      }
    }
  }