diff options
Diffstat (limited to 'client/libacron/net.c')
-rw-r--r-- | client/libacron/net.c | 66 |
1 files changed, 52 insertions, 14 deletions
diff --git a/client/libacron/net.c b/client/libacron/net.c index 55b10ce..cbbe113 100644 --- a/client/libacron/net.c +++ b/client/libacron/net.c @@ -70,7 +70,14 @@ static bool on_message_handler(struct wic_inst *inst, * To prevent the new message from being overwritten (and thus causing * memory leaks), we return false here, making wic backlog the new message * until the next wic_parse. */ + /* wic_parse will return the bytes it read. This includes the bytes of the + * frame that caused us to return false here. Thus, relying on its return value + * to determine if we are blocked here is unreliable. + * We are using our own field to cause ac_receive to know that we are blocked. + * However, we still cannot know where the pos exactly is before this blocked call. + */ LOGD("Two or more frames arrived before wic_parse returns. Keeping the message."); + res->blocked = true; return false; } int r; @@ -202,34 +209,65 @@ int ac_disconnect(void *connection, int ac_receive(void *connection, const void *buffer, + size_t pos, const size_t len, - ac_obj_t **response) { + ac_obj_t **response, + size_t *len_read) { AC_CHECK_INIT; + LOGDV("ac_receive(buffer = %p, pos = %u, len = %u)", + buffer, + pos, + len); struct ac_connection *conn = connection; struct wic_inst *inst = &conn->inst; const uint8_t *ptr = buffer; - size_t retval, pos; + size_t retval; + struct ac_result *res = &conn->result; + bool blocked = false; /* In case the deserializer does not run at all. */ - memset(&conn->result, 0, sizeof(struct ac_result)); - if (!len) { - retval = wic_parse(inst, NULL, 0); - if (wic_get_state(inst) == WIC_STATE_CLOSED) { - LOGE("Connection closed."); - return AC_E_NET; - } - } else { - for (pos = 0U; pos < len; pos += retval) { + memset(res, 0, sizeof(struct ac_result)); + if (len) { + for (; pos < len; pos += retval) { retval = wic_parse(inst, &ptr[pos], len - pos); if (wic_get_state(inst) == WIC_STATE_CLOSED) { LOGE("Connection closed."); return AC_E_NET; } + if (!retval) { + /* Blocked. Ask the client to call again. */ + LOGD("Blocked."); + blocked = true; + break; + } } + *len_read = pos; + } else { + retval = wic_parse(inst, &ptr[pos], len - pos); + if (wic_get_state(inst) == WIC_STATE_CLOSED) { + LOGE("Connection closed."); + return AC_E_NET; + } + /* retval is always 0. We should rely on res->blocked to see if + * there are more than one messages blocked (unlikely). */ } + if (!blocked) { + /* In case we reached pos >= len but there is still a frame + * blocked. Calling wic_parse(NULL, 0) won't help because + * it will return 0 even if nothing is blocking. + * Thus we use the custom 'blocked' field in res to check + * if there is still a blocked message. Then, call wic_parse + * again with NULL and 0 to parse it. + * The assumption is that there is only one possible blocked + * message. + */ + if (res->blocked) blocked = true; + } + LOGDV("Done parsing with %u / %u bytes read.", + pos, + len); - struct ac_result *res = &conn->result; LOGDV("res { has_result = %d, res = %d, obj = %p }", res->has_result, res->res, @@ -240,13 +278,13 @@ int ac_receive(void *connection, return res->res; } else { *response = res->obj; - return AC_E_OK; + return blocked ? AC_E_AGAIN : AC_E_OK; } } else { *response = NULL; } - return AC_E_OK; + return blocked ? AC_E_AGAIN : AC_E_OK; } int ac_request(void *connection, const ac_request_t *request) { |