yong-stropheruby 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/ext/event.c ADDED
@@ -0,0 +1,345 @@
1
+ /* event.c
2
+ ** strophe XMPP client library -- event loop and management
3
+ **
4
+ ** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
5
+ **
6
+ ** This software is provided AS-IS with no warranty, either express
7
+ ** or implied.
8
+ **
9
+ ** This software is distributed under license and may not be copied,
10
+ ** modified or distributed except as expressly authorized under the
11
+ ** terms of the license contained in the file LICENSE.txt in this
12
+ ** distribution.
13
+ */
14
+
15
+ /** @file
16
+ * Event loop and management.
17
+ */
18
+
19
+ /** @defgroup EventLoop Event loop
20
+ * These functions manage the Strophe event loop.
21
+ *
22
+ * Simple tools can use xmpp_run() and xmpp_stop() to manage the life
23
+ * cycle of the program. A common idiom is to set up a few initial
24
+ * event handers, call xmpp_run(), and then respond and react to
25
+ * events as they come in. At some point, one of the handlers will
26
+ * call xmpp_stop() to quit the event loop which leads to the program
27
+ * terminating.
28
+ *
29
+ * More complex programs will have their own event loops, and should
30
+ * ensure that xmpp_run_once() is called regularly from there. For
31
+ * example, a GUI program will already include an event loop to
32
+ * process UI events from users, and xmpp_run_once() would be called
33
+ * from an idle function.
34
+ */
35
+
36
+ #include <stdio.h>
37
+ #include <stdlib.h>
38
+ #include <string.h>
39
+
40
+ #ifndef _WIN32
41
+ #include <sys/select.h>
42
+ #include <errno.h>
43
+ #else
44
+ #include <winsock2.h>
45
+ #define ETIMEDOUT WSAETIMEDOUT
46
+ #define ECONNRESET WSAECONNRESET
47
+ #define ECONNABORTED WSAECONNABORTED
48
+ #endif
49
+
50
+ #include "strophe.h"
51
+ #include "common.h"
52
+
53
+ #ifndef DEFAULT_TIMEOUT
54
+ /** @def DEFAULT_TIMEOUT
55
+ * The default timeout in milliseconds for the event loop.
56
+ * This is set to 1 millisecond.
57
+ */
58
+ #define DEFAULT_TIMEOUT 1
59
+ #endif
60
+
61
+ /** Run the event loop once.
62
+ * This function will run send any data that has been queued by
63
+ * xmpp_send and related functions and run through the Strophe even
64
+ * loop a single time, and will not wait more than timeout
65
+ * milliseconds for events. This is provided to support integration
66
+ * with event loops outside the library, and if used, should be
67
+ * called regularly to achieve low latency event handling.
68
+ *
69
+ * @param ctx a Strophe context object
70
+ * @param timeout time to wait for events in milliseconds
71
+ *
72
+ * @ingroup EventLoop
73
+ */
74
+ int xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
75
+ {
76
+ xmpp_connlist_t *connitem;
77
+ xmpp_conn_t *conn;
78
+ fd_set rfds, wfds;
79
+ sock_t max = 0;
80
+ int ret;
81
+ struct timeval tv;
82
+ xmpp_send_queue_t *sq, *tsq;
83
+ int towrite;
84
+ char buf[4096];
85
+ uint64_t next;
86
+
87
+ if (ctx->loop_status == XMPP_LOOP_QUIT) return -2;
88
+ ctx->loop_status = XMPP_LOOP_RUNNING;
89
+
90
+ /* send queued data */
91
+ connitem = ctx->connlist;
92
+ while (connitem) {
93
+ conn = connitem->conn;
94
+ if (conn->state != XMPP_STATE_CONNECTED) {
95
+ connitem = connitem->next;
96
+ continue;
97
+ }
98
+
99
+ /* if we're running tls, there may be some remaining data waiting to
100
+ * be sent, so push that out */
101
+ if (conn->tls) {
102
+ ret = tls_clear_pending_write(conn->tls);
103
+
104
+ if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
105
+ /* an error occured */
106
+ xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting.");
107
+ conn->error = ECONNABORTED;
108
+ conn_disconnect(conn);
109
+ }
110
+ }
111
+
112
+ /* write all data from the send queue to the socket */
113
+ sq = conn->send_queue_head;
114
+ while (sq) {
115
+ towrite = sq->len - sq->written;
116
+
117
+ if (conn->tls) {
118
+ ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
119
+
120
+ if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
121
+ /* an error occured */
122
+ conn->error = tls_error(conn->tls);
123
+ break;
124
+ } else if (ret < towrite) {
125
+ /* not all data could be sent now */
126
+ if (ret >= 0) sq->written += ret;
127
+ break;
128
+ }
129
+
130
+ } else {
131
+ ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
132
+
133
+ if (ret < 0 && !sock_is_recoverable(sock_error())) {
134
+ /* an error occured */
135
+ conn->error = sock_error();
136
+ break;
137
+ } else if (ret < towrite) {
138
+ /* not all data could be sent now */
139
+ if (ret >= 0) sq->written += ret;
140
+ break;
141
+ }
142
+ }
143
+
144
+ /* all data for this queue item written, delete and move on */
145
+ xmpp_free(ctx, sq->data);
146
+ tsq = sq;
147
+ sq = sq->next;
148
+ xmpp_free(ctx, tsq);
149
+
150
+ /* pop the top item */
151
+ conn->send_queue_head = sq;
152
+ /* if we've sent everything update the tail */
153
+ if (!sq) conn->send_queue_tail = NULL;
154
+ }
155
+
156
+ /* tear down connection on error */
157
+ if (conn->error) {
158
+ /* FIXME: need to tear down send queues and random other things
159
+ * maybe this should be abstracted */
160
+ xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting.");
161
+ conn->error = ECONNABORTED;
162
+ conn_disconnect(conn);
163
+ }
164
+
165
+ connitem = connitem->next;
166
+ }
167
+
168
+ /* reset parsers if needed */
169
+ for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
170
+ if (connitem->conn->reset_parser)
171
+ parser_reset(connitem->conn);
172
+ }
173
+
174
+
175
+ /* fire any ready timed handlers, then
176
+ make sure we don't wait past the time when timed handlers need
177
+ to be called */
178
+ next = handler_fire_timed(ctx);
179
+
180
+ long usec = ((next < timeout) ? next : timeout) * 1000;
181
+ tv.tv_sec = usec / 1000000;
182
+ tv.tv_usec = usec % 1000000;
183
+
184
+ FD_ZERO(&rfds);
185
+ FD_ZERO(&wfds);
186
+
187
+ /* find events to watch */
188
+ connitem = ctx->connlist;
189
+ while (connitem) {
190
+ conn = connitem->conn;
191
+
192
+ switch (conn->state) {
193
+ case XMPP_STATE_CONNECTING:
194
+ /* connect has been called and we're waiting for it to complete */
195
+ /* connection will give us write or error events */
196
+
197
+ /* make sure the timeout hasn't expired */
198
+ if (time_elapsed(conn->timeout_stamp, time_stamp()) <=
199
+ conn->connect_timeout)
200
+ FD_SET(conn->sock, &wfds);
201
+ else {
202
+ conn->error = ETIMEDOUT;
203
+ xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
204
+ conn_disconnect(conn);
205
+ }
206
+ break;
207
+ case XMPP_STATE_CONNECTED:
208
+ FD_SET(conn->sock, &rfds);
209
+ break;
210
+ case XMPP_STATE_DISCONNECTED:
211
+ /* do nothing */
212
+ default:
213
+ break;
214
+ }
215
+
216
+ if (conn->sock > max) max = conn->sock;
217
+
218
+ connitem = connitem->next;
219
+ }
220
+
221
+ /* check for events */
222
+ ret = select(max + 1, &rfds, &wfds, NULL, &tv);
223
+
224
+ /* select errored */
225
+ if (ret < 0) {
226
+ if (!sock_is_recoverable(sock_error())) {
227
+ xmpp_error(ctx, "xmpp", "event watcher internal error %d",
228
+ sock_error());
229
+ return -1;
230
+ }
231
+ }
232
+
233
+ /* no events happened */
234
+ if (ret == 0) return 1;
235
+
236
+ /* process events */
237
+ connitem = ctx->connlist;
238
+ while (connitem) {
239
+ conn = connitem->conn;
240
+
241
+ switch (conn->state) {
242
+ case XMPP_STATE_CONNECTING:
243
+ if (FD_ISSET(conn->sock, &wfds)) {
244
+ /* connection complete */
245
+
246
+ /* check for error */
247
+ if (sock_connect_error(conn->sock) != 0) {
248
+ /* connection failed */
249
+ xmpp_debug(ctx, "xmpp", "connection failed");
250
+ conn_disconnect(conn);
251
+ break;
252
+ }
253
+
254
+ conn->state = XMPP_STATE_CONNECTED;
255
+ xmpp_debug(ctx, "xmpp", "connection successful");
256
+
257
+
258
+ /* send stream init */
259
+ conn_open_stream(conn);
260
+ }
261
+
262
+ break;
263
+ case XMPP_STATE_CONNECTED:
264
+ if (FD_ISSET(conn->sock, &rfds)) {
265
+ if (conn->tls) {
266
+ ret = tls_read(conn->tls, buf, 4096);
267
+ } else {
268
+ ret = sock_read(conn->sock, buf, 4096);
269
+ }
270
+
271
+ if (ret > 0) {
272
+ ret = XML_Parse(conn->parser, buf, ret, 0);
273
+ if (!ret) {
274
+ /* parse error, we need to shut down */
275
+ /* FIXME */
276
+ xmpp_debug(ctx, "xmpp", "parse error, disconnecting");
277
+ conn_disconnect(conn);
278
+ }
279
+ } else {
280
+ if (conn->tls) {
281
+ if (!tls_is_recoverable(tls_error(conn->tls)))
282
+ {
283
+ xmpp_debug(ctx, "xmpp", "Unrecoverable TLS error, %d.", tls_error(conn->tls));
284
+ conn->error = tls_error(conn->tls);
285
+ conn_disconnect(conn);
286
+ }
287
+ } else {
288
+ /* return of 0 means socket closed by server */
289
+ xmpp_debug(ctx, "xmpp", "Socket closed by remote host.");
290
+ conn->error = ECONNRESET;
291
+ conn_disconnect(conn);
292
+ }
293
+ }
294
+ }
295
+
296
+ break;
297
+ case XMPP_STATE_DISCONNECTED:
298
+ /* do nothing */
299
+ default:
300
+ break;
301
+ }
302
+
303
+ connitem = connitem->next;
304
+ }
305
+
306
+ /* fire any ready handlers */
307
+ handler_fire_timed(ctx);
308
+ return 0;
309
+ }
310
+
311
+ /** Start the event loop.
312
+ * This function continuously calls xmpp_run_once and does not return
313
+ * until xmpp_stop has been called.
314
+ *
315
+ * @param ctx a Strophe context object
316
+ *
317
+ * @ingroup EventLoop
318
+ */
319
+ void xmpp_run(xmpp_ctx_t *ctx)
320
+ {
321
+ if (ctx->loop_status != XMPP_LOOP_NOTSTARTED) return;
322
+
323
+ ctx->loop_status = XMPP_LOOP_RUNNING;
324
+ while (ctx->loop_status == XMPP_LOOP_RUNNING) {
325
+ xmpp_run_once(ctx, DEFAULT_TIMEOUT);
326
+ }
327
+
328
+ xmpp_debug(ctx, "event", "Event loop completed.");
329
+ }
330
+
331
+ /** Stop the event loop.
332
+ * This will stop the event loop after the current iteration and cause
333
+ * xmpp_run to exit.
334
+ *
335
+ * @param ctx a Strophe context object
336
+ *
337
+ * @ingroup EventLoop
338
+ */
339
+ void xmpp_stop(xmpp_ctx_t *ctx)
340
+ {
341
+ xmpp_debug(ctx, "event", "Stopping event loop.");
342
+
343
+ if (ctx->loop_status == XMPP_LOOP_RUNNING)
344
+ ctx->loop_status = XMPP_LOOP_QUIT;
345
+ }
data/ext/extconf.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+ $CFLAGS = "#{ENV['CFLAGS']} -Wall -O3 "
3
+ if CONFIG["MAJOR"].to_i >= 1 && CONFIG["MINOR"].to_i >= 8
4
+ $CFLAGS << " -DHAVE_DEFINE_ALLOC_FUNCTION"
5
+ end
6
+ create_makefile('strophe_ruby')