Into

Modules

Documentation

classPiiRemoteObjectServer

#include <PiiRemoteObjectServer.h>

An object that maps QObject's functions and properties into the URI space of a PiiHttpProtocol.

Inherits QObject, PiiHttpProtocol::UriHandler

Description

Usage example

If you just need to start quick, do this:

 QTimer t; // ... or any other class derived from QObject
 PiiHttpServer* pHttpServer = PiiHttpServer::addServer("My server", "tcp://0.0.0.0:3142");
 PiiRemoteObjectServer* pRemoteObjectServer = new PiiRemoteObjectServer(&t);
 pHttpServer->protocol()->registerUriHandler("/timer/", pRemoteObjectServer);
 pHttpServer->start();

See PiiRemoteObjectClient for the client side. If you want the details, read on.

Mapping to URIs

PiiRemoteObjectServer makes QObject's properties, signals, slots, and other invokable functions available to client software by mapping the object to an URI in PiiHttpServer. By default, all properties (including dynamic ones) and public invokable methods will be made available as sub-URIs. For example, a QTimer registered at /timer/ would create the following URI structure:
  • /timer/

  • /timer/functions/

  • /timer/functions/start

  • /timer/functions/stop

  • /timer/signals/

  • /timer/signals/timeout

  • /timer/properties/

  • /timer/properties/active

  • /timer/properties/interval

  • /timer/properties/singleShot

Note that the QTimer::start() function has two overloaded versions, which both can be accessed through the same URI. The server inspects the parameter types to find out which function to call.

PiiHttpServer is configured so that it returns a list of sub-URIs if no handler is defined for a specific URI. That is, a request to /timer/ would list the root "folders":

GET /timer/ HTTP/1.1

Response:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 31
functions/
signals/
properties/

Function calls

A GET request to the /functions/ URI lists all callable functions:

GET /timer/functions/ HTTP/1.1

Response:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 40
start()
start(int)
stop()

A call to a function with no parameters is a simple GET request:

GET /timer/functions/start HTTP/1.1

Note that the Host header is not necessary, although HTTP 1.1 dictates one. The response is equally simple (no return value):

HTTP/1.1 200 OK
Content-Length: 0

If the function has parameters, they can be specified in the URI:

GET /timer/functions/start?msec=1000 HTTP/1.1

The server requires that parameters are given in the order they are declared in the function signature. It ignores the parameter name, which can therefore be omitted. The example above is equivalent to:

GET /timer/functions/start?1000 HTTP/1.1

Parameters are automatically decoded using PiiHttpDevice::decodeVariant(). If the function has a return value, the same encoding will be used in the return message body.

Properties

The properties of a QObject are provided under the "/properties/" URI. Each property has a URI that corresponds to its name. By default, both static and dynamic properties will be listed. Property definitions can be queried with a GET request to /properties/:

GET /timer/properties/ HTTP/1.1

Response:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 41
bool active
int interval
bool singleShot

Individual property values can be retrieved with a GET request to the property URI:

GET /timer/properties/active HTTP/1.1

Response:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 4
true

Setting property values may or may not be allowed by the server. The value of an individual property can be set either with a GET or a POST request:

GET /timer/properties/interval?12345 HTTP/1.1
POST /timer/properties/interval HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
interval=12345

Many properties can be set at once with either a GET request or a POST (x-www-form-urlencoded) request.

GET /timer/properties?interval=1000&singleShot=false HTTP/1.1
POST /timer/properties HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
interval=1000&singleShot=false

Channels and signals

Connecting to a signal requires a persistent return channel the server can use to push data to the client. A return channel must be set up before a signal can be connected to. Then, the return channel can be used to deliver any number of signals and also other data.

To continue with the timer example, the URI structure in fact contains the following additional entries:

  • /timer/functions/register

  • /timer/functions/unregister

  • /timer/functions/close

  • /timer/channels/

  • /timer/channels/new

  • /timer/channels/reconnect

Channels can be connected to under the /channels/ URI. The three functions are used to control what data is sent through the channel:
  • register(QString id, QString uri) - registers a resource to be pushed to a channel. The first parameter is a channel ID and the second one the URI of a resource (such as a signal). This is equivalent to connecting a slot to a signal.

  • unregister(QString id, QString uri) - the opposite: the given resource will no longer be pushed through the indicated channel. This is equivalent to disconnecting a slot from a signal.

  • close(QString) - closes the channel associated with the given channel ID and releases all resources allocated to it.

Now, the apparent question is how to get the channel id. The ID of a newly created channel is the first row of the return body (the preamble of a multipart message) when the client requests /channels/new. The server leaves the requesting socket open, and pushes new data to it when needed.

GET /timer/channels/new HTTP/1.1

Response:

HTTP/1.1 200 OK
Content-Type: multipart/mixed-replace; boundary="ural"
4A40938-2229-9F31-D008-2EFA98EC4E6C
--ural
X-URI: signals/timeout
Content-Length: 0
--ural
...

Now that the channel has been set up, one can add pushable resources to the channel. To request the server to push the data emitted through the timeout signal, the following request must be made:

GET /functions/register?4A40938-2229-9F31-D008-2EFA98EC4E6C&/signals/timeout() HTTP/1.1

The first parameter to the register function is the channel ID. The second parameter identifies the pushable resource as a URI. In the case of a signal, the URI must specify the exact signature of the signal to avoid ambiguous overloads. Note that the URI is not percent-encoded in the example for readability although it should really be.

Whenever new registered data becomes available it will be written to the channel. The URI of the resource will be sent as in a non-standard "X-URI" MIME header. The actual data is written in a serialized format (see Serialization library). If the pushed resource is a signal, its parameters will be encoded as a QVariantList. The client and the server must agree on the data format with other pushable resources that may be provided by subclasses.

The reconnect function reconnects the client to a disconnected channel. It takes the channel ID as a parameter. The server keeps unclosed channels in memory for a while allowing clients to recover from network failures. If the channel is still alive, this call will re-establish it as if /channels/new was requested. The only differences are that all previously added pushable resources are still in effect and that the channel ID will not be returned.

Multiple Instances

By default, there is a single instance of a remote object. All clients will be connected to the same object, and changes made by one will affect others. It is however possible to create a new instance for each client. In this mode, the remote object server is not given an instance of a QObject. The server initially works as a primary server that either creates new server instances or passes requests to existing ones. There will be no "default" server object, but each client must create a instance by requesting /new. The server uses createServer() function to create a new secondary server and assigns a unique ID to it. The ID is then returned to the client.

GET /new HTTP/1.1

Response:

HTTP/1.1 200 OK
Content-Length: 35
243F6A8-885A-308D-3131-98A2E0370734

The returned ID can now be used as the URI of the new instance:

GET /243F6A8-885A-308D-3131-98A2E0370734/properties/ HTTP/1.1

Inactive object instances will be kept alive only a limited amount of time. If no accesses have been made to the object for a specified maximum number of milliseconds (see instanceTimeout()), the object will be automatically deleted. If no other requests are made, the instance can be kept alive by requesting /ping. This is a dummy URI whose sole purpose is to indicate the server that the client is still alive. It can also be used to check the availability of a remote object in a non-intrusive manner.

GET /243F6A8-885A-308D-3131-98A2E0370734/ping HTTP/1.1

Response:

HTTP/1.1 200 OK
Content-Length: 0

The remote object instance can be explicitly destroyed by requesting /delete:

GET /delete?243F6A8-885A-308D-3131-98A2E0370734 HTTP/1.1

It is possible to pass parameters to the new object instance by encoding them into the request (GET or POST). This makes it possible to even create totally different remote objects based on the parameters. Suppose a server that can create QObjects based on class name. Then, the following request would create an instance of a QTimer.

GET /new?className=QTimer HTTP/1.1

Constructors and destructor

Creates a new PiiRemoteObjectServer that maps HTTP request to the given object.

Creates a new PiiRemoteObjectServer with no associated server object.

Public member functions

Q_INVOKABLE bool
( )
int

Returns the current channel time-out.

Q_INVOKABLE void
( )

Returns the signatures of all invokable functions and slots.

virtual void

Handles a request.

int

Returns the instance time-out.

int

Returns the maximum number of remote object instances.

Returns a list of accessible property names with their types.

Q_INVOKABLE bool
( )
void
(
  • int channelTimeout
)

Sets the number of millisecond a channel will be kept alive after a client breaks connection to it without explicitly closing the channel.

void
(
  • int instanceTimeout
)

Sets the number of millisecond an object instance will be kept alive after a client breaks connection to it without explicitly deleting the instance.

void
(
  • int maxInstances
)

Sets the maximum number of remote object instances the server will manage concurrently.

Returns the signatures of all signals.

Protected member functions

(
  • const QVariantMap & parameters
)

Returns a new server instance given the parameters passed in the HTTP request.

void
( )

Puts data to the send queue of all channels to which the pushable resource identified by uri has been added to.

virtual QStringList
( )

Returns a "directory list" of the given folder.

Creates a new PiiRemoteObjectServer with no associated server object.

Function details

  • PiiRemoteObjectServer

    ()

    Creates a new PiiRemoteObjectServer that maps HTTP request to the given object.

    There will be only one instance of the remote object, and all client requests will use it.

  • PiiRemoteObjectServer

    ()
    [protected]

    Creates a new PiiRemoteObjectServer with no associated server object.

    The server will work as a primary server that creates a new secondary server for each client using the createServer() function.

  • ~PiiRemoteObjectServer

    ()
  • Q_INVOKABLE bool addToChannel

    ()
  • int channelTimeout

    ()

    Returns the current channel time-out.

  • Q_INVOKABLE void closeChannel

    ()
  • QStringList functionSignatures

    ()

    Returns the signatures of all invokable functions and slots.

    Each signature contains a return type (if there is one), function name, and a list of parameter types. For example "start(int)" is a valid signature.

  • virtual void handleRequest

    ()
    [virtual]

    Handles a request.

    This function must be thread-safe.

     void MyHandler::handleRequest(const QString& uri, PiiHttpDevice* dev, TimeLimiter*)
     {
       // Find the path of the request wrt to the "root" of this handler
       QString strRequestPath(dev->requestPath(uri));
       if (strRequestPath == "index.html" && dev->requestMethod() == "GET")
         dev->print("<html><head><title>Hello world!</title></head><body><!-- Secret message --></body></html>");
     }

    Parameters
    uri

    the URI the handler was registered at. Use the PiiHttpDevice::requestUri() function to fetch the full request URI.

    dev

    the communication device. PiiHttpProtocol has already fetched request headers, and the device is positioned at the beginning of request data.

    controller

    a progress controller. Call the PiiProgressController::canContinue() with no parameters time to time to ensure you are still allowed to continue communication. Returning from this function will automatically flush the output pending in dev.

    Exceptions
    The

    function may throw a PiiHttpException on error. PiiHttpProtocol sets the response header correspondingly and writes message to the response body.

    Reimplemented from PiiHttpProtocol::UriHandler.

  • int instanceTimeout

    ()

    Returns the instance time-out.

  • int maxInstances

    ()

    Returns the maximum number of remote object instances.

  • QStringList propertyDeclarations

    ()

    Returns a list of accessible property names with their types.

    Each entry in this list consists of a type and a property name, e.g. "int value".

  • Q_INVOKABLE bool removeFromChannel

    ()
  • void setChannelTimeout

    (
    • int channelTimeout
    )

    Sets the number of millisecond a channel will be kept alive after a client breaks connection to it without explicitly closing the channel.

    The default is 10000.

  • void setInstanceTimeout

    (
    • int instanceTimeout
    )

    Sets the number of millisecond an object instance will be kept alive after a client breaks connection to it without explicitly deleting the instance.

    The default is 10000.

  • void setMaxInstances

    (
    • int maxInstances
    )

    Sets the maximum number of remote object instances the server will manage concurrently.

    Once this limit is reached, the server will refuse to create new object instances. The default value is 100.

  • QStringList signalSignatures

    ()

    Returns the signatures of all signals.

  • virtual PiiRemoteObjectServer * createServer

    (
    • const QVariantMap & parameters
    )
    [protected, virtual]

    Returns a new server instance given the parameters passed in the HTTP request.

    If the server is created without an associated server object, this function must be overridden to create a new server object for each client. The default implementation returns 0.

  • void enqueuePushData

    ()
    [protected]

    Puts data to the send queue of all channels to which the pushable resource identified by uri has been added to.

  • virtual QStringList listFolder

    ()
    [protected, virtual]

    Returns a "directory list" of the given folder.

    This function makes it possible for subclasses to add functionality to the default URI tree. If you override this function, add your new entries to the list returned by the default implementation. It is also possible to remove entries from the list.

    Parameters
    uri

    the URI whose sub-URIs are to be listed, for example "/" or "/functions/".

    Returns

    a list of sub-URIs. URIs that may contain sub-URIs should end with a slash (/).

Notes (0)

Add a note

Not a single note added yet. Be the first, add yours.