connection.c
4.49 KB
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/**
* \file
*
* \author Georg Hopp
*
* \copyright
* Copyright © 2014 Georg Hopp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdint.h>
#include <sys/types.h>
#include "trbase.h"
#include "trio.h"
#include "trdata.h"
#include "tr/connection.h"
#include "tr/interface/protocol.h"
#include "tr/interface/comm_end_point.h"
static
int
connectionCtor(void * _this, va_list * params)
{
TR_Connection this = _this;
TR_PARENTCALL(TR_Connection, _this, TR_Class, ctor, params);
this->current_message = NULL;
return 0;
}
static
void
connectionDtor(void * _this)
{
TR_Connection this = _this;
TR_delete(this->current_message);
TR_PARENTCALL(TR_Connection, _this, TR_Class, dtor);
}
static
TR_ProtoMessage
connectionNextMessage(void * _this, TR_RemoteData * data)
{
TR_Connection this = _this;
TR_CommEndPoint comm = _this;
TR_ProtoMessage ret_message = NULL;
TR_RemoteData new_data = NULL;
size_t end;
if (*data) {
if (! this->current_message || this->current_message->ready)
{
this->current_message =
TR_protoCreateMessage(comm->protocol, (*data)->remote);
}
end = TR_protoParse(comm->protocol, this->current_message, *data);
/**
* We define that the only valid reason for a protocol parser to not
* consume all data is, that the current message is complete.
* When a parser returns a not completely consumed data then we first
* check if the current message is ready. If it is we create a
* new data object from the remaining data and return it to the caller
* along with the message. The caller (which is the protocol handler)
* can then call this again with the remaining data.
* If the message is not ready we drop the data silently because there
* is either wrong data or a bug in the parser and the data will never
* be consumed correctly.
* INFO: Usually we do not free data here at all. We leave this to the
* protocol implementation. The protocol might take the data without
* copying it at all or if it copies it is responsible for the free too.
* Only if we got a wrong behaviour of the protocol we free the data
* ourself.
* IMPORTANT: The protocol should never free the data when it does not
* consume it completely.
* IMPORTANT: To keep this maintainable we must write a log message here
* when we drop data. This message should be a WARNING as the protocol
* might want to drop data intentionally... (probably this is not
* true and we should make it an ERROR).
*/
if (this->current_message->ready) {
if (end != ((TR_SizedData)*data)->size) {
new_data = TR_new(
TR_RemoteData,
((TR_SizedData)*data)->data + end,
((TR_SizedData)*data)->size - end,
(*data)->remote);
}
ret_message = this->current_message;
this->current_message = NULL;
} else {
if (end != ((TR_SizedData)*data)->size) {
TR_delete(*data);
TR_loggerLog(
TR_logger,
TR_LOGGER_WARNING,
"Drop data not consumed by protocol.");
}
}
*data = new_data;
}
return ret_message;
}
static
size_t
connectionCompose(void * _this, TR_ProtoMessage message)
{
TR_RemoteData data =
TR_protoCompose(((TR_CommEndPoint)_this)->protocol, message);
if (! data) {
return FALSE;
}
TR_queuePut(((TR_CommEndPoint)_this)->write_buffer, data);
return ((TR_SizedData)data)->size;
}
static
void
connectionCvInit(TR_class_ptr cls)
{
TR_EVENT_CREATE(cls, TR_CON_EVENT_NEW_CON);
}
const char * TR_connectionEventStrings[] = {
"TR_CON_EVENT_NEW_CON"
};
intptr_t connection_events[TR_CON_EVENT_MAX + 1];
TR_INIT_IFACE(TR_Class, connectionCtor, connectionDtor, NULL);
TR_INIT_IFACE(
TR_CommEndPoint,
connectionNextMessage,
connectionCompose);
TR_CREATE_CLASS(
TR_Connection,
TR_CommEndPoint,
connectionCvInit,
TR_IF(TR_Class),
TR_IF(TR_CommEndPoint)) = {
{{
TR_connectionEventStrings,
TR_CON_EVENT_MAX + 1,
connection_events
}}
};
// vim: set ts=4 sw=4: