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,87 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #ifndef PARSER_H_
4
+ #define PARSER_H_
5
+
6
+ #include <stdint.h>
7
+
8
+ //#include "natsp.h"
9
+ #include "status.h"
10
+ #include "buf.h"
11
+
12
+ typedef enum
13
+ {
14
+ OP_START = 0,
15
+ OP_PLUS,
16
+ OP_PLUS_O,
17
+ OP_PLUS_OK,
18
+ OP_MINUS,
19
+ OP_MINUS_E,
20
+ OP_MINUS_ER,
21
+ OP_MINUS_ERR,
22
+ OP_MINUS_ERR_SPC,
23
+ MINUS_ERR_ARG,
24
+ OP_M,
25
+ OP_MS,
26
+ OP_MSG,
27
+ OP_MSG_SPC,
28
+ MSG_ARG,
29
+ MSG_PAYLOAD,
30
+ MSG_END,
31
+ OP_P,
32
+ OP_PI,
33
+ OP_PIN,
34
+ OP_PING,
35
+ OP_PO,
36
+ OP_PON,
37
+ OP_PONG,
38
+ OP_I,
39
+ OP_IN,
40
+ OP_INF,
41
+ OP_INFO,
42
+ OP_INFO_SPC,
43
+ INFO_ARG
44
+
45
+ } natsOp;
46
+
47
+ typedef struct __natsMsgArg
48
+ {
49
+ natsBuffer subjectRec;
50
+ natsBuffer *subject;
51
+ natsBuffer replyRec;
52
+ natsBuffer *reply;
53
+ int64_t sid;
54
+ int size;
55
+
56
+ } natsMsgArg;
57
+
58
+ #define MAX_CONTROL_LINE_SIZE (1024)
59
+
60
+ typedef struct __natsParser
61
+ {
62
+ natsOp state;
63
+ int afterSpace;
64
+ int drop;
65
+ natsMsgArg ma;
66
+ natsBuffer argBufRec;
67
+ natsBuffer *argBuf;
68
+ natsBuffer msgBufRec;
69
+ natsBuffer *msgBuf;
70
+ char scratch[MAX_CONTROL_LINE_SIZE];
71
+
72
+ } natsParser;
73
+
74
+ // This is defined in natsp.h, natsp.h includes us. Alternatively, we can move
75
+ // all the parser defines in natsp.h
76
+ struct __natsConnection;
77
+
78
+ natsStatus
79
+ natsParser_Create(natsParser **newParser);
80
+
81
+ natsStatus
82
+ natsParser_Parse(struct __natsConnection *nc, char *buf, int bufLen);
83
+
84
+ void
85
+ natsParser_Destroy(natsParser *parser);
86
+
87
+ #endif /* PARSER_H_ */
@@ -0,0 +1,293 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #include "natsp.h"
4
+
5
+ #include <string.h>
6
+
7
+ #include "conn.h"
8
+ #include "sub.h"
9
+ #include "msg.h"
10
+ #include "nuid.h"
11
+
12
+ static const char *digits = "0123456789";
13
+
14
+ #define _publish(n, s, r, d, l) _publishEx((n), (s), (r), (d), (l), false)
15
+
16
+ // _publish is the internal function to publish messages to a nats server.
17
+ // Sends a protocol data message by queueing into the bufio writer
18
+ // and kicking the flusher thread. These writes should be protected.
19
+ static natsStatus
20
+ _publishEx(natsConnection *nc, const char *subj,
21
+ const char *reply, const void *data, int dataLen,
22
+ bool directFlush)
23
+ {
24
+ natsStatus s = NATS_OK;
25
+ int msgHdSize = 0;
26
+ char b[12];
27
+ int bSize = sizeof(b);
28
+ int i = bSize;
29
+ int subjLen = 0;
30
+ int replyLen = 0;
31
+ int sizeSize = 0;
32
+
33
+ if (nc == NULL)
34
+ return nats_setDefaultError(NATS_INVALID_ARG);
35
+
36
+ if ((subj == NULL)
37
+ || ((subjLen = (int) strlen(subj)) == 0))
38
+ {
39
+ return nats_setDefaultError(NATS_INVALID_SUBJECT);
40
+ }
41
+
42
+ replyLen = ((reply != NULL) ? (int) strlen(reply) : 0);
43
+
44
+ natsConn_Lock(nc);
45
+
46
+ // Pro-actively reject dataLen over the threshold set by server.
47
+ if ((int64_t) dataLen > nc->info.maxPayload)
48
+ {
49
+ natsConn_Unlock(nc);
50
+
51
+ return nats_setError(NATS_MAX_PAYLOAD,
52
+ "Payload %d greater than maximum allowed: %" PRId64,
53
+ dataLen, nc->info.maxPayload);
54
+
55
+ }
56
+
57
+ if ((s == NATS_OK) && natsConn_isClosed(nc))
58
+ {
59
+ s = nats_setDefaultError(NATS_CONNECTION_CLOSED);
60
+ }
61
+
62
+ // Check if we are reconnecting, and if so check if
63
+ // we have exceeded our reconnect outbound buffer limits.
64
+ if ((s == NATS_OK) && natsConn_isReconnecting(nc))
65
+ {
66
+ // Flush to underlying buffer.
67
+ natsConn_bufferFlush(nc);
68
+
69
+ // Check if we are over
70
+ if (natsBuf_Len(nc->pending) >= nc->opts->reconnectBufSize)
71
+ {
72
+ natsConn_Unlock(nc);
73
+ return NATS_INSUFFICIENT_BUFFER;
74
+ }
75
+ }
76
+
77
+ if (s == NATS_OK)
78
+ {
79
+ if (dataLen > 0)
80
+ {
81
+ int l;
82
+
83
+ for (l = dataLen; l > 0; l /= 10)
84
+ {
85
+ i -= 1;
86
+ b[i] = digits[l%10];
87
+ }
88
+ }
89
+ else
90
+ {
91
+ i -= 1;
92
+ b[i] = digits[0];
93
+ }
94
+
95
+ sizeSize = (bSize - i);
96
+
97
+ msgHdSize = _PUB_P_LEN_
98
+ + subjLen + 1
99
+ + (replyLen > 0 ? replyLen + 1 : 0)
100
+ + sizeSize + _CRLF_LEN_;
101
+
102
+ natsBuf_RewindTo(nc->scratch, _PUB_P_LEN_);
103
+
104
+ if (natsBuf_Capacity(nc->scratch) < msgHdSize)
105
+ {
106
+ // Although natsBuf_Append() would make sure that the buffer
107
+ // grows, it is better to make sure that the buffer is big
108
+ // enough for the pre-calculated size. We make it even a bit bigger.
109
+ s = natsBuf_Expand(nc->scratch, (int) ((float)msgHdSize * 1.1));
110
+ }
111
+ }
112
+
113
+ if (s == NATS_OK)
114
+ s = natsBuf_Append(nc->scratch, subj, subjLen);
115
+ if (s == NATS_OK)
116
+ s = natsBuf_Append(nc->scratch, _SPC_, _SPC_LEN_);
117
+ if ((s == NATS_OK) && (reply != NULL))
118
+ {
119
+ s = natsBuf_Append(nc->scratch, reply, replyLen);
120
+ if (s == NATS_OK)
121
+ s = natsBuf_Append(nc->scratch, _SPC_, _SPC_LEN_);
122
+ }
123
+ if (s == NATS_OK)
124
+ s = natsBuf_Append(nc->scratch, (b+i), sizeSize);
125
+ if (s == NATS_OK)
126
+ s = natsBuf_Append(nc->scratch, _CRLF_, _CRLF_LEN_);
127
+
128
+ if (s == NATS_OK)
129
+ s = natsConn_bufferWrite(nc, natsBuf_Data(nc->scratch), msgHdSize);
130
+
131
+ if (s == NATS_OK)
132
+ s = natsConn_bufferWrite(nc, data, dataLen);
133
+
134
+ if (s == NATS_OK)
135
+ s = natsConn_bufferWrite(nc, _CRLF_, _CRLF_LEN_);
136
+
137
+ if (s == NATS_OK)
138
+ {
139
+ if (directFlush)
140
+ s = natsConn_bufferFlush(nc);
141
+ else
142
+ natsConn_kickFlusher(nc);
143
+ }
144
+
145
+ if (s == NATS_OK)
146
+ {
147
+ nc->stats.outMsgs += 1;
148
+ nc->stats.outBytes += dataLen;
149
+ }
150
+
151
+ natsConn_Unlock(nc);
152
+
153
+ return NATS_UPDATE_ERR_STACK(s);
154
+ }
155
+
156
+ /*
157
+ * Publishes the data argument to the given subject. The data argument is left
158
+ * untouched and needs to be correctly interpreted on the receiver.
159
+ */
160
+ natsStatus
161
+ natsConnection_Publish(natsConnection *nc, const char *subj,
162
+ const void *data, int dataLen)
163
+ {
164
+ natsStatus s = _publish(nc, subj, NULL, data, dataLen);
165
+
166
+ return NATS_UPDATE_ERR_STACK(s);
167
+ }
168
+
169
+ /*
170
+ * Convenient function to publish a string. This call is equivalent to:
171
+ *
172
+ * const char* myString = "hello";
173
+ *
174
+ * natsPublish(nc, subj, (const void*) myString, (int) strlen(myString));
175
+ */
176
+ natsStatus
177
+ natsConnection_PublishString(natsConnection *nc, const char *subj,
178
+ const char *str)
179
+ {
180
+ natsStatus s = _publish(nc, subj, NULL, (const void*) str,
181
+ (str != NULL ? (int) strlen(str) : 0));
182
+
183
+ return NATS_UPDATE_ERR_STACK(s);
184
+ }
185
+
186
+ /*
187
+ * Publishes the natsMsg structure, which includes the subject, an optional
188
+ * reply and optional data.
189
+ */
190
+ natsStatus
191
+ natsConnection_PublishMsg(natsConnection *nc, natsMsg *msg)
192
+ {
193
+ natsStatus s = _publish(nc, msg->subject, msg->reply,
194
+ msg->data, msg->dataLen);
195
+
196
+ return NATS_UPDATE_ERR_STACK(s);
197
+ }
198
+
199
+ /*
200
+ * Publishes the data argument to the given subject expecting a response on
201
+ * the reply subject. Use natsConnection_Request() for automatically waiting for a
202
+ * response inline.
203
+ */
204
+ natsStatus
205
+ natsConnection_PublishRequest(natsConnection *nc, const char *subj,
206
+ const char *reply, const void *data, int dataLen)
207
+ {
208
+ natsStatus s;
209
+
210
+ if ((reply == NULL) || (strlen(reply) == 0))
211
+ return nats_setDefaultError(NATS_INVALID_ARG);
212
+
213
+ s = _publish(nc, subj, reply, data, dataLen);
214
+
215
+ return NATS_UPDATE_ERR_STACK(s);
216
+ }
217
+
218
+ /*
219
+ * Convenient function to publish a request as a string. This call is
220
+ * equivalent to:
221
+ *
222
+ * const char* myString = "hello";
223
+ *
224
+ * natsPublishRequest(nc, subj, reply, (const void*) myString,
225
+ * (int) strlen(myString));
226
+ */
227
+ natsStatus
228
+ natsConnection_PublishRequestString(natsConnection *nc, const char *subj,
229
+ const char *reply, const char *str)
230
+ {
231
+ natsStatus s;
232
+
233
+ if ((reply == NULL) || (strlen(reply) == 0))
234
+ return nats_setDefaultError(NATS_INVALID_ARG);
235
+
236
+ s = _publish(nc, subj, reply, (const void*) str, (int) strlen(str));
237
+
238
+ return NATS_UPDATE_ERR_STACK(s);
239
+ }
240
+
241
+ /*
242
+ * Creates an inbox and performs a natsPublishRequest() call with the reply
243
+ * set to that inbox. Returns the first reply received.
244
+ * This is optimized for the case of multiple responses.
245
+ */
246
+ natsStatus
247
+ natsConnection_Request(natsMsg **replyMsg, natsConnection *nc, const char *subj,
248
+ const void *data, int dataLen, int64_t timeout)
249
+ {
250
+ natsStatus s = NATS_OK;
251
+ natsSubscription *sub = NULL;
252
+ char inbox[NATS_INBOX_PRE_LEN + NUID_BUFFER_LEN + 1];
253
+
254
+ if (replyMsg == NULL)
255
+ return nats_setDefaultError(NATS_INVALID_ARG);
256
+
257
+ s = natsInbox_init(inbox, sizeof(inbox));
258
+ if (s == NATS_OK)
259
+ s = natsConn_subscribe(&sub, nc, inbox, NULL, 0, NULL, NULL);
260
+ if (s == NATS_OK)
261
+ s = natsSubscription_AutoUnsubscribe(sub, 1);
262
+ if (s == NATS_OK)
263
+ s = _publishEx(nc, subj, inbox, data, dataLen, true);
264
+ if (s == NATS_OK)
265
+ s = natsSubscription_NextMsg(replyMsg, sub, timeout);
266
+
267
+ natsSubscription_Destroy(sub);
268
+
269
+ return NATS_UPDATE_ERR_STACK(s);
270
+ }
271
+
272
+ /*
273
+ * Convenient function to send a request as a string. This call is
274
+ * equivalent to:
275
+ *
276
+ * const char* myString = "hello";
277
+ *
278
+ * natsConnection_Request(nc, subj, reply, (const void*) myString,
279
+ * (int) strlen(myString));
280
+ */
281
+ natsStatus
282
+ natsConnection_RequestString(natsMsg **replyMsg, natsConnection *nc,
283
+ const char *subj, const char *str,
284
+ int64_t timeout)
285
+ {
286
+ natsStatus s;
287
+
288
+ s = natsConnection_Request(replyMsg, nc, subj, (const void*) str,
289
+ (str == NULL ? 0 : (int) strlen(str)),
290
+ timeout);
291
+
292
+ return NATS_UPDATE_ERR_STACK(s);
293
+ }
@@ -0,0 +1,380 @@
1
+ // Copyright 2015 Apcera Inc. All rights reserved.
2
+
3
+ #include "natsp.h"
4
+
5
+ #include "mem.h"
6
+ #include "url.h"
7
+
8
+ static void
9
+ _freeSrv(natsSrv *srv)
10
+ {
11
+ if (srv == NULL)
12
+ return;
13
+
14
+ natsUrl_Destroy(srv->url);
15
+ NATS_FREE(srv);
16
+ }
17
+
18
+ static natsStatus
19
+ _createSrv(natsSrv **newSrv, char *url, bool implicit)
20
+ {
21
+ natsStatus s = NATS_OK;
22
+ natsSrv *srv = (natsSrv*) NATS_CALLOC(1, sizeof(natsSrv));
23
+
24
+ if (srv == NULL)
25
+ return nats_setDefaultError(NATS_NO_MEMORY);
26
+
27
+ srv->isImplicit = implicit;
28
+
29
+ s = natsUrl_Create(&(srv->url), url);
30
+ if (s == NATS_OK)
31
+ *newSrv = srv;
32
+ else
33
+ _freeSrv(srv);
34
+
35
+ return NATS_UPDATE_ERR_STACK(s);
36
+ }
37
+
38
+ natsSrv*
39
+ natsSrvPool_GetCurrentServer(natsSrvPool *pool, const natsUrl *url, int *index)
40
+ {
41
+ natsSrv *s = NULL;
42
+ int i;
43
+
44
+ for (i = 0; i < pool->size; i++)
45
+ {
46
+ s = pool->srvrs[i];
47
+ if (s->url == url)
48
+ {
49
+ if (index != NULL)
50
+ *index = i;
51
+
52
+ return s;
53
+ }
54
+ }
55
+
56
+ if (index != NULL)
57
+ *index = -1;
58
+
59
+ return NULL;
60
+ }
61
+
62
+ // Pop the current server and put onto the end of the list. Select head of list as long
63
+ // as number of reconnect attempts under MaxReconnect.
64
+ natsSrv*
65
+ natsSrvPool_GetNextServer(natsSrvPool *pool, natsOptions *opts, const natsUrl *ncUrl)
66
+ {
67
+ natsSrv *s = NULL;
68
+ int i, j;
69
+
70
+ s = natsSrvPool_GetCurrentServer(pool, ncUrl, &i);
71
+ if (i < 0)
72
+ return NULL;
73
+
74
+ // Shift left servers past current to the current's position
75
+ for (j = i; j < pool->size - 1; j++)
76
+ pool->srvrs[j] = pool->srvrs[j+1];
77
+
78
+ if ((opts->maxReconnect < 0)
79
+ || (s->reconnects < opts->maxReconnect))
80
+ {
81
+ // Move the current server to the back of the list
82
+ pool->srvrs[pool->size - 1] = s;
83
+ }
84
+ else
85
+ {
86
+ // Remove the server from the list
87
+ _freeSrv(s);
88
+ pool->size--;
89
+ }
90
+
91
+ if (pool->size <= 0)
92
+ return NULL;
93
+
94
+ return pool->srvrs[0];
95
+ }
96
+
97
+ void
98
+ natsSrvPool_Destroy(natsSrvPool *pool)
99
+ {
100
+ natsSrv *srv;
101
+ int i;
102
+
103
+ if (pool == NULL)
104
+ return;
105
+
106
+ for (i = 0; i < pool->size; i++)
107
+ {
108
+ srv = pool->srvrs[i];
109
+ _freeSrv(srv);
110
+ }
111
+ natsStrHash_Destroy(pool->urls);
112
+ pool->urls = NULL;
113
+
114
+ NATS_FREE(pool->srvrs);
115
+ pool->srvrs = NULL;
116
+ pool->size = 0;
117
+ NATS_FREE(pool);
118
+ }
119
+
120
+ static natsStatus
121
+ _addURLToPool(natsSrvPool *pool, char *sURL, bool implicit)
122
+ {
123
+ natsStatus s;
124
+ natsSrv *srv = NULL;
125
+ bool addedToMap = false;
126
+ char bareURL[256];
127
+
128
+ s = _createSrv(&srv, sURL, implicit);
129
+ if (s != NATS_OK)
130
+ return s;
131
+
132
+ // In the map, we need to add an URL that is just host:port
133
+ snprintf(bareURL, sizeof(bareURL), "%s:%d", srv->url->host, srv->url->port);
134
+ s = natsStrHash_Set(pool->urls, bareURL, true, (void*)1, NULL);
135
+ if (s == NATS_OK)
136
+ {
137
+ addedToMap = true;
138
+ if (pool->size + 1 > pool->cap)
139
+ {
140
+ natsSrv **newArray = NULL;
141
+ int newCap = 2 * pool->cap;
142
+
143
+ newArray = (natsSrv**) NATS_REALLOC(pool->srvrs, newCap * sizeof(char*));
144
+ if (newArray == NULL)
145
+ s = nats_setDefaultError(NATS_NO_MEMORY);
146
+
147
+ if (s == NATS_OK)
148
+ {
149
+ pool->cap = newCap;
150
+ pool->srvrs = newArray;
151
+ }
152
+ }
153
+ if (s == NATS_OK)
154
+ pool->srvrs[pool->size++] = srv;
155
+ }
156
+ if (s != NATS_OK)
157
+ {
158
+ if (addedToMap)
159
+ natsStrHash_Remove(pool->urls, sURL);
160
+
161
+ _freeSrv(srv);
162
+ }
163
+
164
+ return NATS_UPDATE_ERR_STACK(s);
165
+ }
166
+
167
+ static void
168
+ _shufflePool(natsSrvPool *pool)
169
+ {
170
+ int i, j;
171
+ natsSrv *tmp;
172
+
173
+ if (pool->size <= 1)
174
+ return;
175
+
176
+ srand((unsigned int) nats_NowInNanoSeconds());
177
+
178
+ for (i = 0; i < pool->size; i++)
179
+ {
180
+ j = rand() % (i + 1);
181
+ tmp = pool->srvrs[i];
182
+ pool->srvrs[i] = pool->srvrs[j];
183
+ pool->srvrs[j] = tmp;
184
+ }
185
+ }
186
+
187
+ natsStatus
188
+ natsSrvPool_addNewURLs(natsSrvPool *pool, char **urls, int urlCount, bool doShuffle)
189
+ {
190
+ natsStatus s = NATS_OK;
191
+ char url[256];
192
+ int i;
193
+ char *sport;
194
+ int portPos;
195
+ bool found;
196
+ bool isLH;
197
+
198
+ // If we can shuffle, we shuffle the given array, not the entire pool
199
+ if (urlCount > 0 && doShuffle)
200
+ {
201
+ int j;
202
+ char *tmp;
203
+
204
+ for (i = 0; i < urlCount; i++)
205
+ {
206
+ j = rand() % (i + 1);
207
+ tmp = urls[i];
208
+ urls[i] = urls[j];
209
+ urls[j] = tmp;
210
+ }
211
+ }
212
+
213
+ for (i=0; (s == NATS_OK) && (i<urlCount); i++)
214
+ {
215
+ isLH = false;
216
+ found = false;
217
+
218
+ // Consider localhost:<port>, 127.0.0.1:<port> and [::1]:<port>
219
+ // all the same.
220
+ sport = strrchr(urls[i], ':');
221
+ portPos = (int) (sport - urls[i]);
222
+ if (((nats_strcasestr(urls[i], "localhost") == urls[i]) && (portPos == 9))
223
+ || (strncmp(urls[i], "127.0.0.1", portPos) == 0)
224
+ || (strncmp(urls[i], "[::1]", portPos) == 0))
225
+ {
226
+ isLH = ((urls[i][0] == 'l') || (urls[i][0] == 'L'));
227
+
228
+ snprintf(url, sizeof(url), "localhost%s", sport);
229
+ found = (natsStrHash_Get(pool->urls, url) != NULL);
230
+ if (!found)
231
+ {
232
+ snprintf(url, sizeof(url), "127.0.0.1%s", sport);
233
+ found = (natsStrHash_Get(pool->urls, url) != NULL);
234
+ }
235
+ if (!found)
236
+ {
237
+ snprintf(url, sizeof(url), "[::1]%s", sport);
238
+ found = (natsStrHash_Get(pool->urls, url) != NULL);
239
+ }
240
+ }
241
+ else
242
+ {
243
+ found = (natsStrHash_Get(pool->urls, urls[i]) != NULL);
244
+ }
245
+
246
+ if (!found)
247
+ {
248
+ // Make sure that localhost URL is always stored in lower case.
249
+ if (isLH)
250
+ snprintf(url, sizeof(url), "nats://localhost%s", sport);
251
+ else
252
+ snprintf(url, sizeof(url), "nats://%s", urls[i]);
253
+ s = _addURLToPool(pool, url, true);
254
+ }
255
+ }
256
+
257
+ return NATS_UPDATE_ERR_STACK(s);
258
+ }
259
+
260
+ // Create the server pool using the options given.
261
+ // We will place a Url option first, followed by any
262
+ // Server Options. We will randomize the server pool unlesss
263
+ // the NoRandomize flag is set.
264
+ natsStatus
265
+ natsSrvPool_Create(natsSrvPool **newPool, natsOptions *opts)
266
+ {
267
+ natsStatus s = NATS_OK;
268
+ natsSrvPool *pool = NULL;
269
+ int poolSize;
270
+ int i;
271
+
272
+ poolSize = (opts->url != NULL ? 1 : 0);
273
+ poolSize += opts->serversCount;
274
+
275
+ // If the pool is going to be empty, we will add the default URL.
276
+ if (poolSize == 0)
277
+ poolSize = 1;
278
+
279
+ pool = (natsSrvPool*) NATS_CALLOC(1, sizeof(natsSrvPool));
280
+ if (pool == NULL)
281
+ return nats_setDefaultError(NATS_NO_MEMORY);
282
+
283
+ pool->srvrs = (natsSrv**) NATS_CALLOC(poolSize, sizeof(natsSrv*));
284
+ if (pool->srvrs == NULL)
285
+ {
286
+ NATS_FREE(pool);
287
+ return nats_setDefaultError(NATS_NO_MEMORY);
288
+ }
289
+ // Set the current capacity. The array of urls may have to grow in
290
+ // the future.
291
+ pool->cap = poolSize;
292
+
293
+ // Map that helps find out if an URL is already known.
294
+ s = natsStrHash_Create(&(pool->urls), poolSize);
295
+
296
+ // Add URLs from Options' Servers
297
+ for (i=0; (s == NATS_OK) && (i < opts->serversCount); i++)
298
+ s = _addURLToPool(pool, opts->servers[i], false);
299
+
300
+ if (s == NATS_OK)
301
+ {
302
+ // Randomize if allowed to
303
+ if (!(opts->noRandomize))
304
+ _shufflePool(pool);
305
+ }
306
+
307
+ // Normally, if this one is set, Options.Servers should not be,
308
+ // but we always allowed that, so continue to do so.
309
+ if ((s == NATS_OK) && (opts->url != NULL))
310
+ {
311
+ // Add to the end of the array
312
+ s = _addURLToPool(pool, opts->url, false);
313
+ if ((s == NATS_OK) && (pool->size > 1))
314
+ {
315
+ // Then swap it with first to guarantee that Options.Url is tried first.
316
+ natsSrv *opstUrl = pool->srvrs[pool->size-1];
317
+
318
+ pool->srvrs[pool->size-1] = pool->srvrs[0];
319
+ pool->srvrs[0] = opstUrl;
320
+ }
321
+ }
322
+ else if ((s == NATS_OK) && (pool->size == 0))
323
+ {
324
+ // Place default URL if pool is empty.
325
+ s = _addURLToPool(pool, (char*) NATS_DEFAULT_URL, false);
326
+ }
327
+
328
+ if (s == NATS_OK)
329
+ *newPool = pool;
330
+ else
331
+ natsSrvPool_Destroy(pool);
332
+
333
+ return NATS_UPDATE_ERR_STACK(s);
334
+ }
335
+
336
+ natsStatus
337
+ natsSrvPool_GetServers(natsSrvPool *pool, bool implicitOnly, char ***servers, int *count)
338
+ {
339
+ natsStatus s = NATS_OK;
340
+ char **srvrs = NULL;
341
+ natsSrv *srv;
342
+ natsUrl *url;
343
+ int i;
344
+ int discovered = 0;
345
+
346
+ if (pool->size == 0)
347
+ {
348
+ *servers = NULL;
349
+ *count = 0;
350
+ return NATS_OK;
351
+ }
352
+
353
+ srvrs = (char **) NATS_CALLOC(pool->size, sizeof(char*));
354
+ if (srvrs == NULL)
355
+ return nats_setDefaultError(NATS_NO_MEMORY);
356
+
357
+ for (i=0; ((s == NATS_OK) && (i<pool->size)); i++)
358
+ {
359
+ srv = pool->srvrs[i];
360
+ if (implicitOnly && !srv->isImplicit)
361
+ continue;
362
+ url = srv->url;
363
+ if (nats_asprintf(&(srvrs[discovered]), "nats://%s:%d", url->host, url->port) == -1)
364
+ s = nats_setDefaultError(NATS_NO_MEMORY);
365
+ else
366
+ discovered++;
367
+ }
368
+ if (s == NATS_OK)
369
+ {
370
+ *servers = srvrs;
371
+ *count = discovered;
372
+ }
373
+ else
374
+ {
375
+ for (i=0; i<discovered; i++)
376
+ NATS_FREE(srvrs[i]);
377
+ NATS_FREE(srvrs);
378
+ }
379
+ return NATS_UPDATE_ERR_STACK(s);
380
+ }