Subject: bug#78640: elisp url.el cannot handle some valid URLs

From: Robert Pluim <[email-masked]>
Date: Mon, 09 Jun 2025 11:02:44 +0200

>>>>> On Sat, 07 Jun 2025 11:53:17 +0300, Eli Zaretskii <[email-masked]> said:

    >> From: 林羽 <[email-masked]>
    >> Date: Fri, 30 May 2025 10:57:26 +0800
    >> 
    >> (url-insert-file-contents “http://127.0.0.1:9999?test=1”) 
    >> 
    >> GET ?test=1 HTTP/1.1 MIME-Version: 1.0 Connection: keep-alive Host: 127.0.0.1:9999 Accept-encoding:
    >> gzip Accept: */* User-Agent: URL/Emacs Emacs/31.0.50 (TTY; x86_64-suse-linux-gnu)
    >> 
    >> Should it be /?test=1， Missing "/" This url complies with the RFC 3986 standard and can be handled
    >> normally in tools such as curl

    Eli> Robert, any comments?

Yes, an empty path there should be replaced with "/" when we issue the
command. Fixing the http case is easy enough, but similar changes are
probably required for other schemes. Patch below.

Changing the url parsing code to return "/?test=1" would probably work
as well, but that would be a much bigger and riskier change (and we
donʼt have any tests for this stuff).

Robert
-- 
diff --git a/lisp/url/url-http.el b/lisp/url/url-http.el
index 4258da33a33..5a8279a4122 100644
--- a/lisp/url/url-http.el
+++ b/lisp/url/url-http.el
@@ -325,8 +325,12 @@ url-http-create-request
 					  url-http-target-url)
                                          nil 'any nil)))
          (ref-url (url-http--encode-string url-http-referer)))
-    (if (equal "" real-fname)
-	(setq real-fname "/"))
+    (cond ((equal "" real-fname)
+	   (setq real-fname "/"))
+          ;; RFC 3986 section 6.2.3 says an empty path should be
+          ;; normalized to "/".  (Bug#78640)
+          ((not (eq (aref real-fname 0) ?/))
+           (setq real-fname (concat "/" real-fname))))
     (setq no-cache (and no-cache (string-match "no-cache" no-cache)))
     (if auth
 	(setq auth (concat "Authorization: " auth "\r\n")))