ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_fetch.c
Go to the documentation of this file.
1
9#include "mdl_fetch.h"
10
11#include <limits.h>
12#include <stdint.h>
13#include <stdio.h>
14#include <string.h>
15#include <time.h>
16
18#include "mdl_fetch_internal.h"
19#include "mdl_net.h"
20#include "mdl_pathfs.h"
21#include "mdl_storage.h"
22#include "mdl_stream_internal.h"
23#include "mdl_url_guard.h"
24#include "mdl_urlname.h"
25#include "ra8_attributes.h"
26#include "ra8_err.h"
27
29typedef enum : uint16_t {
32
34typedef enum : uint8_t {
37
39typedef enum : uint8_t {
44
59
78 const char* first,
79 const char* second,
80 const char* third)
81{
83 error = priv_mdl_stream_text(error, ctx->diagnostic, second);
84 error = priv_mdl_stream_text(error, ctx->diagnostic, third);
85 if (error != k_ra8_ok) {
86 ctx->progress_error = error;
87 }
88 return error;
89}
90
105RA8_INTERNAL static uint32_t internal_mdl_fetch_max_u32(uint32_t a, uint32_t b)
106{
107 return (a > b) ? a : b;
108}
109
111{
112 return (rc == k_ra8_err_busy) || (rc == k_ra8_err_timeout) || (rc == k_ra8_fail);
113}
114
116{
117 if (stats == nullptr) {
118 return false;
119 }
120 return (stats->chapters_failed > 0U) || (stats->pages_failed > 0U);
121}
122
125RA8_INTERNAL static const char* internal_mdl_fetch_fail_reason(long status)
126{
127 return (status >= (long)k_http_server_err) ? "server error" : "transport error";
128}
129
130RA8_PRIV void priv_mdl_fetch_reason(ra8_err_t err, long status, char* buf, size_t cap)
131{
132 if (buf == nullptr) {
133 return;
134 }
135 if (cap == 0U) {
136 return;
137 }
138 const char* base = nullptr;
139 switch (err) {
140 case k_ra8_ok:
141 base = "ok";
142 break;
144 base = "request timed out";
145 break;
146 case k_ra8_err_busy:
147 base = "rate limited";
148 break;
150 base = "not found";
151 break;
152 case k_ra8_err_no_mem:
153 base = "response exceeded size cap";
154 break;
156 base = "still failing after retries";
157 break;
158 case k_ra8_fail:
159 base = internal_mdl_fetch_fail_reason(status);
160 break;
161 default:
162 base = "error";
163 break;
164 }
165 if (status > 0) {
166 (void)snprintf(buf, cap, "%s (HTTP %ld)", base, status);
167 } else {
168 (void)snprintf(buf, cap, "%s", base);
169 }
170}
171
174RA8_PRIV void
175priv_mdl_fetch_record_fail(const mdl_fetch_ctx_t* ctx, const char* url, long status, ra8_err_t err)
176{
177 if (ctx->faillog == nullptr) {
178 return;
179 }
180 mdl_fetch_faillog_t* log = ctx->faillog;
181 log->total += 1U;
182 if (log->count >= (size_t)k_mdl_fetch_fail_max) {
183 return;
184 }
185 mdl_fetch_fail_t* item = &log->items[log->count];
186 (void)snprintf(item->url, sizeof(item->url), "%s", (url != nullptr) ? url : "");
187 item->status = status;
188 item->err = err;
189 log->count += 1U;
190}
191
194{
195 if (ctx->state_path == nullptr) {
196 return k_ra8_ok;
197 }
198 bool published = false;
199 const ra8_err_t rc = mdl_state_save(ctx->storage, ctx->state_path, ctx->state, &published);
200 if (rc != k_ra8_ok) {
201 priv_mdl_fetch_record_fail(ctx, ctx->state_path, 0L, rc);
202 }
203 return rc;
204}
205
207RA8_INTERNAL static const char* internal_mdl_fetch_page_host(const char* url, char* buf, size_t cap)
208{
209 return mdl_url_host(url, buf, cap) ? buf : nullptr;
210}
211
236 const char* host,
237 const char* url,
238 const mdl_net_req_t* req,
240 uint32_t jmin,
241 uint32_t jmax,
242 mdl_net_resp_t* out_resp,
243 size_t* out_bytes)
244{
245 if (mdl_governor_acquire(ctx->gov, host, jmin, jmax) == k_ra8_err_would_block) {
247 }
248 size_t got = 0U;
249 mdl_net_resp_t resp = {};
250 const ra8_err_t rc = mdl_net_get_body(ctx->session->net, url, req, sink, &got, &resp);
251 mdl_governor_observe(ctx->gov, host, resp.status, resp.retry_after);
252 mdl_governor_release(ctx->gov, host);
253 if (out_resp != nullptr) {
254 *out_resp = resp;
255 }
256 if (out_bytes != nullptr) {
257 *out_bytes = got;
258 }
259 return rc;
260}
261
265 const char* host,
266 const char* url,
267 const mdl_net_req_t* req,
269 uint32_t jmin,
270 uint32_t jmax,
271 mdl_net_resp_t* out_resp,
272 size_t* out_bytes)
273{
275 for (uint8_t attempt = 0U; attempt < (uint8_t)k_fetch_max_attempts; ++attempt) {
277 host,
278 url,
279 req,
280 sink,
281 jmin,
282 jmax,
283 out_resp,
284 out_bytes);
286 break; /* success, or a permanent failure not worth retrying */
287 }
288 }
289 return rc;
290}
291
318 const char* host,
319 const char* url,
320 const mdl_net_req_t* req,
321 uint32_t jmin,
322 uint32_t jmax,
323 const char* target_abs,
324 mdl_net_resp_t* out_resp,
325 size_t* out_bytes)
326{
327 size_t got = 0U;
328 mdl_net_resp_t resp = {};
329 mdl_fetch_body_t body = {};
330 ra8_err_t rc = priv_mdl_fetch_body_init_exact(&body, ctx->storage, target_abs);
332 if (rc == k_ra8_ok) {
333 rc = priv_mdl_fetch_with_retry(ctx, host, url, req, &sink, jmin, jmax, &resp, &got);
334 }
335 if (out_resp != nullptr) {
336 *out_resp = resp;
337 }
338 if (rc == k_ra8_ok) {
339 rc = priv_mdl_fetch_body_prepare(&body);
340 }
341 if (rc == k_ra8_ok) {
342 rc = priv_mdl_fetch_body_commit(&body);
343 }
344 if (rc != k_ra8_ok) {
345 const ra8_err_t aborted = priv_mdl_fetch_body_abort(&body);
346 const ra8_err_t result = (aborted == k_ra8_ok) ? rc : aborted;
347 priv_mdl_fetch_record_fail(ctx, url, resp.status, result);
348 return result;
349 }
350 if (out_bytes != nullptr) {
351 *out_bytes = got;
352 }
353 return k_ra8_ok;
354}
355
357 const char* url,
358 const char* target_abs,
359 const char* referer,
360 mdl_net_resp_t* out_resp,
361 size_t* out_bytes)
362{
363 if (out_resp != nullptr) {
364 *out_resp = (mdl_net_resp_t){};
365 }
366 if (out_bytes != nullptr) {
367 *out_bytes = 0U;
368 }
369 if ((ctx == nullptr) || (ctx->session == nullptr) || (ctx->session->net == nullptr) ||
370 (ctx->site == nullptr) || (url == nullptr) || (url[0] == '\0') || (target_abs == nullptr) ||
371 (target_abs[0] != '/')) {
373 }
374
375 uint32_t crawl = 0U;
376 if (!mdl_session_url_allowed(ctx->session, url, &crawl)) {
378 return k_ra8_fail;
379 }
380 char hostbuf[k_mdl_gov_host_max];
381 const char* host = internal_mdl_fetch_page_host(url, hostbuf, sizeof(hostbuf));
382 if (host == nullptr) {
384 }
385
386 const uint32_t jmin = internal_mdl_fetch_max_u32(ctx->site->img_delay_min, crawl);
387 const uint32_t jmax = internal_mdl_fetch_max_u32(ctx->site->img_delay_max, crawl);
388 const mdl_net_req_t req = {.user_agent = ctx->session->user_agent,
389 .referer = referer,
390 .if_none_match = nullptr,
391 .if_modified_since = nullptr,
392 .timeout_ms = ctx->timeout_ms};
394 host,
395 url,
396 &req,
397 jmin,
398 jmax,
399 target_abs,
400 out_resp,
401 out_bytes);
402}
403
422 const char* url,
423 const mdl_net_req_t* request,
424 char* buffer,
425 size_t capacity,
426 size_t* out_length,
427 mdl_net_resp_t* response)
428{
430 ra8_err_t error = k_ra8_fail;
431 for (uint8_t attempt = 0U; attempt < (uint8_t)k_fetch_max_attempts; ++attempt) {
432 error = mdl_governor_acquire(fetch->ctx->gov, fetch->host, fetch->jmin, fetch->jmax);
433 if (error == k_ra8_ok) {
434 error = mdl_net_get_buf(fetch->ctx->session->net,
435 url,
436 request,
437 buffer,
438 capacity,
439 out_length,
440 response);
441 mdl_governor_observe(fetch->ctx->gov, fetch->host, response->status, response->retry_after);
442 mdl_governor_release(fetch->ctx->gov, fetch->host);
443 }
444 if (!priv_mdl_fetch_is_retryable(error)) {
445 break;
446 }
447 }
448 return error;
449}
450
470internal_mdl_fetch_chapter_html(mdl_fetch_ctx_t* ctx, const char* chapter_url, size_t* out_len)
471{
472 uint32_t crawl = 0U;
473 if (!mdl_session_url_allowed(ctx->session, chapter_url, &crawl)) {
474 priv_mdl_fetch_record_fail(ctx, chapter_url, 0, k_ra8_fail);
475 return k_ra8_fail;
476 }
478 .ctx = ctx,
481 if (!mdl_url_host(chapter_url, fetch.host, sizeof(fetch.host))) {
483 }
484 const mdl_net_req_t req = {.user_agent = ctx->session->user_agent,
485 .referer = ctx->series_url,
486 .timeout_ms = ctx->timeout_ms};
487 size_t len = 0U;
488 mdl_net_resp_t response = {};
489 mdl_cache_result_t cache_result;
490 const ra8_err_t rc = (ctx->cache != nullptr) ? mdl_cache_get_buf(ctx->cache,
491 chapter_url,
492 &req,
494 &fetch,
495 ctx->page_buf,
496 ctx->page_cap,
497 &len,
498 &response,
499 &cache_result)
501 chapter_url,
502 &req,
503 ctx->page_buf,
504 ctx->page_cap,
505 &len,
506 &response);
507 if (rc != k_ra8_ok) {
508 priv_mdl_fetch_record_fail(ctx, chapter_url, response.status, rc);
509 return k_ra8_fail;
510 }
511 const ra8_err_t erc = mdl_extract_images(ctx->page_buf,
512 len,
513 chapter_url,
514 ctx->site->page_img_attr,
516 ctx->images);
517 if (erc != k_ra8_ok) {
518 priv_mdl_fetch_record_fail(ctx, chapter_url, response.status, erc);
519 return erc;
520 }
521 if (ctx->images->count == 0U) {
522 priv_mdl_fetch_record_fail(ctx, chapter_url, response.status, k_ra8_err_no_data);
523 return k_ra8_err_no_data;
524 }
525 *out_len = len;
526 return k_ra8_ok;
527}
528
556 mdl_fetch_layout_t layout,
557 const char* id,
558 const char* combined_abs,
559 const char* combined_rel,
560 size_t global_no,
561 char* chap_abs,
562 size_t chap_cap,
563 const char** dest_abs,
564 const char** dest_rel,
565 size_t* base)
566{
567 if (layout == k_mdl_layout_combined) {
568 *dest_abs = combined_abs;
569 *dest_rel = combined_rel;
570 *base = global_no;
571 return true;
572 }
573 if (!mdl_join_dir_under(ctx->storage, ctx->series_abs_dir, id, chap_abs, chap_cap)) {
574 return false;
575 }
576 *dest_abs = chap_abs;
577 *dest_rel = id;
578 *base = 0U;
579 return true;
580}
581
597 mdl_fetch_layout_t layout,
599 size_t* global_no)
600{
601 rec->pages_done = (uint16_t)ctx->images->count;
602 rec->complete = true;
603 rec->fetched_at = (int64_t)time(nullptr);
604 if (layout == k_mdl_layout_combined) {
605 *global_no += ctx->images->count;
606 }
607}
608
626 const char* chapter_url,
627 mdl_chapter_work_t* work)
628{
629 work->title = work->rec->title;
630 if (ctx->site->chapter_title_selector[0] == '\0') {
631 if (work->rec->title[0] == '\0') {
632 work->title = work->id;
633 }
634 return true;
635 }
637 work->html_len,
639 work->extracted_title,
640 sizeof(work->extracted_title));
641 if (rc == k_ra8_ok) {
642 work->title = work->extracted_title;
643 return true;
644 }
645 if (rc == k_ra8_err_invalid_size) {
646 (void)internal_mdl_fetch_diag3(ctx,
647 "mdl: chapter title for '",
648 chapter_url,
649 "' exceeds the persistent field; refusing truncation\n");
650 priv_mdl_fetch_record_fail(ctx, chapter_url, 0, rc);
651 return false;
652 }
653 const bool preserving = work->rec->title[0] != '\0';
654 ra8_err_t output_error = internal_mdl_fetch_diag3(ctx,
655 "mdl: chapter title selector '",
657 "' failed for '");
658 output_error = priv_mdl_stream_text(output_error, ctx->diagnostic, chapter_url);
659 output_error = priv_mdl_stream_text(output_error, ctx->diagnostic, "'; ");
660 output_error =
661 priv_mdl_stream_text(output_error,
662 ctx->diagnostic,
663 preserving ? "preserving recorded title\n" : "using chapter id\n");
664 if (output_error != k_ra8_ok) {
665 ctx->progress_error = output_error;
666 return false;
667 }
668 if (!preserving) {
669 work->title = work->id;
670 }
671 return true;
672}
673
691 const char* chapter_url,
692 mdl_chapter_work_t* work)
693{
694 if (ctx->site->chapter_number_selector[0] == '\0') {
695 return true;
696 }
697 char extracted[k_mdl_title_max];
699 work->html_len,
701 extracted,
702 sizeof(extracted));
703 if (rc == k_ra8_ok) {
704 if (mdl_urlname_chapter_text_parse(extracted, &work->number)) {
705 work->number_known = true;
706 return true;
707 }
708 ra8_err_t output_error = internal_mdl_fetch_diag3(ctx,
709 "mdl: chapter number selector '",
711 "' returned invalid value '");
712 output_error = priv_mdl_stream_text(output_error, ctx->diagnostic, extracted);
713 output_error = priv_mdl_stream_text(output_error, ctx->diagnostic, "' for '");
714 output_error = priv_mdl_stream_text(output_error, ctx->diagnostic, chapter_url);
715 output_error = priv_mdl_stream_text(output_error, ctx->diagnostic, "'\n");
716 if (output_error != k_ra8_ok) {
717 ctx->progress_error = output_error;
718 }
720 return false;
721 }
722 if (rc == k_ra8_err_invalid_size) {
723 (void)internal_mdl_fetch_diag3(ctx,
724 "mdl: chapter number for '",
725 chapter_url,
726 "' exceeds the bounded selector field\n");
727 priv_mdl_fetch_record_fail(ctx, chapter_url, 0, rc);
728 return false;
729 }
730 ra8_err_t output_error = internal_mdl_fetch_diag3(ctx,
731 "mdl: chapter number selector '",
733 "' failed for '");
734 output_error = priv_mdl_stream_text(output_error, ctx->diagnostic, chapter_url);
735 output_error = priv_mdl_stream_text(output_error, ctx->diagnostic, "'; preserving ");
736 output_error =
737 priv_mdl_stream_text(output_error,
738 ctx->diagnostic,
739 work->number_known ? "recorded number\n" : "unknown number\n");
740 if (output_error != k_ra8_ok) {
741 ctx->progress_error = output_error;
742 return false;
743 }
744 return true;
745}
746
771 const char* chapter_url,
772 mdl_fetch_layout_t layout,
773 const char* combined_abs,
774 const char* combined_rel,
775 size_t* global_no,
776 mdl_fetch_stats_t* stats,
777 size_t chapter_index,
778 size_t chapter_total,
779 mdl_chapter_work_t* work)
780{
781 if (!mdl_state_set_chapter_metadata(work->rec, work->title, work->number, work->number_known)) {
782 return k_ch_failed;
783 }
784 work->rec->page_count = (uint16_t)ctx->images->count;
785 if (work->rec->pages_done > work->rec->page_count) {
786 work->rec->pages_done = 0U;
787 }
788 char chap_abs[PATH_MAX];
789 const char* dest_abs = nullptr;
790 const char* dest_rel = nullptr;
791 size_t base = 0U;
793 layout,
794 work->id,
795 combined_abs,
796 combined_rel,
797 *global_no,
798 chap_abs,
799 sizeof(chap_abs),
800 &dest_abs,
801 &dest_rel,
802 &base)) {
803 return k_ch_failed;
804 }
805 const mdl_run_pos_t pos = {.chapter_index = chapter_index,
806 .chapter_total = chapter_total,
807 .chapter_id = work->id};
809 chapter_url,
810 dest_abs,
811 dest_rel,
812 base,
813 work->rec,
814 stats,
815 &pos) != k_ra8_ok) {
816 (void)priv_mdl_fetch_checkpoint(ctx);
817 return k_ch_failed;
818 }
819 internal_mdl_fetch_mark_complete(ctx, layout, work->rec, global_no);
821}
822
845 const char* chapter_url,
846 mdl_fetch_layout_t layout,
847 const char* combined_abs,
848 const char* combined_rel,
849 size_t* global_no,
850 mdl_fetch_stats_t* stats,
851 size_t chapter_index,
852 size_t chapter_total)
853{
854 mdl_chapter_work_t work = {};
855 mdl_urlname_last_segment(chapter_url, work.id, sizeof(work.id));
856 work.number_known = mdl_urlname_chapter_parse(chapter_url, &work.number);
858 work.id,
859 chapter_url,
860 work.number,
861 work.number_known);
862 if (work.rec == nullptr) {
863 return k_ch_failed;
864 }
865 work.number = work.rec->number;
866 work.number_known = work.rec->number_known;
867 if (ctx->update_only && work.rec->complete) {
868 *global_no += work.rec->page_count;
869 return k_ch_skipped;
870 }
871 work.rec->complete = false;
872 work.rec->fetched_at = 0;
873 if (internal_mdl_fetch_chapter_html(ctx, chapter_url, &work.html_len) != k_ra8_ok) {
874 return k_ch_failed;
875 }
876 if (!internal_mdl_fetch_select_chapter_title(ctx, chapter_url, &work) ||
877 !internal_mdl_fetch_select_chapter_number(ctx, chapter_url, &work)) {
878 return k_ch_failed;
879 }
881 chapter_url,
882 layout,
883 combined_abs,
884 combined_rel,
885 global_no,
886 stats,
887 chapter_index,
888 chapter_total,
889 &work);
890}
891
909{
910 if (s == k_ch_completed) {
911 stats->chapters_completed += 1U;
912 } else if (s == k_ch_skipped) {
913 stats->chapters_skipped += 1U;
914 } else {
915 stats->chapters_failed += 1U;
916 }
917 return s == k_ch_failed;
918}
919
938{
939 if (ctx == nullptr) {
940 return false;
941 }
942 return (ctx->session != nullptr) && (ctx->storage != nullptr) && (ctx->state != nullptr) &&
943 (ctx->series_abs_dir != nullptr) && (ctx->series_url != nullptr) &&
944 (ctx->site != nullptr) && (ctx->page_buf != nullptr) && (ctx->images != nullptr);
945}
946
948 const mdl_url_list_t* chapters,
949 mdl_fetch_layout_t layout,
950 const char* combined_dir_rel,
951 mdl_fetch_stats_t* stats)
952{
953 if ((ctx == nullptr) || (chapters == nullptr) || (stats == nullptr)) {
955 }
958 }
959 memset(stats, 0, sizeof(*stats));
961
962 char combined_abs[PATH_MAX];
963 if (layout == k_mdl_layout_combined) {
964 if ((combined_dir_rel == nullptr) || !mdl_join_dir_under(ctx->storage,
965 ctx->series_abs_dir,
966 combined_dir_rel,
967 combined_abs,
968 sizeof(combined_abs))) {
970 }
971 } else {
972 combined_abs[0] = '\0';
973 }
974
975 size_t global_no = 0U;
976 bool any_fail = false;
977 for (size_t i = 0U; i < chapters->count; ++i) {
979 chapters->urls[i],
980 layout,
981 combined_abs,
982 combined_dir_rel,
983 &global_no,
984 stats,
985 i + 1U,
986 chapters->count);
987 if (internal_mdl_fetch_tally(s, stats)) {
988 any_fail = true;
989 if ((ctx->progress_error != k_ra8_ok) || (layout == k_mdl_layout_combined)) {
990 break; /* the combined archive cannot be completed past a gap */
991 }
992 }
993 }
994 if (ctx->progress_error != k_ra8_ok) {
995 return ctx->progress_error;
996 }
997 return any_fail ? k_ra8_fail : k_ra8_ok;
998}
ra8_err_t mdl_cache_get_buf(mdl_cache_t *cache, const char *url, const mdl_net_req_t *base_request, mdl_cache_fetch_fn fetch, void *fetch_context, char *buffer, size_t capacity, size_t *out_length, mdl_net_resp_t *response, mdl_cache_result_t *result)
Fetch one document through the per-host persistent cache.
Definition mdl_cache.c:537
ra8_err_t mdl_extract_images(const char *html, size_t html_len, const char *base_url, const char *prefer_attr, const char *url_contains, mdl_url_list_t *out)
Scan html for <img> image URLs, resolved to absolute form.
ra8_err_t mdl_extract_selector(const char *html, size_t html_len, const char *selector, char *out, size_t out_cap)
Extract one bounded metadata value using a site-descriptor selector.
static bool internal_mdl_fetch_select_chapter_number(mdl_fetch_ctx_t *ctx, const char *chapter_url, mdl_chapter_work_t *work)
Select and strictly parse optional descriptor-driven chapter numbering.
Definition mdl_fetch.c:690
static mdl_chap_status_t internal_mdl_fetch_process_chapter(mdl_fetch_ctx_t *ctx, const char *chapter_url, mdl_fetch_layout_t layout, const char *combined_abs, const char *combined_rel, size_t *global_no, mdl_fetch_stats_t *stats, size_t chapter_index, size_t chapter_total)
Download one chapter (or skip it), returning its outcome.
Definition mdl_fetch.c:844
ra8_err_t priv_mdl_fetch_checkpoint(const mdl_fetch_ctx_t *ctx)
Persist state atomically when a checkpoint target is configured.
Definition mdl_fetch.c:193
static bool internal_mdl_fetch_tally(mdl_chap_status_t s, mdl_fetch_stats_t *stats)
Fold one chapter outcome into run statistics.
Definition mdl_fetch.c:908
static ra8_err_t internal_mdl_fetch_diag3(mdl_fetch_ctx_t *ctx, const char *first, const char *second, const char *third)
Emit three fetch diagnostics and latch a sink failure.
Definition mdl_fetch.c:77
mdl_fetch_retry_t
Bounded attempts per request: the initial try plus backoff retries.
Definition mdl_fetch.c:34
@ k_fetch_max_attempts
One initial request plus at most three retries.
Definition mdl_fetch.c:35
static bool internal_mdl_fetch_ctx_ready(const mdl_fetch_ctx_t *ctx)
Validate that every required fetch dependency is present.
Definition mdl_fetch.c:937
static const char * internal_mdl_fetch_fail_reason(long status)
Prose for a k_ra8_fail: a 5xx server error versus a transport error.
Definition mdl_fetch.c:125
bool priv_mdl_fetch_run_incomplete(const mdl_fetch_stats_t *stats)
Whether a finished run left anything unfetched (chapter or page).
Definition mdl_fetch.c:115
bool priv_mdl_fetch_is_retryable(ra8_err_t rc)
Whether a failed transfer result is worth retrying.
Definition mdl_fetch.c:110
static bool internal_mdl_fetch_resolve_dest(mdl_fetch_ctx_t *ctx, mdl_fetch_layout_t layout, const char *id, const char *combined_abs, const char *combined_rel, size_t global_no, char *chap_abs, size_t chap_cap, const char **dest_abs, const char **dest_rel, size_t *base)
Resolve the output directory and starting page number for one chapter.
Definition mdl_fetch.c:555
ra8_err_t mdl_fetch_run(mdl_fetch_ctx_t *ctx, const mdl_url_list_t *chapters, mdl_fetch_layout_t layout, const char *combined_dir_rel, mdl_fetch_stats_t *stats)
Download a series' chapters incrementally, resuming and deduping.
Definition mdl_fetch.c:947
ra8_err_t mdl_fetch_asset(mdl_fetch_ctx_t *ctx, const char *url, const char *target_abs, const char *referer, mdl_net_resp_t *out_resp, size_t *out_bytes)
Fetch one policy-governed asset to an absolute file path.
Definition mdl_fetch.c:356
static uint32_t internal_mdl_fetch_max_u32(uint32_t a, uint32_t b)
Perform the max u32 step.
Definition mdl_fetch.c:105
mdl_chap_status_t
Per-chapter outcome reported to the run loop.
Definition mdl_fetch.c:39
@ k_ch_skipped
Already complete (–update skip).
Definition mdl_fetch.c:41
@ k_ch_failed
Left partial by a page failure.
Definition mdl_fetch.c:42
@ k_ch_completed
Every page present and verified.
Definition mdl_fetch.c:40
ra8_err_t priv_mdl_fetch_with_retry(mdl_fetch_ctx_t *ctx, const char *host, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, uint32_t jmin, uint32_t jmax, mdl_net_resp_t *out_resp, size_t *out_bytes)
Bounded, governed retry of one image transfer; last status latched.
Definition mdl_fetch.c:264
static void internal_mdl_fetch_mark_complete(mdl_fetch_ctx_t *ctx, mdl_fetch_layout_t layout, mdl_chapter_rec_t *rec, size_t *global_no)
Mark a chapter complete and advance combined numbering.
Definition mdl_fetch.c:596
static bool internal_mdl_fetch_select_chapter_title(mdl_fetch_ctx_t *ctx, const char *chapter_url, mdl_chapter_work_t *work)
Select one bounded persistent chapter title.
Definition mdl_fetch.c:625
void priv_mdl_fetch_record_fail(const mdl_fetch_ctx_t *ctx, const char *url, long status, ra8_err_t err)
Append one failure to the run's log (when set); always tally total.
Definition mdl_fetch.c:175
void priv_mdl_fetch_reason(ra8_err_t err, long status, char *buf, size_t cap)
Render a human-readable reason for a transfer result and HTTP status.
Definition mdl_fetch.c:130
mdl_fetch_http_t
HTTP-status boundary used when rendering a failure reason.
Definition mdl_fetch.c:29
@ k_http_server_err
First status that is a server error.
Definition mdl_fetch.c:30
static ra8_err_t internal_mdl_fetch_chapter_html(mdl_fetch_ctx_t *ctx, const char *chapter_url, size_t *out_len)
Fetch one chapter HTML document and extract its page URLs.
Definition mdl_fetch.c:470
ra8_err_t priv_mdl_fetch_cache_get_buf(void *context, const char *url, const mdl_net_req_t *request, char *buffer, size_t capacity, size_t *out_length, mdl_net_resp_t *response)
Fetch one cache attempt through governor and bounded retry policy.
Definition mdl_fetch.c:421
static ra8_err_t internal_fetch_asset_execute(mdl_fetch_ctx_t *ctx, const char *host, const char *url, const mdl_net_req_t *req, uint32_t jmin, uint32_t jmax, const char *target_abs, mdl_net_resp_t *out_resp, size_t *out_bytes)
Fetch one asset body and commit it to permanent storage.
Definition mdl_fetch.c:317
static const char * internal_mdl_fetch_page_host(const char *url, char *buf, size_t cap)
Governor host key for url, or NULL when it cannot be parsed.
Definition mdl_fetch.c:207
static ra8_err_t internal_mdl_fetch_governed_get_body(mdl_fetch_ctx_t *ctx, const char *host, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, uint32_t jmin, uint32_t jmax, mdl_net_resp_t *out_resp, size_t *out_bytes)
One governed body transfer: pace, fetch, feed the outcome back.
Definition mdl_fetch.c:235
static mdl_chap_status_t internal_mdl_fetch_chapter_pages_and_checkpoint(mdl_fetch_ctx_t *ctx, const char *chapter_url, mdl_fetch_layout_t layout, const char *combined_abs, const char *combined_rel, size_t *global_no, mdl_fetch_stats_t *stats, size_t chapter_index, size_t chapter_total, mdl_chapter_work_t *work)
Persist metadata, fetch pages, and checkpoint one prepared chapter.
Definition mdl_fetch.c:770
State-aware chapter/page download orchestrator for the media downloader.
@ k_mdl_fetch_fail_max
Failures stored before the log saturates.
Definition mdl_fetch.h:76
mdl_fetch_layout_t
Output directory layout the orchestrator writes.
Definition mdl_fetch.h:51
@ k_mdl_layout_combined
All chapters -> one dir, continuous numbering.
Definition mdl_fetch.h:52
ra8_err_t priv_mdl_fetch_body_commit(mdl_fetch_body_t *body)
Validate exact size/hash and publish one prepared body.
ra8_err_t priv_mdl_fetch_body_init_exact(mdl_fetch_body_t *body, mdl_storage_t *storage, const char *target_abs)
Initialize an exact-path transactional body sink.
mdl_net_body_sink_t priv_mdl_fetch_body_sink(mdl_fetch_body_t *body)
Return the network sink view of one body state.
ra8_err_t priv_mdl_fetch_body_prepare(mdl_fetch_body_t *body)
Finish prefix classification and make a nonempty stage commit-ready.
ra8_err_t priv_mdl_fetch_body_abort(mdl_fetch_body_t *body)
Abort any private stage owned by a body sink.
Portable single-transaction response-body sink for media fetches.
Module-private fetch-loop decisions promoted for host unit tests and the CLI's end-of-run reporting.
ra8_err_t priv_mdl_fetch_chapter_pages(mdl_fetch_ctx_t *ctx, const char *chapter_url, const char *dest_abs, const char *dest_rel, size_t base, mdl_chapter_rec_t *rec, mdl_fetch_stats_t *stats, const mdl_run_pos_t *pos)
Fetch and checkpoint every extracted page in one chapter.
struct mdl_fetch_cache_request mdl_fetch_cache_request_t
Caller-owned governor parameters for one cached buffer fetch.
Streaming HTTP(S) GET seam (a real function-pointer vtable) for the host manga downloader (v0).
ra8_err_t mdl_net_get_buf(mdl_net_iface_t *net, const char *url, const mdl_net_req_t *req, char *buf, size_t cap, size_t *out_len, mdl_net_resp_t *resp)
GET url fully into a caller buffer (used for HTML pages).
Definition mdl_net.c:78
ra8_err_t mdl_net_get_body(mdl_net_iface_t *net, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, size_t *out_len, mdl_net_resp_t *resp)
GET url and stream its body through a caller-owned sink.
Definition mdl_net.c:97
Guarded filesystem directory joins for the media downloader.
bool mdl_join_dir_under(mdl_storage_t *storage, const char *parent_abs, const char *seg, char *out, size_t cap)
Join seg under parent_abs, create it, and verify it stays inside.
Definition mdl_pathfs.c:15
void mdl_governor_observe(mdl_governor_t *g, const char *host, long status, const char *retry_after)
Feed a completed request's outcome back into the host's governor state.
@ k_mdl_gov_host_max
Host-key buffer bytes (matches config).
ra8_err_t mdl_governor_acquire(mdl_governor_t *g, const char *host, uint32_t jitter_min_ms, uint32_t jitter_max_ms)
Reserve an in-flight slot for a request to host, pacing as required.
void mdl_governor_release(mdl_governor_t *g, const char *host)
Release the in-flight slot reserved by a matching mdl_governor_acquire.
bool mdl_session_url_allowed(mdl_session_t *session, const char *url, uint32_t *crawl_delay_ms)
Decide whether url may be fetched under robots.txt, and its delay.
bool mdl_state_set_chapter_metadata(mdl_chapter_rec_t *chapter, const char *title, double number, bool number_known)
Set a chapter's display title and explicit parsed number.
Definition mdl_state.c:293
ra8_err_t mdl_state_save(mdl_storage_t *storage, const char *path, const mdl_state_t *st, bool *out_published)
Publish a checksummed successor without sacrificing the newest state.
mdl_chapter_rec_t * mdl_state_add_chapter_numbered(mdl_state_t *st, const char *id, const char *url, double number, bool number_known)
Find or append a chapter with explicit parsed-number presence.
Definition mdl_state.c:262
@ k_mdl_chapter_id_max
Chapter identifier bytes (sanitised).
Definition mdl_state.h:97
@ k_mdl_title_max
Series title bytes.
Definition mdl_state.h:98
Injected portable storage resources for downloader domain code.
ra8_err_t priv_mdl_stream_text(ra8_err_t prior, ra8_io_stream_t *stream, const char *text)
Append text unless an earlier operation already failed.
Definition mdl_stream.c:16
Private bounded text helpers over the portable byte-stream contract.
Pure URL / address safety predicates for the libcurl backend.
bool mdl_url_host(const char *url, char *out, size_t cap)
Extract the authority (host and optional port) from an http(s) URL.
Bounded URL naming and portable image-classification helpers.
bool mdl_urlname_chapter_text_parse(const char *text, double *out)
Parse a selector result as one complete bounded chapter number.
void mdl_urlname_last_segment(const char *url, char *out, size_t cap)
Sanitised last non-empty path segment of a URL.
Definition mdl_urlname.c:75
bool mdl_urlname_chapter_parse(const char *url, double *out)
Parse an explicitly-marked integral or decimal chapter value.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_retry_limit
Retry budget exhausted – operation still failing after all attempts.
Definition ra8_err.h:447
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_busy
Resource busy – blocking operation cannot proceed.
Definition ra8_err.h:195
@ k_ra8_err_no_data
No application data available (e.g.
Definition ra8_err.h:202
@ k_ra8_err_would_block
Non-blocking operation would have blocked.
Definition ra8_err.h:210
@ k_ra8_fail
Generic unspecified failure.
Definition ra8_err.h:133
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_timeout
Operation exceeded its time budget.
Definition ra8_err.h:188
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
Observable outcome of one cache lookup.
Definition mdl_cache.h:105
One chapter's coverage in library state.
Definition mdl_state.h:128
int64_t fetched_at
Completion time (epoch s).
Definition mdl_state.h:137
uint16_t pages_done
Pages fetched and verified.
Definition mdl_state.h:135
bool complete
All pages present + verified.
Definition mdl_state.h:136
Bounded metadata and state accumulated while processing one chapter.
Definition mdl_fetch.c:50
bool number_known
Explicit number presence.
Definition mdl_fetch.c:54
mdl_chapter_rec_t * rec
Persistent chapter record.
Definition mdl_fetch.c:57
char extracted_title[k_mdl_title_max]
Selector title scratch.
Definition mdl_fetch.c:52
double number
Parsed chapter number.
Definition mdl_fetch.c:53
size_t html_len
Retained chapter HTML.
Definition mdl_fetch.c:55
const char * title
Selected persistent title.
Definition mdl_fetch.c:56
Caller-owned state for one retryable streamed fetch.
mdl_fetch_ctx_t * ctx
Active fetch dependencies.
char host[k_mdl_gov_host_max]
Governor host key.
uint32_t jmin
Minimum request delay.
uint32_t jmax
Maximum request delay.
Everything mdl_fetch_run needs, injected for testability.
Definition mdl_fetch.h:164
mdl_state_t * state
Persistent state, read and updated.
Definition mdl_fetch.h:167
mdl_url_list_t * images
Extracted-image scratch (caller-owned).
Definition mdl_fetch.h:177
const mdl_site_t * site
Selectors + politeness bounds.
Definition mdl_fetch.h:172
mdl_governor_t * gov
Per-host rate/backoff governor, or NULL.
Definition mdl_fetch.h:173
const char * state_path
Atomic checkpoint target, or NULL.
Definition mdl_fetch.h:169
bool update_only
Skip chapters already recorded complete.
Definition mdl_fetch.h:178
mdl_storage_t * storage
Portable storage binding + workspaces.
Definition mdl_fetch.h:166
char * page_buf
Chapter-HTML scratch (caller-owned).
Definition mdl_fetch.h:175
mdl_cache_t * cache
Per-host HTML cache binding.
Definition mdl_fetch.h:168
const char * series_abs_dir
Absolute series dir (paths resolve here).
Definition mdl_fetch.h:170
mdl_fetch_faillog_t * faillog
Caller-cleared failure log, or NULL.
Definition mdl_fetch.h:180
ra8_io_stream_t * diagnostic
Borrowed diagnostic sink.
Definition mdl_fetch.h:181
mdl_session_t * session
Identity + robots + net backend.
Definition mdl_fetch.h:165
const char * series_url
Series URL, sent as the chapter Referer.
Definition mdl_fetch.h:171
size_t page_cap
Capacity of page_buf.
Definition mdl_fetch.h:176
uint32_t timeout_ms
Per-request time budget.
Definition mdl_fetch.h:174
ra8_err_t progress_error
First callback failure from this run.
Definition mdl_fetch.h:184
One recorded page/chapter failure: what failed, and with what status.
Definition mdl_fetch.h:90
ra8_err_t err
Classified failure code (rendered on demand).
Definition mdl_fetch.h:93
char url[k_mdl_url_max]
Failing page or chapter URL (truncation-safe).
Definition mdl_fetch.h:91
long status
HTTP status observed, 0 when none.
Definition mdl_fetch.h:92
Bounded record of every failure in a run (declare at file scope).
Definition mdl_fetch.h:108
Tallies a fetch run reports back (all zero-initialised by the run).
Definition mdl_fetch.h:65
size_t chapters_completed
Chapters finished (all pages present) this run.
Definition mdl_fetch.h:66
size_t chapters_skipped
Chapters skipped as already complete (–update).
Definition mdl_fetch.h:67
size_t chapters_failed
Chapters left partial by a page failure.
Definition mdl_fetch.h:68
size_t pages_failed
Page fetches that failed or robots refused.
Definition mdl_fetch.h:71
Caller-owned lifecycle and write seam for one response body.
Definition mdl_net.h:162
Per-request session parameters.
Definition mdl_net.h:43
Per-transfer response metadata surfaced to the politeness governor.
Definition mdl_net.h:120
long status
Finished transfer's HTTP status, 0 if none.
Definition mdl_net.h:121
char retry_after[k_mdl_retry_after_max]
Raw Retry-After value, "" when absent.
Definition mdl_net.h:122
Stable chapter position carried into per-page progress events.
mdl_net_iface_t * net
Backend used for robots.txt fetches.
Definition mdl_session.h:40
const char * user_agent
Full UA header (caller-owned).
Definition mdl_session.h:41
char chapter_number_selector[k_mdl_match_max]
Per-chapter number selector.
Definition mdl_config.h:80
uint32_t chapter_delay_min
Inter-chapter spacing floor.
Definition mdl_config.h:89
uint32_t img_delay_max
Per-image spacing ceiling.
Definition mdl_config.h:88
uint32_t chapter_delay_max
Inter-chapter spacing ceiling.
Definition mdl_config.h:90
char chapter_title_selector[k_mdl_match_max]
Per-chapter title selector.
Definition mdl_config.h:78
char page_img_url_contains[k_mdl_match_max]
Keep only URLs with this.
Definition mdl_config.h:60
char page_img_attr[k_mdl_attr_max]
Preferred attr (fallback other).
Definition mdl_config.h:59
uint32_t img_delay_min
Per-image spacing floor.
Definition mdl_config.h:87
Bounded list of absolute URLs found on a page.
Definition mdl_extract.h:29
char urls[k_mdl_max_urls][k_mdl_url_max]
Absolute, NUL-terminated.
Definition mdl_extract.h:30
size_t count
Number of valid entries.
Definition mdl_extract.h:31