Server
The Server
class offers a basic API for communicating with a server using POST and GET requests as well as functions for downloading resources.
Server callbacks
Every method in this class will use the asynchronous execution concept: it will return immediately and execute a function when the server has responded.
Server.anyFunction(parameters, function(status, response)
{
if(status == Server.StatusOK)
{
Console.print(response);
}
});
The execution of server calls is guaranteed to be serially and consecutively and will be executed on a separate thread that will never stall the audio or UI thread. Nevertheless it should be obvious that you will never call one of these methods in a MIDI callback, right?
The status
will be an integer containing the HTTP status code
. The Server
API class has a few constants that contains the most important codes (OK, 404, etc).
The response
argument contains an object with the response from the server.
This means whatever you do on the server-side, you have to return a valid JSON-formatted text.
GET vs POST
There are two different kind of HTTP requests, GET and POST. There is plenty of information available about this subject, but the gist of it is that you use GET for non-sensitive data and POST for sensitive data.
About URLs
In 99% of all cases, you will communicate with a single server. In order to save you some typing, the Server
API class uses a Base URL
that you need to setup once using Server.setBaseURL()
.
Calling this method will also start the server thread, so unless you explicitely use it, it won't eat up resources.
From then on, every call to the server just needs a sub URL to work - you also don't need to take care about the slashes:
Server.setBaseURL("https:forum.hise.audio");
Server.callSomething("api/recent");
// => https://forum.hise.audio/api/recent/
Encryption
If you use SSL for the communication and the POST method for passing around parameters, the communication with the server should be safe enough. However if you want to store any kind of data to a file, please use the File.writeEncryptedObject() to make sure that there is no sensitive data lurking around on your hard drive.
Debugging server calls & downloads
The ServerController
floating tile offers a few useful tools that will assist you during the development of Server related tasks, so you might want to consider adding this to the scripting workspace while you're working on your server code.
Class methods
callWithGET
Calls a sub URL and executes the callback when finished.
Server.callWithGET(String subURL, var parameters, var callback)
This calls the given URL with the parameters
as GET arguments and will execute the callback function when the server responded.
The parameters
have to be a non-nested JSON object and will automatically change the URL to embed these parameters
Server.setBaseURL("https://forum.hise.audio");
// The GET arguments as JSON object
const var p =
{
"term": "HISE",
"in": "titlespost"
};
// => https://forum.hise.audio/api/search?term=HISE&in=titlesposts
Server.callWithGET("api/search", p, function(status, response)
{
if(status == Server.StatusOK)
{
// Just use the response like any other JSON object
Console.print("There are " + response.matchCount + " results");
}
});
callWithPOST
Calls a sub URL with POST arguments and executes the callback when finished.
Server.callWithPOST(String subURL, var parameters, var callback)
The arguments in a POST call are not embedded in the URL so they are more suitable for sensitive information.
Server.setBaseURL("http://hise.audio");
const var p =
{
"first_argument": 9000
};
// This dummy file just returns the `first_argument` as `post_argument`...
Server.callWithPOST("post_test.php", p, function(status, response)
{
Console.print(response.post_argument);
});
cleanFinishedDownloads
Removes all finished downloads from the list. Edit on GitHub
Server.cleanFinishedDownloads()
downloadFile
Downloads a file to the given target and returns a Download object.
Server.downloadFile(String subURL, var parameters, var targetFile, var callback)
This method will start a download in the background and periodically executes the callback that can be used to track the progress.
The target
parameter has to be a valid File
object that points to a file (not a directory).
Be aware that this file will be deleted and overwritten!
Unlike the other methods, the function you pass in as callback
must not have any parameters. Instead you can query the state of the download via the this.data
object, which always points to the current download.
The object has these properties:
Property | Type | Description |
finished
|
bool | false
as long as the file is being downloaded. It will be executed exactly once with 'true' at the end of the download (or if you pause the download). |
success
|
bool | at the end of the download (when finished
is true), this can be used to check whether the download was successful, or if it was interrupted by something. If you pause the download, this will also be false
. |
numTotal
|
int | the total download size in bytes. This will not change during download. |
numDownloaded
|
int | the number of bytes that have been downloaded until now. |
In addition to these properties, you can use the this
object in the function with all the API methods available in the Download
class.
Server.setBaseURL("http://hise.audio");
const var target = FileSystem.getFolder(FileSystem.Documents).getChildFile("HISE_1_1_1.exe");
Server.downloadFile("download/HISE_1_1_1.exe", {}, target, function()
{
var message = "";
message += Engine.doubleToString(this.data.numDownloaded / 1024.0 / 1024.0, 1);
message += "MB / " + Engine.doubleToString(this.data.numTotal / 1024.0 / 1024.0, 1) + "MB";
Console.print(message);
if(this.data.finished)
Console.print(this.data.sucess ? "Done" : "Fail");
});
inline function onButton1Control(component, value)
{
if(value)
Server.stopDownload("download/HISE_1_1_1.exe", {});
};
Content.getComponent("Button1").setControlCallback(onButton1Control);
Also be aware that if you call this method again with the same URL and parameters, it will not add another download, but just replace the callback in the already pending download. If you call this method with a file that already exists, the method assumes that it's a previously stopped download and resumes the download at the position (delete the file before calling the method if you don't want this behaviour).
getPendingCalls
Returns a list of all pending Calls. Edit on GitHub
Server.getPendingCalls()
getPendingDownloads
Returns a list of all pending Downloads.
Server.getPendingDownloads()
The lifetime of a Download
object will exceed the download time (so you can display a list of completed downloads). You can query the list of all available downloads with this method. If you want to remove all finished downloads, call Server.cleanFinishedDownloads()
isEmailAddress
Checks if given email address is valid - not fool proof. Edit on GitHub
Server.isEmailAddress(String email)
isOnline
Returns true if the system is connected to the internet. Edit on GitHub
Server.isOnline()
resendLastCall
Resends the last call to the Server (eg. in case that there was no internet connection). Edit on GitHub
Server.resendLastCall()
setBaseURL
Sets the base URL for all server queries. Edit on GitHub
Server.setBaseURL(String url)
setEnforceTrailingSlash
Sets whether to append a trailing slash to each POST call (default is true). Edit on GitHub
Server.setEnforceTrailingSlash(bool shouldAddSlash)
setHttpHeader
Adds the given String to the HTTP POST header. Edit on GitHub
Server.setHttpHeader(String additionalHeader)
setNumAllowedDownloads
Sets the maximal number of parallel downloads. Edit on GitHub
Server.setNumAllowedDownloads(int maxNumberOfParallelDownloads)
setServerCallback
This function will be called whenever there is server activity.
Server.setServerCallback(var callback)
You can use this function to implement some kind of notification to the user that there is something waiting for a response of the server and there is some internet activity.
For a real world use case you might to show some element spinning and then hide it when ready, but in this example, we'll just log to the Console:
Server.setBaseURL("https://forum.hise.audio/api");
Server.setServerCallback(function(isWaiting)
{
Console.print(isWaiting ? "SERVER IS BUSY" : "DONE");
});
function printName(status, obj)
{
if(status == 200)
Console.print(" " + obj.username);
};
// Now hammer the queue with the top 5 Posters
Server.callWithGET("user/d-healey", {}, printName);
Server.callWithGET("user/christoph-hart", {}, printName);
Server.callWithGET("user/ustk", {}, printName);
Server.callWithGET("user/Lindon", {}, printName);
Server.callWithGET("user/hisefilo", {}, printName);
The output:
Interface: SERVER IS BUSY
Interface: d.healey
Interface: Christoph Hart
Interface: ustk
Interface: Lindon
Interface: hisefilo
Interface: DONE
As you can see the start / end callback is only called once. Be aware that this callback is not used when there is a download in progress as this has it's own notification tools.
setTimeoutMessageString
Sets a string that is parsed as timeout message when the server doesn't respond. Default is "{}" (empty JSON object). Edit on GitHub
Server.setTimeoutMessageString(String timeoutMessage)