add file responses
This commit is contained in:
parent
390a03551c
commit
60f55066ce
@ -81,6 +81,105 @@ public class NanoHTTPDWebserver extends NanoHTTPD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Response serveFile(Map<String, String> header, File file, String mime) {
|
||||||
|
Response res;
|
||||||
|
try {
|
||||||
|
// Calculate etag
|
||||||
|
String etag = Integer.toHexString((file.getAbsolutePath() + file.lastModified() + "" + file.length()).hashCode());
|
||||||
|
|
||||||
|
// Support (simple) skipping:
|
||||||
|
long startFrom = 0;
|
||||||
|
long endAt = -1;
|
||||||
|
String range = header.get("range");
|
||||||
|
if (range != null) {
|
||||||
|
if (range.startsWith("bytes=")) {
|
||||||
|
range = range.substring("bytes=".length());
|
||||||
|
int minus = range.indexOf('-');
|
||||||
|
try {
|
||||||
|
if (minus > 0) {
|
||||||
|
startFrom = Long.parseLong(range.substring(0, minus));
|
||||||
|
endAt = Long.parseLong(range.substring(minus + 1));
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get if-range header. If present, it must match etag or else we
|
||||||
|
// should ignore the range request
|
||||||
|
String ifRange = header.get("if-range");
|
||||||
|
boolean headerIfRangeMissingOrMatching = (ifRange == null || etag.equals(ifRange));
|
||||||
|
|
||||||
|
String ifNoneMatch = header.get("if-none-match");
|
||||||
|
boolean headerIfNoneMatchPresentAndMatching = ifNoneMatch != null && ("*".equals(ifNoneMatch) || ifNoneMatch.equals(etag));
|
||||||
|
|
||||||
|
// Change return code and add Content-Range header when skipping is
|
||||||
|
// requested
|
||||||
|
long fileLen = file.length();
|
||||||
|
|
||||||
|
if (headerIfRangeMissingOrMatching && range != null && startFrom >= 0 && startFrom < fileLen) {
|
||||||
|
// range request that matches current etag
|
||||||
|
// and the startFrom of the range is satisfiable
|
||||||
|
if (headerIfNoneMatchPresentAndMatching) {
|
||||||
|
// range request that matches current etag
|
||||||
|
// and the startFrom of the range is satisfiable
|
||||||
|
// would return range from file
|
||||||
|
// respond with not-modified
|
||||||
|
res = newFixedLengthResponse(Status.NOT_MODIFIED, mime, "");
|
||||||
|
res.addHeader("ETag", etag);
|
||||||
|
} else {
|
||||||
|
if (endAt < 0) {
|
||||||
|
endAt = fileLen - 1;
|
||||||
|
}
|
||||||
|
long newLen = endAt - startFrom + 1;
|
||||||
|
if (newLen < 0) {
|
||||||
|
newLen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
fis.skip(startFrom);
|
||||||
|
|
||||||
|
res = Response.newFixedLengthResponse(Status.PARTIAL_CONTENT, mime, fis, newLen);
|
||||||
|
res.addHeader("Accept-Ranges", "bytes");
|
||||||
|
res.addHeader("Content-Length", "" + newLen);
|
||||||
|
res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen);
|
||||||
|
res.addHeader("ETag", etag);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (headerIfRangeMissingOrMatching && range != null && startFrom >= fileLen) {
|
||||||
|
// return the size of the file
|
||||||
|
// 4xx responses are not trumped by if-none-match
|
||||||
|
res = newFixedLengthResponse(Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, "");
|
||||||
|
res.addHeader("Content-Range", "bytes */" + fileLen);
|
||||||
|
res.addHeader("ETag", etag);
|
||||||
|
} else if (range == null && headerIfNoneMatchPresentAndMatching) {
|
||||||
|
// full-file-fetch request
|
||||||
|
// would return entire file
|
||||||
|
// respond with not-modified
|
||||||
|
res = newFixedLengthResponse(Status.NOT_MODIFIED, mime, "");
|
||||||
|
res.addHeader("ETag", etag);
|
||||||
|
} else if (!headerIfRangeMissingOrMatching && headerIfNoneMatchPresentAndMatching) {
|
||||||
|
// range request that doesn't match current etag
|
||||||
|
// would return entire (different) file
|
||||||
|
// respond with not-modified
|
||||||
|
|
||||||
|
res = newFixedLengthResponse(Status.NOT_MODIFIED, mime, "");
|
||||||
|
res.addHeader("ETag", etag);
|
||||||
|
} else {
|
||||||
|
// supply the file
|
||||||
|
res = newFixedFileResponse(file, mime);
|
||||||
|
res.addHeader("Content-Length", "" + fileLen);
|
||||||
|
res.addHeader("ETag", etag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
res = getForbiddenResponse("Reading file failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response serve(IHTTPSession session) {
|
public Response serve(IHTTPSession session) {
|
||||||
Log.d(this.getClass().getName(), "New request is incoming!");
|
Log.d(this.getClass().getName(), "New request is incoming!");
|
||||||
@ -97,7 +196,7 @@ public class NanoHTTPDWebserver extends NanoHTTPD {
|
|||||||
pluginResult.setKeepCallback(true);
|
pluginResult.setKeepCallback(true);
|
||||||
this.webserver.onRequestCallbackContext.sendPluginResult(pluginResult);
|
this.webserver.onRequestCallbackContext.sendPluginResult(pluginResult);
|
||||||
|
|
||||||
while (!this.webserver.responses.containsKey(requestUUID)) {
|
while (!this.webserver.responses.containsKey(requestUUID) && !this.webserver.responses.containsKey('file')) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1);
|
Thread.sleep(1);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@ -105,6 +204,11 @@ public class NanoHTTPDWebserver extends NanoHTTPD {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.webserver.responses.containsKey('file')) {
|
||||||
|
// TODO should specify a more correct mime-type
|
||||||
|
return serveFile(session.getHeaders(), new File((String) this.webserver.responses.get('file')), 'application/octet-stream');
|
||||||
|
}
|
||||||
|
|
||||||
JSONObject responseObject = (JSONObject) this.webserver.responses.get(requestUUID);
|
JSONObject responseObject = (JSONObject) this.webserver.responses.get(requestUUID);
|
||||||
Log.d(this.getClass().getName(), "responseObject: " + responseObject.toString());
|
Log.d(this.getClass().getName(), "responseObject: " + responseObject.toString());
|
||||||
Response response = null;
|
Response response = null;
|
||||||
|
@ -44,6 +44,10 @@ public class Webserver extends CordovaPlugin {
|
|||||||
this.sendResponse(args, callbackContext);
|
this.sendResponse(args, callbackContext);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if ("sendFileResponse".equals(action)) {
|
||||||
|
this.sendFileResponse(args, callbackContext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false; // Returning false results in a "MethodNotFound" error.
|
return false; // Returning false results in a "MethodNotFound" error.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +100,11 @@ public class Webserver extends CordovaPlugin {
|
|||||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendFileResponse(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||||
|
this.responses.put('file', args.get(1));
|
||||||
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just register the onRequest and send no result. This is needed to save the callbackContext to
|
* Just register the onRequest and send no result. This is needed to save the callbackContext to
|
||||||
* invoke it later
|
* invoke it later
|
||||||
|
@ -4,6 +4,7 @@ const WEBSERVER_CLASS = 'Webserver';
|
|||||||
const START_FUNCTION = 'start';
|
const START_FUNCTION = 'start';
|
||||||
const ONREQUEST_FUNCTION = 'onRequest';
|
const ONREQUEST_FUNCTION = 'onRequest';
|
||||||
const SENDRESPONSE_FUNCION = 'sendResponse';
|
const SENDRESPONSE_FUNCION = 'sendResponse';
|
||||||
|
const SENDFILE_FUNCION = 'sendFileResponse';
|
||||||
const STOP_FUNCTION = 'stop';
|
const STOP_FUNCTION = 'stop';
|
||||||
|
|
||||||
export function start(success_callback, error_callback, port) {
|
export function start(success_callback, error_callback, port) {
|
||||||
@ -45,6 +46,21 @@ export function sendResponse(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sendFile(
|
||||||
|
requestId,
|
||||||
|
params,
|
||||||
|
success_callback,
|
||||||
|
error_callback
|
||||||
|
) {
|
||||||
|
exec(
|
||||||
|
success_callback,
|
||||||
|
error_callback,
|
||||||
|
WEBSERVER_CLASS,
|
||||||
|
SENDFILE_FUNCION,
|
||||||
|
[requestId, params]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function stop(success_callback, error_callback) {
|
export function stop(success_callback, error_callback) {
|
||||||
exec(
|
exec(
|
||||||
success_callback,
|
success_callback,
|
||||||
|
@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|||||||
exports.start = start;
|
exports.start = start;
|
||||||
exports.onRequest = onRequest;
|
exports.onRequest = onRequest;
|
||||||
exports.sendResponse = sendResponse;
|
exports.sendResponse = sendResponse;
|
||||||
|
exports.sendFile = sendFile;
|
||||||
exports.stop = stop;
|
exports.stop = stop;
|
||||||
|
|
||||||
var _exec = require('cordova/exec');
|
var _exec = require('cordova/exec');
|
||||||
@ -18,6 +19,7 @@ var WEBSERVER_CLASS = 'Webserver';
|
|||||||
var START_FUNCTION = 'start';
|
var START_FUNCTION = 'start';
|
||||||
var ONREQUEST_FUNCTION = 'onRequest';
|
var ONREQUEST_FUNCTION = 'onRequest';
|
||||||
var SENDRESPONSE_FUNCION = 'sendResponse';
|
var SENDRESPONSE_FUNCION = 'sendResponse';
|
||||||
|
var SENDFILE_FUNCION = 'sendFileResponse';
|
||||||
var STOP_FUNCTION = 'stop';
|
var STOP_FUNCTION = 'stop';
|
||||||
|
|
||||||
function start(success_callback, error_callback, port) {
|
function start(success_callback, error_callback, port) {
|
||||||
@ -38,6 +40,10 @@ function sendResponse(requestId, params, success_callback, error_callback) {
|
|||||||
(0, _exec2.default)(success_callback, error_callback, WEBSERVER_CLASS, SENDRESPONSE_FUNCION, [requestId, params]);
|
(0, _exec2.default)(success_callback, error_callback, WEBSERVER_CLASS, SENDRESPONSE_FUNCION, [requestId, params]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendFile(requestId, params, success_callback, error_callback) {
|
||||||
|
(0, _exec2.default)(success_callback, error_callback, WEBSERVER_CLASS, SENDFILE_FUNCION, [requestId, params]);
|
||||||
|
}
|
||||||
|
|
||||||
function stop(success_callback, error_callback) {
|
function stop(success_callback, error_callback) {
|
||||||
(0, _exec2.default)(success_callback, error_callback, WEBSERVER_CLASS, STOP_FUNCTION, []);
|
(0, _exec2.default)(success_callback, error_callback, WEBSERVER_CLASS, STOP_FUNCTION, []);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user