Permalink
Browse files

Anjay 1.5.1

Features:
- Support HTTP download resumption

Bugfixes:
- Fix some race conditions in integration tests that revealed themselves
  on slow machines
- Fix anjay_download() retrying download forever even in case of terminal
  failures
- Fix Firmware Update resetting by a null-byte Write
- Stop scheduling useless LwM2M Updates to the Bootstrap Server

Improvements:
- Test Firmware Update "not enough storage" scenario over CoAP
- Test Firmware Update "connection lost during download" scenario over CoAP
  • Loading branch information...
sznaider committed Nov 27, 2017
1 parent 14fda85 commit 2c2f9bc1a9022f1d3728711a762e91801a1c2820
View
@@ -26,7 +26,7 @@ if(WITH_FUZZ_TESTS)
endif()
project(anjay C)
set(ANJAY_VERSION "1.5.0" CACHE STRING "Anjay library version")
set(ANJAY_VERSION "1.5.1" CACHE STRING "Anjay library version")
set(ANJAY_BINARY_VERSION 1.0.0)
set(ANJAY_BUILD_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/output")
@@ -469,14 +469,14 @@ static int write_firmware_to_stream(anjay_t *anjay,
return 0;
}
static int expect_no_firmware_content(anjay_input_ctx_t *ctx) {
char ignored_byte;
static int expect_single_nullbyte(anjay_input_ctx_t *ctx) {
char bytes[2];
size_t bytes_read;
bool finished = false;
if (anjay_get_bytes(ctx, &bytes_read, &finished, &ignored_byte, 1)) {
if (anjay_get_bytes(ctx, &bytes_read, &finished, bytes, sizeof(bytes))) {
fw_log(ERROR, "anjay_get_bytes() failed");
return ANJAY_ERR_INTERNAL;
} else if (bytes_read > 0 || !finished) {
} else if (bytes_read != 1 || !finished || bytes[0] != '\0') {
return ANJAY_ERR_BAD_REQUEST;
}
return 0;
@@ -527,7 +527,7 @@ static int fw_write(anjay_t *anjay,
{
int result = 0;
if (fw->state == UPDATE_STATE_DOWNLOADED) {
result = expect_no_firmware_content(ctx);
result = expect_single_nullbyte(ctx);
if (!result) {
reset(anjay, fw);
}
@@ -703,23 +703,24 @@ initialize_fw_repr(anjay_t *anjay,
return 0;
case ANJAY_FW_UPDATE_INITIAL_DOWNLOADING:
repr->user_state.state = UPDATE_STATE_DOWNLOADING;
if (initial_state->resume_offset > 0 && !initial_state->resume_etag) {
fw_log(WARNING, "ETag not set, cannot resume firmware download");
size_t resume_offset = initial_state->resume_offset;
if (resume_offset > 0 && !initial_state->resume_etag) {
fw_log(WARNING, "ETag not set, need to start from the beginning");
user_state_reset(&repr->user_state);
} else if (!initial_state->persisted_uri
resume_offset = 0;
}
if (!initial_state->persisted_uri
|| !(repr->package_uri =
avs_strdup(initial_state->persisted_uri))) {
fw_log(WARNING, "Could not copy the persisted Package URI, "
"not resuming firmware download");
user_state_reset(&repr->user_state);
} else if (schedule_background_anjay_download(
anjay, repr,
initial_state->resume_offset, initial_state->resume_etag)) {
anjay, repr, resume_offset, initial_state->resume_etag)) {
fw_log(WARNING, "Could not resume firmware download");
user_state_reset(&repr->user_state);
if (repr->result == UPDATE_RESULT_CONNECTION_LOST
&& (initial_state->resume_offset > 0
|| initial_state->resume_etag)
&& initial_state->resume_etag
&& schedule_background_anjay_download(anjay, repr,
0, NULL)) {
fw_log(WARNING, "Could not retry firmware download");
@@ -227,7 +227,7 @@ static int send_block_msg(coap_block_transfer_ctx_t *ctx,
coap_log(DEBUG, "timeout reached, next: %" PRId64 ".%09" PRId32 " s",
retry_state.recv_timeout.seconds,
retry_state.recv_timeout.nanoseconds);
} while (retry_state.retry_count < tx_params.max_retransmit);
} while (retry_state.retry_count <= tx_params.max_retransmit);
if (!result) {
ctx->timed_out = false;
@@ -296,7 +296,7 @@ static int send_confirmable_with_retry(coap_client_t *client,
coap_log(DEBUG, "timeout reached, next: %" PRId64 ".09%" PRId32 " s",
retry_state.recv_timeout.seconds,
retry_state.recv_timeout.nanoseconds);
} while (retry_state.retry_count < avs_coap_ctx_get_tx_params(
} while (retry_state.retry_count <= avs_coap_ctx_get_tx_params(
client->common.coap_ctx).max_retransmit);
assert(result <= 0 || result == COAP_CLIENT_RECEIVE_RESET);
View
@@ -61,6 +61,7 @@ typedef struct {
* received.
*/
anjay_sched_handle_t sched_job;
avs_coap_retry_state_t retry_state;
} anjay_coap_download_ctx_t;
static void cleanup_coap_transfer(anjay_downloader_t *dl,
@@ -111,48 +112,19 @@ static int fill_coap_request_info(avs_coap_msg_info_t *req_info,
return 0;
}
static int request_coap_block(anjay_downloader_t *dl,
anjay_coap_download_ctx_t *ctx);
static int request_coap_block_job(anjay_t *anjay,
void *id_) {
uintptr_t id = (uintptr_t)id_;
AVS_LIST(anjay_download_ctx_t) *ctx =
_anjay_downloader_find_ctx_ptr_by_id(&anjay->downloader, id);
if (!ctx) {
dl_log(DEBUG, "download id = %" PRIuPTR " not found (expired?)", id);
return 0;
}
static int request_coap_block_job(anjay_t *anjay, void *id);
request_coap_block(&anjay->downloader, (anjay_coap_download_ctx_t *) *ctx);
// return non-zero to ensure job retries
return -1;
}
static int schedule_coap_retransmission(anjay_downloader_t *dl,
anjay_coap_download_ctx_t *ctx) {
static int
schedule_coap_retransmission(anjay_downloader_t *dl,
anjay_coap_download_ctx_t *ctx) {
anjay_t *anjay = _anjay_downloader_get_anjay(dl);
const avs_coap_tx_params_t *tx_params = &anjay->udp_tx_params;
avs_coap_retry_state_t retry_state = { 0, { 0, 0 } };
// first retry
avs_coap_update_retry_state(&retry_state, tx_params, &dl->rand_seed);
avs_time_duration_t delay = retry_state.recv_timeout;
// second retry
avs_coap_update_retry_state(&retry_state, tx_params, &dl->rand_seed);
anjay_sched_retryable_backoff_t backoff = {
.delay = retry_state.recv_timeout,
.max_delay = avs_coap_max_transmit_span(tx_params)
};
avs_coap_update_retry_state(&ctx->retry_state, &anjay->udp_tx_params,
&dl->rand_seed);
_anjay_sched_del(anjay->sched, &ctx->sched_job);
return _anjay_sched_retryable(anjay->sched, &ctx->sched_job, delay, backoff,
request_coap_block_job,
(void*)ctx->common.id);
return _anjay_sched(anjay->sched, &ctx->sched_job,
ctx->retry_state.recv_timeout,
request_coap_block_job, (void *) ctx->common.id);
}
static int request_coap_block(anjay_downloader_t *dl,
@@ -191,6 +163,36 @@ static int request_coap_block(anjay_downloader_t *dl,
return result;
}
static int request_coap_block_job(anjay_t *anjay, void *id_) {
uintptr_t id = (uintptr_t)id_;
AVS_LIST(anjay_download_ctx_t) *ctx_ptr =
_anjay_downloader_find_ctx_ptr_by_id(&anjay->downloader, id);
if (!ctx_ptr) {
dl_log(DEBUG, "download id = %" PRIuPTR " not found (expired?)", id);
return 0;
}
anjay_coap_download_ctx_t *ctx = (anjay_coap_download_ctx_t *) *ctx_ptr;
if (ctx->retry_state.retry_count > anjay->udp_tx_params.max_retransmit) {
dl_log(ERROR, "Limit of retransmissions reached, aborting download "
"id = %" PRIuPTR, id);
_anjay_downloader_abort_transfer(&anjay->downloader, ctx_ptr,
ANJAY_DOWNLOAD_ERR_FAILED, ETIMEDOUT);
} else {
request_coap_block(&anjay->downloader, ctx);
if (schedule_coap_retransmission(&anjay->downloader, ctx)) {
dl_log(WARNING, "could not schedule retransmission for download "
"id = %" PRIuPTR, ctx->common.id);
_anjay_downloader_abort_transfer(&anjay->downloader, ctx_ptr,
ANJAY_DOWNLOAD_ERR_FAILED, ENOMEM);
return -1;
}
}
return 0;
}
static int map_coap_ctx_err_to_errno(int err) {
switch (err) {
case AVS_COAP_CTX_ERR_TIMEOUT:
@@ -206,6 +208,7 @@ static int request_next_coap_block(anjay_downloader_t *dl,
AVS_LIST(anjay_download_ctx_t) *ctx_ptr) {
anjay_coap_download_ctx_t *ctx = (anjay_coap_download_ctx_t *) *ctx_ptr;
ctx->last_req_id = _anjay_coap_id_source_get(dl->id_source);
memset(&ctx->retry_state, 0, sizeof(ctx->retry_state));
int result;
if ((result = request_coap_block(dl, ctx))
@@ -356,9 +359,7 @@ static void handle_coap_response(const avs_coap_msg_t *msg,
return;
}
if (ctx->bytes_downloaded == 0) {
assert(ctx->etag.size == 0 && "overwriting ETag!? we're supposed "
"to be handling the first packet!");
if (ctx->etag.size == 0) {
ctx->etag = etag;
} else if (!etag_matches(&etag, &ctx->etag)) {
dl_log(DEBUG, "remote resource expired, aborting download");
@@ -125,15 +125,18 @@ int _anjay_downloader_get_sockets(
AVS_LIST(anjay_download_ctx_t) dl_ctx;
AVS_LIST_FOREACH(dl_ctx, dl->downloads) {
AVS_LIST(avs_net_abstract_socket_t *) elem =
AVS_LIST_NEW_ELEMENT(avs_net_abstract_socket_t *);
if (!elem) {
AVS_LIST_CLEAR(&sockets);
return -1;
avs_net_abstract_socket_t *socket = get_ctx_socket(dl, dl_ctx);
if (socket) {
AVS_LIST(avs_net_abstract_socket_t *) elem =
AVS_LIST_NEW_ELEMENT(avs_net_abstract_socket_t *);
if (!elem) {
AVS_LIST_CLEAR(&sockets);
return -1;
}
*elem = socket;
AVS_LIST_INSERT(&sockets, elem);
}
*elem = get_ctx_socket(dl, dl_ctx);
AVS_LIST_INSERT(&sockets, elem);
}
AVS_LIST_INSERT(out_socks, sockets);
@@ -160,7 +163,7 @@ int _anjay_downloader_handle_packet(anjay_downloader_t *dl,
AVS_LIST(anjay_download_ctx_t) *ctx =
find_ctx_ptr_by_socket(dl, socket);
if (!ctx) {
dl_log(DEBUG, "unknown socket");
// unknown socket
return -1;
}
Oops, something went wrong.

0 comments on commit 2c2f9bc

Please sign in to comment.