ffi-nats-core 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }