<feed xmlns='http://www.w3.org/2005/Atom'>
<title>uclient, branch master</title>
<subtitle>libubox HTTP client library</subtitle>
<id>https://git-03.infra.openwrt.org/project/uclient/atom?h=master</id>
<link rel='self' href='https://git-03.infra.openwrt.org/project/uclient/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/'/>
<updated>2026-06-07T11:26:25Z</updated>
<entry>
<title>uclient-fetch: handle init_request failure on the 204 resume retry</title>
<updated>2026-06-07T11:26:25Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:31:21Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=daad21fa2c17cae54d37149bf27bb3dd78e50a3f'/>
<id>urn:sha1:daad21fa2c17cae54d37149bf27bb3dd78e50a3f</id>
<content type='text'>
When a ranged (resume) request gets a 204 response, header_done_cb()
restarts the download from the beginning by calling init_request() but
ignored its return value. Every other caller checks it: main() reports
the error and exits, request_done() falls through to uloop_end(). If the
restarted request fails to be issued here, no uloop event remains
pending and uloop_run() blocks forever.

Finalize the request (set error_ret and call request_done()) when the
retry cannot be started, matching the other call sites.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uclient-fetch: don't run the progress meter in spider mode</title>
<updated>2026-06-07T11:26:20Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:25:11Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=74c07b12f7ab3a9420a41fb53fb050e66c533377'/>
<id>urn:sha1:74c07b12f7ab3a9420a41fb53fb050e66c533377</id>
<content type='text'>
The progress meter state (pmt) and its timer callback are only
initialized in open_output_file(), which is not called when there is no
output file, e.g. for --spider. eof_cb() nevertheless invoked
pmt_update() whenever output was not quiet, so "uclient-fetch --spider
URL" ran the meter against a zero-initialized pmt: progress_update()
passed the NULL pmt.curfile to a "%-20.20s" conversion, which is
undefined behavior. (The meter was never set up at all for this run -
curfile is NULL and pmt_timer.cb is unset.)

Skip the progress meter in eof_cb() when no_output is set; the
completion/error messages below still print.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uclient-fetch: attach credentials to the proxy target, not the proxy</title>
<updated>2026-06-07T11:26:15Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:22:20Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=03b17e4fc793d4e58ae68871f27fc3af9de87eed'/>
<id>urn:sha1:03b17e4fc793d4e58ae68871f27fc3af9de87eed</id>
<content type='text'>
For the first (or only) URL, main() created the client with the
credentials bound to the proxy URL (uclient_new(proxy_url, auth_str))
and set the real target with no auth (uclient_set_proxy_url(.., NULL)).
request_done() does the opposite for subsequent URLs: no auth on the
proxy, credentials on the target. HTTP origin (401) authentication
applies to the target, so the request_done() arrangement is the correct
one.

This inconsistency became a regression once authentication started
reading the request URL (the proxy target) rather than cl-&gt;url: the
common single-URL "http_proxy=... uclient-fetch --user/--password url"
case stopped sending credentials. Align main() with request_done() by
passing the credentials to uclient_set_proxy_url() and none to the proxy
URL.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>ucode: don't leak string references in status()/get_headers()</title>
<updated>2026-06-07T11:26:10Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:19:59Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=43bed27539984c3bab023445891dace1255c9557'/>
<id>urn:sha1:43bed27539984c3bab023445891dace1255c9557</id>
<content type='text'>
ucv_object_add() takes ownership of the value it stores (it ucv_put()s
the previous value on replacement and releases each value when the
object is freed). The status() and get_headers() helpers passed
ucv_get(ucv_string_new(...)), adding an extra reference that nobody ever
releases, so each ucv_string_new() string outlived the object holding it.

get_headers() leaked one string per response header and status() leaked
the local_addr and remote_addr strings on every call, accumulating in a
long-running ucode program. (The int64 ports are tagged values for which
ucv_get is a no-op, but the spurious calls are removed too.)

Pass the freshly created values directly so their single reference is
transferred to the object.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uclient-fetch: concatenate multiple URLs into a single -O file</title>
<updated>2026-06-07T11:26:05Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:13:39Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=83659a5a9bc15cfb65b934842942199e125e0c2e'/>
<id>urn:sha1:83659a5a9bc15cfb65b934842942199e125e0c2e</id>
<content type='text'>
With "-O file url1 url2 ...", each URL's header_done_cb reopened the
output file with O_TRUNC, so the file ended up containing only the last
URL's contents. wget concatenates the downloads in this case.

Open the -O file once (for the first URL) and reuse that descriptor for
the remaining URLs by returning the already-open fd from
open_output_file(), and keep it open across URLs in request_done()
(it was previously closed between URLs, only to be reopened and
truncated). The descriptor is closed at the end as before.

Only the second and later URLs of a multi-URL -O run are affected;
single-URL downloads, auto-named outputs, and -O - (stdout) are
unchanged.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uclient-fetch: finalize request on invalid 206 Content-Range</title>
<updated>2026-06-07T11:25:59Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:12:58Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=070c868486dc8af6da3bb682e540bc147b6bb76f'/>
<id>urn:sha1:070c868486dc8af6da3bb682e540bc147b6bb76f</id>
<content type='text'>
When a 206 Partial Content response was missing or had a malformed
Content-Range header, header_done_cb() set error_ret=8 and broke out of
the switch without calling request_done(). The state machine then kept
reading and discarding the body until EOF, after which eof_cb() reported
a misleading completion/reset before the request was torn down.

Call request_done() in both error branches, matching the adjacent
"partial content not requested" branch, so the transfer is aborted
immediately with the error code.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uclient-http: follow 303 and 308 redirects</title>
<updated>2026-06-07T11:25:54Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:12:31Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=5ce9983b2a114ebd9c5ac02bfef4b084dd0b5481'/>
<id>urn:sha1:5ce9983b2a114ebd9c5ac02bfef4b084dd0b5481</id>
<content type='text'>
uclient_http_status_redirect() only recognized 301/302/307, so 303 (See
Other) and 308 (Permanent Redirect) responses were treated as errors
instead of being followed. Add both.

308 preserves the request method like 307. 303 must be retrieved with
GET per RFC 9110 §15.4.4 (a HEAD request stays HEAD), so downgrade the method to
GET in uclient_http_redirect() for 303. The redirect already re-issues
without resending a streamed body, which matches GET semantics.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uclient-http: accept unquoted digest auth parameter values</title>
<updated>2026-06-07T11:25:49Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:11:20Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=db3bb00ddda799cdb8e500df28c14ffa7ad76369'/>
<id>urn:sha1:db3bb00ddda799cdb8e500df28c14ffa7ad76369</id>
<content type='text'>
The WWW-Authenticate digest parser used digest_unquote_sep() for
realm/qop/nonce/opaque/domain, which returns NULL when the value is not
a double-quoted string. RFC 7616 §3.3 quotes these parameters, but some
servers emit bare tokens (e.g. qop=auth or an unquoted nonce); against
such servers digest authentication failed with -EINVAL.

Add digest_value() that parses a quoted string when the value starts
with '"' and otherwise takes a comma-delimited token (trimming trailing
whitespace), and use it for all parsed values.

This does not add qop=auth-int support, which would require hashing the
request body and is a separate feature.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uclient-http: retry 401 auth for all body-less request methods</title>
<updated>2026-06-07T11:25:44Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:10:33Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=322c89c6ca0a72c74de937278e89664e78b92bce'/>
<id>urn:sha1:322c89c6ca0a72c74de937278e89664e78b92bce</id>
<content type='text'>
The transparent re-request after an initial 401 only re-sends the
headers (the request body is streamed by the caller and cannot be
replayed), so it must be limited to methods that do not carry a body.
The condition hardcoded REQ_HEAD/REQ_GET, which left out other body-less
methods such as OPTIONS and the WebDAV COPY/MOVE/UNLOCK verbs, so a
single 401 on those failed even with valid credentials.

Gate the retry on !uclient_request_supports_body() instead, which covers
exactly the body-less methods and still excludes POST/PUT/etc. whose body
cannot be resent.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
<entry>
<title>uclient-http: use the proxy target URL for HTTP authentication</title>
<updated>2026-06-07T11:25:37Z</updated>
<author>
<name>Hauke Mehrtens</name>
</author>
<published>2026-05-31T16:10:09Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/uclient/commit/?id=8507588e7ce720f4fb9944984edd38459b96dfd5'/>
<id>urn:sha1:8507588e7ce720f4fb9944984edd38459b96dfd5</id>
<content type='text'>
When a proxy is configured, cl-&gt;url holds the proxy server and
cl-&gt;proxy_url holds the real target. uclient_http_send_headers() puts
the target (proxy_url) on the request line and in the Host header, but
the authentication code read cl-&gt;url instead:

 - uclient_http_add_auth_header() gated on cl-&gt;url-&gt;auth, which is the
   proxy's userinfo (usually NULL), so origin (401) credentials supplied
   for the target were never sent through a proxy at all;
 - the digest "uri" field was taken from cl-&gt;url-&gt;location instead of
   the request-URI that is actually sent, so a digest response computed
   through a proxy would not match.

Add uclient_http_request_url() returning the proxy target when set, and
use it in add_auth_header/add_auth_basic/add_auth_digest. The non-proxy
path is unchanged because proxy_url is NULL there.

Assisted-by: Claude:claude-opus-4-8
Link: https://github.com/openwrt/uclient/pull/16
Signed-off-by: Hauke Mehrtens &lt;hauke@hauke-m.de&gt;
</content>
</entry>
</feed>
