UPnP fixes part 6:

Fix malformed HTTP requests
Check HTTP response code in Parser
Check content type in Parser
Debug log in Parser
Show device URL in CLI
Don't retry after parser exception
Close resources in finally block
This commit is contained in:
zzz
2020-05-22 18:22:56 +00:00
parent a3fc8af1dd
commit 6aa81f7ec6
3 changed files with 53 additions and 13 deletions

View File

@ -1634,6 +1634,9 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis
": " + DataHelper.escapeHTML(device.getFriendlyName()) + "</h3>"); ": " + DataHelper.escapeHTML(device.getFriendlyName()) + "</h3>");
System.out.println("<p>UDN: " + DataHelper.escapeHTML(device.getUDN())); System.out.println("<p>UDN: " + DataHelper.escapeHTML(device.getUDN()));
System.out.println("<br>IP: " + getIP(device)); System.out.println("<br>IP: " + getIP(device));
String loc = device.getLocation();
if (loc != null && loc.length() > 0)
System.out.println("<br>URL: <a href=\"" + loc + "\">" + loc + "</a>");
System.out.println(sb.toString()); System.out.println(sb.toString());
sb.setLength(0); sb.setLength(0);
} }

View File

@ -64,6 +64,7 @@ package org.cybergarage.upnp;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Locale;
import org.cybergarage.http.HTTPRequest; import org.cybergarage.http.HTTPRequest;
import org.cybergarage.http.HTTPRequestListener; import org.cybergarage.http.HTTPRequestListener;
@ -253,6 +254,15 @@ public class ControlPoint implements HTTPRequestListener
String location = ssdpPacket.getLocation(); String location = ssdpPacket.getLocation();
try { try {
URL locationUrl = new URL(location); URL locationUrl = new URL(location);
// I2P
// Roku fake json port, the real UPnP port is 8060
if (locationUrl.getPort() == 9080) {
String lcusn = usn.toLowerCase(Locale.US);
if (lcusn.contains("rku") || lcusn.contains("roku")) {
Debug.warning("Ignoring Roku at " + location);
return;
}
}
Parser parser = UPnP.getXMLParser(); Parser parser = UPnP.getXMLParser();
Node rootNode = parser.parse(locationUrl); Node rootNode = parser.parse(locationUrl);
Device rootDev = getDevice(rootNode); Device rootDev = getDevice(rootNode);
@ -270,12 +280,10 @@ public class ControlPoint implements HTTPRequestListener
performAddDeviceListener( rootDev ); performAddDeviceListener( rootDev );
} }
catch (MalformedURLException me) { catch (MalformedURLException me) {
Debug.warning(ssdpPacket.toString()); Debug.warning("Bad location: " + location, me);
Debug.warning(me);
} }
catch (ParserException pe) { catch (ParserException pe) {
Debug.warning(ssdpPacket.toString()); Debug.warning("Error parsing data at location: " + location, pe);
Debug.warning(pe);
} }
} }

View File

@ -21,6 +21,7 @@ package org.cybergarage.xml;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -29,6 +30,7 @@ import java.net.URL;
import org.cybergarage.http.HTTP; import org.cybergarage.http.HTTP;
import org.cybergarage.http.HTTPRequest; import org.cybergarage.http.HTTPRequest;
import org.cybergarage.http.HTTPResponse; import org.cybergarage.http.HTTPResponse;
import org.cybergarage.util.Debug;
public abstract class Parser public abstract class Parser
{ {
@ -58,9 +60,15 @@ public abstract class Parser
if (port == -1) if (port == -1)
port = 80; port = 80;
String uri = locationURL.getPath(); String uri = locationURL.getPath();
// I2P note: Roku port 9080 now ignored in ControlPoint.addDevice()
// I2P fix - Roku
if (uri.length() <= 0)
uri = "/";
HttpURLConnection urlCon = null;
InputStream urlIn = null;
try { try {
HttpURLConnection urlCon = (HttpURLConnection)locationURL.openConnection(); urlCon = (HttpURLConnection)locationURL.openConnection();
// I2P mods to prevent hangs (see HTTPRequest for more info) // I2P mods to prevent hangs (see HTTPRequest for more info)
// this seems to work, getInputStream actually does the connect(), // this seems to work, getInputStream actually does the connect(),
// (as shown by a thread dump) // (as shown by a thread dump)
@ -73,29 +81,50 @@ public abstract class Parser
if (host != null) if (host != null)
urlCon.setRequestProperty(HTTP.HOST, host); urlCon.setRequestProperty(HTTP.HOST, host);
InputStream urlIn = urlCon.getInputStream(); // I2P fix
int code = urlCon.getResponseCode();
if (code < 200 || code >= 300)
throw new ParserException("Bad response code " + code);
// I2P fix - Roku port 9080
// not valid json either; returns "status=ok"
if ("application/json".equals(urlCon.getContentType()))
throw new ParserException("JSON response");
urlIn = urlCon.getInputStream();
Node rootElem = parse(urlIn); Node rootElem = parse(urlIn);
urlIn.close();
urlCon.disconnect();
return rootElem; return rootElem;
} catch (ParserException pe) {
throw pe;
} catch (Exception e) { } catch (Exception e) {
// Why try twice???
//throw new ParserException(e); //throw new ParserException(e);
Debug.warning("Failed fetch but retrying with HTTPRequest, URL: " + locationURL, e);
} finally {
if (urlIn != null) try { urlIn.close(); } catch (IOException ioe) {}
if (urlCon != null) urlCon.disconnect();
} }
HTTPRequest httpReq = new HTTPRequest(); HTTPRequest httpReq = new HTTPRequest();
httpReq.setMethod(HTTP.GET); httpReq.setMethod(HTTP.GET);
httpReq.setURI(uri); httpReq.setURI(uri);
HTTPResponse httpRes = httpReq.post(host, port); HTTPResponse httpRes = httpReq.post(host, port);
if (httpRes.isSuccessful() == false) if (!httpRes.isSuccessful())
throw new ParserException("HTTP comunication failed: no answer from peer." + throw new ParserException("HTTP comunication failed. " +
"Unable to retrive resoure -> "+locationURL.toString()); "Unable to retrieve resource -> " + locationURL +
"\nRequest:\n" + httpReq +
"\nResponse:\n" + httpRes);
String content = new String(httpRes.getContent()); String content = new String(httpRes.getContent());
ByteArrayInputStream strBuf = new ByteArrayInputStream(content.getBytes()); ByteArrayInputStream strBuf = new ByteArrayInputStream(content.getBytes());
return parse(strBuf); try {
return parse(strBuf);
} catch (ParserException pe) {
Debug.warning("Parse error at resource " + locationURL +
"\nRequest:\n" + httpReq +
"\nResponse:\n" + httpRes, pe);
throw pe;
}
} }
//////////////////////////////////////////////// ////////////////////////////////////////////////