aboutsummaryrefslogtreecommitdiff
path: root/client/libacron/net.c
diff options
context:
space:
mode:
Diffstat (limited to 'client/libacron/net.c')
-rw-r--r--client/libacron/net.c66
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) {