diff --git a/src/MQTTAsync.c b/src/MQTTAsync.c index b9a25997..3f60ab97 100755 --- a/src/MQTTAsync.c +++ b/src/MQTTAsync.c @@ -275,9 +275,6 @@ typedef struct } dis; struct { - int timeout; - int serverURIcount; - char** serverURIs; int currentURI; int MQTTVersion; /**< current MQTT version being used to connect */ } conn; @@ -300,8 +297,10 @@ typedef struct MQTTAsync_struct MQTTAsync_connected* connected; void* connected_context; /* the context to be associated with the connected callback*/ - MQTTAsync_command connect; /* Connect operation properties */ - MQTTAsync_command disconnect; /* Disconnect operation properties */ + /* Each time connect is called, we store the options that were used. These are reused in + any call to reconnect, or an automatic reconnect attempt */ + MQTTAsync_command connect; /* Connect operation properties */ + MQTTAsync_command disconnect; /* Disconnect operation properties */ MQTTAsync_command* pending_write; /* Is there a socket write pending? */ List* responses; @@ -317,6 +316,9 @@ typedef struct MQTTAsync_struct int automaticReconnect; int minRetryInterval; int maxRetryInterval; + int serverURIcount; + char** serverURIs; + int connectTimeout; int currentInterval; START_TIME_TYPE lastConnectionFailedTime; @@ -381,15 +383,19 @@ void MQTTAsync_unlock_mutex(mutex_type amutex) } +/* + Check whether there are any more connect options. If not then we are finished + with connect attempts. +*/ int MQTTAsync_checkConn(MQTTAsync_command* command, MQTTAsyncs* client) { - int rc; + int rc; - FUNC_ENTRY; - rc = command->details.conn.currentURI < command->details.conn.serverURIcount || - (command->details.conn.MQTTVersion == 4 && client->c->MQTTVersion == MQTTVERSION_DEFAULT); - FUNC_EXIT_RC(rc); - return rc; + FUNC_ENTRY; + rc = command->details.conn.currentURI < client->serverURIcount || + (command->details.conn.MQTTVersion == 4 && client->c->MQTTVersion == MQTTVERSION_DEFAULT); + FUNC_EXIT_RC(rc); + return rc; } @@ -860,29 +866,29 @@ int MQTTAsync_reconnect(MQTTAsync handle) if (m->automaticReconnect) { - if (m->shouldBeConnected) - { - m->reconnectNow = 1; - if (m->retrying == 0) - { - m->currentInterval = m->minRetryInterval; - m->retrying = 1; - } - rc = MQTTASYNC_SUCCESS; + if (m->shouldBeConnected) + { + m->reconnectNow = 1; + if (m->retrying == 0) + { + m->currentInterval = m->minRetryInterval; + m->retrying = 1; + } + rc = MQTTASYNC_SUCCESS; } } else { - /* to reconnect, put the connect command to the head of the command queue */ + /* to reconnect, put the connect command to the head of the command queue */ MQTTAsync_queuedCommand* conn = malloc(sizeof(MQTTAsync_queuedCommand)); memset(conn, '\0', sizeof(MQTTAsync_queuedCommand)); conn->client = m; conn->command = m->connect; /* make sure that the version attempts are restarted */ if (m->c->MQTTVersion == MQTTVERSION_DEFAULT) - conn->command.details.conn.MQTTVersion = 0; + conn->command.details.conn.MQTTVersion = 0; MQTTAsync_addCommand(conn, sizeof(m->connect)); - rc = MQTTASYNC_SUCCESS; + rc = MQTTASYNC_SUCCESS; } MQTTAsync_unlock_mutex(mqttasync_mutex); @@ -948,17 +954,14 @@ void MQTTProtocol_checkPendingWrites() } -void MQTTAsync_freeConnect(MQTTAsync_command command) +void MQTTAsync_freeServerURIs(MQTTAsyncs* m) { - if (command.type == CONNECT) - { - int i; + int i; - for (i = 0; i < command.details.conn.serverURIcount; ++i) - free(command.details.conn.serverURIs[i]); - if (command.details.conn.serverURIs) - free(command.details.conn.serverURIs); - } + for (i = 0; i < m->serverURIcount; ++i) + free(m->serverURIs[i]); + if (m->serverURIs) + free(m->serverURIs); } @@ -1113,20 +1116,21 @@ int MQTTAsync_processCommand() { char* serverURI = command->client->serverURI; - if (command->command.details.conn.serverURIcount > 0) + if (command->client->serverURIcount > 0) { if (command->client->c->MQTTVersion == MQTTVERSION_DEFAULT) { - if (command->command.details.conn.MQTTVersion == 3) + if (command->command.details.conn.MQTTVersion == MQTTVERSION_3_1) { command->command.details.conn.currentURI++; - command->command.details.conn.MQTTVersion = 4; - } + command->command.details.conn.MQTTVersion = MQTTVERSION_DEFAULT; + } } else command->command.details.conn.currentURI++; - serverURI = command->command.details.conn.serverURIs[command->command.details.conn.currentURI]; - + + serverURI = command->client->serverURIs[command->command.details.conn.currentURI]; + if (strncmp(URI_TCP, serverURI, strlen(URI_TCP)) == 0) serverURI += strlen(URI_TCP); #if defined(OPENSSL) @@ -1265,6 +1269,7 @@ int MQTTAsync_processCommand() { MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer; MQTTAsync_disconnect(command->client, &opts); /* not "internal" because we don't want to call connection lost */ + command->client->shouldBeConnected = 1; /* as above call is not "internal" we need to reset this */ } else MQTTAsync_disconnect_internal(command->client, 0); @@ -1282,7 +1287,6 @@ int MQTTAsync_processCommand() Log(TRACE_MIN, -1, "Calling command failure for client %s", command->client->c->clientID); (*(command->command.onFailure))(command->command.context, NULL); } - MQTTAsync_freeConnect(command->command); MQTTAsync_freeCommand(command); /* free up the command if necessary */ } } @@ -1319,7 +1323,7 @@ void MQTTAsync_checkTimeouts() MQTTAsyncs* m = (MQTTAsyncs*)(current->content); /* check connect timeout */ - if (m->c->connect_state != 0 && MQTTAsync_elapsed(m->connect.start_time) > (m->connect.details.conn.timeout * 1000)) + if (m->c->connect_state != 0 && MQTTAsync_elapsed(m->connect.start_time) > (m->connectTimeout * 1000)) { if (MQTTAsync_checkConn(&m->connect, m)) { @@ -1337,14 +1341,13 @@ void MQTTAsync_checkTimeouts() else { MQTTAsync_closeSession(m->c); - MQTTAsync_freeConnect(m->connect); if (m->connect.onFailure) { MQTTAsync_failureData data; - data.token = 0; - data.code = MQTTASYNC_FAILURE; - data.message = "TCP connect timeout"; + data.token = 0; + data.code = MQTTASYNC_FAILURE; + data.message = "TCP connect timeout"; Log(TRACE_MIN, -1, "Calling connect failure for client %s", m->c->clientID); (*(m->connect.onFailure))(m->connect.context, &data); } @@ -1388,9 +1391,9 @@ void MQTTAsync_checkTimeouts() memset(conn, '\0', sizeof(MQTTAsync_queuedCommand)); conn->client = m; conn->command = m->connect; - /* make sure that the version attempts are restarted */ - if (m->c->MQTTVersion == MQTTVERSION_DEFAULT) - conn->command.details.conn.MQTTVersion = 0; + /* make sure that the version attempts are restarted */ + if (m->c->MQTTVersion == MQTTVERSION_DEFAULT) + conn->command.details.conn.MQTTVersion = 0; Log(TRACE_MIN, -1, "Automatically attempting to reconnect"); MQTTAsync_addCommand(conn, sizeof(m->connect)); m->reconnectNow = 0; @@ -1563,6 +1566,7 @@ void MQTTAsync_destroy(MQTTAsync* handle) free(m->serverURI); if (m->createOptions) free(m->createOptions); + MQTTAsync_freeServerURIs(m); if (!ListRemove(handles, m)) Log(LOG_ERROR, -1, "free error"); *handle = NULL; @@ -1724,18 +1728,17 @@ thread_return_type WINAPI MQTTAsync_receiveThread(void* n) if (rc == MQTTASYNC_SUCCESS) { - if (m->connect.details.conn.serverURIcount > 0) + if (m->serverURIcount > 0) Log(TRACE_MIN, -1, "Connect succeeded to %s", - m->connect.details.conn.serverURIs[m->connect.details.conn.currentURI]); - MQTTAsync_freeConnect(m->connect); + m->serverURIs[m->connect.details.conn.currentURI]); int onSuccess = (m->connect.onSuccess != NULL); /* save setting of onSuccess callback */ if (m->connect.onSuccess) { MQTTAsync_successData data; memset(&data, '\0', sizeof(data)); Log(TRACE_MIN, -1, "Calling connect success for client %s", m->c->clientID); - if (m->connect.details.conn.serverURIcount > 0) - data.alt.connect.serverURI = m->connect.details.conn.serverURIs[m->connect.details.conn.currentURI]; + if (m->serverURIcount > 0) + data.alt.connect.serverURI = m->serverURIs[m->connect.details.conn.currentURI]; else data.alt.connect.serverURI = m->serverURI; data.alt.connect.MQTTVersion = m->connect.details.conn.MQTTVersion; @@ -1768,7 +1771,6 @@ thread_return_type WINAPI MQTTAsync_receiveThread(void* n) else { MQTTAsync_closeSession(m->c); - MQTTAsync_freeConnect(m->connect); if (m->connect.onFailure) { MQTTAsync_failureData data; @@ -2176,6 +2178,7 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options) m->connect.onSuccess = options->onSuccess; m->connect.onFailure = options->onFailure; m->connect.context = options->context; + m->connectTimeout = options->connectTimeout; tostop = 0; if (sendThread_state != STARTING && sendThread_state != RUNNING) @@ -2263,6 +2266,20 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options) m->c->password = options->password; m->c->retryInterval = options->retryInterval; m->shouldBeConnected = 1; + + m->connectTimeout = options->connectTimeout; + + MQTTAsync_freeServerURIs(m); + if (options->struct_version >= 2 && options->serverURIcount > 0) + { + int i; + + m->serverURIcount = options->serverURIcount; + m->serverURIs = malloc(options->serverURIcount * sizeof(char*)); + for (i = 0; i < options->serverURIcount; ++i) + m->serverURIs[i] = MQTTStrdup(options->serverURIs[i]); + conn->command.details.conn.currentURI = 0; + } /* Add connect request to operation queue */ conn = malloc(sizeof(MQTTAsync_queuedCommand)); @@ -2273,18 +2290,6 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options) conn->command.onSuccess = options->onSuccess; conn->command.onFailure = options->onFailure; conn->command.context = options->context; - conn->command.details.conn.timeout = options->connectTimeout; - - if (options->struct_version >= 2 && options->serverURIcount > 0) - { - int i; - - conn->command.details.conn.serverURIcount = options->serverURIcount; - conn->command.details.conn.serverURIs = malloc(options->serverURIcount * sizeof(char*)); - for (i = 0; i < options->serverURIcount; ++i) - conn->command.details.conn.serverURIs[i] = MQTTStrdup(options->serverURIs[i]); - conn->command.details.conn.currentURI = 0; - } } conn->command.type = CONNECT; rc = MQTTAsync_addCommand(conn, sizeof(conn)); @@ -2764,7 +2769,6 @@ exit: else { MQTTAsync_closeSession(m->c); - MQTTAsync_freeConnect(m->connect); if (m->connect.onFailure) { MQTTAsync_failureData data; @@ -2840,14 +2844,13 @@ MQTTPacket* MQTTAsync_cycle(int* sock, unsigned long timeout, int* rc) else { MQTTAsync_closeSession(m->c); - MQTTAsync_freeConnect(m->connect); if (m->connect.onFailure) { MQTTAsync_failureData data; - data.token = 0; - data.code = MQTTASYNC_FAILURE; - data.message = "TCP connect completion failure"; + data.token = 0; + data.code = MQTTASYNC_FAILURE; + data.message = "TCP connect completion failure"; Log(TRACE_MIN, -1, "Calling connect failure for client %s", m->c->clientID); (*(m->connect.onFailure))(m->connect.context, &data); } diff --git a/test/test9.c b/test/test9.c index c796e092..8e5e0cd5 100644 --- a/test/test9.c +++ b/test/test9.c @@ -95,10 +95,6 @@ void getopts(int argc, char** argv) } -#if 0 -#include /* For general log messages */ -#define MyLog logaLine -#else #define LOGA_DEBUG 0 #define LOGA_INFO 1 #include @@ -129,7 +125,6 @@ void MyLog(int LOGA_level, char* format, ...) printf("%s\n", msg_buf); fflush(stdout); } -#endif void MySleep(long milliseconds) { @@ -227,7 +222,7 @@ void myassert(char* filename, int lineno, char* description, int value, va_list args; ++failures; - printf("Assertion failed, file %s, line %d, description: %s", filename, + MyLog(LOGA_INFO, "Assertion failed, file %s, line %d, description: %s", filename, lineno, description); va_start(args, format); @@ -238,8 +233,7 @@ void myassert(char* filename, int lineno, char* description, int value, description, filename, lineno); } else - MyLog(LOGA_DEBUG, - "Assertion succeeded, file %s, line %d, description: %s", + MyLog(LOGA_DEBUG, "Assertion succeeded, file %s, line %d, description: %s", filename, lineno, description); } @@ -250,7 +244,7 @@ void myassert(char* filename, int lineno, char* description, int value, 1. send some messages while disconnected, check that they are sent 2. repeat test 1 using serverURIs 3. repeat test 1 using auto reconnect - 4. repeast test 2 using auto reconnect + 4. repeat test 2 using auto reconnect 5. check max-buffered 6. check auto-reconnect parms alter behaviour as expected @@ -343,13 +337,13 @@ void test1donSubscribe(void* context, MQTTAsync_successData* response) { MQTTAsync c = (MQTTAsync)context; MyLog(LOGA_DEBUG, "In subscribe onSuccess callback for client d, %p granted qos %d", c, response->alt.qos); - test1dReady = 1; + test1dReady = 1; } void test1dOnConnect(void* context, MQTTAsync_successData* response) { - MQTTAsync c = (MQTTAsync)context; + MQTTAsync c = (MQTTAsync)context; MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; int rc; int qoss[2] = {2, 2}; @@ -369,10 +363,10 @@ int test1c_connected = 0; void test1cConnected(void* context, char* cause) { - MQTTAsync c = (MQTTAsync)context; + MQTTAsync c = (MQTTAsync)context; MyLog(LOGA_DEBUG, "In connected callback for client c, context %p\n", context); - test1c_connected = 1; + test1c_connected = 1; } @@ -402,7 +396,7 @@ int test1(struct Options options) fprintf(xml, "payloadlen, message->payload); + + if (memcmp(message->payload, "will message", message->payloadlen) == 0) + test2_will_message_received = 1; + else + test2_messages_received++; + + MQTTAsync_freeMessage(&message); + MQTTAsync_free(topicName); + + return 1; +} + +int test2Finished = 0; + +int test2OnFailureCalled = 0; + +void test2cOnFailure(void* context, MQTTAsync_failureData* response) +{ + MyLog(LOGA_DEBUG, "In connect onFailure callback, context %p", context); + + test2OnFailureCalled++; + test2Finished = 1; +} + +void test2dOnFailure(void* context, MQTTAsync_failureData* response) +{ + MyLog(LOGA_DEBUG, "In connect onFailure callback, context %p", context); + + test2OnFailureCalled++; + test2Finished = 1; +} + +void test2cOnConnect(void* context, MQTTAsync_successData* response) +{ + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + MyLog(LOGA_DEBUG, "In connect onSuccess callback for client d, context %p\n", context); + MQTTAsync c = (MQTTAsync)context; + int rc; + + /* send a message to the proxy to break the connection */ + pubmsg.payload = "TERMINATE"; + pubmsg.payloadlen = strlen(pubmsg.payload); + pubmsg.qos = 0; + pubmsg.retained = 0; + rc = MQTTAsync_sendMessage(c, "MQTTSAS topic", &pubmsg, NULL); + assert("Good rc from sendMessage", rc == MQTTASYNC_SUCCESS, "rc was %d", rc); +} + + +int test2dReady = 0; +char willTopic[100]; +char test_topic[50]; + +void test2donSubscribe(void* context, MQTTAsync_successData* response) +{ + MQTTAsync c = (MQTTAsync)context; + MyLog(LOGA_DEBUG, "In subscribe onSuccess callback for client d, %p granted qos %d", c, response->alt.qos); + test2dReady = 1; +} + + +void test2dOnConnect(void* context, MQTTAsync_successData* response) +{ + MQTTAsync c = (MQTTAsync)context; + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + int rc; + int qoss[2] = {2, 2}; + char* topics[2] = {willTopic, test_topic}; + + MyLog(LOGA_DEBUG, "In connect onSuccess callback for client c, context %p\n", context); + opts.onSuccess = test2donSubscribe; + opts.context = c; + + rc = MQTTAsync_subscribeMany(c, 2, topics, qoss, &opts); + assert("Good rc from subscribe", rc == MQTTASYNC_SUCCESS, "rc was %d", rc); + if (rc != MQTTASYNC_SUCCESS) + test2Finished = 1; +} + +int test2c_connected = 0; + +void test2cConnected(void* context, char* cause) +{ + MQTTAsync c = (MQTTAsync)context; + + MyLog(LOGA_DEBUG, "In connected callback for client c, context %p\n", context); + test2c_connected = 1; +} + + +int test2(struct Options options) +{ + char* testname = "test2"; + int subsqos = 2; + MQTTAsync c, d; + MQTTAsync_connectOptions opts = MQTTAsync_connectOptions_initializer; + MQTTAsync_willOptions wopts = MQTTAsync_willOptions_initializer; + MQTTAsync_createOptions createOptions = MQTTAsync_createOptions_initializer; + int rc = 0; + int count = 0; + char clientidc[50]; + char clientidd[50]; + int i = 0; + MQTTAsync_token *tokens; + char *URIs[2] = {"rubbish", options.proxy_connection}; + + sprintf(willTopic, "paho-test9-2-%s", unique); + sprintf(clientidc, "paho-test9-2-c-%s", unique); + sprintf(clientidd, "paho-test9-2-d-%s", unique); + sprintf(test_topic, "paho-test9-2-test topic %s", unique); + + test2Finished = 0; + failures = 0; + MyLog(LOGA_INFO, "Starting Offline buffering 2 - messages while disconnected with serverURIs"); + fprintf(xml, "message = "will message"; + opts.will->qos = 1; + opts.will->retained = 0; + opts.will->topicName = willTopic; + opts.onSuccess = test2cOnConnect; + opts.onFailure = test2cOnFailure; + opts.context = c; + opts.cleansession = 0; + opts.serverURIs = URIs; + opts.serverURIcount = 2; + + MyLog(LOGA_DEBUG, "Connecting client c"); + rc = MQTTAsync_connect(c, &opts); + assert("Good rc from connect", rc == MQTTASYNC_SUCCESS, "rc was %d ", rc); + if (rc != MQTTASYNC_SUCCESS) + { + failures++; + goto exit; + } + + /* wait for will message */ + while (!test2_will_message_received && ++count < 10000) + MySleep(100); + + MyLog(LOGA_DEBUG, "Now we can send some messages to be buffered"); + + test2c_connected = 0; + /* send some messages. Then reconnect (check connected callback), and check that those messages are received */ + for (i = 0; i < 3; ++i) + { + char buf[50]; + + MQTTAsync_message pubmsg = MQTTAsync_message_initializer; + MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer; + sprintf(buf, "QoS %d message", i); + pubmsg.payload = buf; + pubmsg.payloadlen = strlen(pubmsg.payload) + 1; + pubmsg.qos = i; + pubmsg.retained = 0; + rc = MQTTAsync_sendMessage(c, test_topic, &pubmsg, &opts); + assert("Good rc from sendMessage", rc == MQTTASYNC_SUCCESS, "rc was %d ", rc); + } + + rc = MQTTAsync_getPendingTokens(c, &tokens); + assert("Good rc from getPendingTokens", rc == MQTTASYNC_SUCCESS, "rc was %d ", rc); + i = 0; + if (tokens) + { + while (tokens[i] != -1) + ++i; + MQTTAsync_free(tokens); + } + assert("Number of getPendingTokens should be 3", i == 3, "i was %d ", i); + + rc = MQTTAsync_reconnect(c); + assert("Good rc from reconnect", rc == MQTTASYNC_SUCCESS, "rc was %d ", rc); + + /* wait for client to be reconnected */ + while (!test2c_connected == 0 && ++count < 10000) + MySleep(100); + + /* wait for success or failure callback */ + while (test2_messages_received < 3 && ++count < 10000) + MySleep(100); + + rc = MQTTAsync_disconnect(c, NULL); + assert("Good rc from disconnect", rc == MQTTASYNC_SUCCESS, "rc was %d ", rc); + + rc = MQTTAsync_disconnect(d, NULL); + assert("Good rc from disconnect", rc == MQTTASYNC_SUCCESS, "rc was %d ", rc); + +exit: + MQTTAsync_destroy(&c); + MQTTAsync_destroy(&d); + MyLog(LOGA_INFO, "%s: test %s. %d tests run, %d failures.", + (failures == 0) ? "passed" : "failed", testname, tests, failures); + write_test_result(); + return failures; +} + void handleTrace(enum MQTTASYNC_TRACE_LEVELS level, char* message) { printf("%s\n", message); @@ -537,7 +801,7 @@ int main(int argc, char** argv) { int* numtests = &tests; int rc = 0; - int (*tests[])() = { NULL, test1, }; + int (*tests[])() = { NULL, test1, test2}; sprintf(unique, "%u", rand()); MyLog(LOGA_INFO, "Random prefix/suffix is %s", unique); @@ -553,7 +817,7 @@ int main(int argc, char** argv) for (options.test_no = 1; options.test_no < ARRAY_SIZE(tests); ++options.test_no) { failures = 0; - MQTTAsync_setTraceLevel(MQTTASYNC_TRACE_PROTOCOL); + MQTTAsync_setTraceLevel(MQTTASYNC_TRACE_ERROR); rc += tests[options.test_no](options); /* return number of failures. 0 = test succeeded */ } }