Skip to content

Instantly share code, notes, and snippets.

@Wilfred
Created June 17, 2015 09:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Wilfred/f6fc1ef185d172fdb1f0 to your computer and use it in GitHub Desktop.
Save Wilfred/f6fc1ef185d172fdb1f0 to your computer and use it in GitHub Desktop.
excorporate URL fetching on Emacs 24.5
;;; exco-url-http-24.5.el --- Override some HTTP retrieval routines
;; Copyright (C) 1999, 2001, 2004-2014 Free Software Foundation, Inc.
;; Author: Bill Perry <wmperry@gnu.org>
;; Keywords: comm, data, processes
;; 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:
;; Override url-http-parse-headers to clear Authorization elements
;; from url-http-extra-headers prior to executing a redirect. The
;; only change is to apply this backward-compatible patch:
;;
;; diff --git a/lisp/url/url-http.el b/lisp/url/url-http.el
;; index b0a3b68..80d9cca 100644
;; --- a/lisp/url/url-http.el
;; +++ b/lisp/url/url-http.el
;; @@ -617,6 +617,15 @@ should be shown to the user."
;; ;; compute the redirection relative to the URL of the proxy.
;; (setq redirect-uri
;; (url-expand-file-name redirect-uri url-http-target-url)))
;; + ;; Don't automatically include authorization header in redirect.
;; + ;; If needed it will be regenerated by the relevant auth scheme
;; + ;; when the new request happens.
;; + (setq url-http-extra-headers
;; + (let (result)
;; + (dolist (header url-http-extra-headers)
;; + (if (not (equal (car header) "Authorization"))
;; + (push header result)))
;; + (nreverse result)))
;; (let ((url-request-method url-http-method)
;; (url-request-data url-http-data)
;; (url-request-extra-headers url-http-extra-headers))
;;; Code:
(eval-when-compile (require 'cl))
(require 'url-gw)
(require 'url-parse)
(require 'url-cookie)
(require 'mail-parse)
(require 'url-auth)
(require 'url)
(autoload 'url-cache-create-filename "url-cache")
(require 'url-http)
(defun url-http-parse-headers ()
"Parse and handle HTTP specific headers.
Return t if and only if the current buffer is still active and
should be shown to the user."
;; The comments after each status code handled are taken from RFC
;; 2616 (HTTP/1.1)
(url-http-mark-connection-as-free (url-host url-current-object)
(url-port url-current-object)
url-http-process)
(if (or (not (boundp 'url-http-end-of-headers))
(not url-http-end-of-headers))
(error "Trying to parse headers in odd buffer: %s" (buffer-name)))
(goto-char (point-min))
(url-http-debug "url-http-parse-headers called in (%s)" (buffer-name))
(url-http-parse-response)
(mail-narrow-to-head)
;;(narrow-to-region (point-min) url-http-end-of-headers)
(let ((connection (mail-fetch-field "Connection")))
;; In HTTP 1.0, keep the connection only if there is a
;; "Connection: keep-alive" header.
;; In HTTP 1.1 (and greater), keep the connection unless there is a
;; "Connection: close" header
(cond
((string= url-http-response-version "1.0")
(unless (and connection
(string= (downcase connection) "keep-alive"))
(delete-process url-http-process)))
(t
(when (and connection
(string= (downcase connection) "close"))
(delete-process url-http-process)))))
(let* ((buffer (current-buffer))
(class (/ url-http-response-status 100))
(success nil)
;; other status symbols: jewelry and luxury cars
(status-symbol (cadr (assq url-http-response-status url-http-codes))))
(url-http-debug "Parsed HTTP headers: class=%d status=%d"
class url-http-response-status)
(when (url-use-cookies url-http-target-url)
(url-http-handle-cookies))
(pcase class
;; Classes of response codes
;;
;; 5xx = Server Error
;; 4xx = Client Error
;; 3xx = Redirection
;; 2xx = Successful
;; 1xx = Informational
(1 ; Information messages
;; 100 = Continue with request
;; 101 = Switching protocols
;; 102 = Processing (Added by DAV)
(url-mark-buffer-as-dead buffer)
(error "HTTP responses in class 1xx not supported (%d)"
url-http-response-status))
(2 ; Success
;; 200 Ok
;; 201 Created
;; 202 Accepted
;; 203 Non-authoritative information
;; 204 No content
;; 205 Reset content
;; 206 Partial content
;; 207 Multi-status (Added by DAV)
(pcase status-symbol
((or `no-content `reset-content)
;; No new data, just stay at the same document
(url-mark-buffer-as-dead buffer))
(_
;; Generic success for all others. Store in the cache, and
;; mark it as successful.
(widen)
(if (and url-automatic-caching (equal url-http-method "GET"))
(url-store-in-cache buffer))))
(setq success t))
(3 ; Redirection
;; 300 Multiple choices
;; 301 Moved permanently
;; 302 Found
;; 303 See other
;; 304 Not modified
;; 305 Use proxy
;; 307 Temporary redirect
(let ((redirect-uri (or (mail-fetch-field "Location")
(mail-fetch-field "URI"))))
(pcase status-symbol
(`multiple-choices ; 300
;; Quoth the spec (section 10.3.1)
;; -------------------------------
;; The requested resource corresponds to any one of a set of
;; representations, each with its own specific location and
;; agent-driven negotiation information is being provided so
;; that the user can select a preferred representation and
;; redirect its request to that location.
;; [...]
;; If the server has a preferred choice of representation, it
;; SHOULD include the specific URI for that representation in
;; the Location field; user agents MAY use the Location field
;; value for automatic redirection.
;; -------------------------------
;; We do not support agent-driven negotiation, so we just
;; redirect to the preferred URI if one is provided.
nil)
((or `moved-permanently `found `temporary-redirect) ; 301 302 307
;; If the 301|302 status code is received in response to a
;; request other than GET or HEAD, the user agent MUST NOT
;; automatically redirect the request unless it can be
;; confirmed by the user, since this might change the
;; conditions under which the request was issued.
(unless (member url-http-method '("HEAD" "GET"))
(setq redirect-uri 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
;; 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
;; 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
;; to repeat this single request via the proxy. 305
;; responses MUST only be generated by origin servers.
(error "Redirection thru a proxy server not supported: %s"
redirect-uri))
(_
;; Treat everything like '300'
nil))
(when redirect-uri
;; Clean off any whitespace and/or <...> cruft.
(if (string-match "\\([^ \t]+\\)[ \t]" redirect-uri)
(setq redirect-uri (match-string 1 redirect-uri)))
(if (string-match "^<\\(.*\\)>$" redirect-uri)
(setq redirect-uri (match-string 1 redirect-uri)))
;; Some stupid sites (like sourceforge) send a
;; non-fully-qualified URL (ie: /), which royally confuses
;; the URL library.
(if (not (string-match url-nonrelative-link redirect-uri))
;; Be careful to use the real target URL, otherwise we may
;; compute the redirection relative to the URL of the proxy.
(setq redirect-uri
(url-expand-file-name redirect-uri url-http-target-url)))
;; Don't automatically include authorization header in redirect.
;; If needed it will be regenerated by the relevant auth scheme
;; when the new request happens.
(setq url-http-extra-headers
(let (result)
(dolist (header url-http-extra-headers)
(if (not (equal (car header) "Authorization"))
(push header result)))
(nreverse result)))
(let ((url-request-method url-http-method)
(url-request-data url-http-data)
(url-request-extra-headers url-http-extra-headers))
;; Check existing number of redirects
(if (or (< url-max-redirections 0)
(and (> url-max-redirections 0)
(let ((events (car url-callback-arguments))
(old-redirects 0))
(while events
(if (eq (car events) :redirect)
(setq old-redirects (1+ old-redirects)))
(and (setq events (cdr events))
(setq events (cdr events))))
(< old-redirects url-max-redirections))))
;; url-max-redirections hasn't been reached, so go
;; ahead and redirect.
(progn
;; Remember that the request was redirected.
(setf (car url-callback-arguments)
(nconc (list :redirect redirect-uri)
(car url-callback-arguments)))
;; Put in the current buffer a forwarding pointer to the new
;; destination buffer.
;; FIXME: This is a hack to fix url-retrieve-synchronously
;; without changing the API. Instead url-retrieve should
;; either simply not return the "destination" buffer, or it
;; should take an optional `dest-buf' argument.
(set (make-local-variable 'url-redirect-buffer)
(url-retrieve-internal
redirect-uri url-callback-function
url-callback-arguments
(url-silent url-current-object)
(not (url-use-cookies url-current-object))))
(url-mark-buffer-as-dead buffer))
;; We hit url-max-redirections, so issue an error and
;; stop redirecting.
(url-http-debug "Maximum redirections reached")
(setf (car url-callback-arguments)
(nconc (list :error (list 'error 'http-redirect-limit
redirect-uri))
(car url-callback-arguments)))
(setq success t))))))
(4 ; Client error
;; 400 Bad Request
;; 401 Unauthorized
;; 402 Payment required
;; 403 Forbidden
;; 404 Not found
;; 405 Method not allowed
;; 406 Not acceptable
;; 407 Proxy authentication required
;; 408 Request time-out
;; 409 Conflict
;; 410 Gone
;; 411 Length required
;; 412 Precondition failed
;; 413 Request entity too large
;; 414 Request-URI too large
;; 415 Unsupported media type
;; 416 Requested range not satisfiable
;; 417 Expectation failed
;; 422 Unprocessable Entity (Added by DAV)
;; 423 Locked
;; 424 Failed Dependency
(setq success
(pcase status-symbol
(`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
;; This code is reserved for future use
(url-mark-buffer-as-dead buffer)
(error "Somebody wants you to give them money"))
(`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
t)
(`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
;; 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
;; 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
;; 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
;; 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
;; might be able to resolve the conflict and resubmit the
;; request. The response body SHOULD include enough
;; information for the user to recognize the source of the
;; conflict.
t)
(`gone ; 410
;; The requested resource is no longer available at the
;; server and no forwarding address is known.
t)
(`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
;; length of the message-body in the request message.
;;
;; NOTE - this will never happen because
;; `url-http-create-request' automatically calculates the
;; content-length.
t)
(`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
;; 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
;; 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
;; 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
;; 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
;; request could not be met by the next-hop server.
t)
(_
;; The request could not be understood by the server due to
;; malformed syntax. The client SHOULD NOT repeat the
;; request without modifications.
t)))
;; Tell the callback that an error occurred, and what the
;; status code was.
(when success
(setf (car url-callback-arguments)
(nconc (list :error (list 'error 'http url-http-response-status))
(car url-callback-arguments)))))
(5
;; 500 Internal server error
;; 501 Not implemented
;; 502 Bad gateway
;; 503 Service unavailable
;; 504 Gateway time-out
;; 505 HTTP version not supported
;; 507 Insufficient storage
(setq success t)
(pcase url-http-response-status
(`not-implemented ; 501
;; The server does not support the functionality required to
;; fulfill the request.
nil)
(`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
;; 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
;; which will be alleviated after some delay. If known, the
;; length of the delay MAY be indicated in a Retry-After
;; header. If no Retry-After is given, the client SHOULD
;; handle the response as it would for a 500 response.
nil)
(`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
;; 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)
;; 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
;; condition is considered to be temporary. If the request
;; which received this status code was the result of a user
;; action, the request MUST NOT be repeated until it is
;; requested by a separate user action.
nil))
;; Tell the callback that an error occurred, and what the
;; status code was.
(when success
(setf (car url-callback-arguments)
(nconc (list :error (list 'error 'http url-http-response-status))
(car url-callback-arguments)))))
(_
(error "Unknown class of HTTP response code: %d (%d)"
class url-http-response-status)))
(if (not success)
(url-mark-buffer-as-dead buffer)
(url-handle-content-transfer-encoding))
(url-http-debug "Finished parsing HTTP headers: %S" success)
(widen)
(goto-char (point-min))
success))
(provide 'exco-url-http-24.5)
;;; exco-url-http-24.5.el ends here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment