ffi-nats-core 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/ffi-nats-core.gemspec +8 -0
  3. data/lib/ffi/nats/core/version.rb +1 -1
  4. data/vendor/cnats/CMakeLists.txt +137 -0
  5. data/vendor/cnats/adapters/libevent.h +220 -0
  6. data/vendor/cnats/adapters/libuv.h +472 -0
  7. data/vendor/cnats/examples/CMakeLists.txt +56 -0
  8. data/vendor/cnats/examples/asynctimeout.c +83 -0
  9. data/vendor/cnats/examples/examples.h +322 -0
  10. data/vendor/cnats/examples/libevent-pub.c +136 -0
  11. data/vendor/cnats/examples/libevent-sub.c +104 -0
  12. data/vendor/cnats/examples/libuv-pub.c +120 -0
  13. data/vendor/cnats/examples/libuv-sub.c +114 -0
  14. data/vendor/cnats/examples/publisher.c +62 -0
  15. data/vendor/cnats/examples/queuegroup.c +132 -0
  16. data/vendor/cnats/examples/replier.c +149 -0
  17. data/vendor/cnats/examples/requestor.c +75 -0
  18. data/vendor/cnats/examples/subscriber.c +133 -0
  19. data/vendor/cnats/src/CMakeLists.txt +31 -0
  20. data/vendor/cnats/src/asynccb.c +66 -0
  21. data/vendor/cnats/src/asynccb.h +42 -0
  22. data/vendor/cnats/src/buf.c +246 -0
  23. data/vendor/cnats/src/buf.h +116 -0
  24. data/vendor/cnats/src/comsock.c +474 -0
  25. data/vendor/cnats/src/comsock.h +81 -0
  26. data/vendor/cnats/src/conn.c +2725 -0
  27. data/vendor/cnats/src/conn.h +75 -0
  28. data/vendor/cnats/src/err.h +31 -0
  29. data/vendor/cnats/src/gc.h +27 -0
  30. data/vendor/cnats/src/hash.c +725 -0
  31. data/vendor/cnats/src/hash.h +141 -0
  32. data/vendor/cnats/src/include/n-unix.h +56 -0
  33. data/vendor/cnats/src/include/n-win.h +59 -0
  34. data/vendor/cnats/src/mem.h +20 -0
  35. data/vendor/cnats/src/msg.c +155 -0
  36. data/vendor/cnats/src/msg.h +43 -0
  37. data/vendor/cnats/src/nats.c +1734 -0
  38. data/vendor/cnats/src/nats.h +2024 -0
  39. data/vendor/cnats/src/natsp.h +518 -0
  40. data/vendor/cnats/src/natstime.c +79 -0
  41. data/vendor/cnats/src/natstime.h +27 -0
  42. data/vendor/cnats/src/nuid.c +265 -0
  43. data/vendor/cnats/src/nuid.h +21 -0
  44. data/vendor/cnats/src/opts.c +1030 -0
  45. data/vendor/cnats/src/opts.h +19 -0
  46. data/vendor/cnats/src/parser.c +869 -0
  47. data/vendor/cnats/src/parser.h +87 -0
  48. data/vendor/cnats/src/pub.c +293 -0
  49. data/vendor/cnats/src/srvpool.c +380 -0
  50. data/vendor/cnats/src/srvpool.h +71 -0
  51. data/vendor/cnats/src/stats.c +54 -0
  52. data/vendor/cnats/src/stats.h +21 -0
  53. data/vendor/cnats/src/status.c +60 -0
  54. data/vendor/cnats/src/status.h +95 -0
  55. data/vendor/cnats/src/sub.c +956 -0
  56. data/vendor/cnats/src/sub.h +34 -0
  57. data/vendor/cnats/src/timer.c +86 -0
  58. data/vendor/cnats/src/timer.h +57 -0
  59. data/vendor/cnats/src/unix/cond.c +103 -0
  60. data/vendor/cnats/src/unix/mutex.c +107 -0
  61. data/vendor/cnats/src/unix/sock.c +105 -0
  62. data/vendor/cnats/src/unix/thread.c +162 -0
  63. data/vendor/cnats/src/url.c +134 -0
  64. data/vendor/cnats/src/url.h +24 -0
  65. data/vendor/cnats/src/util.c +823 -0
  66. data/vendor/cnats/src/util.h +75 -0
  67. data/vendor/cnats/src/version.h +29 -0
  68. data/vendor/cnats/src/version.h.in +29 -0
  69. data/vendor/cnats/src/win/cond.c +86 -0
  70. data/vendor/cnats/src/win/mutex.c +54 -0
  71. data/vendor/cnats/src/win/sock.c +158 -0
  72. data/vendor/cnats/src/win/strings.c +108 -0
  73. data/vendor/cnats/src/win/thread.c +180 -0
  74. data/vendor/cnats/test/CMakeLists.txt +35 -0
  75. data/vendor/cnats/test/certs/ca.pem +38 -0
  76. data/vendor/cnats/test/certs/client-cert.pem +30 -0
  77. data/vendor/cnats/test/certs/client-key.pem +51 -0
  78. data/vendor/cnats/test/certs/server-cert.pem +31 -0
  79. data/vendor/cnats/test/certs/server-key.pem +51 -0
  80. data/vendor/cnats/test/dylib/CMakeLists.txt +10 -0
  81. data/vendor/cnats/test/dylib/nonats.c +13 -0
  82. data/vendor/cnats/test/list.txt +125 -0
  83. data/vendor/cnats/test/test.c +11655 -0
  84. data/vendor/cnats/test/tls.conf +15 -0
  85. data/vendor/cnats/test/tlsverify.conf +19 -0
  86. metadata +83 -1
@@ -0,0 +1,81 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #ifndef SOCK_H_
4
+ #define SOCK_H_
5
+
6
+ #include "natsp.h"
7
+
8
+ natsStatus
9
+ natsSock_Init(natsSockCtx *ctx);
10
+
11
+ void
12
+ natsSock_Clear(natsSockCtx *ctx);
13
+
14
+ natsStatus
15
+ natsSock_WaitReady(int waitMode, natsSockCtx *ctx);
16
+
17
+ natsStatus
18
+ natsSock_ConnectTcp(natsSockCtx *ctx, const char *host, int port);
19
+
20
+ natsStatus
21
+ natsSock_SetBlocking(natsSock fd, bool blocking);
22
+
23
+ natsStatus
24
+ natsSock_CreateFDSet(fd_set **newFDSet);
25
+
26
+ void
27
+ natsSock_DestroyFDSet(fd_set *fdSet);
28
+
29
+ bool
30
+ natsSock_IsConnected(natsSock fd);
31
+
32
+ // Reads a line from the socket and returns it without the line-ending characters.
33
+ // This call blocks until the line is complete, or the socket is closed or an
34
+ // error occurs.
35
+ // Handles blocking and non-blocking sockets. For the later, an optional 'deadline'
36
+ // indicates how long it can wait for the full read to complete.
37
+ //
38
+ // NOTE: 'buffer[0]' must be set to '\0' prior to the very first call. If the
39
+ // peer is sending multiple lines, it is possible that this function reads the
40
+ // next line(s) (or partials) in a single call. In this case, the caller needs
41
+ // to repeat the call with the same buffer to "read" the next line.
42
+ natsStatus
43
+ natsSock_ReadLine(natsSockCtx *ctx, char *buffer, size_t maxBufferSize);
44
+
45
+ // Reads up to 'maxBufferSize' bytes from the socket and put them in 'buffer'.
46
+ // If the socket is blocking, wait until some data is available or the socket
47
+ // is closed or an error occurs.
48
+ // If the socket is non-blocking, wait up to the optional deadline (set in
49
+ // the context). If NULL, behaves like a blocking socket.
50
+ // If an external event loop is used, it is possible that this function
51
+ // returns NATS_OK with 'n' == 0.
52
+ natsStatus
53
+ natsSock_Read(natsSockCtx *ctx, char *buffer, size_t maxBufferSize, int *n);
54
+
55
+ // Writes up to 'len' bytes to the socket. If the socket is blocking,
56
+ // wait for some data to be sent. If the socket is non-blocking, wait up
57
+ // to the optional deadline (set in ctx).
58
+ // If an external event loop is used, it is possible that this function
59
+ // returns NATS_OK with 'n' == 0.
60
+ natsStatus
61
+ natsSock_Write(natsSockCtx *ctx, const char *data, int len, int *n);
62
+
63
+ // Writes 'len' bytes to the socket. Does not return until all bytes
64
+ // have been written, unless the socket is closed or an error occurs.
65
+ natsStatus
66
+ natsSock_WriteFully(natsSockCtx *ctx, const char *data, int len);
67
+
68
+ natsStatus
69
+ natsSock_Flush(natsSock fd);
70
+
71
+ void
72
+ natsSock_Close(natsSock fd);
73
+
74
+ natsStatus
75
+ natsSock_SetCommonTcpOptions(natsSock fd);
76
+
77
+ void
78
+ natsSock_Shutdown(natsSock fd);
79
+
80
+
81
+ #endif /* SOCK_H_ */
@@ -0,0 +1,2725 @@
1
+ // Copyright 2015-2016 Apcera Inc. All rights reserved.
2
+
3
+ #include "natsp.h"
4
+
5
+ #include <assert.h>
6
+ #include <stdio.h>
7
+ #include <string.h>
8
+ #include <errno.h>
9
+
10
+ #include "conn.h"
11
+ #include "mem.h"
12
+ #include "buf.h"
13
+ #include "parser.h"
14
+ #include "srvpool.h"
15
+ #include "url.h"
16
+ #include "opts.h"
17
+ #include "util.h"
18
+ #include "timer.h"
19
+ #include "sub.h"
20
+ #include "msg.h"
21
+ #include "asynccb.h"
22
+ #include "comsock.h"
23
+
24
+ #define DEFAULT_SCRATCH_SIZE (512)
25
+ #define DEFAULT_BUF_SIZE (32768)
26
+ #define DEFAULT_PENDING_SIZE (1024 * 1024)
27
+
28
+ #define NATS_EVENT_ACTION_ADD (true)
29
+ #define NATS_EVENT_ACTION_REMOVE (false)
30
+
31
+ #ifdef DEV_MODE
32
+ // For type safety
33
+
34
+ static void _retain(natsConnection *nc) { nc->refs++; }
35
+ static void _release(natsConnection *nc) { nc->refs--; }
36
+
37
+ void natsConn_Lock(natsConnection *nc) { natsMutex_Lock(nc->mu); }
38
+ void natsConn_Unlock(natsConnection *nc) { natsMutex_Unlock(nc->mu); }
39
+
40
+ #else
41
+ // We know what we are doing :-)
42
+
43
+ #define _retain(c) ((c)->refs++)
44
+ #define _release(c) ((c)->refs--)
45
+
46
+ #endif // DEV_MODE
47
+
48
+
49
+ // CLIENT_PROTO_ZERO is the original client protocol from 2009.
50
+ // http://nats.io/documentation/internals/nats-protocol/
51
+ #define CLIENT_PROTO_ZERO (0)
52
+
53
+ // CLIENT_PROTO_INFO signals a client can receive more then the original INFO block.
54
+ // This can be used to update clients on other cluster members, etc.
55
+ #define CLIENT_PROTO_INFO (1)
56
+
57
+ /*
58
+ * Forward declarations:
59
+ */
60
+ static natsStatus
61
+ _spinUpSocketWatchers(natsConnection *nc);
62
+
63
+ static natsStatus
64
+ _processConnInit(natsConnection *nc);
65
+
66
+ static void
67
+ _close(natsConnection *nc, natsConnStatus status, bool doCBs);
68
+
69
+ /*
70
+ * ----------------------------------------
71
+ */
72
+
73
+ struct threadsToJoin
74
+ {
75
+ natsThread *readLoop;
76
+ natsThread *flusher;
77
+ natsThread *reconnect;
78
+ bool joinReconnect;
79
+
80
+ } threadsToJoin;
81
+
82
+ static void
83
+ _initThreadsToJoin(struct threadsToJoin *ttj, natsConnection *nc, bool joinReconnect)
84
+ {
85
+ memset(ttj, 0, sizeof(threadsToJoin));
86
+
87
+ ttj->joinReconnect = joinReconnect;
88
+
89
+ if (nc->readLoopThread != NULL)
90
+ {
91
+ ttj->readLoop = nc->readLoopThread;
92
+ nc->readLoopThread = NULL;
93
+ }
94
+
95
+ if (joinReconnect && (nc->reconnectThread != NULL))
96
+ {
97
+ ttj->reconnect = nc->reconnectThread;
98
+ nc->reconnectThread = NULL;
99
+ }
100
+
101
+ if (nc->flusherThread != NULL)
102
+ {
103
+ nc->flusherStop = true;
104
+ natsCondition_Signal(nc->flusherCond);
105
+
106
+ ttj->flusher = nc->flusherThread;
107
+ nc->flusherThread = NULL;
108
+ }
109
+ }
110
+
111
+ static void
112
+ _joinThreads(struct threadsToJoin *ttj)
113
+ {
114
+ if (ttj->readLoop != NULL)
115
+ {
116
+ natsThread_Join(ttj->readLoop);
117
+ natsThread_Destroy(ttj->readLoop);
118
+ }
119
+
120
+ if (ttj->joinReconnect && (ttj->reconnect != NULL))
121
+ {
122
+ natsThread_Join(ttj->reconnect);
123
+ natsThread_Destroy(ttj->reconnect);
124
+ }
125
+
126
+ if (ttj->flusher != NULL)
127
+ {
128
+ natsThread_Join(ttj->flusher);
129
+ natsThread_Destroy(ttj->flusher);
130
+ }
131
+ }
132
+
133
+ static void
134
+ _clearServerInfo(natsServerInfo *si)
135
+ {
136
+ int i;
137
+
138
+ NATS_FREE(si->id);
139
+ NATS_FREE(si->host);
140
+ NATS_FREE(si->version);
141
+
142
+ for (i=0; i<si->connectURLsCount; i++)
143
+ NATS_FREE(si->connectURLs[i]);
144
+ NATS_FREE(si->connectURLs);
145
+
146
+ memset(si, 0, sizeof(natsServerInfo));
147
+ }
148
+
149
+ static void
150
+ _freeConn(natsConnection *nc)
151
+ {
152
+ if (nc == NULL)
153
+ return;
154
+
155
+ natsTimer_Destroy(nc->ptmr);
156
+ natsBuf_Destroy(nc->pending);
157
+ natsBuf_Destroy(nc->scratch);
158
+ natsBuf_Destroy(nc->bw);
159
+ natsSrvPool_Destroy(nc->srvPool);
160
+ _clearServerInfo(&(nc->info));
161
+ natsCondition_Destroy(nc->flusherCond);
162
+ natsCondition_Destroy(nc->pongs.cond);
163
+ natsParser_Destroy(nc->ps);
164
+ natsThread_Destroy(nc->readLoopThread);
165
+ natsThread_Destroy(nc->flusherThread);
166
+ natsHash_Destroy(nc->subs);
167
+ natsOptions_Destroy(nc->opts);
168
+ natsSock_Clear(&nc->sockCtx);
169
+ if (nc->sockCtx.ssl != NULL)
170
+ SSL_free(nc->sockCtx.ssl);
171
+ NATS_FREE(nc->el.buffer);
172
+ natsMutex_Destroy(nc->mu);
173
+
174
+ NATS_FREE(nc);
175
+
176
+ natsLib_Release();
177
+ }
178
+
179
+ void
180
+ natsConn_retain(natsConnection *nc)
181
+ {
182
+ if (nc == NULL)
183
+ return;
184
+
185
+ natsConn_Lock(nc);
186
+
187
+ nc->refs++;
188
+
189
+ natsConn_Unlock(nc);
190
+ }
191
+
192
+ void
193
+ natsConn_release(natsConnection *nc)
194
+ {
195
+ int refs = 0;
196
+
197
+ if (nc == NULL)
198
+ return;
199
+
200
+ natsConn_Lock(nc);
201
+
202
+ refs = --(nc->refs);
203
+
204
+ natsConn_Unlock(nc);
205
+
206
+ if (refs == 0)
207
+ _freeConn(nc);
208
+ }
209
+
210
+ void
211
+ natsConn_lockAndRetain(natsConnection *nc)
212
+ {
213
+ natsConn_Lock(nc);
214
+ nc->refs++;
215
+ }
216
+
217
+ void
218
+ natsConn_unlockAndRelease(natsConnection *nc)
219
+ {
220
+ int refs = 0;
221
+
222
+ refs = --(nc->refs);
223
+
224
+ natsConn_Unlock(nc);
225
+
226
+ if (refs == 0)
227
+ _freeConn(nc);
228
+ }
229
+
230
+ natsStatus
231
+ natsConn_bufferFlush(natsConnection *nc)
232
+ {
233
+ natsStatus s = NATS_OK;
234
+ int bufLen = natsBuf_Len(nc->bw);
235
+
236
+ if (bufLen == 0)
237
+ return NATS_OK;
238
+
239
+ if (nc->usePending)
240
+ {
241
+ s = natsBuf_Append(nc->pending, natsBuf_Data(nc->bw), bufLen);
242
+ }
243
+ else if (nc->sockCtx.useEventLoop)
244
+ {
245
+ if (!(nc->el.writeAdded))
246
+ {
247
+ nc->el.writeAdded = true;
248
+ s = nc->opts->evCbs.write(nc->el.data, NATS_EVENT_ACTION_ADD);
249
+ if (s != NATS_OK)
250
+ nats_setError(s, "Error processing write request: %d - %s",
251
+ s, natsStatus_GetText(s));
252
+ }
253
+
254
+ return NATS_UPDATE_ERR_STACK(s);
255
+ }
256
+ else
257
+ {
258
+ s = natsSock_WriteFully(&(nc->sockCtx), natsBuf_Data(nc->bw), bufLen);
259
+ }
260
+
261
+ if (s == NATS_OK)
262
+ natsBuf_Reset(nc->bw);
263
+
264
+ return NATS_UPDATE_ERR_STACK(s);
265
+ }
266
+
267
+ natsStatus
268
+ natsConn_bufferWrite(natsConnection *nc, const char *buffer, int len)
269
+ {
270
+ natsStatus s = NATS_OK;
271
+ int offset = 0;
272
+ int avail = 0;
273
+
274
+ if (len <= 0)
275
+ return NATS_OK;
276
+
277
+ if (nc->usePending)
278
+ return natsBuf_Append(nc->pending, buffer, len);
279
+
280
+ if (nc->sockCtx.useEventLoop)
281
+ {
282
+ s = natsBuf_Append(nc->bw, buffer, len);
283
+ if ((s == NATS_OK)
284
+ && (natsBuf_Len(nc->bw) >= DEFAULT_BUF_SIZE)
285
+ && !(nc->el.writeAdded))
286
+ {
287
+ nc->el.writeAdded = true;
288
+ s = nc->opts->evCbs.write(nc->el.data, NATS_EVENT_ACTION_ADD);
289
+ if (s != NATS_OK)
290
+ nats_setError(s, "Error processing write request: %d - %s",
291
+ s, natsStatus_GetText(s));
292
+ }
293
+
294
+ return NATS_UPDATE_ERR_STACK(s);
295
+ }
296
+
297
+ // If we have more data that can fit..
298
+ while ((s == NATS_OK) && (len > natsBuf_Available(nc->bw)))
299
+ {
300
+ // If there is nothing in the buffer...
301
+ if (natsBuf_Len(nc->bw) == 0)
302
+ {
303
+ // Do a single socket write to avoid a copy
304
+ s = natsSock_WriteFully(&(nc->sockCtx), buffer + offset, len);
305
+
306
+ // We are done
307
+ return NATS_UPDATE_ERR_STACK(s);
308
+ }
309
+
310
+ // We already have data in the buffer, check how many more bytes
311
+ // can we fit
312
+ avail = natsBuf_Available(nc->bw);
313
+
314
+ // Append that much bytes
315
+ s = natsBuf_Append(nc->bw, buffer + offset, avail);
316
+
317
+ // Flush the buffer
318
+ if (s == NATS_OK)
319
+ s = natsConn_bufferFlush(nc);
320
+
321
+ // If success, then decrement what's left to send and update the
322
+ // offset.
323
+ if (s == NATS_OK)
324
+ {
325
+ len -= avail;
326
+ offset += avail;
327
+ }
328
+ }
329
+
330
+ // If there is data left, the buffer can now hold this data.
331
+ if ((s == NATS_OK) && (len > 0))
332
+ s = natsBuf_Append(nc->bw, buffer + offset, len);
333
+
334
+ return NATS_UPDATE_ERR_STACK(s);
335
+ }
336
+
337
+ natsStatus
338
+ natsConn_bufferWriteString(natsConnection *nc, const char *string)
339
+ {
340
+ natsStatus s = natsConn_bufferWrite(nc, string, (int) strlen(string));
341
+
342
+ return NATS_UPDATE_ERR_STACK(s);
343
+ }
344
+
345
+ // _createConn will connect to the server and do the right thing when an
346
+ // existing connection is in place.
347
+ static natsStatus
348
+ _createConn(natsConnection *nc)
349
+ {
350
+ natsStatus s = NATS_OK;
351
+ natsSrv *cur = NULL;
352
+
353
+ cur = natsSrvPool_GetCurrentServer(nc->srvPool, nc->url, NULL);
354
+ if (cur == NULL)
355
+ return nats_setDefaultError(NATS_NO_SERVER);
356
+
357
+ cur->lastAttempt = nats_Now();
358
+
359
+ // Sets a deadline for the connect process (not just the low level
360
+ // tcp connect. The deadline will be removed when we have received
361
+ // the PONG to our initial PING. See _processConnInit().
362
+ natsDeadline_Init(&(nc->sockCtx.deadline), nc->opts->timeout);
363
+
364
+ // Set the IP resolution order
365
+ nc->sockCtx.orderIP = nc->opts->orderIP;
366
+
367
+ s = natsSock_ConnectTcp(&(nc->sockCtx), nc->url->host, nc->url->port);
368
+ if (s == NATS_OK)
369
+ {
370
+ nc->sockCtx.fdActive = true;
371
+
372
+ if ((nc->pending != NULL) && (nc->bw != NULL)
373
+ && (natsBuf_Len(nc->bw) > 0))
374
+ {
375
+ // Move to pending buffer
376
+ s = natsConn_bufferWrite(nc, natsBuf_Data(nc->bw),
377
+ natsBuf_Len(nc->bw));
378
+ }
379
+ }
380
+
381
+ if (s == NATS_OK)
382
+ {
383
+ if (nc->bw == NULL)
384
+ s = natsBuf_Create(&(nc->bw), DEFAULT_BUF_SIZE);
385
+ else
386
+ natsBuf_Reset(nc->bw);
387
+ }
388
+
389
+ if (s != NATS_OK)
390
+ {
391
+ // reset the deadline
392
+ natsDeadline_Clear(&(nc->sockCtx.deadline));
393
+ }
394
+
395
+ return NATS_UPDATE_ERR_STACK(s);
396
+ }
397
+
398
+ static void
399
+ _clearControlContent(natsControl *control)
400
+ {
401
+ NATS_FREE(control->op);
402
+ NATS_FREE(control->args);
403
+ }
404
+
405
+ static void
406
+ _initControlContent(natsControl *control)
407
+ {
408
+ control->op = NULL;
409
+ control->args = NULL;
410
+ }
411
+
412
+ static bool
413
+ _isConnecting(natsConnection *nc)
414
+ {
415
+ return nc->status == CONNECTING;
416
+ }
417
+
418
+ bool
419
+ natsConn_isClosed(natsConnection *nc)
420
+ {
421
+ return nc->status == CLOSED;
422
+ }
423
+
424
+ bool
425
+ natsConn_isReconnecting(natsConnection *nc)
426
+ {
427
+ return (nc->status == RECONNECTING);
428
+ }
429
+
430
+ static natsStatus
431
+ _readOp(natsConnection *nc, natsControl *control)
432
+ {
433
+ natsStatus s = NATS_OK;
434
+ char buffer[DEFAULT_BUF_SIZE];
435
+
436
+ buffer[0] = '\0';
437
+
438
+ s = natsSock_ReadLine(&(nc->sockCtx), buffer, sizeof(buffer));
439
+ if (s == NATS_OK)
440
+ s = nats_ParseControl(control, buffer);
441
+
442
+ return NATS_UPDATE_ERR_STACK(s);
443
+ }
444
+
445
+ // _processInfo is used to parse the info messages sent
446
+ // from the server.
447
+ // This function may update the server pool.
448
+ static natsStatus
449
+ _processInfo(natsConnection *nc, char *info, int len)
450
+ {
451
+ natsStatus s = NATS_OK;
452
+ nats_JSON *json = NULL;
453
+
454
+ if (info == NULL)
455
+ return NATS_OK;
456
+
457
+ _clearServerInfo(&(nc->info));
458
+
459
+ s = nats_JSONParse(&json, info, len);
460
+ if (s != NATS_OK)
461
+ return NATS_UPDATE_ERR_STACK(s);
462
+
463
+ if (s == NATS_OK)
464
+ s = nats_JSONGetValue(json, "server_id", TYPE_STR,
465
+ (void**) &(nc->info.id));
466
+ if (s == NATS_OK)
467
+ s = nats_JSONGetValue(json, "version", TYPE_STR,
468
+ (void**) &(nc->info.version));
469
+ if (s == NATS_OK)
470
+ s = nats_JSONGetValue(json, "host", TYPE_STR,
471
+ (void**) &(nc->info.host));
472
+ if (s == NATS_OK)
473
+ s = nats_JSONGetValue(json, "port", TYPE_INT,
474
+ (void**) &(nc->info.port));
475
+ if (s == NATS_OK)
476
+ s = nats_JSONGetValue(json, "auth_required", TYPE_BOOL,
477
+ (void**) &(nc->info.authRequired));
478
+ if (s == NATS_OK)
479
+ s = nats_JSONGetValue(json, "tls_required", TYPE_BOOL,
480
+ (void**) &(nc->info.tlsRequired));
481
+ if (s == NATS_OK)
482
+ s = nats_JSONGetValue(json, "max_payload", TYPE_LONG,
483
+ (void**) &(nc->info.maxPayload));
484
+ if (s == NATS_OK)
485
+ s = nats_JSONGetArrayValue(json, "connect_urls", TYPE_STR,
486
+ (void***) &(nc->info.connectURLs),
487
+ &(nc->info.connectURLsCount));
488
+
489
+ #if 0
490
+ fprintf(stderr, "Id=%s Version=%s Host=%s Port=%d Auth=%s SSL=%s Payload=%d\n",
491
+ nc->info.id, nc->info.version, nc->info.host, nc->info.port,
492
+ nats_GetBoolStr(nc->info.authRequired),
493
+ nats_GetBoolStr(nc->info.tlsRequired),
494
+ (int) nc->info.maxPayload);
495
+ #endif
496
+
497
+ if (s == NATS_OK)
498
+ s = natsSrvPool_addNewURLs(nc->srvPool,
499
+ nc->info.connectURLs,
500
+ nc->info.connectURLsCount,
501
+ !nc->opts->noRandomize);
502
+
503
+ if (s != NATS_OK)
504
+ s = nats_setError(NATS_PROTOCOL_ERROR,
505
+ "Invalid protocol: %s", nats_GetLastError(NULL));
506
+
507
+ nats_JSONDestroy(json);
508
+
509
+ return NATS_UPDATE_ERR_STACK(s);
510
+ }
511
+
512
+ // natsConn_processAsyncINFO does the same than processInfo, but is called
513
+ // from the parser. Calls processInfo under connection's lock
514
+ // protection.
515
+ void
516
+ natsConn_processAsyncINFO(natsConnection *nc, char *buf, int len)
517
+ {
518
+ natsConn_Lock(nc);
519
+ // Ignore errors, we will simply not update the server pool...
520
+ (void) _processInfo(nc, buf, len);
521
+ natsConn_Unlock(nc);
522
+ }
523
+
524
+ // makeTLSConn will wrap an existing Conn using TLS
525
+ static natsStatus
526
+ _makeTLSConn(natsConnection *nc)
527
+ {
528
+ #if defined(NATS_HAS_TLS)
529
+ natsStatus s = NATS_OK;
530
+ SSL *ssl = NULL;
531
+
532
+ // Reset nc->errStr before initiating the handshake...
533
+ nc->errStr[0] = '\0';
534
+
535
+ natsMutex_Lock(nc->opts->sslCtx->lock);
536
+
537
+ s = natsSock_SetBlocking(nc->sockCtx.fd, true);
538
+ if (s == NATS_OK)
539
+ {
540
+ ssl = SSL_new(nc->opts->sslCtx->ctx);
541
+ if (ssl == NULL)
542
+ {
543
+ s = nats_setError(NATS_SSL_ERROR,
544
+ "Error creating SSL object: %s",
545
+ NATS_SSL_ERR_REASON_STRING);
546
+ }
547
+ else
548
+ {
549
+ nats_sslRegisterThreadForCleanup();
550
+
551
+ SSL_set_ex_data(ssl, 0, (void*) nc);
552
+ }
553
+ }
554
+ if (s == NATS_OK)
555
+ {
556
+ SSL_set_connect_state(ssl);
557
+
558
+ if (SSL_set_fd(ssl, (int) nc->sockCtx.fd) != 1)
559
+ {
560
+ s = nats_setError(NATS_SSL_ERROR,
561
+ "Error connecting the SSL object to a file descriptor : %s",
562
+ NATS_SSL_ERR_REASON_STRING);
563
+ }
564
+ }
565
+ if (s == NATS_OK)
566
+ {
567
+ if (SSL_do_handshake(ssl) != 1)
568
+ {
569
+ s = nats_setError(NATS_SSL_ERROR,
570
+ "SSL handshake error: %s",
571
+ NATS_SSL_ERR_REASON_STRING);
572
+ }
573
+ }
574
+ if ((s == NATS_OK) && !nc->opts->sslCtx->skipVerify)
575
+ {
576
+ X509 *cert = SSL_get_peer_certificate(ssl);
577
+
578
+ if (cert != NULL)
579
+ {
580
+ if ((SSL_get_verify_result(ssl) != X509_V_OK)
581
+ || (nc->errStr[0] != '\0'))
582
+ {
583
+ s = nats_setError(NATS_SSL_ERROR,
584
+ "Server certificate verification failed: %s",
585
+ nc->errStr);
586
+ }
587
+ X509_free(cert);
588
+ }
589
+ else
590
+ {
591
+ s = nats_setError(NATS_SSL_ERROR, "%s",
592
+ "Server did not provide a certificate");
593
+ }
594
+ }
595
+
596
+ if (s == NATS_OK)
597
+ s = natsSock_SetBlocking(nc->sockCtx.fd, false);
598
+
599
+ natsMutex_Unlock(nc->opts->sslCtx->lock);
600
+
601
+ if (s != NATS_OK)
602
+ {
603
+ if (ssl != NULL)
604
+ SSL_free(ssl);
605
+ }
606
+ else
607
+ {
608
+ nc->sockCtx.ssl = ssl;
609
+ }
610
+
611
+ return NATS_UPDATE_ERR_STACK(s);
612
+ #else
613
+ return nats_setError(NATS_ILLEGAL_STATE, "%s", NO_SSL_ERR);
614
+ #endif
615
+ }
616
+
617
+ // This will check to see if the connection should be
618
+ // secure. This can be dictated from either end and should
619
+ // only be called after the INIT protocol has been received.
620
+ static natsStatus
621
+ _checkForSecure(natsConnection *nc)
622
+ {
623
+ natsStatus s = NATS_OK;
624
+
625
+ // Check for mismatch in setups
626
+ if (nc->opts->secure && !nc->info.tlsRequired)
627
+ s = nats_setDefaultError(NATS_SECURE_CONNECTION_WANTED);
628
+ else if (nc->info.tlsRequired && !nc->opts->secure)
629
+ s = nats_setDefaultError(NATS_SECURE_CONNECTION_REQUIRED);
630
+
631
+ if ((s == NATS_OK) && nc->opts->secure)
632
+ s = _makeTLSConn(nc);
633
+
634
+ return NATS_UPDATE_ERR_STACK(s);
635
+ }
636
+
637
+ static natsStatus
638
+ _processExpectedInfo(natsConnection *nc)
639
+ {
640
+ natsControl control;
641
+ natsStatus s;
642
+
643
+ _initControlContent(&control);
644
+
645
+ s = _readOp(nc, &control);
646
+ if (s != NATS_OK)
647
+ return NATS_UPDATE_ERR_STACK(s);
648
+
649
+ if ((s == NATS_OK)
650
+ && ((control.op == NULL)
651
+ || (strcmp(control.op, _INFO_OP_) != 0)))
652
+ {
653
+ s = nats_setError(NATS_PROTOCOL_ERROR,
654
+ "Unexpected protocol: got '%s' instead of '%s'",
655
+ (control.op == NULL ? "<null>" : control.op),
656
+ _INFO_OP_);
657
+ }
658
+ if (s == NATS_OK)
659
+ s = _processInfo(nc, control.args, -1);
660
+ if (s == NATS_OK)
661
+ s = _checkForSecure(nc);
662
+
663
+ _clearControlContent(&control);
664
+
665
+ return NATS_UPDATE_ERR_STACK(s);
666
+ }
667
+
668
+ static natsStatus
669
+ _connectProto(natsConnection *nc, char **proto)
670
+ {
671
+ natsOptions *opts = nc->opts;
672
+ const char *token= NULL;
673
+ const char *user = NULL;
674
+ const char *pwd = NULL;
675
+ const char *name = NULL;
676
+ int res;
677
+
678
+ if (nc->url->username != NULL)
679
+ user = nc->url->username;
680
+ if (nc->url->password != NULL)
681
+ pwd = nc->url->password;
682
+ if ((user != NULL) && (pwd == NULL))
683
+ {
684
+ token = user;
685
+ user = NULL;
686
+ }
687
+ if ((user == NULL) && (token == NULL))
688
+ {
689
+ // Take from options (possibly all NULL)
690
+ user = nc->opts->user;
691
+ pwd = nc->opts->password;
692
+ token = nc->opts->token;
693
+ }
694
+ if (opts->name != NULL)
695
+ name = opts->name;
696
+
697
+ res = nats_asprintf(proto,
698
+ "CONNECT {\"verbose\":%s,\"pedantic\":%s,%s%s%s%s%s%s%s%s%s\"tls_required\":%s," \
699
+ "\"name\":\"%s\",\"lang\":\"%s\",\"version\":\"%s\",\"protocol\":%d}%s",
700
+ nats_GetBoolStr(opts->verbose),
701
+ nats_GetBoolStr(opts->pedantic),
702
+ (user != NULL ? "\"user\":\"" : ""),
703
+ (user != NULL ? user : ""),
704
+ (user != NULL ? "\"," : ""),
705
+ (pwd != NULL ? "\"pass\":\"" : ""),
706
+ (pwd != NULL ? pwd : ""),
707
+ (pwd != NULL ? "\"," : ""),
708
+ (token != NULL ? "\"auth_token\":\"" :""),
709
+ (token != NULL ? token : ""),
710
+ (token != NULL ? "\"," : ""),
711
+ nats_GetBoolStr(opts->secure),
712
+ (name != NULL ? name : ""),
713
+ CString, NATS_VERSION_STRING,
714
+ CLIENT_PROTO_INFO,
715
+ _CRLF_);
716
+ if (res < 0)
717
+ return NATS_NO_MEMORY;
718
+
719
+ return NATS_OK;
720
+ }
721
+
722
+ static natsStatus
723
+ _sendUnsubProto(natsConnection *nc, int64_t subId, int max)
724
+ {
725
+ natsStatus s = NATS_OK;
726
+ char *proto = NULL;
727
+ int res = 0;
728
+
729
+ if (max > 0)
730
+ res = nats_asprintf(&proto, _UNSUB_PROTO_, subId, max);
731
+ else
732
+ res = nats_asprintf(&proto, _UNSUB_NO_MAX_PROTO_, subId);
733
+
734
+ if (res < 0)
735
+ s = nats_setDefaultError(NATS_NO_MEMORY);
736
+ else
737
+ {
738
+ s = natsConn_bufferWriteString(nc, proto);
739
+ NATS_FREE(proto);
740
+ }
741
+
742
+ return NATS_UPDATE_ERR_STACK(s);
743
+ }
744
+
745
+ static natsStatus
746
+ _resendSubscriptions(natsConnection *nc)
747
+ {
748
+ natsStatus s = NATS_OK;
749
+ natsSubscription *sub = NULL;
750
+ natsHashIter iter;
751
+ char *proto;
752
+ int res;
753
+ int adjustedMax;
754
+
755
+ natsHashIter_Init(&iter, nc->subs);
756
+ while ((s == NATS_OK) && natsHashIter_Next(&iter, NULL, (void**) &sub))
757
+ {
758
+ proto = NULL;
759
+
760
+ adjustedMax = 0;
761
+ natsSub_Lock(sub);
762
+ if (sub->max > 0)
763
+ {
764
+ if (sub->delivered < sub->max)
765
+ adjustedMax = (int)(sub->max - sub->delivered);
766
+
767
+ // The adjusted max could be 0 here if the number of delivered
768
+ // messages have reached the max, if so, unsubscribe.
769
+ if (adjustedMax == 0)
770
+ {
771
+ natsSub_Unlock(sub);
772
+ s = _sendUnsubProto(nc, sub->sid, 0);
773
+ continue;
774
+ }
775
+ }
776
+ natsSub_Unlock(sub);
777
+
778
+ // These sub's fields are immutable
779
+ res = nats_asprintf(&proto, _SUB_PROTO_,
780
+ sub->subject,
781
+ (sub->queue == NULL ? "" : sub->queue),
782
+ (int) sub->sid);
783
+ if (res < 0)
784
+ s = NATS_NO_MEMORY;
785
+
786
+ if (s == NATS_OK)
787
+ {
788
+ s = natsConn_bufferWriteString(nc, proto);
789
+ NATS_FREE(proto);
790
+ proto = NULL;
791
+ }
792
+
793
+ if ((s == NATS_OK) && (adjustedMax > 0))
794
+ s = _sendUnsubProto(nc, sub->sid, adjustedMax);
795
+ }
796
+
797
+ return s;
798
+ }
799
+
800
+ static natsStatus
801
+ _flushReconnectPendingItems(natsConnection *nc)
802
+ {
803
+ natsStatus s = NATS_OK;
804
+
805
+ if (nc->pending == NULL)
806
+ return NATS_OK;
807
+
808
+ if (natsBuf_Len(nc->pending) > 0)
809
+ {
810
+ s = natsBuf_Append(nc->bw, natsBuf_Data(nc->pending),
811
+ natsBuf_Len(nc->pending));
812
+ }
813
+
814
+ return s;
815
+ }
816
+
817
+ static void
818
+ _removePongFromList(natsConnection *nc, natsPong *pong)
819
+ {
820
+ if (pong->prev != NULL)
821
+ pong->prev->next = pong->next;
822
+
823
+ if (pong->next != NULL)
824
+ pong->next->prev = pong->prev;
825
+
826
+ if (nc->pongs.head == pong)
827
+ nc->pongs.head = pong->next;
828
+
829
+ if (nc->pongs.tail == pong)
830
+ nc->pongs.tail = pong->prev;
831
+
832
+ pong->prev = pong->next = NULL;
833
+ }
834
+
835
+ // When the connection is closed, or is disconnected and we are about
836
+ // to reconnect, we need to unblock all pending natsConnection_Flush[Timeout]()
837
+ // calls: there is no chance that a PING sent to a server is going to be
838
+ // echoed by the new server.
839
+ static void
840
+ _clearPendingFlushRequests(natsConnection *nc)
841
+ {
842
+ natsPong *pong = NULL;
843
+
844
+ while ((pong = nc->pongs.head) != NULL)
845
+ {
846
+ // Pop from the queue
847
+ _removePongFromList(nc, pong);
848
+
849
+ // natsConnection_Flush[Timeout]() is waiting on a condition
850
+ // variable and exit when this value is != 0. "Flush" will
851
+ // return an error to the caller if the connection status
852
+ // is not CONNECTED at that time.
853
+ pong->id = -1;
854
+
855
+ // There may be more than one user-thread making
856
+ // natsConnection_Flush() calls.
857
+ natsCondition_Broadcast(nc->pongs.cond);
858
+ }
859
+
860
+ nc->pongs.incoming = 0;
861
+ nc->pongs.outgoingPings = 0;
862
+ }
863
+
864
+ // Try to reconnect using the option parameters.
865
+ // This function assumes we are allowed to reconnect.
866
+ static void
867
+ _doReconnect(void *arg)
868
+ {
869
+ natsStatus s = NATS_OK;
870
+ natsConnection *nc = (natsConnection*) arg;
871
+ natsThread *tReconnect = NULL;
872
+ natsSrv *cur;
873
+ int64_t elapsed;
874
+ natsSrvPool *pool = NULL;
875
+ int64_t sleepTime;
876
+ struct threadsToJoin ttj;
877
+
878
+ natsConn_Lock(nc);
879
+
880
+ _initThreadsToJoin(&ttj, nc, false);
881
+
882
+ natsConn_Unlock(nc);
883
+
884
+ _joinThreads(&ttj);
885
+
886
+ natsConn_Lock(nc);
887
+
888
+ // Kick out all calls to natsConnection_Flush[Timeout]().
889
+ _clearPendingFlushRequests(nc);
890
+
891
+ // Clear any error.
892
+ nc->err = NATS_OK;
893
+ nc->errStr[0] = '\0';
894
+
895
+ pool = nc->srvPool;
896
+
897
+ // Perform appropriate callback if needed for a disconnect.
898
+ if (nc->opts->disconnectedCb != NULL)
899
+ natsAsyncCb_PostConnHandler(nc, ASYNC_DISCONNECTED);
900
+
901
+ // Note that the pool's size may decrement after the call to
902
+ // natsSrvPool_GetNextServer.
903
+ while ((s == NATS_OK) && (natsSrvPool_GetSize(pool) > 0))
904
+ {
905
+ cur = natsSrvPool_GetNextServer(pool, nc->opts, nc->url);
906
+ nc->url = (cur == NULL ? NULL : cur->url);
907
+ if (cur == NULL)
908
+ {
909
+ nc->err = NATS_NO_SERVER;
910
+ break;
911
+ }
912
+
913
+ sleepTime = 0;
914
+
915
+ // Sleep appropriate amount of time before the
916
+ // connection attempt if connecting to same server
917
+ // we just got disconnected from..
918
+ if (((elapsed = nats_Now() - cur->lastAttempt)) < nc->opts->reconnectWait)
919
+ sleepTime = (nc->opts->reconnectWait - elapsed);
920
+
921
+ natsConn_Unlock(nc);
922
+
923
+ if (sleepTime > 0)
924
+ nats_Sleep(sleepTime);
925
+ else
926
+ natsThread_Yield();
927
+
928
+ natsConn_Lock(nc);
929
+
930
+ // Check if we have been closed first.
931
+ if (natsConn_isClosed(nc))
932
+ break;
933
+
934
+ // Mark that we tried a reconnect
935
+ cur->reconnects += 1;
936
+
937
+ // Try to create a new connection
938
+ s = _createConn(nc);
939
+ if (s != NATS_OK)
940
+ {
941
+ // Reset error here. We will return NATS_NO_SERVERS at the end of
942
+ // this loop if appropriate.
943
+ nc->err = NATS_OK;
944
+
945
+ // Reset status
946
+ s = NATS_OK;
947
+
948
+ // Not yet connected, retry...
949
+ // Continue to hold the lock
950
+ continue;
951
+ }
952
+
953
+ // We have a valid FD and the writer buffer was moved to pending.
954
+ // We are now going to send data directly to the newly connected
955
+ // server, so we need to disable the use of 'pending' for the
956
+ // moment
957
+ nc->usePending = false;
958
+
959
+ // We are reconnected
960
+ nc->stats.reconnects += 1;
961
+
962
+ // Process Connect logic
963
+ s = _processConnInit(nc);
964
+
965
+ // Send existing subscription state
966
+ if (s == NATS_OK)
967
+ s = _resendSubscriptions(nc);
968
+
969
+ // Now send off and clear pending buffer
970
+ if (s == NATS_OK)
971
+ s = _flushReconnectPendingItems(nc);
972
+
973
+ // This is where we are truly connected.
974
+ if (s == NATS_OK)
975
+ nc->status = CONNECTED;
976
+
977
+ if (s != NATS_OK)
978
+ {
979
+ // In case we were at the last iteration, this is the error
980
+ // we will report.
981
+ nc->err = s;
982
+
983
+ // Reset status
984
+ s = NATS_OK;
985
+
986
+ // Close the socket since we were connected, but a problem occurred.
987
+ // (not doing this would cause an FD leak)
988
+ natsSock_Close(nc->sockCtx.fd);
989
+ nc->sockCtx.fd = NATS_SOCK_INVALID;
990
+
991
+ // We need to re-activate the use of pending since we
992
+ // may go back to sleep and release the lock
993
+ nc->usePending = true;
994
+ natsBuf_Reset(nc->bw);
995
+
996
+ nc->status = RECONNECTING;
997
+ continue;
998
+ }
999
+
1000
+ // No more failure allowed past this point.
1001
+
1002
+ // Clear out server stats for the server we connected to..
1003
+ cur->didConnect = true;
1004
+ cur->reconnects = 0;
1005
+
1006
+ tReconnect = nc->reconnectThread;
1007
+ nc->reconnectThread = NULL;
1008
+
1009
+ // At this point we know that we don't need the pending buffer
1010
+ // anymore. Destroy now.
1011
+ natsBuf_Destroy(nc->pending);
1012
+ nc->pending = NULL;
1013
+ nc->usePending = false;
1014
+
1015
+ // Call reconnectedCB if appropriate. Since we are in a separate
1016
+ // thread, we could invoke the callback directly, however, we
1017
+ // still post it so all callbacks from a connection are serialized.
1018
+ if (nc->opts->reconnectedCb != NULL)
1019
+ natsAsyncCb_PostConnHandler(nc, ASYNC_RECONNECTED);
1020
+
1021
+ // Release lock here, we will return below.
1022
+ natsConn_Unlock(nc);
1023
+
1024
+ // Make sure we flush everything
1025
+ (void) natsConnection_Flush(nc);
1026
+
1027
+ natsThread_Join(tReconnect);
1028
+ natsThread_Destroy(tReconnect);
1029
+
1030
+ return;
1031
+ }
1032
+
1033
+ // Call into close.. We have no servers left..
1034
+ if (nc->err == NATS_OK)
1035
+ nc->err = NATS_NO_SERVER;
1036
+
1037
+ natsConn_Unlock(nc);
1038
+
1039
+ _close(nc, CLOSED, true);
1040
+ }
1041
+
1042
+ // Notifies the flusher thread that there is pending data to send to the
1043
+ // server.
1044
+ void
1045
+ natsConn_kickFlusher(natsConnection *nc)
1046
+ {
1047
+ if (!(nc->flusherSignaled) && (nc->bw != NULL))
1048
+ {
1049
+ nc->flusherSignaled = true;
1050
+ natsCondition_Signal(nc->flusherCond);
1051
+ }
1052
+ }
1053
+
1054
+ static natsStatus
1055
+ _sendProto(natsConnection *nc, const char* proto, int protoLen)
1056
+ {
1057
+ natsStatus s;
1058
+
1059
+ natsConn_Lock(nc);
1060
+
1061
+ s = natsConn_bufferWrite(nc, proto, protoLen);
1062
+ if (s == NATS_OK)
1063
+ natsConn_kickFlusher(nc);
1064
+
1065
+ natsConn_Unlock(nc);
1066
+
1067
+ return s;
1068
+ }
1069
+
1070
+ static natsStatus
1071
+ _sendConnect(natsConnection *nc)
1072
+ {
1073
+ natsStatus s = NATS_OK;
1074
+ char *cProto = NULL;
1075
+ char buffer[DEFAULT_BUF_SIZE];
1076
+
1077
+ buffer[0] = '\0';
1078
+
1079
+ // Create the CONNECT protocol
1080
+ s = _connectProto(nc, &cProto);
1081
+
1082
+ // Add it to the buffer
1083
+ if (s == NATS_OK)
1084
+ s = natsConn_bufferWriteString(nc, cProto);
1085
+
1086
+ // Add the PING protocol to the buffer
1087
+ if (s == NATS_OK)
1088
+ s = natsConn_bufferWrite(nc, _PING_OP_, _PING_OP_LEN_);
1089
+ if (s == NATS_OK)
1090
+ s = natsConn_bufferWrite(nc, _CRLF_, _CRLF_LEN_);
1091
+
1092
+ // Flush the buffer
1093
+ if (s == NATS_OK)
1094
+ s = natsConn_bufferFlush(nc);
1095
+
1096
+ // Now read the response from the server.
1097
+ if (s == NATS_OK)
1098
+ s = natsSock_ReadLine(&(nc->sockCtx), buffer, sizeof(buffer));
1099
+
1100
+ // If Verbose is set, we expect +OK first.
1101
+ if ((s == NATS_OK) && nc->opts->verbose)
1102
+ {
1103
+ // Check protocol is as expected
1104
+ if (strncmp(buffer, _OK_OP_, _OK_OP_LEN_) != 0)
1105
+ {
1106
+ s = nats_setError(NATS_PROTOCOL_ERROR,
1107
+ "Expected '%s', got '%s'",
1108
+ _OK_OP_, buffer);
1109
+ }
1110
+
1111
+ // Read the rest now...
1112
+ if (s == NATS_OK)
1113
+ s = natsSock_ReadLine(&(nc->sockCtx), buffer, sizeof(buffer));
1114
+ }
1115
+
1116
+ // We except the PONG protocol
1117
+ if ((s == NATS_OK) && (strncmp(buffer, _PONG_OP_, _PONG_OP_LEN_) != 0))
1118
+ {
1119
+ // But it could be something else, like -ERR
1120
+
1121
+ if (strncmp(buffer, _ERR_OP_, _ERR_OP_LEN_) == 0)
1122
+ {
1123
+ // Remove -ERR, trim spaces and quotes.
1124
+ nats_NormalizeErr(buffer);
1125
+
1126
+ // Search if the error message says something about
1127
+ // authentication failure.
1128
+
1129
+ if (nats_strcasestr(buffer, "authorization") != NULL)
1130
+ s = nats_setError(NATS_CONNECTION_AUTH_FAILED,
1131
+ "%s", buffer);
1132
+ else
1133
+ s = nats_setError(NATS_ERR, "%s", buffer);
1134
+ }
1135
+ else
1136
+ {
1137
+ s = nats_setError(NATS_PROTOCOL_ERROR,
1138
+ "Expected '%s', got '%s'",
1139
+ _PONG_OP_, buffer);
1140
+ }
1141
+ }
1142
+
1143
+ if (s == NATS_OK)
1144
+ nc->status = CONNECTED;
1145
+
1146
+ free(cProto);
1147
+
1148
+ return NATS_UPDATE_ERR_STACK(s);
1149
+ }
1150
+
1151
+ static natsStatus
1152
+ _processConnInit(natsConnection *nc)
1153
+ {
1154
+ natsStatus s = NATS_OK;
1155
+
1156
+ nc->status = CONNECTING;
1157
+
1158
+ // Process the INFO protocol that we should be receiving
1159
+ s = _processExpectedInfo(nc);
1160
+
1161
+ // Send the CONNECT and PING protocol, and wait for the PONG.
1162
+ if (s == NATS_OK)
1163
+ s = _sendConnect(nc);
1164
+
1165
+ // Clear our deadline, regardless of error
1166
+ natsDeadline_Clear(&(nc->sockCtx.deadline));
1167
+
1168
+ // Switch to blocking socket here...
1169
+ if (s == NATS_OK)
1170
+ s = natsSock_SetBlocking(nc->sockCtx.fd, true);
1171
+
1172
+ // Start the readLoop and flusher threads
1173
+ if (s == NATS_OK)
1174
+ s = _spinUpSocketWatchers(nc);
1175
+
1176
+ if ((s == NATS_OK) && (nc->opts->evLoop != NULL))
1177
+ {
1178
+ s = natsSock_SetBlocking(nc->sockCtx.fd, false);
1179
+
1180
+ // If we are reconnecting, buffer will have already been allocated
1181
+ if ((s == NATS_OK) && (nc->el.buffer == NULL))
1182
+ {
1183
+ nc->el.buffer = (char*) malloc(DEFAULT_BUF_SIZE);
1184
+ if (nc->el.buffer == NULL)
1185
+ s = nats_setDefaultError(NATS_NO_MEMORY);
1186
+ }
1187
+ if (s == NATS_OK)
1188
+ {
1189
+ // Set this first in case the event loop triggers the first READ
1190
+ // event just after this call returns.
1191
+ nc->sockCtx.useEventLoop = true;
1192
+
1193
+ s = nc->opts->evCbs.attach(&(nc->el.data),
1194
+ nc->opts->evLoop,
1195
+ nc,
1196
+ (int) nc->sockCtx.fd);
1197
+ if (s == NATS_OK)
1198
+ {
1199
+ nc->el.attached = true;
1200
+ }
1201
+ else
1202
+ {
1203
+ nc->sockCtx.useEventLoop = false;
1204
+
1205
+ nats_setError(s,
1206
+ "Error attaching to the event loop: %d - %s",
1207
+ s, natsStatus_GetText(s));
1208
+ }
1209
+ }
1210
+ }
1211
+
1212
+ return NATS_UPDATE_ERR_STACK(s);
1213
+ }
1214
+
1215
+ // Main connect function. Will connect to the server
1216
+ static natsStatus
1217
+ _connect(natsConnection *nc)
1218
+ {
1219
+ natsStatus s = NATS_OK;
1220
+ natsStatus retSts= NATS_OK;
1221
+ natsSrvPool *pool = NULL;
1222
+ int i;
1223
+ int poolSize;
1224
+
1225
+ natsConn_Lock(nc);
1226
+
1227
+ pool = nc->srvPool;
1228
+
1229
+ // Create actual socket connection
1230
+ // For first connect we walk all servers in the pool and try
1231
+ // to connect immediately.
1232
+
1233
+ // Get the size of the pool. The pool may change inside the loop
1234
+ // iteration due to INFO protocol.
1235
+ poolSize = natsSrvPool_GetSize(pool);
1236
+ for (i = 0; i < poolSize; i++)
1237
+ {
1238
+ nc->url = natsSrvPool_GetSrvUrl(pool,i);
1239
+
1240
+ s = _createConn(nc);
1241
+ if (s == NATS_OK)
1242
+ {
1243
+ s = _processConnInit(nc);
1244
+
1245
+ if (s == NATS_OK)
1246
+ {
1247
+ natsSrvPool_SetSrvDidConnect(pool, i, true);
1248
+ natsSrvPool_SetSrvReconnects(pool, i, 0);
1249
+ retSts = NATS_OK;
1250
+ break;
1251
+ }
1252
+ else
1253
+ {
1254
+ retSts = s;
1255
+
1256
+ natsConn_Unlock(nc);
1257
+
1258
+ _close(nc, DISCONNECTED, false);
1259
+
1260
+ natsConn_Lock(nc);
1261
+
1262
+ nc->url = NULL;
1263
+ }
1264
+ // Refresh our view of pool length since it may have been
1265
+ // modified when processing the INFO protocol.
1266
+ poolSize = natsSrvPool_GetSize(pool);
1267
+ }
1268
+ else
1269
+ {
1270
+ if (s == NATS_IO_ERROR)
1271
+ retSts = NATS_OK;
1272
+ }
1273
+ }
1274
+
1275
+ if ((retSts == NATS_OK) && (nc->status != CONNECTED))
1276
+ {
1277
+ s = nats_setDefaultError(NATS_NO_SERVER);
1278
+ }
1279
+
1280
+ natsConn_Unlock(nc);
1281
+
1282
+ return NATS_UPDATE_ERR_STACK(s);
1283
+ }
1284
+
1285
+ // _processOpError handles errors from reading or parsing the protocol.
1286
+ // The lock should not be held entering this function.
1287
+ static void
1288
+ _processOpError(natsConnection *nc, natsStatus s)
1289
+ {
1290
+ natsConn_Lock(nc);
1291
+
1292
+ if (_isConnecting(nc) || natsConn_isClosed(nc) || natsConn_isReconnecting(nc))
1293
+ {
1294
+ natsConn_Unlock(nc);
1295
+
1296
+ return;
1297
+ }
1298
+
1299
+ // Do reconnect only if allowed and we were actually connected
1300
+ if (nc->opts->allowReconnect && (nc->status == CONNECTED))
1301
+ {
1302
+ natsStatus ls = NATS_OK;
1303
+
1304
+ // Set our new status
1305
+ nc->status = RECONNECTING;
1306
+
1307
+ if (nc->ptmr != NULL)
1308
+ natsTimer_Stop(nc->ptmr);
1309
+
1310
+ if (nc->sockCtx.fdActive)
1311
+ {
1312
+ natsConn_bufferFlush(nc);
1313
+
1314
+ natsSock_Shutdown(nc->sockCtx.fd);
1315
+ nc->sockCtx.fdActive = false;
1316
+ }
1317
+
1318
+ // If we use an external event loop, we need to stop polling
1319
+ // on the socket since we are going to reconnect.
1320
+ if (nc->el.attached)
1321
+ {
1322
+ // Stop polling for READ/WRITE events on that socket.
1323
+ nc->sockCtx.useEventLoop = false;
1324
+ nc->el.writeAdded = false;
1325
+ ls = nc->opts->evCbs.read(nc->el.data, NATS_EVENT_ACTION_REMOVE);
1326
+ if (ls == NATS_OK)
1327
+ ls = nc->opts->evCbs.write(nc->el.data, NATS_EVENT_ACTION_REMOVE);
1328
+ }
1329
+
1330
+ // Create the pending buffer to hold all write requests while we try
1331
+ // to reconnect.
1332
+ ls = natsBuf_Create(&(nc->pending), nc->opts->reconnectBufSize);
1333
+ if (ls == NATS_OK)
1334
+ {
1335
+ nc->usePending = true;
1336
+
1337
+ // Start the reconnect thread
1338
+ ls = natsThread_Create(&(nc->reconnectThread),
1339
+ _doReconnect, (void*) nc);
1340
+ }
1341
+ if (ls == NATS_OK)
1342
+ {
1343
+ natsConn_Unlock(nc);
1344
+
1345
+ return;
1346
+ }
1347
+ }
1348
+
1349
+ // reconnect not allowed or we failed to setup the reconnect code.
1350
+
1351
+ nc->status = DISCONNECTED;
1352
+ nc->err = s;
1353
+
1354
+ natsConn_Unlock(nc);
1355
+
1356
+ _close(nc, CLOSED, true);
1357
+ }
1358
+
1359
+ static void
1360
+ natsConn_clearSSL(natsConnection *nc)
1361
+ {
1362
+ if (nc->sockCtx.ssl == NULL)
1363
+ return;
1364
+
1365
+ SSL_free(nc->sockCtx.ssl);
1366
+ nc->sockCtx.ssl = NULL;
1367
+ }
1368
+
1369
+ static void
1370
+ _readLoop(void *arg)
1371
+ {
1372
+ natsStatus s = NATS_OK;
1373
+ char buffer[DEFAULT_BUF_SIZE];
1374
+ natsSock fd;
1375
+ int n;
1376
+
1377
+ natsConnection *nc = (natsConnection*) arg;
1378
+
1379
+ natsConn_Lock(nc);
1380
+
1381
+ if (nc->sockCtx.ssl != NULL)
1382
+ nats_sslRegisterThreadForCleanup();
1383
+
1384
+ fd = nc->sockCtx.fd;
1385
+
1386
+ if (nc->ps == NULL)
1387
+ s = natsParser_Create(&(nc->ps));
1388
+
1389
+ while ((s == NATS_OK)
1390
+ && !natsConn_isClosed(nc)
1391
+ && !natsConn_isReconnecting(nc))
1392
+ {
1393
+ natsConn_Unlock(nc);
1394
+
1395
+ n = 0;
1396
+
1397
+ s = natsSock_Read(&(nc->sockCtx), buffer, sizeof(buffer), &n);
1398
+ if (s == NATS_OK)
1399
+ s = natsParser_Parse(nc, buffer, n);
1400
+
1401
+ if (s != NATS_OK)
1402
+ _processOpError(nc, s);
1403
+
1404
+ natsConn_Lock(nc);
1405
+ }
1406
+
1407
+ natsSock_Close(fd);
1408
+ nc->sockCtx.fd = NATS_SOCK_INVALID;
1409
+ nc->sockCtx.fdActive = false;
1410
+
1411
+ // We need to cleanup some things if the connection was SSL.
1412
+ if (nc->sockCtx.ssl != NULL)
1413
+ natsConn_clearSSL(nc);
1414
+
1415
+ natsParser_Destroy(nc->ps);
1416
+ nc->ps = NULL;
1417
+
1418
+ // This unlocks and releases the connection to compensate for the retain
1419
+ // when this thread was created.
1420
+ natsConn_unlockAndRelease(nc);
1421
+ }
1422
+
1423
+ static void
1424
+ _flusher(void *arg)
1425
+ {
1426
+ natsConnection *nc = (natsConnection*) arg;
1427
+ natsStatus s;
1428
+
1429
+ while (true)
1430
+ {
1431
+ natsConn_Lock(nc);
1432
+
1433
+ while (!(nc->flusherSignaled) && !(nc->flusherStop))
1434
+ natsCondition_Wait(nc->flusherCond, nc->mu);
1435
+
1436
+ if (nc->flusherStop)
1437
+ {
1438
+ natsConn_Unlock(nc);
1439
+ break;
1440
+ }
1441
+
1442
+ //TODO: If we process the request right away, performance
1443
+ // will suffer when sending quickly very small messages.
1444
+ // The buffer is going to be always flushed, which
1445
+ // defeats the purpose of a write buffer.
1446
+ // We need to revisit this.
1447
+
1448
+ // Give a chance to accumulate more requests...
1449
+ natsCondition_TimedWait(nc->flusherCond, nc->mu, 1);
1450
+
1451
+ nc->flusherSignaled = false;
1452
+
1453
+ if (natsConn_isClosed(nc) || natsConn_isReconnecting(nc))
1454
+ {
1455
+ natsConn_Unlock(nc);
1456
+ break;
1457
+ }
1458
+
1459
+ if (nc->sockCtx.fdActive && (natsBuf_Len(nc->bw) > 0))
1460
+ {
1461
+ s = natsConn_bufferFlush(nc);
1462
+ if ((s != NATS_OK) && (nc->err == NATS_OK))
1463
+ nc->err = s;
1464
+ }
1465
+
1466
+ natsConn_Unlock(nc);
1467
+ }
1468
+
1469
+ // Release the connection to compensate for the retain when this thread
1470
+ // was created.
1471
+ natsConn_release(nc);
1472
+ }
1473
+
1474
+ static void
1475
+ _sendPing(natsConnection *nc, natsPong *pong)
1476
+ {
1477
+ natsStatus s = NATS_OK;
1478
+
1479
+ s = natsConn_bufferWrite(nc, _PING_PROTO_, _PING_PROTO_LEN_);
1480
+ if (s == NATS_OK)
1481
+ {
1482
+ // Flush the buffer in place.
1483
+ s = natsConn_bufferFlush(nc);
1484
+ }
1485
+ if (s == NATS_OK)
1486
+ {
1487
+ // Now that we know the PING was sent properly, update
1488
+ // the number of PING sent.
1489
+ nc->pongs.outgoingPings++;
1490
+
1491
+ if (pong != NULL)
1492
+ {
1493
+ pong->id = nc->pongs.outgoingPings;
1494
+
1495
+ // Add this pong to the list.
1496
+ pong->next = NULL;
1497
+ pong->prev = nc->pongs.tail;
1498
+
1499
+ if (nc->pongs.tail != NULL)
1500
+ nc->pongs.tail->next = pong;
1501
+
1502
+ nc->pongs.tail = pong;
1503
+
1504
+ if (nc->pongs.head == NULL)
1505
+ nc->pongs.head = pong;
1506
+ }
1507
+ }
1508
+ }
1509
+
1510
+ static void
1511
+ _processPingTimer(natsTimer *timer, void *arg)
1512
+ {
1513
+ natsConnection *nc = (natsConnection*) arg;
1514
+
1515
+ natsConn_Lock(nc);
1516
+
1517
+ if (nc->status != CONNECTED)
1518
+ {
1519
+ natsConn_Unlock(nc);
1520
+ return;
1521
+ }
1522
+
1523
+ // If we have more PINGs out than PONGs in, consider
1524
+ // the connection stale.
1525
+ if (++(nc->pout) > nc->opts->maxPingsOut)
1526
+ {
1527
+ natsConn_Unlock(nc);
1528
+ _processOpError(nc, NATS_STALE_CONNECTION);
1529
+ return;
1530
+ }
1531
+
1532
+ _sendPing(nc, NULL);
1533
+
1534
+ natsConn_Unlock(nc);
1535
+ }
1536
+
1537
+ static void
1538
+ _pingStopppedCb(natsTimer *timer, void *closure)
1539
+ {
1540
+ natsConnection *nc = (natsConnection*) closure;
1541
+
1542
+ natsConn_release(nc);
1543
+ }
1544
+
1545
+ static natsStatus
1546
+ _spinUpSocketWatchers(natsConnection *nc)
1547
+ {
1548
+ natsStatus s = NATS_OK;
1549
+
1550
+ nc->pout = 0;
1551
+ nc->flusherStop = false;
1552
+
1553
+ if (nc->opts->evLoop == NULL)
1554
+ {
1555
+ // Let's not rely on the created threads acquiring lock that would make it
1556
+ // safe to retain only on success.
1557
+ _retain(nc);
1558
+
1559
+ s = natsThread_Create(&(nc->readLoopThread), _readLoop, (void*) nc);
1560
+ if (s != NATS_OK)
1561
+ _release(nc);
1562
+ }
1563
+
1564
+ if (s == NATS_OK)
1565
+ {
1566
+ _retain(nc);
1567
+
1568
+ s = natsThread_Create(&(nc->flusherThread), _flusher, (void*) nc);
1569
+ if (s != NATS_OK)
1570
+ _release(nc);
1571
+ }
1572
+
1573
+ if ((s == NATS_OK) && (nc->opts->pingInterval > 0))
1574
+ {
1575
+ _retain(nc);
1576
+
1577
+ if (nc->ptmr == NULL)
1578
+ {
1579
+ s = natsTimer_Create(&(nc->ptmr),
1580
+ _processPingTimer,
1581
+ _pingStopppedCb,
1582
+ nc->opts->pingInterval,
1583
+ (void*) nc);
1584
+ if (s != NATS_OK)
1585
+ _release(nc);
1586
+ }
1587
+ else
1588
+ {
1589
+ natsTimer_Reset(nc->ptmr, nc->opts->pingInterval);
1590
+ }
1591
+ }
1592
+
1593
+ return s;
1594
+ }
1595
+
1596
+ // Remove all subscriptions. This will kick out the delivery threads,
1597
+ // and unblock NextMsg() calls.
1598
+ static void
1599
+ _removeAllSubscriptions(natsConnection *nc)
1600
+ {
1601
+ natsHashIter iter;
1602
+ natsSubscription *sub;
1603
+
1604
+ natsHashIter_Init(&iter, nc->subs);
1605
+ while (natsHashIter_Next(&iter, NULL, (void**) &sub))
1606
+ {
1607
+ (void) natsHashIter_RemoveCurrent(&iter);
1608
+
1609
+ natsSub_close(sub, true);
1610
+
1611
+ natsSub_release(sub);
1612
+ }
1613
+ }
1614
+
1615
+
1616
+ // Low level close call that will do correct cleanup and set
1617
+ // desired status. Also controls whether user defined callbacks
1618
+ // will be triggered. The lock should not be held entering this
1619
+ // function. This function will handle the locking manually.
1620
+ static void
1621
+ _close(natsConnection *nc, natsConnStatus status, bool doCBs)
1622
+ {
1623
+ struct threadsToJoin ttj;
1624
+ bool sockWasActive = false;
1625
+ bool detach = false;
1626
+
1627
+ natsConn_lockAndRetain(nc);
1628
+
1629
+ if (natsConn_isClosed(nc))
1630
+ {
1631
+ nc->status = status;
1632
+
1633
+ natsConn_unlockAndRelease(nc);
1634
+ return;
1635
+ }
1636
+
1637
+ nc->status = CLOSED;
1638
+
1639
+ _initThreadsToJoin(&ttj, nc, true);
1640
+
1641
+ // Kick out all calls to natsConnection_Flush[Timeout]().
1642
+ _clearPendingFlushRequests(nc);
1643
+
1644
+ if (nc->ptmr != NULL)
1645
+ natsTimer_Stop(nc->ptmr);
1646
+
1647
+ // Remove all subscriptions. This will kick out the delivery threads,
1648
+ // and unblock NextMsg() calls.
1649
+ _removeAllSubscriptions(nc);
1650
+
1651
+ // Go ahead and make sure we have flushed the outbound buffer.
1652
+ nc->status = CLOSED;
1653
+ if (nc->sockCtx.fdActive)
1654
+ {
1655
+ natsConn_bufferFlush(nc);
1656
+
1657
+ // If there is no readLoop, then it is our responsibility to close
1658
+ // the socket. Otherwise, _readLoop is the one doing it.
1659
+ if ((ttj.readLoop == NULL) && (nc->opts->evLoop == NULL))
1660
+ {
1661
+ natsSock_Close(nc->sockCtx.fd);
1662
+ nc->sockCtx.fd = NATS_SOCK_INVALID;
1663
+
1664
+ // We need to cleanup some things if the connection was SSL.
1665
+ if (nc->sockCtx.ssl != NULL)
1666
+ natsConn_clearSSL(nc);
1667
+ }
1668
+ else
1669
+ {
1670
+ // Shutdown the socket to stop any read/write operations.
1671
+ // The socket will be closed by the _readLoop thread.
1672
+ natsSock_Shutdown(nc->sockCtx.fd);
1673
+ }
1674
+ nc->sockCtx.fdActive = false;
1675
+ sockWasActive = true;
1676
+ }
1677
+
1678
+ // Perform appropriate callback if needed for a disconnect.
1679
+ // Do not invoke if we were disconnected and failed to reconnect (since
1680
+ // it has already been invoked in doReconnect).
1681
+ if (doCBs && (nc->opts->disconnectedCb != NULL) && sockWasActive)
1682
+ natsAsyncCb_PostConnHandler(nc, ASYNC_DISCONNECTED);
1683
+
1684
+ natsConn_Unlock(nc);
1685
+
1686
+ _joinThreads(&ttj);
1687
+
1688
+ natsConn_Lock(nc);
1689
+
1690
+ // Perform appropriate callback if needed for a connection closed.
1691
+ if (doCBs && (nc->opts->closedCb != NULL))
1692
+ natsAsyncCb_PostConnHandler(nc, ASYNC_CLOSED);
1693
+
1694
+ nc->status = status;
1695
+
1696
+ if (nc->el.attached)
1697
+ {
1698
+ nc->el.attached = false;
1699
+ detach = true;
1700
+ _retain(nc);
1701
+ }
1702
+
1703
+ natsConn_unlockAndRelease(nc);
1704
+
1705
+ if (detach)
1706
+ {
1707
+ nc->opts->evCbs.detach(nc->el.data);
1708
+ natsConn_release(nc);
1709
+ }
1710
+ }
1711
+
1712
+ static void
1713
+ _processSlowConsumer(natsConnection *nc, natsSubscription *sub)
1714
+ {
1715
+ nc->err = NATS_SLOW_CONSUMER;
1716
+
1717
+ if (!(sub->slowConsumer) && (nc->opts->asyncErrCb != NULL))
1718
+ natsAsyncCb_PostErrHandler(nc, sub, NATS_SLOW_CONSUMER);
1719
+
1720
+ sub->slowConsumer = true;
1721
+ }
1722
+
1723
+ static natsStatus
1724
+ _createMsg(natsMsg **newMsg, natsConnection *nc, char *buf, int bufLen)
1725
+ {
1726
+ natsStatus s = NATS_OK;
1727
+ int subjLen = 0;
1728
+ char *reply = NULL;
1729
+ int replyLen = 0;
1730
+
1731
+ subjLen = natsBuf_Len(nc->ps->ma.subject);
1732
+
1733
+ if (nc->ps->ma.reply != NULL)
1734
+ {
1735
+ reply = natsBuf_Data(nc->ps->ma.reply);
1736
+ replyLen = natsBuf_Len(nc->ps->ma.reply);
1737
+ }
1738
+
1739
+ s = natsMsg_create(newMsg,
1740
+ (const char*) natsBuf_Data(nc->ps->ma.subject), subjLen,
1741
+ (const char*) reply, replyLen,
1742
+ (const char*) buf, bufLen);
1743
+ return s;
1744
+ }
1745
+
1746
+ natsStatus
1747
+ natsConn_processMsg(natsConnection *nc, char *buf, int bufLen)
1748
+ {
1749
+ natsStatus s = NATS_OK;
1750
+ natsSubscription *sub = NULL;
1751
+ natsMsg *msg = NULL;
1752
+ natsMsgDlvWorker *ldw = NULL;
1753
+
1754
+ natsConn_Lock(nc);
1755
+
1756
+ nc->stats.inMsgs += 1;
1757
+ nc->stats.inBytes += (uint64_t) bufLen;
1758
+
1759
+ sub = natsHash_Get(nc->subs, nc->ps->ma.sid);
1760
+ if (sub == NULL)
1761
+ {
1762
+ natsConn_Unlock(nc);
1763
+ return NATS_OK;
1764
+ }
1765
+
1766
+ // Do this outside of sub's lock, even if we end-up having to destroy
1767
+ // it because we have reached the maxPendingMsgs count. This reduces
1768
+ // lock contention.
1769
+ s = _createMsg(&msg, nc, buf, bufLen);
1770
+ if (s != NATS_OK)
1771
+ {
1772
+ natsConn_Unlock(nc);
1773
+ return s;
1774
+ }
1775
+
1776
+ if ((ldw = sub->libDlvWorker) != NULL)
1777
+ natsMutex_Lock(ldw->lock);
1778
+ else
1779
+ natsSub_Lock(sub);
1780
+
1781
+ sub->msgList.msgs++;
1782
+ sub->msgList.bytes += bufLen;
1783
+
1784
+ if (((sub->msgsLimit > 0) && (sub->msgList.msgs > sub->msgsLimit))
1785
+ || ((sub->bytesLimit > 0) && (sub->msgList.bytes > sub->bytesLimit)))
1786
+ {
1787
+ natsMsg_Destroy(msg);
1788
+
1789
+ sub->dropped++;
1790
+
1791
+ // Undo stats from above.
1792
+ sub->msgList.msgs--;
1793
+ sub->msgList.bytes -= bufLen;
1794
+
1795
+ _processSlowConsumer(nc, sub);
1796
+ }
1797
+ else
1798
+ {
1799
+ natsMsgList *list = NULL;
1800
+
1801
+ if (sub->msgList.msgs > sub->msgsMax)
1802
+ sub->msgsMax = sub->msgList.msgs;
1803
+
1804
+ if (sub->msgList.bytes > sub->bytesMax)
1805
+ sub->bytesMax = sub->msgList.bytes;
1806
+
1807
+ sub->slowConsumer = false;
1808
+
1809
+ if (ldw != NULL)
1810
+ {
1811
+ msg->sub = sub;
1812
+ list = &ldw->msgList;
1813
+ }
1814
+ else
1815
+ {
1816
+ list = &sub->msgList;
1817
+ }
1818
+
1819
+ if (list->head == NULL)
1820
+ list->head = msg;
1821
+
1822
+ if (list->tail != NULL)
1823
+ list->tail->next = msg;
1824
+
1825
+ list->tail = msg;
1826
+
1827
+ if (ldw != NULL)
1828
+ {
1829
+ if (ldw->inWait)
1830
+ natsCondition_Broadcast(ldw->cond);
1831
+ }
1832
+ else
1833
+ {
1834
+ if (sub->inWait > 0)
1835
+ natsCondition_Broadcast(sub->cond);
1836
+ }
1837
+ }
1838
+
1839
+ if (ldw != NULL)
1840
+ natsMutex_Unlock(ldw->lock);
1841
+ else
1842
+ natsSub_Unlock(sub);
1843
+
1844
+ natsConn_Unlock(nc);
1845
+
1846
+ return s;
1847
+ }
1848
+
1849
+ void
1850
+ natsConn_processOK(natsConnection *nc)
1851
+ {
1852
+ // Do nothing for now.
1853
+ }
1854
+
1855
+ void
1856
+ natsConn_processErr(natsConnection *nc, char *buf, int bufLen)
1857
+ {
1858
+ char error[256];
1859
+
1860
+ // Copy the error in this local buffer.
1861
+ snprintf(error, sizeof(error), "%.*s", bufLen, buf);
1862
+
1863
+ // Trim spaces and remove quotes.
1864
+ nats_NormalizeErr(error);
1865
+
1866
+ if (strcasecmp(error, STALE_CONNECTION) == 0)
1867
+ {
1868
+ _processOpError(nc, NATS_STALE_CONNECTION);
1869
+ }
1870
+ else
1871
+ {
1872
+ natsConn_Lock(nc);
1873
+ nc->err = NATS_ERR;
1874
+ snprintf(nc->errStr, sizeof(nc->errStr), "%s", error);
1875
+ natsConn_Unlock(nc);
1876
+ _close(nc, CLOSED, true);
1877
+ }
1878
+ }
1879
+
1880
+ void
1881
+ natsConn_processPing(natsConnection *nc)
1882
+ {
1883
+ _sendProto(nc, _PONG_PROTO_, _PONG_PROTO_LEN_);
1884
+ }
1885
+
1886
+ void
1887
+ natsConn_processPong(natsConnection *nc)
1888
+ {
1889
+ natsPong *pong = NULL;
1890
+
1891
+ natsConn_Lock(nc);
1892
+
1893
+ nc->pongs.incoming++;
1894
+
1895
+ // Check if the first pong's id in the list matches the incoming Id.
1896
+ if (((pong = nc->pongs.head) != NULL)
1897
+ && (pong->id == nc->pongs.incoming))
1898
+ {
1899
+ // Remove the pong from the list
1900
+ _removePongFromList(nc, pong);
1901
+
1902
+ // Release the Flush[Timeout] call
1903
+ pong->id = 0;
1904
+
1905
+ // There may be more than one thread waiting on this
1906
+ // condition variable, so we use broadcast instead of
1907
+ // signal.
1908
+ natsCondition_Broadcast(nc->pongs.cond);
1909
+ }
1910
+
1911
+ nc->pout = 0;
1912
+
1913
+ natsConn_Unlock(nc);
1914
+ }
1915
+
1916
+ natsStatus
1917
+ natsConn_addSubcription(natsConnection *nc, natsSubscription *sub)
1918
+ {
1919
+ natsStatus s = NATS_OK;
1920
+ natsSubscription *oldSub = NULL;
1921
+
1922
+ s = natsHash_Set(nc->subs, sub->sid, (void*) sub, (void**) &oldSub);
1923
+ if (s == NATS_OK)
1924
+ {
1925
+ assert(oldSub == NULL);
1926
+ natsSub_retain(sub);
1927
+ }
1928
+
1929
+ return NATS_UPDATE_ERR_STACK(s);
1930
+ }
1931
+
1932
+ void
1933
+ natsConn_removeSubscription(natsConnection *nc, natsSubscription *removedSub, bool needsLock)
1934
+ {
1935
+ natsSubscription *sub = NULL;
1936
+
1937
+ if (needsLock)
1938
+ natsConn_Lock(nc);
1939
+
1940
+ sub = natsHash_Remove(nc->subs, removedSub->sid);
1941
+
1942
+ // Note that the sub may have already been removed, so 'sub == NULL'
1943
+ // is not an error.
1944
+ if (sub != NULL)
1945
+ natsSub_close(sub, false);
1946
+
1947
+ if (needsLock)
1948
+ natsConn_Unlock(nc);
1949
+
1950
+ // If we really removed the subscription, then release it.
1951
+ if (sub != NULL)
1952
+ natsSub_release(sub);
1953
+ }
1954
+
1955
+ // subscribe is the internal subscribe function that indicates interest in a
1956
+ // subject.
1957
+ natsStatus
1958
+ natsConn_subscribe(natsSubscription **newSub,
1959
+ natsConnection *nc, const char *subj, const char *queue,
1960
+ int64_t timeout, natsMsgHandler cb, void *cbClosure)
1961
+ {
1962
+ natsStatus s = NATS_OK;
1963
+ natsSubscription *sub = NULL;
1964
+
1965
+ if (nc == NULL)
1966
+ return nats_setDefaultError(NATS_INVALID_ARG);
1967
+
1968
+ if ((subj == NULL) || (strlen(subj) == 0))
1969
+ return nats_setDefaultError(NATS_INVALID_SUBJECT);
1970
+
1971
+ natsConn_Lock(nc);
1972
+
1973
+ if (natsConn_isClosed(nc))
1974
+ {
1975
+ natsConn_Unlock(nc);
1976
+
1977
+ return nats_setDefaultError(NATS_CONNECTION_CLOSED);
1978
+ }
1979
+
1980
+ s = natsSub_create(&sub, nc, subj, queue, timeout, cb, cbClosure);
1981
+ if (s == NATS_OK)
1982
+ {
1983
+ sub->sid = ++(nc->ssid);
1984
+ s = natsConn_addSubcription(nc, sub);
1985
+ }
1986
+
1987
+ if (s == NATS_OK)
1988
+ {
1989
+ // We will send these for all subs when we reconnect
1990
+ // so that we can suppress here.
1991
+ if (!natsConn_isReconnecting(nc))
1992
+ {
1993
+ char *proto = NULL;
1994
+ int res = 0;
1995
+
1996
+ res = nats_asprintf(&proto, _SUB_PROTO_,
1997
+ subj,
1998
+ (queue == NULL ? "" : queue),
1999
+ (int) sub->sid);
2000
+ if (res < 0)
2001
+ s = nats_setDefaultError(NATS_NO_MEMORY);
2002
+
2003
+ if (s == NATS_OK)
2004
+ {
2005
+ s = natsConn_bufferWriteString(nc, proto);
2006
+ if (s == NATS_OK)
2007
+ natsConn_kickFlusher(nc);
2008
+
2009
+ // We should not return a failure if we get an issue
2010
+ // with the buffer write (except if it is no memory).
2011
+ // For IO errors (if we just got disconnected), the
2012
+ // reconnect logic will resend the sub protocol.
2013
+
2014
+ if (s != NATS_NO_MEMORY)
2015
+ s = NATS_OK;
2016
+ }
2017
+
2018
+ NATS_FREE(proto);
2019
+ }
2020
+ }
2021
+
2022
+ if (s == NATS_OK)
2023
+ {
2024
+ *newSub = sub;
2025
+ }
2026
+ else if (sub != NULL)
2027
+ {
2028
+ // A delivery thread may have been started, but the subscription not
2029
+ // added to the connection's subscription map. So this is necessary
2030
+ // for the delivery thread to unroll.
2031
+ natsSub_close(sub, false);
2032
+
2033
+ natsConn_removeSubscription(nc, sub, false);
2034
+
2035
+ natsSub_release(sub);
2036
+ }
2037
+
2038
+ natsConn_Unlock(nc);
2039
+
2040
+ return NATS_UPDATE_ERR_STACK(s);
2041
+ }
2042
+
2043
+ // Performs the low level unsubscribe to the server.
2044
+ natsStatus
2045
+ natsConn_unsubscribe(natsConnection *nc, natsSubscription *sub, int max)
2046
+ {
2047
+ natsStatus s = NATS_OK;
2048
+
2049
+ natsConn_Lock(nc);
2050
+
2051
+ if (natsConn_isClosed(nc))
2052
+ {
2053
+ natsConn_Unlock(nc);
2054
+ return nats_setDefaultError(NATS_CONNECTION_CLOSED);
2055
+ }
2056
+
2057
+ sub = natsHash_Get(nc->subs, sub->sid);
2058
+ if (sub == NULL)
2059
+ {
2060
+ // Already unsubscribed
2061
+ natsConn_Unlock(nc);
2062
+ return NATS_OK;
2063
+ }
2064
+
2065
+ natsSub_Lock(sub);
2066
+ sub->max = max;
2067
+ natsSub_Unlock(sub);
2068
+
2069
+ if (max == 0)
2070
+ natsConn_removeSubscription(nc, sub, false);
2071
+
2072
+ if (!natsConn_isReconnecting(nc))
2073
+ {
2074
+ // We will send these for all subs when we reconnect
2075
+ // so that we can suppress here.
2076
+ s = _sendUnsubProto(nc, sub->sid, max);
2077
+ if (s == NATS_OK)
2078
+ natsConn_kickFlusher(nc);
2079
+
2080
+ // We should not return a failure if we get an issue
2081
+ // with the buffer write (except if it is no memory).
2082
+ // For IO errors (if we just got disconnected), the
2083
+ // reconnect logic will resend the unsub protocol.
2084
+ if ((s != NATS_OK) && (s != NATS_NO_MEMORY))
2085
+ {
2086
+ nats_clearLastError();
2087
+ s = NATS_OK;
2088
+ }
2089
+ }
2090
+
2091
+ natsConn_Unlock(nc);
2092
+
2093
+ return s;
2094
+ }
2095
+
2096
+ static natsStatus
2097
+ _setupServerPool(natsConnection *nc)
2098
+ {
2099
+ natsStatus s;
2100
+
2101
+ s = natsSrvPool_Create(&(nc->srvPool), nc->opts);
2102
+ if (s == NATS_OK)
2103
+ nc->url = natsSrvPool_GetSrvUrl(nc->srvPool, 0);
2104
+
2105
+ return NATS_UPDATE_ERR_STACK(s);
2106
+ }
2107
+
2108
+ natsStatus
2109
+ natsConn_create(natsConnection **newConn, natsOptions *options)
2110
+ {
2111
+ natsStatus s = NATS_OK;
2112
+ natsConnection *nc = NULL;
2113
+
2114
+ s = nats_Open(-1);
2115
+ if (s != NATS_OK)
2116
+ return s;
2117
+
2118
+ nc = NATS_CALLOC(1, sizeof(natsConnection));
2119
+ if (nc == NULL)
2120
+ {
2121
+ // options have been cloned or created for the connection,
2122
+ // which was supposed to take ownership, so destroy it now.
2123
+ natsOptions_Destroy(options);
2124
+ return nats_setDefaultError(NATS_NO_MEMORY);
2125
+ }
2126
+
2127
+ natsLib_Retain();
2128
+
2129
+ nc->refs = 1;
2130
+ nc->sockCtx.fd = NATS_SOCK_INVALID;
2131
+ nc->opts = options;
2132
+
2133
+ if (nc->opts->maxPingsOut == 0)
2134
+ nc->opts->maxPingsOut = NATS_OPTS_DEFAULT_MAX_PING_OUT;
2135
+
2136
+ if (nc->opts->maxPendingMsgs == 0)
2137
+ nc->opts->maxPendingMsgs = NATS_OPTS_DEFAULT_MAX_PENDING_MSGS;
2138
+
2139
+ if (nc->opts->reconnectBufSize == 0)
2140
+ nc->opts->reconnectBufSize = NATS_OPTS_DEFAULT_RECONNECT_BUF_SIZE;
2141
+
2142
+ nc->errStr[0] = '\0';
2143
+
2144
+ s = natsMutex_Create(&(nc->mu));
2145
+ if (s == NATS_OK)
2146
+ s = _setupServerPool(nc);
2147
+ if (s == NATS_OK)
2148
+ s = natsHash_Create(&(nc->subs), 8);
2149
+ if (s == NATS_OK)
2150
+ s = natsSock_Init(&nc->sockCtx);
2151
+ if (s == NATS_OK)
2152
+ {
2153
+ s = natsBuf_Create(&(nc->scratch), DEFAULT_SCRATCH_SIZE);
2154
+ if (s == NATS_OK)
2155
+ s = natsBuf_Append(nc->scratch, _PUB_P_, _PUB_P_LEN_);
2156
+ }
2157
+ if (s == NATS_OK)
2158
+ s = natsCondition_Create(&(nc->flusherCond));
2159
+ if (s == NATS_OK)
2160
+ s = natsCondition_Create(&(nc->pongs.cond));
2161
+
2162
+ if (s == NATS_OK)
2163
+ *newConn = nc;
2164
+ else
2165
+ natsConn_release(nc);
2166
+
2167
+ return NATS_UPDATE_ERR_STACK(s);
2168
+ }
2169
+
2170
+ natsStatus
2171
+ natsConnection_Connect(natsConnection **newConn, natsOptions *options)
2172
+ {
2173
+ natsStatus s = NATS_OK;
2174
+ natsConnection *nc = NULL;
2175
+ natsOptions *opts = NULL;
2176
+
2177
+ if (options == NULL)
2178
+ {
2179
+ s = natsConnection_ConnectTo(newConn, NATS_DEFAULT_URL);
2180
+ return NATS_UPDATE_ERR_STACK(s);
2181
+ }
2182
+
2183
+ opts = natsOptions_clone(options);
2184
+ if (opts == NULL)
2185
+ s = NATS_NO_MEMORY;
2186
+
2187
+ if (s == NATS_OK)
2188
+ s = natsConn_create(&nc, opts);
2189
+ if (s == NATS_OK)
2190
+ s = _connect(nc);
2191
+
2192
+ if (s == NATS_OK)
2193
+ *newConn = nc;
2194
+ else
2195
+ natsConn_release(nc);
2196
+
2197
+ return NATS_UPDATE_ERR_STACK(s);
2198
+ }
2199
+
2200
+ static natsStatus
2201
+ _processUrlString(natsOptions *opts, const char *urls)
2202
+ {
2203
+ int count = 0;
2204
+ natsStatus s = NATS_OK;
2205
+ char **serverUrls = NULL;
2206
+ char *urlsCopy = NULL;
2207
+ char *commaPos = NULL;
2208
+ char *ptr = NULL;
2209
+ int len;
2210
+
2211
+ ptr = (char*) urls;
2212
+ while ((ptr = strchr(ptr, ',')) != NULL)
2213
+ {
2214
+ ptr++;
2215
+ count++;
2216
+ }
2217
+ if (count == 0)
2218
+ return natsOptions_SetURL(opts, urls);
2219
+
2220
+ serverUrls = (char**) NATS_CALLOC(count + 1, sizeof(char*));
2221
+ if (serverUrls == NULL)
2222
+ s = NATS_NO_MEMORY;
2223
+ if (s == NATS_OK)
2224
+ {
2225
+ urlsCopy = NATS_STRDUP(urls);
2226
+ if (urlsCopy == NULL)
2227
+ {
2228
+ NATS_FREE(serverUrls);
2229
+ return NATS_NO_MEMORY;
2230
+ }
2231
+ }
2232
+
2233
+ count = 0;
2234
+ ptr = urlsCopy;
2235
+
2236
+ do
2237
+ {
2238
+ while (*ptr == ' ')
2239
+ ptr++;
2240
+ serverUrls[count++] = ptr;
2241
+
2242
+ commaPos = strchr(ptr, ',');
2243
+ if (commaPos != NULL)
2244
+ {
2245
+ ptr = (char*)(commaPos + 1);
2246
+ *(commaPos) = '\0';
2247
+ }
2248
+
2249
+ len = (int) strlen(ptr);
2250
+ while ((len > 0) && (ptr[len-1] == ' '))
2251
+ ptr[--len] = '\0';
2252
+
2253
+ } while (commaPos != NULL);
2254
+
2255
+ if (s == NATS_OK)
2256
+ s = natsOptions_SetServers(opts, (const char**) serverUrls, count);
2257
+
2258
+ NATS_FREE(urlsCopy);
2259
+ NATS_FREE(serverUrls);
2260
+
2261
+ return s;
2262
+ }
2263
+
2264
+ natsStatus
2265
+ natsConnection_ConnectTo(natsConnection **newConn, const char *url)
2266
+ {
2267
+ natsStatus s = NATS_OK;
2268
+ natsConnection *nc = NULL;
2269
+ natsOptions *opts = NULL;
2270
+
2271
+ s = natsOptions_Create(&opts);
2272
+ if (s == NATS_OK)
2273
+ s = _processUrlString(opts, url);
2274
+ if (s == NATS_OK)
2275
+ s = natsConn_create(&nc, opts);
2276
+ if (s == NATS_OK)
2277
+ s = _connect(nc);
2278
+
2279
+ if (s == NATS_OK)
2280
+ *newConn = nc;
2281
+ else
2282
+ natsConn_release(nc);
2283
+
2284
+ return NATS_UPDATE_ERR_STACK(s);
2285
+ }
2286
+
2287
+ // Test if connection has been closed.
2288
+ bool
2289
+ natsConnection_IsClosed(natsConnection *nc)
2290
+ {
2291
+ bool closed;
2292
+
2293
+ if (nc == NULL)
2294
+ return true;
2295
+
2296
+ natsConn_Lock(nc);
2297
+
2298
+ closed = natsConn_isClosed(nc);
2299
+
2300
+ natsConn_Unlock(nc);
2301
+
2302
+ return closed;
2303
+ }
2304
+
2305
+ // Test if connection is reconnecting.
2306
+ bool
2307
+ natsConnection_IsReconnecting(natsConnection *nc)
2308
+ {
2309
+ bool reconnecting;
2310
+
2311
+ if (nc == NULL)
2312
+ return false;
2313
+
2314
+ natsConn_Lock(nc);
2315
+
2316
+ reconnecting = natsConn_isReconnecting(nc);
2317
+
2318
+ natsConn_Unlock(nc);
2319
+
2320
+ return reconnecting;
2321
+ }
2322
+
2323
+ // Returns the current state of the connection.
2324
+ natsConnStatus
2325
+ natsConnection_Status(natsConnection *nc)
2326
+ {
2327
+ natsConnStatus cs;
2328
+
2329
+ if (nc == NULL)
2330
+ return CLOSED;
2331
+
2332
+ natsConn_Lock(nc);
2333
+
2334
+ cs = nc->status;
2335
+
2336
+ natsConn_Unlock(nc);
2337
+
2338
+ return cs;
2339
+ }
2340
+
2341
+ static void
2342
+ _destroyPong(natsConnection *nc, natsPong *pong)
2343
+ {
2344
+ // If this pong is the cached one, do not free
2345
+ if (pong == &(nc->pongs.cached))
2346
+ memset(pong, 0, sizeof(natsPong));
2347
+ else
2348
+ NATS_FREE(pong);
2349
+ }
2350
+
2351
+ natsStatus
2352
+ natsConnection_FlushTimeout(natsConnection *nc, int64_t timeout)
2353
+ {
2354
+ natsStatus s = NATS_OK;
2355
+ int64_t target = 0;
2356
+ natsPong *pong = NULL;
2357
+
2358
+ if (nc == NULL)
2359
+ return nats_setDefaultError(NATS_INVALID_ARG);
2360
+
2361
+ if (timeout <= 0)
2362
+ return nats_setDefaultError(NATS_INVALID_TIMEOUT);
2363
+
2364
+ natsConn_lockAndRetain(nc);
2365
+
2366
+ if (natsConn_isClosed(nc))
2367
+ s = nats_setDefaultError(NATS_CONNECTION_CLOSED);
2368
+
2369
+ if (s == NATS_OK)
2370
+ {
2371
+ // Use the cached PONG instead of creating one if the list
2372
+ // is empty
2373
+ if (nc->pongs.head == NULL)
2374
+ pong = &(nc->pongs.cached);
2375
+ else
2376
+ pong = (natsPong*) NATS_CALLOC(1, sizeof(natsPong));
2377
+
2378
+ if (pong == NULL)
2379
+ s = nats_setDefaultError(NATS_NO_MEMORY);
2380
+ }
2381
+
2382
+ if (s == NATS_OK)
2383
+ {
2384
+ // Send the ping (and add the pong to the list)
2385
+ _sendPing(nc, pong);
2386
+
2387
+ target = nats_Now() + timeout;
2388
+
2389
+ // When the corresponding PONG is received, the PONG processing code
2390
+ // will set pong->id to 0 and do a broadcast. This will allow this
2391
+ // code to break out of the 'while' loop.
2392
+ while ((s != NATS_TIMEOUT)
2393
+ && !natsConn_isClosed(nc)
2394
+ && (pong->id > 0))
2395
+ {
2396
+ s = natsCondition_AbsoluteTimedWait(nc->pongs.cond, nc->mu, target);
2397
+ }
2398
+
2399
+ if ((s == NATS_OK) && (nc->status == CLOSED))
2400
+ {
2401
+ // The connection has been closed while we were waiting
2402
+ s = nats_setDefaultError(NATS_CONNECTION_CLOSED);
2403
+ }
2404
+ else if ((s == NATS_OK) && (pong->id == -1))
2405
+ {
2406
+ // The connection was disconnected and the library is in the
2407
+ // process of trying to reconnect
2408
+ s = nats_setDefaultError(NATS_CONNECTION_DISCONNECTED);
2409
+ }
2410
+
2411
+ if (s != NATS_OK)
2412
+ {
2413
+ // If we are here, it is possible that we timed-out, or some other
2414
+ // error occurred. Make sure the request is no longer in the list.
2415
+ _removePongFromList(nc, pong);
2416
+
2417
+ // Set the error. If we don't do that, and flush is called in a loop,
2418
+ // the stack would be growing with Flush/FlushTimeout.
2419
+ s = nats_setDefaultError(s);
2420
+ }
2421
+
2422
+ // We are done with the pong
2423
+ _destroyPong(nc, pong);
2424
+ }
2425
+
2426
+ natsConn_unlockAndRelease(nc);
2427
+
2428
+ return NATS_UPDATE_ERR_STACK(s);
2429
+ }
2430
+
2431
+ natsStatus
2432
+ natsConnection_Flush(natsConnection *nc)
2433
+ {
2434
+ natsStatus s = natsConnection_FlushTimeout(nc, 60000);
2435
+ return NATS_UPDATE_ERR_STACK(s);
2436
+ }
2437
+
2438
+ int
2439
+ natsConnection_Buffered(natsConnection *nc)
2440
+ {
2441
+ int buffered = -1;
2442
+
2443
+ if (nc == NULL)
2444
+ return nats_setDefaultError(NATS_INVALID_ARG);
2445
+
2446
+ natsConn_Lock(nc);
2447
+
2448
+ if ((nc->status != CLOSED) && (nc->bw != NULL))
2449
+ buffered = natsBuf_Len(nc->bw);
2450
+
2451
+ natsConn_Unlock(nc);
2452
+
2453
+ return buffered;
2454
+ }
2455
+
2456
+ int64_t
2457
+ natsConnection_GetMaxPayload(natsConnection *nc)
2458
+ {
2459
+ int64_t mp = 0;
2460
+
2461
+ if (nc == NULL)
2462
+ return 0;
2463
+
2464
+ natsConn_Lock(nc);
2465
+
2466
+ mp = nc->info.maxPayload;
2467
+
2468
+ natsConn_Unlock(nc);
2469
+
2470
+ return mp;
2471
+ }
2472
+
2473
+ natsStatus
2474
+ natsConnection_GetStats(natsConnection *nc, natsStatistics *stats)
2475
+ {
2476
+ natsStatus s = NATS_OK;
2477
+
2478
+ if ((nc == NULL) || (stats == NULL))
2479
+ return nats_setDefaultError(NATS_INVALID_ARG);
2480
+
2481
+ natsConn_Lock(nc);
2482
+
2483
+ memcpy(stats, &(nc->stats), sizeof(natsStatistics));
2484
+
2485
+ natsConn_Unlock(nc);
2486
+
2487
+ return s;
2488
+ }
2489
+
2490
+ natsStatus
2491
+ natsConnection_GetConnectedUrl(natsConnection *nc, char *buffer, size_t bufferSize)
2492
+ {
2493
+ natsStatus s = NATS_OK;
2494
+
2495
+ if ((nc == NULL) || (buffer == NULL))
2496
+ return nats_setDefaultError(NATS_INVALID_ARG);
2497
+
2498
+ natsConn_Lock(nc);
2499
+
2500
+ buffer[0] = '\0';
2501
+
2502
+ if ((nc->status == CONNECTED) && (nc->url->fullUrl != NULL))
2503
+ {
2504
+ if (strlen(nc->url->fullUrl) >= bufferSize)
2505
+ s = nats_setDefaultError(NATS_INSUFFICIENT_BUFFER);
2506
+
2507
+ if (s == NATS_OK)
2508
+ snprintf(buffer, bufferSize, "%s", nc->url->fullUrl);
2509
+ }
2510
+
2511
+ natsConn_Unlock(nc);
2512
+
2513
+ return s;
2514
+ }
2515
+
2516
+ natsStatus
2517
+ natsConnection_GetConnectedServerId(natsConnection *nc, char *buffer, size_t bufferSize)
2518
+ {
2519
+ natsStatus s = NATS_OK;
2520
+
2521
+ if ((nc == NULL) || (buffer == NULL))
2522
+ return nats_setDefaultError(NATS_INVALID_ARG);
2523
+
2524
+ natsConn_Lock(nc);
2525
+
2526
+ buffer[0] = '\0';
2527
+
2528
+ if ((nc->status == CONNECTED) && (nc->info.id != NULL))
2529
+ {
2530
+ if (strlen(nc->info.id) >= bufferSize)
2531
+ s = nats_setDefaultError(NATS_INSUFFICIENT_BUFFER);
2532
+
2533
+ if (s == NATS_OK)
2534
+ snprintf(buffer, bufferSize, "%s", nc->info.id);
2535
+ }
2536
+
2537
+ natsConn_Unlock(nc);
2538
+
2539
+ return s;
2540
+ }
2541
+
2542
+ natsStatus
2543
+ natsConnection_GetServers(natsConnection *nc, char ***servers, int *count)
2544
+ {
2545
+ natsStatus s = NATS_OK;
2546
+
2547
+ if ((nc == NULL) || (servers == NULL) || (count == NULL))
2548
+ return nats_setDefaultError(NATS_INVALID_ARG);
2549
+
2550
+ natsConn_Lock(nc);
2551
+
2552
+ s = natsSrvPool_GetServers(nc->srvPool, false, servers, count);
2553
+
2554
+ natsConn_Unlock(nc);
2555
+
2556
+ return NATS_UPDATE_ERR_STACK(s);
2557
+ }
2558
+
2559
+ natsStatus
2560
+ natsConnection_GetDiscoveredServers(natsConnection *nc, char ***servers, int *count)
2561
+ {
2562
+ natsStatus s = NATS_OK;
2563
+
2564
+ if ((nc == NULL) || (servers == NULL) || (count == NULL))
2565
+ return nats_setDefaultError(NATS_INVALID_ARG);
2566
+
2567
+ natsConn_Lock(nc);
2568
+
2569
+ s = natsSrvPool_GetServers(nc->srvPool, true, servers, count);
2570
+
2571
+ natsConn_Unlock(nc);
2572
+
2573
+ return NATS_UPDATE_ERR_STACK(s);
2574
+ }
2575
+
2576
+ natsStatus
2577
+ natsConnection_GetLastError(natsConnection *nc, const char **lastError)
2578
+ {
2579
+ natsStatus s;
2580
+
2581
+ if (nc == NULL)
2582
+ return nats_setDefaultError(NATS_INVALID_ARG);
2583
+
2584
+ natsConn_Lock(nc);
2585
+
2586
+ s = nc->err;
2587
+ if (s == NATS_OK)
2588
+ nc->errStr[0] = '\0';
2589
+ else if (nc->errStr[0] == '\0')
2590
+ snprintf(nc->errStr, sizeof(nc->errStr), "%s", natsStatus_GetText(s));
2591
+
2592
+ *lastError = nc->errStr;
2593
+
2594
+ natsConn_Unlock(nc);
2595
+
2596
+ return s;
2597
+ }
2598
+
2599
+ void
2600
+ natsConnection_Close(natsConnection *nc)
2601
+ {
2602
+ if (nc == NULL)
2603
+ return;
2604
+
2605
+ nats_doNotUpdateErrStack(true);
2606
+
2607
+ _close(nc, CLOSED, true);
2608
+
2609
+ nats_doNotUpdateErrStack(false);
2610
+ }
2611
+
2612
+ void
2613
+ natsConnection_Destroy(natsConnection *nc)
2614
+ {
2615
+ if (nc == NULL)
2616
+ return;
2617
+
2618
+ nats_doNotUpdateErrStack(true);
2619
+
2620
+ _close(nc, CLOSED, true);
2621
+
2622
+ nats_doNotUpdateErrStack(false);
2623
+
2624
+ natsConn_release(nc);
2625
+ }
2626
+
2627
+ void
2628
+ natsConnection_ProcessReadEvent(natsConnection *nc)
2629
+ {
2630
+ natsStatus s = NATS_OK;
2631
+ int n = 0;
2632
+ char *buffer;
2633
+ int size;
2634
+
2635
+ natsConn_Lock(nc);
2636
+
2637
+ if (!(nc->el.attached))
2638
+ {
2639
+ natsConn_Unlock(nc);
2640
+ return;
2641
+ }
2642
+
2643
+ if (nc->ps == NULL)
2644
+ {
2645
+ s = natsParser_Create(&(nc->ps));
2646
+ if (s != NATS_OK)
2647
+ nats_setDefaultError(NATS_NO_MEMORY);
2648
+ }
2649
+
2650
+ if ((s != NATS_OK) || natsConn_isClosed(nc) || natsConn_isReconnecting(nc))
2651
+ {
2652
+ (void) NATS_UPDATE_ERR_STACK(s);
2653
+ natsConn_Unlock(nc);
2654
+ return;
2655
+ }
2656
+
2657
+ _retain(nc);
2658
+
2659
+ buffer = nc->el.buffer;
2660
+ size = DEFAULT_BUF_SIZE;
2661
+
2662
+ natsConn_Unlock(nc);
2663
+
2664
+ // Do not try to read again here on success. If more than one connection
2665
+ // is attached to the same loop, and there is a constant stream of data
2666
+ // coming for the first connection, this would starve the second connection.
2667
+ // So return and we will be called back later by the event loop.
2668
+ s = natsSock_Read(&(nc->sockCtx), buffer, size, &n);
2669
+ if (s == NATS_OK)
2670
+ s = natsParser_Parse(nc, buffer, n);
2671
+
2672
+ if (s != NATS_OK)
2673
+ _processOpError(nc, s);
2674
+
2675
+ natsConn_release(nc);
2676
+ }
2677
+
2678
+ void
2679
+ natsConnection_ProcessWriteEvent(natsConnection *nc)
2680
+ {
2681
+ natsStatus s = NATS_OK;
2682
+ int n = 0;
2683
+ char *buf;
2684
+ int len;
2685
+
2686
+ natsConn_Lock(nc);
2687
+
2688
+ if (!(nc->el.attached) || (nc->sockCtx.fd == NATS_SOCK_INVALID))
2689
+ {
2690
+ natsConn_Unlock(nc);
2691
+ return;
2692
+ }
2693
+
2694
+ buf = natsBuf_Data(nc->bw);
2695
+ len = natsBuf_Len(nc->bw);
2696
+
2697
+ s = natsSock_Write(&(nc->sockCtx), buf, len, &n);
2698
+ if (s == NATS_OK)
2699
+ {
2700
+ if (n == len)
2701
+ {
2702
+ // We sent all the data, reset buffer and remove WRITE event.
2703
+ natsBuf_Reset(nc->bw);
2704
+
2705
+ s = nc->opts->evCbs.write(nc->el.data, NATS_EVENT_ACTION_REMOVE);
2706
+ if (s == NATS_OK)
2707
+ nc->el.writeAdded = false;
2708
+ else
2709
+ nats_setError(s, "Error processing write request: %d - %s",
2710
+ s, natsStatus_GetText(s));
2711
+ }
2712
+ else
2713
+ {
2714
+ // We sent some part of the buffer. Move the remaining at the beginning.
2715
+ natsBuf_Consume(nc->bw, n);
2716
+ }
2717
+ }
2718
+
2719
+ natsConn_Unlock(nc);
2720
+
2721
+ if (s != NATS_OK)
2722
+ _processOpError(nc, s);
2723
+
2724
+ (void) NATS_UPDATE_ERR_STACK(s);
2725
+ }