📄 upnphosting.java
字号:
package no.auc.one.portableplayer.communication.upnphosting;
import java.io.*;
import java.lang.*;
import java.util.*;
import javax.microedition.io.*;
import javax.microedition.rms.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import org.apache.log4j.*;
import no.auc.one.portableplayer.utils.Utilities;
import no.auc.one.portableplayer.communication.soap.SoapFault;
public final class UPnPHosting extends Thread {
private static Logger LOG = Logger.getLogger("Host");
private final static String STANDARD_RESPONSE_404 =
"HTTP/1.1 404 Not Found\r\nDate: " + Utilities.getRfcDateString(
Calendar.getInstance(TimeZone.getTimeZone("GMT"))) +
"Content-Length: 0\r\nConnection: close\r\n\r\n";
private final static byte[] STANDARD_RESPONSE_503 =
("HTTP/1.1 503 Service Unavailable\r\nDate: " + Utilities.getRfcDateString(
Calendar.getInstance(TimeZone.getTimeZone("GMT"))) +
"\r\nRetry-After: 60\r\nContent-Length: 0\r\nConnection: close\r\n\r\n").getBytes();
private final static int MAX_CONCURRENT_CURRENT_REQUESTS = 8;
private Hashtable entities = new Hashtable(10);
private ServerSocketConnection server;
private RecordStore descriptionDocumentCache;
private static UPnPHosting instance = null;
private Timer notificationTimer;
private static SAXParserFactory pfactory;
private Hashtable httpRequestHandlers = new Hashtable(2);
private int requestsCurrentlyHandling = 0;
/**
* Seconds between device advertisements.
*
* XXX Should make the period configurable (Settings in the UI)
*
*/
private final int notifyPeriod = 300;
private UPnPHosting() {
try {
registerHttpHandler(
ServerRootHttpHandler.NAMESPACE_NAME,
new ServerRootHttpHandler());
registerHttpHandler(
SsdpHttpHandler.NAMESPACE_NAME,
new SsdpHttpHandler());
server = (ServerSocketConnection)Connector.open("socket://");
descriptionDocumentCache = RecordStore.openRecordStore(
"UPnP Hosting XML Cache",
true);
try {
RecordEnumeration re = descriptionDocumentCache.enumerateRecords(
null,
null,
false);
while(re.hasNextElement()) {
descriptionDocumentCache.deleteRecord(re.nextRecordId());
}
re.destroy();
} catch (Exception e) {
System.out.println("Exception while deleting records of desc doc cache");
System.out.println(e);
e.printStackTrace();
}
notificationTimer = new Timer();
pfactory = SAXParserFactory.newInstance();
pfactory.setNamespaceAware(true);
} catch(IOException ioe) {
LOG.fatal(ioe);
} catch(RecordStoreNotFoundException rsnfe) {
LOG.fatal(rsnfe);
} catch(RecordStoreFullException rsfe) {
LOG.fatal(rsfe);
} catch(RecordStoreException rse) {
LOG.fatal(rse);
} catch(IllegalArgumentException iae) {
LOG.fatal(iae);
}
}
public static UPnPHosting getInstance() {
if (null == instance) {
instance = new UPnPHosting();
}
return instance;
}
/**
* Retrieve the address of the server.
*/
public String getLocalAddress() throws IOException {
return server.getLocalAddress();
}
/**
* Retrieve the port of the server.
*/
public int getLocalPort() throws IOException {
return server.getLocalPort();
}
/**
* Used to start the UPnP Hosting Support Framework.
*
* This thread will take care of sending out NOTIFY messages for all
* registered devices and services, according to the UPnP architecture.
*/
public void run() {
// Create a new thread for listening to incoming service control
// requests and downloading of description documents (dev+svc)
try {
// XXX Need some synch stuff to stop acceptAndOpen() when
// it is time to say good bye...
do {
SocketConnection sc = (SocketConnection)server.acceptAndOpen();
if (requestsCurrentlyHandling < MAX_CONCURRENT_CURRENT_REQUESTS) {
ConnectionHandler ch = new ConnectionHandler(sc);
ch.start();
incrementNumRequestsHandling();
} else {
OutputStream os = sc.openOutputStream();
os.write(STANDARD_RESPONSE_503);
sc.close();
}
} while (true);
} catch (IOException ioe) {
LOG.fatal("IOException while starting");
LOG.fatal(ioe);
} finally {
try {
server.close();
} catch (IOException ioe) {
LOG.fatal(ioe);
} catch (Exception e) {
LOG.fatal(e);
}
}
}
private synchronized int decrementNumRequestsHandling() {
requestsCurrentlyHandling--;
return requestsCurrentlyHandling;
}
private synchronized int incrementNumRequestsHandling() {
requestsCurrentlyHandling++;
return requestsCurrentlyHandling;
}
private final class ConnectionHandler extends Thread {
private SocketConnection sc;
public ConnectionHandler(SocketConnection sc) {
this.sc = sc;
}
public void run() {
try {
//
// Handle the sc request
//
// XXX Should change this to something, hopefully, more robust:
// 1) First read (as currently implemented below) all
// header values (until CRLFCRLF is detected).
// 2) Parse the header values
// 3) Look for Content-Length and Transfer-Encoding
// 4) These must be used to calculate how much to data to
// be read.
//
// See HTTP 2616 section 4.4 Message Length for more info.
//
long time = 0;
InputStream is = sc.openInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
System.err.println("[" + Thread.currentThread() + "][" + System.currentTimeMillis() + "] Start to receive request: ");
int pos = 0;
int nextPos = 0;
do {
// System.err.print("; " + is.available());
pos = nextPos;
byte[] inputBuffer = new byte[is.available()];
int rd = is.read(inputBuffer);
if (rd == -1)
break;
bos.write(inputBuffer);
nextPos = rd;
} while (bos.toString().indexOf("\r\n\r\n", (pos > 4 ? pos - 4 : pos)) == -1);
System.err.println("[" + Thread.currentThread() + "][" + System.currentTimeMillis() + "] Got request");
String requestHeaders = new String(bos.toByteArray());
if(requestHeaders.startsWith("\r\n")) {
requestHeaders = requestHeaders.substring(2);
System.out.println("[" + Thread.currentThread()+ "] New request due to leading new-line:");
System.out.println("[" + Thread.currentThread()+ "] " + requestHeaders);
}
HttpRequest req = new HttpRequest();
//
// XXX Must set the HTTP version of the req object too!
//
setRequestHeaders(requestHeaders, req);
//
// Get the content, if any
//
if (req.hasHeader("Content-Length")) {
int contentLength = Integer.parseInt(req.getHeader("Content-Length"));
if (contentLength > 0) {
byte[] content = new byte[contentLength];
is.read(content);
bos.close();
is.close();
if (content == null || content.length <= 0) {
System.out.println("[" + Thread.currentThread()+ "] Something is wrong with the request...");
System.out.flush();
return;
}
req.setBody(new String(content));
}
}
int firstSpace = requestHeaders.indexOf(' ') + 1;
int nextSpace = requestHeaders.indexOf(' ', firstSpace);
String reqUri = null;
String namespace = null;
if (nextSpace - firstSpace > 1) {
reqUri = requestHeaders.substring(firstSpace, nextSpace);
namespace = reqUri.substring(1);
int firstSlash = namespace.indexOf('/');
if (firstSlash >= 0) {
namespace = namespace.substring(0, firstSlash);
} else {
namespace = ServerRootHttpHandler.NAMESPACE_NAME;
}
} else {
reqUri = new String(new char[]{requestHeaders.charAt(firstSpace)});
namespace = ServerRootHttpHandler.NAMESPACE_NAME;
}
req.setRequestUri(reqUri);
req.setSocketConnection(sc);
time = System.currentTimeMillis();
System.out.println("[" + Thread.currentThread()+ "][" + time +"] Namespace: " + namespace);
byte[] response = null;
if (!httpRequestHandlers.containsKey(namespace)) {
time = System.currentTimeMillis();
System.out.println("[" + Thread.currentThread()+ "][" + time +"] Unknown namespace. Return error to client");
response = STANDARD_RESPONSE_404.getBytes();
} else {
HttpRequestHandler handler =
(HttpRequestHandler)httpRequestHandlers.get(namespace);
if(requestHeaders.startsWith("GET")) {
req.setRequestMethod(HttpRequest.REQUEST_METHOD_GET);
response = handler.handleGetRequest(req);
} else if(requestHeaders.startsWith("POST")) {
req.setRequestMethod(HttpRequest.REQUEST_METHOD_POST);
response = handler.handlePostRequest(req);
} else if (requestHeaders.startsWith("OPTIONS")) {
req.setRequestMethod(HttpRequest.REQUEST_METHOD_OPTIONS);
response = handler.handleOptionsRequest(req);
/* } else if(request.startsWith("SUBSCRIBE")) {
System.out.println("Request is SUBSCRIBE");
req.requestMethod = REQUEST_METHOD_SUBSCRIBE;
response = handleSubscribeRequest(req);
} else if(request.startsWith("UNSUBSCRIBE")) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -