summaryrefslogtreecommitdiff
path: root/lisp/url/url-http.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/url/url-http.el')
-rw-r--r--lisp/url/url-http.el187
1 files changed, 120 insertions, 67 deletions
diff --git a/lisp/url/url-http.el b/lisp/url/url-http.el
index 1bcfc10645d..527760118d4 100644
--- a/lisp/url/url-http.el
+++ b/lisp/url/url-http.el
@@ -54,6 +54,7 @@
(defvar url-http-target-url)
(defvar url-http-transfer-encoding)
(defvar url-show-status)
+(defvar url-http-referer)
(require 'url-gw)
(require 'url-parse)
@@ -149,15 +150,6 @@ request.")
;; These routines will allow us to implement persistent HTTP
;; connections.
(defsubst url-http-debug (&rest args)
- (if quit-flag
- (let ((proc (get-buffer-process (current-buffer))))
- ;; The user hit C-g, honor it! Some things can get in an
- ;; incredibly tight loop (chunked encoding)
- (if proc
- (progn
- (set-process-sentinel proc nil)
- (set-process-filter proc nil)))
- (error "Transfer interrupted!")))
(apply 'url-debug 'http args))
(defun url-http-mark-connection-as-busy (host port proc)
@@ -238,6 +230,35 @@ request.")
emacs-info os-info))
" ")))
+(defun url-http--get-referer (url)
+ (url-http-debug "getting referer from buffer: buffer:%S target-url:%S lastloc:%S" (current-buffer) url url-current-lastloc)
+ (when url-current-lastloc
+ (if (not (url-p url-current-lastloc))
+ (setq url-current-lastloc (url-generic-parse-url url-current-lastloc)))
+ (let ((referer (copy-sequence url-current-lastloc)))
+ (setf (url-host referer) (puny-encode-domain (url-host referer)))
+ (let ((referer-string (url-recreate-url referer)))
+ (when (and (not (memq url-privacy-level '(low high paranoid)))
+ (not (and (listp url-privacy-level)
+ (memq 'lastloc url-privacy-level))))
+ ;; url-privacy-level allows referer. But url-lastloc-privacy-level
+ ;; may restrict who we send it to.
+ (cl-case url-lastloc-privacy-level
+ (host-match
+ (let ((referer-host (url-host referer))
+ (url-host (url-host url)))
+ (when (string= referer-host url-host)
+ referer-string)))
+ (domain-match
+ (let ((referer-domain (url-domain referer))
+ (url-domain (url-domain url)))
+ (when (and referer-domain
+ url-domain
+ (string= referer-domain url-domain))
+ referer-string)))
+ (otherwise
+ referer-string)))))))
+
;; Building an HTTP request
(defun url-http-user-agent-string ()
"Compute a User-Agent string.
@@ -254,8 +275,9 @@ The string is based on `url-privacy-level' and `url-user-agent'."
((eq url-user-agent 'default) (url-http--user-agent-default-string))))))
(if ua-string (format "User-Agent: %s\r\n" (string-trim ua-string)) "")))
-(defun url-http-create-request (&optional ref-url)
- "Create an HTTP request for `url-http-target-url', referred to by REF-URL."
+(defun url-http-create-request ()
+ "Create an HTTP request for `url-http-target-url'.
+Use `url-http-referer' as the Referer-header (subject to `url-privacy-level')."
(let* ((extra-headers)
(request nil)
(no-cache (cdr-safe (assoc "Pragma" url-http-extra-headers)))
@@ -268,13 +290,14 @@ The string is based on `url-privacy-level' and `url-user-agent'."
'url-http-proxy-basic-auth-storage))
(url-get-authentication url-http-proxy nil 'any nil))))
(real-fname (url-filename url-http-target-url))
- (host (url-http--encode-string (url-host url-http-target-url)))
+ (host (url-host url-http-target-url))
(auth (if (cdr-safe (assoc "Authorization" url-http-extra-headers))
nil
(url-get-authentication (or
(and (boundp 'proxy-info)
proxy-info)
- url-http-target-url) nil 'any nil))))
+ url-http-target-url) nil 'any nil)))
+ (ref-url (url-http--encode-string url-http-referer)))
(if (equal "" real-fname)
(setq real-fname "/"))
(setq no-cache (and no-cache (string-match "no-cache" no-cache)))
@@ -288,12 +311,6 @@ The string is based on `url-privacy-level' and `url-user-agent'."
(string= ref-url "")))
(setq ref-url nil))
- ;; We do not want to expose the referrer if the user is paranoid.
- (if (or (memq url-privacy-level '(low high paranoid))
- (and (listp url-privacy-level)
- (memq 'lastloc url-privacy-level)))
- (setq ref-url nil))
-
;; url-http-extra-headers contains an assoc-list of
;; header/value pairs that we need to put into the request.
(setq extra-headers (mapconcat
@@ -329,9 +346,11 @@ The string is based on `url-privacy-level' and `url-user-agent'."
(url-scheme-get-property
(url-type url-http-target-url) 'default-port))
(format
- "Host: %s:%d\r\n" (puny-encode-domain host)
+ "Host: %s:%d\r\n" (url-http--encode-string
+ (puny-encode-domain host))
(url-port url-http-target-url))
- (format "Host: %s\r\n" (puny-encode-domain host)))
+ (format "Host: %s\r\n"
+ (url-http--encode-string (puny-encode-domain host))))
;; Who its from
(if url-personal-mail-address
(concat
@@ -485,11 +504,11 @@ Return the number of characters removed."
(url-http-debug "url-http-parse-response called in (%s)" (buffer-name))
(goto-char (point-min))
(skip-chars-forward " \t\n") ; Skip any blank crap
- (skip-chars-forward "HTTP/") ; Skip HTTP Version
+ (skip-chars-forward "/HPT") ; Skip HTTP Version "HTTP/".
(setq url-http-response-version
(buffer-substring (point)
(progn
- (skip-chars-forward "[0-9].")
+ (skip-chars-forward "0-9.")
(point))))
(setq url-http-response-status (read (current-buffer))))
@@ -511,6 +530,23 @@ work correctly."
(declare-function gnutls-peer-status "gnutls.c" (proc))
(declare-function gnutls-negotiate "gnutls.el" t t)
+(defun url-http--insert-file-helper (buffer url &optional visit)
+ (with-current-buffer buffer
+ (when (bound-and-true-p url-http-response-status)
+ ;; Don't signal an error if VISIT is non-nil, because
+ ;; 'insert-file-contents' doesn't. This is required to
+ ;; support, e.g., 'browse-url-emacs', which is a fancy way of
+ ;; visiting the HTML source of a URL: in that case, we want to
+ ;; display a file buffer even if the URL does not exist and
+ ;; 'url-retrieve-synchronously' returns 404 or whatever.
+ (unless (or visit
+ (and (>= url-http-response-status 200)
+ (< url-http-response-status 300)))
+ (let ((desc (nth 2 (assq url-http-response-status url-http-codes))))
+ (kill-buffer buffer)
+ ;; Signal file-error per bug#16733.
+ (signal 'file-error (list url desc)))))))
+
(defun url-http-parse-headers ()
"Parse and handle HTTP specific headers.
Return t if and only if the current buffer is still active and
@@ -585,7 +621,7 @@ should be shown to the user."
;; 206 Partial content
;; 207 Multi-status (Added by DAV)
(pcase status-symbol
- ((or `no-content `reset-content)
+ ((or 'no-content 'reset-content)
;; No new data, just stay at the same document
(url-mark-buffer-as-dead buffer))
(_
@@ -606,7 +642,7 @@ should be shown to the user."
(let ((redirect-uri (or (mail-fetch-field "Location")
(mail-fetch-field "URI"))))
(pcase status-symbol
- (`multiple-choices ; 300
+ ('multiple-choices ; 300
;; Quoth the spec (section 10.3.1)
;; -------------------------------
;; The requested resource corresponds to any one of a set of
@@ -623,20 +659,26 @@ should be shown to the user."
;; We do not support agent-driven negotiation, so we just
;; redirect to the preferred URI if one is provided.
nil)
- (`see-other ; 303
+ ('found ; 302
+ ;; 302 Found was ambiguously defined in the standards, but
+ ;; it's now recommended that it's treated like 303 instead
+ ;; of 307, since that's what most servers expect.
+ (setq url-http-method "GET"
+ url-http-data nil))
+ ('see-other ; 303
;; The response to the request can be found under a different
;; URI and SHOULD be retrieved using a GET method on that
;; resource.
(setq url-http-method "GET"
url-http-data nil))
- (`not-modified ; 304
+ ('not-modified ; 304
;; The 304 response MUST NOT contain a message-body.
(url-http-debug "Extracting document from cache... (%s)"
(url-cache-create-filename (url-view-url t)))
(url-cache-extract (url-cache-create-filename (url-view-url t)))
(setq redirect-uri nil
success t))
- (`use-proxy ; 305
+ ('use-proxy ; 305
;; The requested resource MUST be accessed through the
;; proxy given by the Location field. The Location field
;; gives the URI of the proxy. The recipient is expected
@@ -734,50 +776,50 @@ should be shown to the user."
;; 424 Failed Dependency
(setq success
(pcase status-symbol
- (`unauthorized ; 401
+ ('unauthorized ; 401
;; The request requires user authentication. The response
;; MUST include a WWW-Authenticate header field containing a
;; challenge applicable to the requested resource. The
;; client MAY repeat the request with a suitable
;; Authorization header field.
(url-http-handle-authentication nil))
- (`payment-required ; 402
+ ('payment-required ; 402
;; This code is reserved for future use
(url-mark-buffer-as-dead buffer)
(error "Somebody wants you to give them money"))
- (`forbidden ; 403
+ ('forbidden ; 403
;; The server understood the request, but is refusing to
;; fulfill it. Authorization will not help and the request
;; SHOULD NOT be repeated.
t)
- (`not-found ; 404
+ ('not-found ; 404
;; Not found
t)
- (`method-not-allowed ; 405
+ ('method-not-allowed ; 405
;; The method specified in the Request-Line is not allowed
;; for the resource identified by the Request-URI. The
;; response MUST include an Allow header containing a list of
;; valid methods for the requested resource.
t)
- (`not-acceptable ; 406
+ ('not-acceptable ; 406
;; The resource identified by the request is only capable of
;; generating response entities which have content
;; characteristics not acceptable according to the accept
;; headers sent in the request.
t)
- (`proxy-authentication-required ; 407
+ ('proxy-authentication-required ; 407
;; This code is similar to 401 (Unauthorized), but indicates
;; that the client must first authenticate itself with the
;; proxy. The proxy MUST return a Proxy-Authenticate header
;; field containing a challenge applicable to the proxy for
;; the requested resource.
(url-http-handle-authentication t))
- (`request-timeout ; 408
+ ('request-timeout ; 408
;; The client did not produce a request within the time that
;; the server was prepared to wait. The client MAY repeat
;; the request without modifications at any later time.
t)
- (`conflict ; 409
+ ('conflict ; 409
;; The request could not be completed due to a conflict with
;; the current state of the resource. This code is only
;; allowed in situations where it is expected that the user
@@ -786,11 +828,11 @@ should be shown to the user."
;; information for the user to recognize the source of the
;; conflict.
t)
- (`gone ; 410
+ ('gone ; 410
;; The requested resource is no longer available at the
;; server and no forwarding address is known.
t)
- (`length-required ; 411
+ ('length-required ; 411
;; The server refuses to accept the request without a defined
;; Content-Length. The client MAY repeat the request if it
;; adds a valid Content-Length header field containing the
@@ -800,29 +842,29 @@ should be shown to the user."
;; `url-http-create-request' automatically calculates the
;; content-length.
t)
- (`precondition-failed ; 412
+ ('precondition-failed ; 412
;; The precondition given in one or more of the
;; request-header fields evaluated to false when it was
;; tested on the server.
t)
- ((or `request-entity-too-large `request-uri-too-large) ; 413 414
+ ((or 'request-entity-too-large 'request-uri-too-large) ; 413 414
;; The server is refusing to process a request because the
;; request entity|URI is larger than the server is willing or
;; able to process.
t)
- (`unsupported-media-type ; 415
+ ('unsupported-media-type ; 415
;; The server is refusing to service the request because the
;; entity of the request is in a format not supported by the
;; requested resource for the requested method.
t)
- (`requested-range-not-satisfiable ; 416
+ ('requested-range-not-satisfiable ; 416
;; A server SHOULD return a response with this status code if
;; a request included a Range request-header field, and none
;; of the range-specifier values in this field overlap the
;; current extent of the selected resource, and the request
;; did not include an If-Range request-header field.
t)
- (`expectation-failed ; 417
+ ('expectation-failed ; 417
;; The expectation given in an Expect request-header field
;; could not be met by this server, or, if the server is a
;; proxy, the server has unambiguous evidence that the
@@ -849,16 +891,16 @@ should be shown to the user."
;; 507 Insufficient storage
(setq success t)
(pcase url-http-response-status
- (`not-implemented ; 501
+ ('not-implemented ; 501
;; The server does not support the functionality required to
;; fulfill the request.
nil)
- (`bad-gateway ; 502
+ ('bad-gateway ; 502
;; The server, while acting as a gateway or proxy, received
;; an invalid response from the upstream server it accessed
;; in attempting to fulfill the request.
nil)
- (`service-unavailable ; 503
+ ('service-unavailable ; 503
;; The server is currently unable to handle the request due
;; to a temporary overloading or maintenance of the server.
;; The implication is that this is a temporary condition
@@ -867,19 +909,19 @@ should be shown to the user."
;; header. If no Retry-After is given, the client SHOULD
;; handle the response as it would for a 500 response.
nil)
- (`gateway-timeout ; 504
+ ('gateway-timeout ; 504
;; The server, while acting as a gateway or proxy, did not
;; receive a timely response from the upstream server
;; specified by the URI (e.g. HTTP, FTP, LDAP) or some other
;; auxiliary server (e.g. DNS) it needed to access in
;; attempting to complete the request.
nil)
- (`http-version-not-supported ; 505
+ ('http-version-not-supported ; 505
;; The server does not support, or refuses to support, the
;; HTTP protocol version that was used in the request
;; message.
nil)
- (`insufficient-storage ; 507 (DAV)
+ ('insufficient-storage ; 507 (DAV)
;; The method could not be performed on the resource
;; because the server is unable to store the representation
;; needed to successfully complete the request. This
@@ -905,7 +947,8 @@ should be shown to the user."
(goto-char (point-min))
success))
-(declare-function zlib-decompress-region "decompress.c" (start end))
+(declare-function zlib-decompress-region "decompress.c"
+ (start end &optional allow-partial))
(defun url-handle-content-transfer-encoding ()
(let ((encoding (mail-fetch-field "content-encoding")))
@@ -917,7 +960,7 @@ should be shown to the user."
(widen)
(goto-char (point-min))
(when (search-forward "\n\n")
- (zlib-decompress-region (point) (point-max)))))))
+ (zlib-decompress-region (point) (point-max) t))))))
;; Miscellaneous
(defun url-http-activate-callback ()
@@ -973,7 +1016,8 @@ should be shown to the user."
(defun url-http-simple-after-change-function (_st _nd _length)
;; Function used when we do NOT know how long the document is going to be
;; Just _very_ simple 'downloaded %d' type of info.
- (url-lazy-message "Reading %s..." (file-size-human-readable (buffer-size))))
+ (url-lazy-message "Reading %s..."
+ (file-size-human-readable (buffer-size) 'iec " ")))
(defun url-http-content-length-after-change-function (_st nd _length)
"Function used when we DO know how long the document is going to be.
@@ -986,16 +1030,16 @@ the callback to be triggered."
(url-percentage (- nd url-http-end-of-headers)
url-http-content-length)
url-http-content-type
- (file-size-human-readable (- nd url-http-end-of-headers))
- (file-size-human-readable url-http-content-length)
+ (file-size-human-readable (- nd url-http-end-of-headers) 'iec " ")
+ (file-size-human-readable url-http-content-length 'iec " ")
(url-percentage (- nd url-http-end-of-headers)
url-http-content-length))
(url-display-percentage
"Reading... %s of %s (%d%%)"
(url-percentage (- nd url-http-end-of-headers)
url-http-content-length)
- (file-size-human-readable (- nd url-http-end-of-headers))
- (file-size-human-readable url-http-content-length)
+ (file-size-human-readable (- nd url-http-end-of-headers) 'iec " ")
+ (file-size-human-readable url-http-content-length 'iec " ")
(url-percentage (- nd url-http-end-of-headers)
url-http-content-length)))
@@ -1054,10 +1098,16 @@ the end of the document."
(if no-initial-crlf (skip-chars-forward "\r\n"))
(if (not (looking-at regexp))
(progn
- ;; Must not have received the entirety of the chunk header,
+ ;; Must not have received the entirety of the chunk header,
;; need to spin some more.
(url-http-debug "Did not see start of chunk @ %d!" (point))
(setq read-next-chunk nil))
+ ;; The data we got may have started in the middle of the
+ ;; initial chunk header, so move back to the start of the
+ ;; line and re-compute.
+ (when (= url-http-chunked-counter 0)
+ (beginning-of-line)
+ (looking-at regexp))
(add-text-properties (match-beginning 0) (match-end 0)
(list 'start-open t
'end-open t
@@ -1073,8 +1123,7 @@ the end of the document."
(or url-http-chunked-start
(make-marker))
(match-end 0)))
-; (if (not url-http-debug)
- (delete-region (match-beginning 0) (match-end 0));)
+ (delete-region (match-beginning 0) (match-end 0))
(url-http-debug "Saw start of chunk %d (length=%d, start=%d"
url-http-chunked-counter url-http-chunked-length
(marker-position url-http-chunked-start))
@@ -1258,7 +1307,8 @@ The return value of this function is the retrieval buffer."
(mime-accept-string url-mime-accept-string)
(buffer (or retry-buffer
(generate-new-buffer
- (format " *http %s:%d*" (url-host url) (url-port url))))))
+ (format " *http %s:%d*" (url-host url) (url-port url)))))
+ (referer (url-http--encode-string (url-http--get-referer url))))
(if (not connection)
;; Failed to open the connection for some reason
(progn
@@ -1293,7 +1343,8 @@ The return value of this function is the retrieval buffer."
url-http-no-retry
url-http-connection-opened
url-mime-accept-string
- url-http-proxy))
+ url-http-proxy
+ url-http-referer))
(set (make-local-variable var) nil))
(setq url-http-method (or url-request-method "GET")
@@ -1311,15 +1362,16 @@ The return value of this function is the retrieval buffer."
url-http-no-retry retry-buffer
url-http-connection-opened nil
url-mime-accept-string mime-accept-string
- url-http-proxy url-using-proxy)
+ url-http-proxy url-using-proxy
+ url-http-referer referer)
(set-process-buffer connection buffer)
(set-process-filter connection 'url-http-generic-filter)
(pcase (process-status connection)
- (`connect
+ ('connect
;; Asynchronous connection
(set-process-sentinel connection 'url-http-async-sentinel))
- (`failed
+ ('failed
;; Asynchronous connection failed
(error "Could not create connection to %s:%d" (url-host url)
(url-port url)))
@@ -1375,7 +1427,9 @@ The return value of this function is the retrieval buffer."
'url-http-wait-for-headers-change-function)
(set-process-filter tls-connection 'url-http-generic-filter)
(process-send-string tls-connection
- (url-http-create-request)))
+ ;; Use the non-proxy form of the request
+ (let (url-http-proxy)
+ (url-http-create-request))))
(gnutls-error
(url-http-activate-callback)
(error "gnutls-error: %s" e))
@@ -1563,7 +1617,6 @@ p3p
;; HTTPS. This used to be in url-https.el, but that file collides
;; with url-http.el on systems with 8-character file names.
-(require 'tls)
(defconst url-https-asynchronous-p t "HTTPS retrievals are asynchronous.")