summaryrefslogtreecommitdiff
path: root/docs/api-notes/common/lib_server/Protocol.txt
blob: 09d3c1f1a626ce7d7cd5ac0d7ad61d85318a9a96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
CLASS Protocol

Protocol

* serialises and deserialises data objects
* sends arbitary streams

through a bi-directional IOStream object, usually a socket.

These data objects are auto-generated by a perl script, along with the logic to implement a simple protocol where one end is a client, and the other a server. The client sends a command object (and optional streams) and the server returns a reply object (and optional streams).

The perl script uses a description file which specifies the data inside these objects (which can be extended to include any type which implements the standard serialisation functions), the required responses to the commands, and whether streams are involved.

It then implements a server object, which given a stream and a user defined context object, waits for commands, processes them, and sends the results back. All you need to do is implement a DoCommand() function for each command object you define.

On the client side, a Query() function is implemented which takes objects as parameters, and returns the right type of reply object (or throws an exception if it doesn't get it.) Short cut Query<CommandName>() functions are also implemented which don't require objects to be created manually.

Thus, implementing a server is as simple as deriving a daemon off ServerStream or ServerTLS, and implementing the Connection() function as 

	void TestProtocolServer::Connection(SocketStream &rStream)
	{
		TestProtocolServer server(rStream);
		TestContext context;
		server.DoServer(context);
	}

and that's it. TestContext is a user defined class which keeps track of all the state of the connection, and is passed to all the DoCommand() functions, which look like this:

	std::auto_ptr<ProtocolObject>
		TestProtocolServerSimple::DoCommand(TestProtocolServer &rProtocol,
		TestContext &rContext)
	{
		return std::auto_ptr<ProtocolObject>
			(new TestProtocolServerSimpleReply(mValue+1));
	}

(taken from test/basicserver)

The client code looks like this

	SocketStream conn;
	conn.Open(Socket::TypeUNIX, "testfiles/srv4.sock");
	
	TestProtocolClient protocol(conn);
	
	// Query
	{
		std::auto_ptr<TestProtocolClientSimpleReply>
			reply(protocol.QuerySimple(41));
		TEST_THAT(reply->GetValuePlusOne() == 42);
	}


Finally, debug logging can be generated which allows a list of all commands and their parameters to be logged to syslog or a file.


SUBTITLE Protocol Description File

This file is passed to the lib/server/makeprotocol.pl script, which generates a h and cpp file to implement the protocol.

It is in two sections, separated by a 'BEGIN_OBJECTS' on a line of it's own.

In the top half, the following statements must be made.

Name 			<name>
	The name of the protocol, used in naming classes.

IdentString		<string>
	The idenfitifaction string sent over the IOStream to confirm it it
	is talking to another Protocol object speaking the same Protocol.

ServerContextClass	<class-name>	<header-file>
	The user defined context class used for the server, and the header
	file it is defined in.

Additionally, the following optional commands can be made.

ClientType <description-typename> <C++ typename> <headerfile>
ServerType (similarly)
	Extends the types used in the objects below. Server and client
	can use different types for the same object type.

ImplementLog	(Client|Server)	(syslog|file)
	Implement command logging for client or server into syslog or a file.

LogTypeToText (Client|Server) <description-typename> <printf-element>
		<evaluate>
	For extended types, optionally define how to convert them into printf
	elements and the code to run to get the argument. Within the evaluate
	parameter, VAR is replaced by the name of the variable to display.
	If this is not specified for a given type, OPAQUE is output instead.


In the object section, an object is defined by a line

<name> <id number> <attributes>

followed by lines beginning with whitespace defining the data transmitted in the object. The type may be list<type>, which specifies a list (implemented as a std::vector) of entries of that type.

The attributes specify exactly how that object is used in the defined protocol.

Reply
	The object is a reply object, sent from the server to the client.

Command(Reply-Type)
	The object is a command, send from the client to the server, and the server
	will send back an object of type Reply-Type.

IsError(Type-Field,SubType-Field)
	The command is an error object, and the two files specify the data member
	which describes the error type and sub type.

EndsConversation
	When this command is received, the connection is to be terminated.
	(ie a logout command)

StreamWithCommand
	When this command is sent as a command, a stream follows it.