222 lines
6.7 KiB
Java
222 lines
6.7 KiB
Java
package com.limelight.nvstream.http;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.io.Reader;
|
|
import java.io.StringReader;
|
|
import java.net.Inet6Address;
|
|
import java.net.InetAddress;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.net.URLConnection;
|
|
import java.util.LinkedList;
|
|
import java.util.Scanner;
|
|
import java.util.Stack;
|
|
|
|
import javax.crypto.SecretKey;
|
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
import org.xmlpull.v1.XmlPullParserFactory;
|
|
|
|
|
|
public class NvHTTP {
|
|
private String uniqueId;
|
|
private PairingManager pm;
|
|
private LimelightCryptoProvider cryptoProvider;
|
|
|
|
public static final int PORT = 47984;
|
|
public static final int CONNECTION_TIMEOUT = 5000;
|
|
|
|
private final boolean verbose = false;
|
|
|
|
public String baseUrl;
|
|
|
|
public NvHTTP(InetAddress host, String uniqueId, String deviceName, LimelightCryptoProvider cryptoProvider) {
|
|
this.uniqueId = uniqueId;
|
|
this.cryptoProvider = cryptoProvider;
|
|
|
|
String safeAddress;
|
|
if (host instanceof Inet6Address) {
|
|
// RFC2732-formatted IPv6 address for use in URL
|
|
safeAddress = "["+host.getHostAddress()+"]";
|
|
}
|
|
else {
|
|
safeAddress = host.getHostAddress();
|
|
}
|
|
|
|
this.baseUrl = "https://" + safeAddress + ":" + PORT;
|
|
this.pm = new PairingManager(this, cryptoProvider);
|
|
}
|
|
|
|
static String getXmlString(Reader r, String tagname) throws XmlPullParserException, IOException {
|
|
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
|
factory.setNamespaceAware(true);
|
|
XmlPullParser xpp = factory.newPullParser();
|
|
|
|
xpp.setInput(r);
|
|
int eventType = xpp.getEventType();
|
|
Stack<String> currentTag = new Stack<String>();
|
|
|
|
while (eventType != XmlPullParser.END_DOCUMENT) {
|
|
switch (eventType) {
|
|
case (XmlPullParser.START_TAG):
|
|
if (xpp.getName().equals("root")) {
|
|
verifyResponseStatus(xpp);
|
|
}
|
|
currentTag.push(xpp.getName());
|
|
break;
|
|
case (XmlPullParser.END_TAG):
|
|
currentTag.pop();
|
|
break;
|
|
case (XmlPullParser.TEXT):
|
|
if (currentTag.peek().equals(tagname)) {
|
|
return xpp.getText();
|
|
}
|
|
break;
|
|
}
|
|
eventType = xpp.next();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
static String getXmlString(String str, String tagname) throws XmlPullParserException, IOException {
|
|
return getXmlString(new StringReader(str), tagname);
|
|
}
|
|
|
|
static String getXmlString(InputStream in, String tagname) throws XmlPullParserException, IOException {
|
|
return getXmlString(new InputStreamReader(in), tagname);
|
|
}
|
|
|
|
private static void verifyResponseStatus(XmlPullParser xpp) throws GfeHttpResponseException {
|
|
int statusCode = Integer.parseInt(xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_code"));
|
|
if (statusCode != 200) {
|
|
throw new GfeHttpResponseException(statusCode, xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_message"));
|
|
}
|
|
}
|
|
|
|
private InputStream openHttpConnection(String url) throws IOException {
|
|
URLConnection conn = new URL(url).openConnection();
|
|
if (verbose) {
|
|
System.out.println(url);
|
|
}
|
|
conn.setConnectTimeout(CONNECTION_TIMEOUT);
|
|
conn.setUseCaches(false);
|
|
conn.connect();
|
|
return conn.getInputStream();
|
|
}
|
|
|
|
String openHttpConnectionToString(String url) throws MalformedURLException, IOException {
|
|
Scanner s = new Scanner(openHttpConnection(url));
|
|
|
|
String str = "";
|
|
while (s.hasNext()) {
|
|
str += s.next() + " ";
|
|
}
|
|
|
|
s.close();
|
|
|
|
if (verbose) {
|
|
System.out.println(str);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
public String getServerVersion() throws XmlPullParserException, IOException {
|
|
InputStream in = openHttpConnection(baseUrl + "/serverinfo?uniqueid=" + uniqueId);
|
|
return getXmlString(in, "appversion");
|
|
}
|
|
|
|
public PairingManager.PairState getPairState() throws IOException, XmlPullParserException {
|
|
return pm.getPairState(uniqueId);
|
|
}
|
|
|
|
public int getCurrentGame() throws IOException, XmlPullParserException {
|
|
InputStream in = openHttpConnection(baseUrl + "/serverinfo?uniqueid=" + uniqueId);
|
|
String game = getXmlString(in, "currentgame");
|
|
return Integer.parseInt(game);
|
|
}
|
|
|
|
public NvApp getSteamApp() throws IOException,
|
|
XmlPullParserException {
|
|
LinkedList<NvApp> appList = getAppList();
|
|
for (NvApp app : appList) {
|
|
if (app.getAppName().equals("Steam")) {
|
|
return app;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public PairingManager.PairState pair(String pin) throws Exception {
|
|
return pm.pair(uniqueId, pin);
|
|
}
|
|
|
|
public LinkedList<NvApp> getAppList() throws GfeHttpResponseException, IOException, XmlPullParserException {
|
|
InputStream in = openHttpConnection(baseUrl + "/applist?uniqueid=" + uniqueId);
|
|
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
|
factory.setNamespaceAware(true);
|
|
XmlPullParser xpp = factory.newPullParser();
|
|
|
|
xpp.setInput(new InputStreamReader(in));
|
|
int eventType = xpp.getEventType();
|
|
LinkedList<NvApp> appList = new LinkedList<NvApp>();
|
|
Stack<String> currentTag = new Stack<String>();
|
|
|
|
while (eventType != XmlPullParser.END_DOCUMENT) {
|
|
switch (eventType) {
|
|
case (XmlPullParser.START_TAG):
|
|
if (xpp.getName().equals("root")) {
|
|
verifyResponseStatus(xpp);
|
|
}
|
|
currentTag.push(xpp.getName());
|
|
if (xpp.getName().equals("App")) {
|
|
appList.addLast(new NvApp());
|
|
}
|
|
break;
|
|
case (XmlPullParser.END_TAG):
|
|
currentTag.pop();
|
|
break;
|
|
case (XmlPullParser.TEXT):
|
|
NvApp app = appList.getLast();
|
|
if (currentTag.peek().equals("AppTitle")) {
|
|
app.setAppName(xpp.getText());
|
|
} else if (currentTag.peek().equals("ID")) {
|
|
app.setAppId(xpp.getText());
|
|
} else if (currentTag.peek().equals("IsRunning")) {
|
|
app.setIsRunning(xpp.getText());
|
|
}
|
|
break;
|
|
}
|
|
eventType = xpp.next();
|
|
}
|
|
return appList;
|
|
}
|
|
|
|
public int launchApp(int appId, int width, int height, int refreshRate, SecretKey inputKey) throws IOException, XmlPullParserException {
|
|
InputStream in = openHttpConnection(baseUrl +
|
|
"/launch?uniqueid=" + uniqueId +
|
|
"&appid=" + appId +
|
|
"&mode=" + width + "x" + height + "x" + refreshRate +
|
|
"&additionalStates=1&sops=1&rikey="+cryptoProvider.encodeBase64String(inputKey.getEncoded()));
|
|
String gameSession = getXmlString(in, "gamesession");
|
|
return Integer.parseInt(gameSession);
|
|
}
|
|
|
|
public boolean resumeApp(SecretKey inputKey) throws IOException, XmlPullParserException {
|
|
InputStream in = openHttpConnection(baseUrl + "/resume?uniqueid=" + uniqueId +
|
|
"&rikey="+cryptoProvider.encodeBase64String(inputKey.getEncoded()));
|
|
String resume = getXmlString(in, "resume");
|
|
return Integer.parseInt(resume) != 0;
|
|
}
|
|
|
|
public boolean quitApp() throws IOException, XmlPullParserException {
|
|
InputStream in = openHttpConnection(baseUrl + "/cancel?uniqueid=" + uniqueId);
|
|
String cancel = getXmlString(in, "cancel");
|
|
return Integer.parseInt(cancel) != 0;
|
|
}
|
|
}
|