* url-expand.el and url-parse.el not conforming to RFC3986
@ 2015-11-27 15:22 Alain Schneble (Realize IT GmbH)
2015-11-28 14:58 ` Stephen Leake
0 siblings, 1 reply; 3+ messages in thread
From: Alain Schneble (Realize IT GmbH) @ 2015-11-27 15:22 UTC (permalink / raw)
To: emacs-devel@gnu.org
[-- Attachment #1: Type: text/plain, Size: 3826 bytes --]
Hello
url-expand.el and url-parse.el seem to not follow RFC3986 "Uniform
Resource Identifier (URI): Generic Syntax" in some cases. But I guess
they should. So I started to study RFC3986 in more details and write
tests against url-expand-file-name and url-generic-parse-url (see
attached patch).
The tests reveal the following issues:
1. resolving relative "fragment-only" URIs against a given absolute
base URI (see RFC3986, section 5. Reference Resolution, and
especially 5.2.2. Transform References):
(url-expand-file-name "#s" "http://a/b/c/d;p?q")
=> "#s" but should be http://a/b/c/d;p?q#s"
(url-expand-file-name "#bar" "http://host")
=> "#bar" but should be "http://host#bar"
(url-expand-file-name "#bar" "http://host/")
=> "#bar" but should be "http://host/#bar"
(url-expand-file-name "#bar" "http://host/foo")
=> "#bar" but should be "http://host/foo#bar"
2. resolving relative "query-only" URIs against a given absolute base
URI (see RFC3986, same sections as mentioned in point 1.):
(url-expand-file-name "?y" "http://a/b/c/d;p?q")
=> "http://a/b/c/?y" but should be "http://a/b/c/d;p?y"
(url-expand-file-name "?y" "http://a/b/c/d")
=> "http://a/b/c/?y" but should be "http://a/b/c/d?y")
3. removing dot segments (see RFC3986, section 5.2.4. Remove Dot
Segments):
(url-expand-file-name "/./g" "http://a/b/c/d;p?q")
=> "http://a/./g" but should be "http://a/g"
(url-expand-file-name "/../g" "http://a/b/c/d;p?q")
=> "http://a/../g" but should be "http://a/g"
4. empty fragment information is lost after parsing URI:
(equal (url-generic-parse-url "#")
(url-parse-make-urlobj nil nil nil nil nil "" "" nil nil))
^
=> nil but should be t (fragment component is actually nil instead
of an empty string)
Same issue with URLs having a number sign (#) as suffix:
"/foo/bar#"
"/foo/bar/#"
"http://host#"
"http://host?#"
"http://host?query#"
"http://host/#"
"http://host/?#"
"http://host/?query#"
"http://host/foo#"
"http://host/foo?#"
"http://host/foo?query#"
... and so forth
The problem with this is that the inverse function url-recreate-url
won't be able to reconstruct exactly the same URI. For example:
(url-recreate-url (url-generic-parse-url "#"))
=> "" but should be "#"
To address these issues, I propose changes to url-parse.el and
url-expand.el, see attached patch. Here is the detailed summary:
- url-parse-tests.el: add tests for url-generic-parse-url
- url-expand-tests.el: add tests for url-expand-file-name
- url-generic-parse-url: keep empty fragment information in URL-struct
- url-path-and-query: do not artificially turn empty path and query
into nil path and query, respectively
- url-expander-remove-relative-links: do not turn empty path into an
absolute path ("/"). Remark: due to the name of this function, would
it be better to fix this case where this function is called?
- url-expand-file-name: properly resolve fragment-only URIs. Do not
just return them unchanged. I think that this bug was due to a
misinterpretation of RFC3986, section 5.1. Establishing a Base URI:
"Aside from fragment-only references (Section 4.4), relative
references are only usable when a base URI is known."
To me, this does not mean that they should not be resolved
properly. And the expamples given in the RFC emphasize this as well.
- url-default-expander: an empty path in the relative reference URI
should not drop the last segment.
Please let me know if I should follow a different procedure to submit
these changes. I signed the copyright assignment "GNU EMACS" this year.
Thanks,
Alain
[-- Attachment #2: 0001-Make-relative-URL-parsing-and-resolution-consistent-.patch --]
[-- Type: application/octet-stream, Size: 29637 bytes --]
From 371cc54ea800ec2004829790f38cadfc031eadf0 Mon Sep 17 00:00:00 2001
From: Alain Nicolas Schneble <a.s@realize.ch>
Date: Fri, 27 Nov 2015 15:54:12 +0100
Subject: [PATCH] Make relative URL parsing and resolution consistent with RFC
3986
* test/automated/url-parse-tests.el: add tests covering url-generic-parse-url.
* test/automated/url-expand-tests.el: add tests covering url-expand-file-name.
* test/automated/url-parse.el (url-generic-parse-url): keep empty fragment
information in URL-struct.
* lisp/url/url-parse.el (url-path-and-query): do not artificially turn empty
path and query into nil path and query, respectively.
* lisp/url/url-expand.el (url-expander-remove-relative-links): do not turn
empty path into an absolute path ("/").
* lisp/url/url-expand.el (url-expand-file-name): properly resolve
fragment-only URLs. Do not just return them unchanged.
* lisp/url/url-expand.el (url-default-expander): an empty path in the relative
reference URL should not drop the last segment.
---
lisp/url/url-expand.el | 84 +++++++++----------
lisp/url/url-parse.el | 5 +-
test/automated/url-expand-tests.el | 105 +++++++++++++++++++++++
test/automated/url-parse-tests.el | 167 +++++++++++++++++++++++++++++++++++++
4 files changed, 313 insertions(+), 48 deletions(-)
create mode 100644 test/automated/url-expand-tests.el
create mode 100644 test/automated/url-parse-tests.el
diff --git a/lisp/url/url-expand.el b/lisp/url/url-expand.el
index c468a79..600a36d 100644
--- a/lisp/url/url-expand.el
+++ b/lisp/url/url-expand.el
@@ -26,32 +26,35 @@
(require 'url-parse)
(defun url-expander-remove-relative-links (name)
- ;; Strip . and .. from pathnames
- (let ((new (if (not (string-match "^/" name))
- (concat "/" name)
- name)))
-
- ;; If it ends with a '/.' or '/..', tack on a trailing '/' sot hat
- ;; the tests that follow are not too complicated in terms of
- ;; looking for '..' or '../', etc.
- (if (string-match "/\\.+$" new)
- (setq new (concat new "/")))
-
- ;; Remove '/./' first
- (while (string-match "/\\(\\./\\)" new)
- (setq new (concat (substring new 0 (match-beginning 1))
- (substring new (match-end 1)))))
-
- ;; Then remove '/../'
- (while (string-match "/\\([^/]*/\\.\\./\\)" new)
- (setq new (concat (substring new 0 (match-beginning 1))
- (substring new (match-end 1)))))
-
- ;; Remove cruft at the beginning of the string, so people that put
- ;; in extraneous '..' because they are morons won't lose.
- (while (string-match "^/\\.\\.\\(/\\)" new)
- (setq new (substring new (match-beginning 1) nil)))
- new))
+ (if (equal name "")
+ ;; An empty name is a properly valid relative URL reference/path.
+ ""
+ ;; Strip . and .. from pathnames
+ (let ((new (if (not (string-match "^/" name))
+ (concat "/" name)
+ name)))
+
+ ;; If it ends with a '/.' or '/..', tack on a trailing '/' sot hat
+ ;; the tests that follow are not too complicated in terms of
+ ;; looking for '..' or '../', etc.
+ (if (string-match "/\\.+$" new)
+ (setq new (concat new "/")))
+
+ ;; Remove '/./' first
+ (while (string-match "/\\(\\./\\)" new)
+ (setq new (concat (substring new 0 (match-beginning 1))
+ (substring new (match-end 1)))))
+
+ ;; Then remove '/../'
+ (while (string-match "/\\([^/]*/\\.\\./\\)" new)
+ (setq new (concat (substring new 0 (match-beginning 1))
+ (substring new (match-end 1)))))
+
+ ;; Remove cruft at the beginning of the string, so people that put
+ ;; in extraneous '..' because they are morons won't lose.
+ (while (string-match "^/\\.\\.\\(/\\)" new)
+ (setq new (substring new (match-beginning 1) nil)))
+ new)))
(defun url-expand-file-name (url &optional default)
"Convert URL to a fully specified URL, and canonicalize it.
@@ -89,8 +92,6 @@ path components followed by `..' are removed, along with the `..' itself."
(cond
((= (length url) 0) ; nil or empty string
(url-recreate-url default))
- ((string-match "^#" url) ; Offset link, use it raw
- url)
((string-match url-nonrelative-link url) ; Fully-qualified URL, return it immediately
url)
(t
@@ -120,29 +121,24 @@ path components followed by `..' are removed, along with the `..' itself."
(setf (url-host urlobj) (or (url-host urlobj) (url-host defobj))))
(if (string= "ftp" (url-type urlobj))
(setf (url-user urlobj) (or (url-user urlobj) (url-user defobj))))
- (if (string= (url-filename urlobj) "")
- (setf (url-filename urlobj) "/"))
;; If the object we're expanding from is full, then we are now
;; full.
(unless (url-fullness urlobj)
(setf (url-fullness urlobj) (url-fullness defobj)))
- (if (string-match "^/" (url-filename urlobj))
- nil
- (let ((query nil)
- (file nil)
- (sepchar nil))
- (if (string-match "[?#]" (url-filename urlobj))
- (setq query (substring (url-filename urlobj) (match-end 0))
- file (substring (url-filename urlobj) 0 (match-beginning 0))
- sepchar (substring (url-filename urlobj) (match-beginning 0) (match-end 0)))
- (setq file (url-filename urlobj)))
+ (let* ((pathandquery (url-path-and-query urlobj))
+ (defpathandquery (url-path-and-query defobj))
+ (file (car pathandquery))
+ (query (or (cdr pathandquery) (and (equal file "") (cdr defpathandquery)))))
+ (if (string-match "^/" (url-filename urlobj))
+ (setq file (url-expander-remove-relative-links file))
;; We use concat rather than expand-file-name to combine
;; directory and file name, since urls do not follow the same
;; rules as local files on all platforms.
- (setq file (url-expander-remove-relative-links
- (concat (url-file-directory (url-filename defobj)) file)))
- (setf (url-filename urlobj)
- (if query (concat file sepchar query) file))))))
+ (setq file (url-expander-remove-relative-links
+ (if (equal file "")
+ (or (car (url-path-and-query defobj)) "")
+ (concat (url-file-directory (url-filename defobj)) file)))))
+ (setf (url-filename urlobj) (if query (concat file "?" query) file)))))
(provide 'url-expand)
diff --git a/lisp/url/url-parse.el b/lisp/url/url-parse.el
index dbf0c38..c3159a7 100644
--- a/lisp/url/url-parse.el
+++ b/lisp/url/url-parse.el
@@ -59,8 +59,6 @@ where each of PATH and QUERY are strings or nil."
(setq path (substring name 0 (match-beginning 0))
query (substring name (match-end 0)))
(setq path name)))
- (if (equal path "") (setq path nil))
- (if (equal query "") (setq query nil))
(cons path query)))
(defun url-port-if-non-default (urlobj)
@@ -217,8 +215,7 @@ parses to
(when (looking-at "#")
(let ((opoint (point)))
(forward-char 1)
- (unless (eobp)
- (setq fragment (buffer-substring (point) (point-max))))
+ (setq fragment (buffer-substring (point) (point-max)))
(delete-region opoint (point-max)))))
(if (and host (string-match "%[0-9][0-9]" host))
diff --git a/test/automated/url-expand-tests.el b/test/automated/url-expand-tests.el
new file mode 100644
index 0000000..88c9b3b
--- /dev/null
+++ b/test/automated/url-expand-tests.el
@@ -0,0 +1,105 @@
+;;; url-expand-tests.el --- Test suite for relative URI/URL resolution.
+
+;; Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+;; Author: Alain Nicolas Schneble <a.s@realize.ch>
+;; Version: 1.0
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Test cases covering URI reference resolution as described in RFC3986,
+;; section 5. Reference Resolution and especially the relative resolution
+;; rules specified in section 5.2. Relative Resolution.
+
+;; Each test calls `url-expand-file-name', typically with a relative
+;; reference URI and a base URI as string and compares the result (Actual)
+;; against a manually specified URI (Expected)
+
+;;; Code:
+
+(require 'url-expand)
+(require 'ert)
+
+(ert-deftest url-expand-file-name/relative-resolution-normal-examples ()
+ "RFC 3986, Section 5.4 Reference Resolution Examples / Section 5.4.1. Normal Examples"
+ (should (equal (url-expand-file-name "g:h" "http://a/b/c/d;p?q") "g:h"))
+ (should (equal (url-expand-file-name "g" "http://a/b/c/d;p?q") "http://a/b/c/g"))
+ (should (equal (url-expand-file-name "./g" "http://a/b/c/d;p?q") "http://a/b/c/g"))
+ (should (equal (url-expand-file-name "g/" "http://a/b/c/d;p?q") "http://a/b/c/g/"))
+ (should (equal (url-expand-file-name "/g" "http://a/b/c/d;p?q") "http://a/g"))
+ (should (equal (url-expand-file-name "//g" "http://a/b/c/d;p?q") "http://g"))
+ (should (equal (url-expand-file-name "?y" "http://a/b/c/d;p?q") "http://a/b/c/d;p?y"))
+ (should (equal (url-expand-file-name "g?y" "http://a/b/c/d;p?q") "http://a/b/c/g?y"))
+ (should (equal (url-expand-file-name "#s" "http://a/b/c/d;p?q") "http://a/b/c/d;p?q#s"))
+ (should (equal (url-expand-file-name "g#s" "http://a/b/c/d;p?q") "http://a/b/c/g#s"))
+ (should (equal (url-expand-file-name "g?y#s" "http://a/b/c/d;p?q") "http://a/b/c/g?y#s"))
+ (should (equal (url-expand-file-name ";x" "http://a/b/c/d;p?q") "http://a/b/c/;x"))
+ (should (equal (url-expand-file-name "g;x" "http://a/b/c/d;p?q") "http://a/b/c/g;x"))
+ (should (equal (url-expand-file-name "g;x?y#s" "http://a/b/c/d;p?q") "http://a/b/c/g;x?y#s"))
+ (should (equal (url-expand-file-name "" "http://a/b/c/d;p?q") "http://a/b/c/d;p?q"))
+ (should (equal (url-expand-file-name "." "http://a/b/c/d;p?q") "http://a/b/c/"))
+ (should (equal (url-expand-file-name "./" "http://a/b/c/d;p?q") "http://a/b/c/"))
+ (should (equal (url-expand-file-name ".." "http://a/b/c/d;p?q") "http://a/b/"))
+ (should (equal (url-expand-file-name "../" "http://a/b/c/d;p?q") "http://a/b/"))
+ (should (equal (url-expand-file-name "../g" "http://a/b/c/d;p?q") "http://a/b/g"))
+ (should (equal (url-expand-file-name "../.." "http://a/b/c/d;p?q") "http://a/"))
+ (should (equal (url-expand-file-name "../../" "http://a/b/c/d;p?q") "http://a/"))
+ (should (equal (url-expand-file-name "../../g" "http://a/b/c/d;p?q") "http://a/g")))
+
+(ert-deftest url-expand-file-name/relative-resolution-absolute-examples ()
+ "RFC 3986, Section 5.4 Reference Resolution Examples / Section 5.4.2. Abnormal Examples"
+ (should (equal (url-expand-file-name "../../../g" "http://a/b/c/d;p?q") "http://a/g"))
+ (should (equal (url-expand-file-name "../../../../g" "http://a/b/c/d;p?q") "http://a/g"))
+
+ (should (equal (url-expand-file-name "/./g" "http://a/b/c/d;p?q") "http://a/g"))
+ (should (equal (url-expand-file-name "/../g" "http://a/b/c/d;p?q") "http://a/g"))
+ (should (equal (url-expand-file-name "g." "http://a/b/c/d;p?q") "http://a/b/c/g."))
+ (should (equal (url-expand-file-name ".g" "http://a/b/c/d;p?q") "http://a/b/c/.g"))
+ (should (equal (url-expand-file-name "g.." "http://a/b/c/d;p?q") "http://a/b/c/g.."))
+ (should (equal (url-expand-file-name "..g" "http://a/b/c/d;p?q") "http://a/b/c/..g"))
+
+ (should (equal (url-expand-file-name "./../g" "http://a/b/c/d;p?q") "http://a/b/g"))
+ (should (equal (url-expand-file-name "./g/." "http://a/b/c/d;p?q") "http://a/b/c/g/"))
+ (should (equal (url-expand-file-name "g/./h" "http://a/b/c/d;p?q") "http://a/b/c/g/h"))
+ (should (equal (url-expand-file-name "g/../h" "http://a/b/c/d;p?q") "http://a/b/c/h"))
+ (should (equal (url-expand-file-name "g;x=1/./y" "http://a/b/c/d;p?q") "http://a/b/c/g;x=1/y"))
+ (should (equal (url-expand-file-name "g;x=1/../y" "http://a/b/c/d;p?q") "http://a/b/c/y"))
+
+ (should (equal (url-expand-file-name "g?y/./x" "http://a/b/c/d;p?q") "http://a/b/c/g?y/./x"))
+ (should (equal (url-expand-file-name "g?y/../x" "http://a/b/c/d;p?q") "http://a/b/c/g?y/../x"))
+ (should (equal (url-expand-file-name "g#s/./x" "http://a/b/c/d;p?q") "http://a/b/c/g#s/./x"))
+ (should (equal (url-expand-file-name "g#s/../x" "http://a/b/c/d;p?q") "http://a/b/c/g#s/../x"))
+
+ (should (equal (url-expand-file-name "http:g" "http://a/b/c/d;p?q") "http:g")) ; for strict parsers
+ )
+
+(ert-deftest url-expand-file-name/relative-resolution-additional-examples ()
+ "Reference Resolution Examples / Arbitrary Examples"
+ (should (equal (url-expand-file-name "" "http://host/foobar") "http://host/foobar"))
+ (should (equal (url-expand-file-name "?y" "http://a/b/c/d") "http://a/b/c/d?y"))
+ (should (equal (url-expand-file-name "?y" "http://a/b/c/d/") "http://a/b/c/d/?y"))
+ (should (equal (url-expand-file-name "?y#fragment" "http://a/b/c/d;p?q") "http://a/b/c/d;p?y#fragment"))
+ (should (equal (url-expand-file-name "#bar" "http://host") "http://host#bar"))
+ (should (equal (url-expand-file-name "#bar" "http://host/") "http://host/#bar"))
+ (should (equal (url-expand-file-name "#bar" "http://host/foo") "http://host/foo#bar"))
+ (should (equal (url-expand-file-name "foo#bar" "http://host/foobar") "http://host/foo#bar"))
+ (should (equal (url-expand-file-name "foo#bar" "http://host/foobar/") "http://host/foobar/foo#bar")))
+
+(provide 'url-expand-tests)
+
+;;; url-expand-tests.el ends here
diff --git a/test/automated/url-parse-tests.el b/test/automated/url-parse-tests.el
new file mode 100644
index 0000000..cded361
--- /dev/null
+++ b/test/automated/url-parse-tests.el
@@ -0,0 +1,167 @@
+;;; url-parse-tests.el --- Test suite for URI/URL parsing.
+
+;; Copyright (C) 2012-2015 Free Software Foundation, Inc.
+
+;; Author: Alain Nicolas Schneble <a.s@realize.ch>
+;; Version: 1.0
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Test cases covering generic URI syntax as described in RFC3986,
+;; section 3. Syntax Components and 4. Usage. See also appendix
+;; A. Collected ABNF for URI, as the example given here are all
+;; productions of this grammar.
+
+;; Each tests parses a given URI string - whether relative or absolute -
+;; using `url-generic-parse-url' and compares the constructed
+;; URL-struct (Actual) against a manually `url-parse-make-urlobj'-
+;; constructed URL-struct (Expected).
+
+;;; Code:
+
+(require 'url-parse)
+(require 'ert)
+
+(ert-deftest url-generic-parse-url/generic-uri-examples ()
+ "RFC 3986, section 1.1.2. Examples / Example illustrating several URI schemes and variations in their common syntax components"
+ (should (equal (url-generic-parse-url "ftp://ftp.is.co.za/rfc/rfc1808.txt") (url-parse-make-urlobj "ftp" nil nil "ftp.is.co.za" nil "/rfc/rfc1808.txt" nil nil t)))
+ (should (equal (url-generic-parse-url "http://www.ietf.org/rfc/rfc2396.txt") (url-parse-make-urlobj "http" nil nil "www.ietf.org" nil "/rfc/rfc2396.txt" nil nil t)))
+ (should (equal (url-generic-parse-url "ldap://[2001:db8::7]/c=GB?objectClass?one") (url-parse-make-urlobj "ldap" nil nil "[2001:db8::7]" nil "/c=GB?objectClass?one" nil nil t)))
+ (should (equal (url-generic-parse-url "mailto:John.Doe@example.com") (url-parse-make-urlobj "mailto" nil nil nil nil "John.Doe@example.com" nil nil nil)))
+ (should (equal (url-generic-parse-url "news:comp.infosystems.www.servers.unix") (url-parse-make-urlobj "news" nil nil nil nil "comp.infosystems.www.servers.unix" nil nil nil)))
+ (should (equal (url-generic-parse-url "tel:+1-816-555-1212") (url-parse-make-urlobj "tel" nil nil nil nil "+1-816-555-1212" nil nil nil)))
+ (should (equal (url-generic-parse-url "telnet://192.0.2.16:80/") (url-parse-make-urlobj "telnet" nil nil "192.0.2.16" 80 "/" nil nil t)))
+ (should (equal (url-generic-parse-url "urn:oasis:names:specification:docbook:dtd:xml:4.1.2") (url-parse-make-urlobj "urn" nil nil nil nil "oasis:names:specification:docbook:dtd:xml:4.1.2" nil nil nil))))
+
+(ert-deftest url-generic-parse-url/generic-uri ()
+ "RFC 3986, section 3. Syntax Components / generic URI syntax"
+ ;; empty path
+ (should (equal (url-generic-parse-url "http://host#") (url-parse-make-urlobj "http" nil nil "host" nil "" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host?#") (url-parse-make-urlobj "http" nil nil "host" nil "?" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host?query#") (url-parse-make-urlobj "http" nil nil "host" nil "?query" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host?#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "?" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host?query#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "?query" "fragment" nil t)))
+ ;; absolute path /
+ (should (equal (url-generic-parse-url "http://host/#") (url-parse-make-urlobj "http" nil nil "host" nil "/" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/?#") (url-parse-make-urlobj "http" nil nil "host" nil "/?" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/?query#") (url-parse-make-urlobj "http" nil nil "host" nil "/?query" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/?#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/?" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/?query#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/?query" "fragment" nil t)))
+ ;; absolute path /foo
+ (should (equal (url-generic-parse-url "http://host/foo#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo?#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo?" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo?query#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo?query" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo?#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo?" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo?query#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo?query" "fragment" nil t)))
+ ;; absolute path /foo/
+ (should (equal (url-generic-parse-url "http://host/foo/#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/?#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/?" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/?query#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/?query" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/?#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/?" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/?query#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/?query" "fragment" nil t)))
+ ;; absolute path /foo/bar
+ (should (equal (url-generic-parse-url "http://host/foo/bar#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar?#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar?" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar?query#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar?query" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar?#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar?" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar?query#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar?query" "fragment" nil t)))
+ ;; absolute path /foo/bar/
+ (should (equal (url-generic-parse-url "http://host/foo/bar/#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar/#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar/?#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/?" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar/?query#") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/?query" "" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar/?#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/?" "fragment" nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar/?query#fragment") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/?query" "fragment" nil t)))
+ ;; for more examples of URIs without fragments, see tests covering section 4.3. Absolute URI
+ )
+
+(ert-deftest url-generic-parse-url/network-path-reference ()
+ "RFC 3986, section 4.2. Relative Reference / network-path reference: a relative reference that begins with two slash characters"
+ (should (equal (url-generic-parse-url "//host") (url-parse-make-urlobj nil nil nil "host" nil "" nil nil t)))
+ (should (equal (url-generic-parse-url "//host/") (url-parse-make-urlobj nil nil nil "host" nil "/" nil nil t)))
+ (should (equal (url-generic-parse-url "//host/foo") (url-parse-make-urlobj nil nil nil "host" nil "/foo" nil nil t)))
+ (should (equal (url-generic-parse-url "//host/foo/bar") (url-parse-make-urlobj nil nil nil "host" nil "/foo/bar" nil nil t)))
+ (should (equal (url-generic-parse-url "//host/foo/bar/") (url-parse-make-urlobj nil nil nil "host" nil "/foo/bar/" nil nil t))))
+
+(ert-deftest url-generic-parse-url/absolute-path-reference ()
+ "RFC 3986, section 4.2. Relative Reference / absolute-path reference: a relative reference that begins with a single slash character"
+ (should (equal (url-generic-parse-url "/") (url-parse-make-urlobj nil nil nil nil nil "/" nil nil nil)))
+ (should (equal (url-generic-parse-url "/foo") (url-parse-make-urlobj nil nil nil nil nil "/foo" nil nil nil)))
+ (should (equal (url-generic-parse-url "/foo/bar") (url-parse-make-urlobj nil nil nil nil nil "/foo/bar" nil nil nil)))
+ (should (equal (url-generic-parse-url "/foo/bar/") (url-parse-make-urlobj nil nil nil nil nil "/foo/bar/" nil nil nil)))
+ (should (equal (url-generic-parse-url "/foo/bar#") (url-parse-make-urlobj nil nil nil nil nil "/foo/bar" "" nil nil)))
+ (should (equal (url-generic-parse-url "/foo/bar/#") (url-parse-make-urlobj nil nil nil nil nil "/foo/bar/" "" nil nil))))
+
+(ert-deftest url-generic-parse-url/relative-path-reference ()
+ "RFC 3986, section 4.2. Relative Reference / relative-path reference: a relative reference that does not begin with a slash character"
+ (should (equal (url-generic-parse-url "foo") (url-parse-make-urlobj nil nil nil nil nil "foo" nil nil nil)))
+ (should (equal (url-generic-parse-url "foo/bar") (url-parse-make-urlobj nil nil nil nil nil "foo/bar" nil nil nil)))
+ (should (equal (url-generic-parse-url "foo/bar/") (url-parse-make-urlobj nil nil nil nil nil "foo/bar/" nil nil nil)))
+ (should (equal (url-generic-parse-url "./foo") (url-parse-make-urlobj nil nil nil nil nil "./foo" nil nil nil)))
+ (should (equal (url-generic-parse-url "./foo/bar") (url-parse-make-urlobj nil nil nil nil nil "./foo/bar" nil nil nil)))
+ (should (equal (url-generic-parse-url "./foo/bar/") (url-parse-make-urlobj nil nil nil nil nil "./foo/bar/" nil nil nil)))
+ (should (equal (url-generic-parse-url "../foo") (url-parse-make-urlobj nil nil nil nil nil "../foo" nil nil nil)))
+ (should (equal (url-generic-parse-url "../foo/bar") (url-parse-make-urlobj nil nil nil nil nil "../foo/bar" nil nil nil)))
+ (should (equal (url-generic-parse-url "../foo/bar/") (url-parse-make-urlobj nil nil nil nil nil "../foo/bar/" nil nil nil)))
+ (should (equal (url-generic-parse-url "./this:that") (url-parse-make-urlobj nil nil nil nil nil "./this:that" nil nil nil)))
+ ;; for more examples of relative-path references, see tests covering section 4.4. Same-Document Reference
+ )
+
+(ert-deftest url-generic-parse-url/absolute-uri ()
+ "RFC 3986, section 4.3. Absolute URI / absolute URI: absolute form of a URI without a fragment identifier"
+ ;; empty path
+ (should (equal (url-generic-parse-url "http://host") (url-parse-make-urlobj "http" nil nil "host" nil "" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host?") (url-parse-make-urlobj "http" nil nil "host" nil "?" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host?query") (url-parse-make-urlobj "http" nil nil "host" nil "?query" nil nil t)))
+ ;; absolute path /
+ (should (equal (url-generic-parse-url "http://host/") (url-parse-make-urlobj "http" nil nil "host" nil "/" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/?") (url-parse-make-urlobj "http" nil nil "host" nil "/?" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/?query") (url-parse-make-urlobj "http" nil nil "host" nil "/?query" nil nil t)))
+ ;; absolute path /foo
+ (should (equal (url-generic-parse-url "http://host/foo") (url-parse-make-urlobj "http" nil nil "host" nil "/foo" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo?") (url-parse-make-urlobj "http" nil nil "host" nil "/foo?" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo?query") (url-parse-make-urlobj "http" nil nil "host" nil "/foo?query" nil nil t)))
+ ;; absolute path /foo/
+ (should (equal (url-generic-parse-url "http://host/foo/") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/?") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/?" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/?query") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/?query" nil nil t)))
+ ;; absolute path /foo/bar
+ (should (equal (url-generic-parse-url "http://host/foo/bar") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar?") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar?" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar?query") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar?query" nil nil t)))
+ ;; absolute path /foo/bar/
+ (should (equal (url-generic-parse-url "http://host/foo/bar/") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar/?") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/?" nil nil t)))
+ (should (equal (url-generic-parse-url "http://host/foo/bar/?query") (url-parse-make-urlobj "http" nil nil "host" nil "/foo/bar/?query" nil nil t)))
+ ;; example mentioned in RFC3986, section 5.4. Reference Resolution Examples
+ (should (equal (url-generic-parse-url "http://a/b/c/d;p?q") (url-parse-make-urlobj "http" nil nil "a" nil "/b/c/d;p?q" nil nil t))))
+
+(ert-deftest url-generic-parse-url/same-decument-reference ()
+ "RFC 3986, section 4.4. Same-Document Reference / same-document reference: empty or number sign (\"#\") followed by a fragment identifier"
+ (should (equal (url-generic-parse-url "") (url-parse-make-urlobj nil nil nil nil nil "" nil nil nil)))
+ (should (equal (url-generic-parse-url "#") (url-parse-make-urlobj nil nil nil nil nil "" "" nil nil)))
+ (should (equal (url-generic-parse-url "#foo") (url-parse-make-urlobj nil nil nil nil nil "" "foo" nil nil))))
+
+(provide 'url-parse-tests)
+
+;;; url-parse-tests.el ends here
--
2.6.2.windows.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: url-expand.el and url-parse.el not conforming to RFC3986
2015-11-27 15:22 url-expand.el and url-parse.el not conforming to RFC3986 Alain Schneble (Realize IT GmbH)
@ 2015-11-28 14:58 ` Stephen Leake
2015-11-29 0:10 ` Alain Schneble
0 siblings, 1 reply; 3+ messages in thread
From: Stephen Leake @ 2015-11-28 14:58 UTC (permalink / raw)
To: Alain Schneble (Realize IT GmbH); +Cc: emacs-devel@gnu.org
"Alain Schneble (Realize IT GmbH)" <alain.schneble@realize.ch> writes:
> url-expand.el and url-parse.el seem to not follow RFC3986 "Uniform
> Resource Identifier (URI): Generic Syntax" in some cases. But I guess
> they should. So I started to study RFC3986 in more details and write
> tests against url-expand-file-name and url-generic-parse-url (see
> attached patch).
>
> The tests reveal the following issues:
Thanks for writing these tests, and the fixes.
> Please let me know if I should follow a different procedure to submit
> these changes. I signed the copyright assignment "GNU EMACS" this
> year.
Please file a bug report (use M-x report-emacs-bug), and follow up here
with the bug number.
In the first bug report, just outline the issue. In a follow up bug
report, attach the patch.
--
-- Stephe
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: url-expand.el and url-parse.el not conforming to RFC3986
2015-11-28 14:58 ` Stephen Leake
@ 2015-11-29 0:10 ` Alain Schneble
0 siblings, 0 replies; 3+ messages in thread
From: Alain Schneble @ 2015-11-29 0:10 UTC (permalink / raw)
To: Stephen Leake; +Cc: emacs-devel@gnu.org
Stephen Leake <stephen_leake@stephe-leake.org> writes:
> "Alain Schneble (Realize IT GmbH)" <alain.schneble@realize.ch> writes:
>
>> url-expand.el and url-parse.el seem to not follow RFC3986 "Uniform
>> Resource Identifier (URI): Generic Syntax" in some cases. But I guess
>> they should. So I started to study RFC3986 in more details and write
>> tests against url-expand-file-name and url-generic-parse-url (see
>> attached patch).
>>
>> The tests reveal the following issues:
>
> Thanks for writing these tests, and the fixes.
>
>> Please let me know if I should follow a different procedure to submit
>> these changes. I signed the copyright assignment "GNU EMACS" this
>> year.
>
> Please file a bug report (use M-x report-emacs-bug), and follow up here
> with the bug number.
>
> In the first bug report, just outline the issue. In a follow up bug
> report, attach the patch.
Many thanks for your help! I filed a bug report (bug#22044) and replied
to it with the proposed patch. Unfortunately, it seems like the pseudo
header "Tags: patch" in the body of the reply was not properly processed
by debbugs as it still appears in the message. I have no idea why...
I would be very happy to help if there is anything else I can do for the
proposed changes to be accepted.
Alain
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2015-11-29 0:10 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-11-27 15:22 url-expand.el and url-parse.el not conforming to RFC3986 Alain Schneble (Realize IT GmbH)
2015-11-28 14:58 ` Stephen Leake
2015-11-29 0:10 ` Alain Schneble
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.