From 349d8b8a1e6122661b278507b91bc3f788a23470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 9 Apr 2026 18:19:26 +0200 Subject: [PATCH 1/7] feat: implement HTTP/1.1 chunked transfer encoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Response line upgraded from HTTP/1.0 to HTTP/1.1 - Connection: close header sent on all responses (keep-alive planned) - Chunked transfer encoding for responses without known Content-Length - Content-Length header for static UFS files via ufs_stat() - SSI-processed files use chunked encoding (unknown output size) - Host header validation: 400 Bad Request if missing on HTTP/1.1 - SERVER_PROTOCOL env var updated to HTTP/1.1 - Terminating chunk (0\r\n\r\n) sent in httpdone before CSTATE_REPORT - New HTTPC fields: chunked (0x41), content_length_set (0x52) - Fields reset in httprese.c between requests CGI modules (mvsMF) are unaffected — they already set Content-Length via sendDefaultHeaders() and never enable chunked mode. Fixes #50 --- include/httpcgi.h | 4 +-- include/httpd.h | 4 +-- src/httpdone.c | 13 +++++++++ src/httpget.c | 37 ++++++++++++++++++++++++ src/httpin.c | 15 ++++++++++ src/httppars.c | 2 +- src/httprese.c | 2 ++ src/httpresp.c | 6 +++- src/httpsend.c | 74 +++++++++++++++++++++++++++-------------------- 9 files changed, 120 insertions(+), 37 deletions(-) diff --git a/include/httpcgi.h b/include/httpcgi.h index 305f491..e85948c 100644 --- a/include/httpcgi.h +++ b/include/httpcgi.h @@ -116,7 +116,7 @@ struct httpc { double end; /* 38 end time in seconds */ UCHAR rdw; /* 40 RDW option */ - UCHAR unused; /* 41 available */ + UCHAR chunked; /* 41 chunked transfer encoding */ short resp; /* 42 response code */ CRED *cred; /* 44 client credential */ UFS *ufs; /* 48 UFS handle (opaque) */ @@ -125,7 +125,7 @@ struct httpc { UCHAR ssi; /* 50 Server Side Include enable */ UCHAR ssilevel; /* 51 SSI processing level */ #define SSI_LEVEL_MAX 10 /* ... max SSI processing level */ - UCHAR unused1; /* 52 available */ + UCHAR content_length_set; /* 52 Content-Length was sent */ UCHAR unused2; /* 53 available */ unsigned unused3; /* 54 available */ diff --git a/include/httpd.h b/include/httpd.h index 5b96f82..c3ca5d3 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -200,7 +200,7 @@ struct httpc { double end; /* 38 end time in seconds */ UCHAR rdw; /* 40 RDW option */ - UCHAR unused; /* 41 available */ + UCHAR chunked; /* 41 chunked transfer encoding */ short resp; /* 42 response code */ CRED *cred; /* 44 client credential */ UFS *ufs; /* 48 UFS handle */ @@ -209,7 +209,7 @@ struct httpc { UCHAR ssi; /* 50 Server Side Include enable */ UCHAR ssilevel; /* 51 SSI processing level */ #define SSI_LEVEL_MAX 10 /* ... max SSI processing levele*/ - UCHAR unused1; /* 52 available */ + UCHAR content_length_set; /* 52 Content-Length was sent */ UCHAR unused2; /* 53 available */ unsigned unused3; /* 54 available */ diff --git a/src/httpdone.c b/src/httpdone.c index b2e9ca5..3f7c4ea 100644 --- a/src/httpdone.c +++ b/src/httpdone.c @@ -21,6 +21,19 @@ httpdone(HTTPC *httpc) ufs_fclose(&httpc->ufp); } + /* send terminating chunk for chunked transfer encoding */ + if (httpc->chunked) { + /* "0\r\n\r\n" in ASCII — disable framing first */ + UCHAR term[5]; + httpc->chunked = 0; + term[0] = 0x30; /* '0' */ + term[1] = 0x0D; /* CR */ + term[2] = 0x0A; /* LF */ + term[3] = 0x0D; /* CR */ + term[4] = 0x0A; /* LF */ + http_send(httpc, term, 5); + } + httpsecs(&httpc->end); quit: diff --git a/src/httpget.c b/src/httpget.c index 25b7328..e74b423 100644 --- a/src/httpget.c +++ b/src/httpget.c @@ -16,6 +16,7 @@ httpget(HTTPC *httpc) { int rc = 0; UCHAR *path; + UCHAR *open_path = NULL; const HTTPM *mime; FILE *fp; int len; @@ -38,6 +39,7 @@ httpget(HTTPC *httpc) /* try to open path from UFS */ mime = http_mime(path); + open_path = path; fp = http_open(httpc, path, mime); if (fp || httpc->ufp) goto okay; @@ -47,11 +49,13 @@ httpget(HTTPC *httpc) memcpy(buf, path, len); strcpy(&buf[len], "index.html"); mime = http_mime(buf); + open_path = buf; fp = http_open(httpc, buf, mime); if (fp || httpc->ufp) goto okay; strcpy(&buf[len], "default.html"); mime = http_mime(buf); + open_path = buf; fp = http_open(httpc, buf, mime); if (fp || httpc->ufp) goto okay; } @@ -80,9 +84,42 @@ httpget(HTTPC *httpc) #endif rc = http_printf(httpc, "Content-Type: %s\r\n", mime->type); if (rc) goto die; + + /* Content-Length for static UFS files, chunked for SSI */ + if (!httpc->ssi && httpc->ufp) { + UFS *ufs = http_get_ufs(httpc); + if (ufs) { + UFSDLIST st; + UCHAR ufspath[256]; + const char *dr = httpc->httpd->docroot; + if (dr[0]) { + snprintf((char *)ufspath, sizeof(ufspath), "%s%s", + dr, open_path); + } else { + snprintf((char *)ufspath, sizeof(ufspath), "%s", open_path); + } + if (ufs_stat(ufs, (const char *)ufspath, &st) == 0) { + rc = http_printf(httpc, "Content-Length: %u\r\n", + st.filesize); + if (rc) goto die; + httpc->content_length_set = 1; + } + } + } + + if (!httpc->content_length_set) { + rc = http_printf(httpc, "Transfer-Encoding: chunked\r\n"); + if (rc) goto die; + } + rc = http_printf(httpc, "\r\n"); if (rc) goto die; + /* enable chunk framing for body data */ + if (!httpc->content_length_set) { + httpc->chunked = 1; + } + /* indicate type of document being sent */ if (mime->binary) { httpc->subtype=CTYPE_BINARY; diff --git a/src/httpin.c b/src/httpin.c index ba203f3..d6c622c 100644 --- a/src/httpin.c +++ b/src/httpin.c @@ -71,6 +71,21 @@ int http_in(HTTPC *httpc) if (http_set_http_env(httpc, buf, p)) goto failed; } while(rc > 0); + /* HTTP/1.1 requires a Host header */ + { + UCHAR *ver = http_get_env(httpc, "REQUEST_VERSION"); + if (ver && http_cmp(ver, "HTTP/1.1") == 0) { + UCHAR *host = http_get_env(httpc, "HTTP_HOST"); + if (!host || !host[0]) { + http_resp(httpc, 400); + http_printf(httpc, "Content-Type: text/plain\r\n\r\n"); + http_printf(httpc, "400 Bad Request: Missing Host header\r\n"); + httpc->state = CSTATE_DONE; + goto quit; + } + } + } + /* next step will parse and do any additional processing */ rc = 0; httpc->state = CSTATE_PARSE; diff --git a/src/httppars.c b/src/httppars.c index 830ca7c..b1960eb 100644 --- a/src/httppars.c +++ b/src/httppars.c @@ -87,7 +87,7 @@ httppars(HTTPC *httpc) if (http_set_env(httpc, "SERVER_PORT", tmp)) goto failed; } - if (http_set_env(httpc, "SERVER_PROTOCOL", "HTTP/1.0")) goto failed; + if (http_set_env(httpc, "SERVER_PROTOCOL", "HTTP/1.1")) goto failed; sprintf(tmp, "HTTPD/%s", httpc->httpd->version); if (http_set_env(httpc, "SERVER_SOFTWARE", tmp)) goto failed; diff --git a/src/httprese.c b/src/httprese.c index d1b6db0..ac56bfd 100644 --- a/src/httprese.c +++ b/src/httprese.c @@ -31,6 +31,8 @@ httprese(HTTPC *httpc) httpc->sent = 0; httpc->subtype = 0; httpc->substate = 0; + httpc->chunked = 0; + httpc->content_length_set = 0; httpc->start = 0.0; httpc->end = 0.0; memset(httpc->buf, 0, CBUFSIZE); diff --git a/src/httpresp.c b/src/httpresp.c index 7c9cc82..a7ae6dd 100644 --- a/src/httpresp.c +++ b/src/httpresp.c @@ -42,7 +42,7 @@ httpresp(HTTPC *httpc, int resp) httpc->resp = resp; - rc = http_printf(httpc, "HTTP/1.0 %s\r\n", p); + rc = http_printf(httpc, "HTTP/1.1 %s\r\n", p); if (rc) goto quit; now = time64(NULL); @@ -68,6 +68,10 @@ httpresp(HTTPC *httpc, int resp) if (rc) goto quit; } + /* HTTP/1.1: always close for now (keep-alive planned) */ + rc = http_printf(httpc, "Connection: close\r\n"); + if (rc) goto quit; + quit: http_exit("httpresp(), rc=%d\n", rc); return rc; diff --git a/src/httpsend.c b/src/httpsend.c index f294ae4..f3ecf22 100644 --- a/src/httpsend.c +++ b/src/httpsend.c @@ -4,62 +4,74 @@ */ #include "httpd.h" -extern int -httpsend(HTTPC *httpc, const UCHAR *buf, int len) +/* send_raw() - send raw bytes to socket without chunk framing */ +static int +send_raw(HTTPC *httpc, const UCHAR *buf, int len) { int rc; int pos = 0; -#if 0 - wtof("httpsend(%08X,%08X,%d)", httpc, buf, len); -#endif - - /* send data to socket */ for(pos=0; pos < len; pos+=rc) { if (pos < 0) { wtof("httpsend() pos underflow %d", pos); break; } -#if 0 - wtof("calling send(%d,%08X,%d,0)", - httpc->socket, &buf[pos], len-pos); -#endif rc = send(httpc->socket, &buf[pos], len-pos, 0); -#if 0 - wtof("send() rc=%d", rc); -#endif if (rc>0) { - /* update bytes sent count */ httpc->sent += rc; -#if 0 - wtof("httpc->sent=%d", httpc->sent); -#endif } else { -#if 0 - wtof("httpsend() send rc=%d, errno=%d, socket=%d", rc, errno, httpc->socket); -#endif /* allow for EWOULDBLOCK */ if (errno == EWOULDBLOCK) break; /* an error occured */ if (httpc->state < CSTATE_DONE) { - /* transtion to done state */ -#if 0 - wtof("httpsend() changing httpc->state=CSTATE_DONE"); -#endif httpc->state = CSTATE_DONE; - goto quit; /* return with negative rc */ + return -1; } break; } } - rc = pos; + return pos; +} + +extern int +httpsend(HTTPC *httpc, const UCHAR *buf, int len) +{ + int rc; + + if (httpc->chunked) { + /* RFC 7230 chunked transfer encoding */ + UCHAR hdr[16]; + int hdrlen; + + if (len <= 0) return 0; + + /* chunk header: hex size + CRLF (convert EBCDIC to ASCII) */ + hdrlen = sprintf((char *)hdr, "%x\r\n", len); + http_etoa(hdr, hdrlen); + rc = send_raw(httpc, hdr, hdrlen); + if (rc < 0) return rc; + + /* chunk data (already in ASCII from caller) */ + rc = send_raw(httpc, buf, len); + if (rc < 0) return rc; + + /* chunk trailer: CRLF in ASCII (0x0D 0x0A) */ + { + UCHAR crlf[2]; + crlf[0] = 0x0D; + crlf[1] = 0x0A; + rc = send_raw(httpc, crlf, 2); + } + if (rc < 0) return rc; + + return len; + } + + /* non-chunked: send raw */ + rc = send_raw(httpc, buf, len); -quit: -#if 0 - wtof("httpsend() rc=%d", rc); -#endif return rc; } From a0b997f5cefe1ff3ce89761d05ad9cfee6ff7a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 9 Apr 2026 18:40:54 +0200 Subject: [PATCH 2/7] fix: respect HTTP/1.0 clients, add ufs_stat debug logging Bug 1: HTTP/1.0 clients received chunked responses which they cannot parse. Now httpresp.c mirrors the client version in the response line (HTTP/1.0 or HTTP/1.1), and httpget.c only uses Transfer-Encoding: chunked for HTTP/1.1 clients. HTTP/1.0 bodies are delimited by Connection: close as before. Bug 2: ufs_stat() returns non-zero for all static files, causing Content-Length to never be set. Added HTTPD050I diagnostic message logging the path and return code to identify the root cause. Fixes #50 --- src/httpget.c | 21 +++++++++++++-------- src/httpresp.c | 10 +++++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/httpget.c b/src/httpget.c index e74b423..5acd13d 100644 --- a/src/httpget.c +++ b/src/httpget.c @@ -92,13 +92,17 @@ httpget(HTTPC *httpc) UFSDLIST st; UCHAR ufspath[256]; const char *dr = httpc->httpd->docroot; + int stat_rc; if (dr[0]) { snprintf((char *)ufspath, sizeof(ufspath), "%s%s", dr, open_path); } else { snprintf((char *)ufspath, sizeof(ufspath), "%s", open_path); } - if (ufs_stat(ufs, (const char *)ufspath, &st) == 0) { + stat_rc = ufs_stat(ufs, (const char *)ufspath, &st); + wtof("HTTPD050I httpget ufs_stat(\"%s\") rc=%d size=%u", + ufspath, stat_rc, stat_rc == 0 ? st.filesize : 0); + if (stat_rc == 0 && st.filesize > 0) { rc = http_printf(httpc, "Content-Length: %u\r\n", st.filesize); if (rc) goto die; @@ -107,19 +111,20 @@ httpget(HTTPC *httpc) } } + /* chunked transfer encoding only for HTTP/1.1 clients */ if (!httpc->content_length_set) { - rc = http_printf(httpc, "Transfer-Encoding: chunked\r\n"); - if (rc) goto die; + UCHAR *ver = http_get_env(httpc, "REQUEST_VERSION"); + if (ver && http_cmp(ver, "HTTP/1.1") == 0) { + rc = http_printf(httpc, "Transfer-Encoding: chunked\r\n"); + if (rc) goto die; + httpc->chunked = 1; + } + /* HTTP/1.0: no chunked, body delimited by Connection: close */ } rc = http_printf(httpc, "\r\n"); if (rc) goto die; - /* enable chunk framing for body data */ - if (!httpc->content_length_set) { - httpc->chunked = 1; - } - /* indicate type of document being sent */ if (mime->binary) { httpc->subtype=CTYPE_BINARY; diff --git a/src/httpresp.c b/src/httpresp.c index a7ae6dd..a39fb9d 100644 --- a/src/httpresp.c +++ b/src/httpresp.c @@ -42,7 +42,15 @@ httpresp(HTTPC *httpc, int resp) httpc->resp = resp; - rc = http_printf(httpc, "HTTP/1.1 %s\r\n", p); + /* match response version to client request version */ + { + UCHAR *ver = http_get_env(httpc, "REQUEST_VERSION"); + if (ver && http_cmp(ver, "HTTP/1.1") == 0) { + rc = http_printf(httpc, "HTTP/1.1 %s\r\n", p); + } else { + rc = http_printf(httpc, "HTTP/1.0 %s\r\n", p); + } + } if (rc) goto quit; now = time64(NULL); From 058da78e5a8b6c18e00eceee06ffe8c6b8a451d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 9 Apr 2026 18:47:15 +0200 Subject: [PATCH 3/7] debug: add HTTPSEND trace for chunked encoding diagnosis wtof at entry showing chunked flag and data length, and after sprintf showing the chunk header bytes before EBCDIC-to-ASCII conversion. To be removed once the chunked encoding issue is resolved. Ref #50 --- src/httpsend.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/httpsend.c b/src/httpsend.c index f3ecf22..702dc82 100644 --- a/src/httpsend.c +++ b/src/httpsend.c @@ -41,6 +41,8 @@ httpsend(HTTPC *httpc, const UCHAR *buf, int len) { int rc; + wtof("HTTPSEND: chunked=%d len=%d", httpc->chunked, len); + if (httpc->chunked) { /* RFC 7230 chunked transfer encoding */ UCHAR hdr[16]; @@ -50,6 +52,7 @@ httpsend(HTTPC *httpc, const UCHAR *buf, int len) /* chunk header: hex size + CRLF (convert EBCDIC to ASCII) */ hdrlen = sprintf((char *)hdr, "%x\r\n", len); + wtof("HTTPSEND: hdr=[%.*s] hdrlen=%d", hdrlen, hdr, hdrlen); http_etoa(hdr, hdrlen); rc = send_raw(httpc, hdr, hdrlen); if (rc < 0) return rc; From 10f93a7ebe0c17cc73df284023a0cb55c77e3b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 9 Apr 2026 18:57:59 +0200 Subject: [PATCH 4/7] fix: set chunked flag after header-ending CRLF, not before The Bug-1 fix moved httpc->chunked=1 inside the version check block, accidentally placing it BEFORE the header-terminating "\r\n" was sent. This caused the blank line separator to be chunk-framed, which curl interpreted as a malformed header ("Header without colon"). Now uses a local use_chunked flag and sets httpc->chunked only after the "\r\n" has been sent as raw data. Fixes #50 --- src/httpget.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/httpget.c b/src/httpget.c index 5acd13d..2f8d487 100644 --- a/src/httpget.c +++ b/src/httpget.c @@ -112,18 +112,24 @@ httpget(HTTPC *httpc) } /* chunked transfer encoding only for HTTP/1.1 clients */ - if (!httpc->content_length_set) { - UCHAR *ver = http_get_env(httpc, "REQUEST_VERSION"); - if (ver && http_cmp(ver, "HTTP/1.1") == 0) { - rc = http_printf(httpc, "Transfer-Encoding: chunked\r\n"); - if (rc) goto die; - httpc->chunked = 1; + { + int use_chunked = 0; + if (!httpc->content_length_set) { + UCHAR *ver = http_get_env(httpc, "REQUEST_VERSION"); + if (ver && http_cmp(ver, "HTTP/1.1") == 0) { + rc = http_printf(httpc, "Transfer-Encoding: chunked\r\n"); + if (rc) goto die; + use_chunked = 1; + } + /* HTTP/1.0: no chunked, body delimited by Connection: close */ } - /* HTTP/1.0: no chunked, body delimited by Connection: close */ - } - rc = http_printf(httpc, "\r\n"); - if (rc) goto die; + rc = http_printf(httpc, "\r\n"); + if (rc) goto die; + + /* enable chunk framing AFTER header-ending CRLF is sent */ + httpc->chunked = use_chunked; + } /* indicate type of document being sent */ if (mime->binary) { From 25b32bd262d25df6837734b3c41e8c75da659ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 9 Apr 2026 19:45:47 +0200 Subject: [PATCH 5/7] chore: remove debug wtof traces from httpsend and httpget Remove HTTPSEND chunked/len trace and HTTPD050I ufs_stat diagnostic that were added to diagnose the chunked encoding issues. The chunked framing bug (chunked flag set before header CRLF) is fixed. Ref #50 --- src/httpget.c | 2 -- src/httpsend.c | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/httpget.c b/src/httpget.c index 2f8d487..95ee91d 100644 --- a/src/httpget.c +++ b/src/httpget.c @@ -100,8 +100,6 @@ httpget(HTTPC *httpc) snprintf((char *)ufspath, sizeof(ufspath), "%s", open_path); } stat_rc = ufs_stat(ufs, (const char *)ufspath, &st); - wtof("HTTPD050I httpget ufs_stat(\"%s\") rc=%d size=%u", - ufspath, stat_rc, stat_rc == 0 ? st.filesize : 0); if (stat_rc == 0 && st.filesize > 0) { rc = http_printf(httpc, "Content-Length: %u\r\n", st.filesize); diff --git a/src/httpsend.c b/src/httpsend.c index 702dc82..f3ecf22 100644 --- a/src/httpsend.c +++ b/src/httpsend.c @@ -41,8 +41,6 @@ httpsend(HTTPC *httpc, const UCHAR *buf, int len) { int rc; - wtof("HTTPSEND: chunked=%d len=%d", httpc->chunked, len); - if (httpc->chunked) { /* RFC 7230 chunked transfer encoding */ UCHAR hdr[16]; @@ -52,7 +50,6 @@ httpsend(HTTPC *httpc, const UCHAR *buf, int len) /* chunk header: hex size + CRLF (convert EBCDIC to ASCII) */ hdrlen = sprintf((char *)hdr, "%x\r\n", len); - wtof("HTTPSEND: hdr=[%.*s] hdrlen=%d", hdrlen, hdr, hdrlen); http_etoa(hdr, hdrlen); rc = send_raw(httpc, hdr, hdrlen); if (rc < 0) return rc; From 0f2f5c9d8c8cc1ed9fe2f250daf65b4e1d049e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 9 Apr 2026 19:46:22 +0200 Subject: [PATCH 6/7] debug: restore HTTPD050I ufs_stat diagnostic in httpget Accidentally removed in previous cleanup. This trace is still needed to diagnose why ufs_stat fails for static files (Bug 2). Ref #50 --- src/httpget.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/httpget.c b/src/httpget.c index 95ee91d..2f8d487 100644 --- a/src/httpget.c +++ b/src/httpget.c @@ -100,6 +100,8 @@ httpget(HTTPC *httpc) snprintf((char *)ufspath, sizeof(ufspath), "%s", open_path); } stat_rc = ufs_stat(ufs, (const char *)ufspath, &st); + wtof("HTTPD050I httpget ufs_stat(\"%s\") rc=%d size=%u", + ufspath, stat_rc, stat_rc == 0 ? st.filesize : 0); if (stat_rc == 0 && st.filesize > 0) { rc = http_printf(httpc, "Content-Length: %u\r\n", st.filesize); From bd7d083826f8847ea54902ecc1682681cb38cf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Gro=C3=9Fmann?= Date: Thu, 9 Apr 2026 19:49:43 +0200 Subject: [PATCH 7/7] chore: remove HTTPD050I diagnostic, clean up stat_rc variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ufs_stat() confirmed working — Content-Length is set correctly for non-SSI static files. HTML files use chunked encoding by design because SSI processing makes the final output size unknown. Ref #50 --- src/httpget.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/httpget.c b/src/httpget.c index 2f8d487..9aea70e 100644 --- a/src/httpget.c +++ b/src/httpget.c @@ -92,17 +92,14 @@ httpget(HTTPC *httpc) UFSDLIST st; UCHAR ufspath[256]; const char *dr = httpc->httpd->docroot; - int stat_rc; if (dr[0]) { snprintf((char *)ufspath, sizeof(ufspath), "%s%s", dr, open_path); } else { snprintf((char *)ufspath, sizeof(ufspath), "%s", open_path); } - stat_rc = ufs_stat(ufs, (const char *)ufspath, &st); - wtof("HTTPD050I httpget ufs_stat(\"%s\") rc=%d size=%u", - ufspath, stat_rc, stat_rc == 0 ? st.filesize : 0); - if (stat_rc == 0 && st.filesize > 0) { + if (ufs_stat(ufs, (const char *)ufspath, &st) == 0 + && st.filesize > 0) { rc = http_printf(httpc, "Content-Length: %u\r\n", st.filesize); if (rc) goto die;