[[http://elpa.gnu.org/packages/plz.html][file:http://elpa.gnu.org/packages/plz.svg]]
~plz~ is an HTTP library for Emacs. It uses ~curl~ as a backend, which avoids some of the issues with using Emacs's built-in ~url~ library. It supports both synchronous and asynchronous requests. Its API is intended to be simple, natural, and expressive. Its code is intended to be simple and well-organized. Every feature is tested against [[https://httpbin.org/][httpbin]].
Contents :noexport: :PROPERTIES: :TOC: :include siblings :END: :CONTENTS:
[[#installation][Installation]]
[[#usage][Usage]]
[[#changelog][Changelog]]
[[#credits][Credits]]
[[#development][Development]]
Installation :PROPERTIES: :TOC: :depth 0 :END:
** GNU ELPA
~plz~ is available in [[http://elpa.gnu.org/packages/plz.html][GNU ELPA]]. It may be installed in Emacs using the ~package-install~ command.
** Manual
~plz~ has no dependencies other than Emacs and ~curl~. It's known to work on Emacs 26.3 or later. To install it manually, simply place =plz.el= in your ~load-path~ and ~(require 'plz)~.
The main public function is ~plz~, which sends an HTTP request and returns either the result of the specified type (for a synchronous request), or the ~curl~ process object (for asynchronous requests). For asynchronous requests, callback, error-handling, and finalizer functions may be specified, as well as various other options.
** Examples
Synchronously =GET= a URL and return the response body as a decoded string (here, raw JSON):
(plz 'get "https://httpbin.org/user-agent")
"{\n \"user-agent\": \"curl/7.35.0\"\n}\n"
Synchronously =GET= a URL that returns a JSON object, and parse and return it as an alist:
(plz 'get "https://httpbin.org/get" :as #'json-read)
((args) (headers (Accept . "/") (Accept-Encoding . "deflate, gzip") (Host . "httpbin.org") (User-Agent . "curl/7.35.0")) (url . "https://httpbin.org/get"))
Asynchronously =POST= a JSON object in the request body, then parse a JSON object from the response body, and call a function with the result:
(plz 'post "https://httpbin.org/post" :headers '(("Content-Type" . "application/json")) :body (json-encode '(("key" . "value"))) :as #'json-read :then (lambda (alist) (message "Result: %s" (alist-get 'data alist))))
: Result: {"key":"value"}
Synchronously download a JPEG file, then create an Emacs image object from the data:
(let ((jpeg-data (plz 'get "https://httpbin.org/image/jpeg" :as 'binary))) (create-image jpeg-data nil 'data))
: (image :type jpeg :data ""ÿØÿà^@^PJFIF...")
** Functions
~plz~ :: /(method url &key headers body else finally noquery (as 'string) (then 'sync) (body-type 'text) (decode t decode-s) (connect-timeout plz-connect-timeout) (timeout plz-timeout))/
Request ~METHOD~ from ~URL~ with curl. Return the curl process object or, for a synchronous request, the selected result.
~HEADERS~ may be an alist of extra headers to send with the request.
~BODY~ may be a string, a buffer, or a list like ~(file FILENAME)~ to upload a file from disk.
~BODY-TYPE~ may be ~text~ to send ~BODY~ as text, or ~binary~ to send it as binary.
~AS~ selects the kind of result to pass to the callback function ~THEN~, or the kind of result to return for synchronous requests. It may be:
If ~DECODE~ is non-nil, the response body is decoded automatically. For binary content, it should be nil. When ~AS~ is ~binary~, ~DECODE~ is automatically set to nil.
~THEN~ is a callback function, whose sole argument is selected above with ~AS~; if the request fails and no ~ELSE~ function is given (see below), the argument will be a ~plz-error~ structure describing the error. Or ~THEN~ may be ~sync~ to make a synchronous request, in which case the result is returned directly from this function.
~ELSE~ is an optional callback function called when the request fails (i.e. if curl fails, or if the ~HTTP~ response has a non-2xx status code). It is called with one argument, a ~plz-error~ structure. If ~ELSE~ is nil, a ~plz-curl-error~ or ~plz-http-error~ is signaled when the request fails, with a ~plz-error~ structure as the error data. For synchronous requests, this argument is ignored.
~NOTE~: In v0.8 of ~plz~, only one error will be signaled: ~plz-error~. The existing errors, ~plz-curl-error~ and ~plz-http-error~, inherit from ~plz-error~ to allow applications to update their code while using v0.7 (i.e. any ~condition-case~ forms should now handle only ~plz-error~, not the other two).
~FINALLY~ is an optional function called without argument after ~THEN~ or ~ELSE~, as appropriate. For synchronous requests, this argument is ignored.
~CONNECT-TIMEOUT~ and ~TIMEOUT~ are a number of seconds that limit how long it takes to connect to a host and to receive a response from a host, respectively.
~NOQUERY~ is passed to ~make-process~, which see.
~FILTER~ is an optional function to be used as the process filter for the curl process. It can be used to handle HTTP responses in a streaming way. The function must accept 2 arguments, the process object running curl, and a string which is output received from the process. The default process filter inserts the output of the process into the process buffer. The provided ~FILTER~ function should at least insert output up to the HTTP body into the process buffer.
** Queueing
~plz~ provides a simple system for queueing HTTP requests. First, make a ~plz-queue~ struct by calling ~make-plz-queue~. Then call ~plz-queue~ with the struct as the first argument, and the rest of the arguments being the same as those passed to ~plz~. Then call ~plz-run~ to run the queued requests.
All of the queue-related functions return the queue as their value, making them easy to use. For example:
(defvar my-queue (make-plz-queue :limit 2))
(plz-run (plz-queue my-queue 'get "https://httpbin.org/get?foo=0" :then (lambda (body) (message "%s" body))))
Or:
(let ((queue (make-plz-queue :limit 2 :finally (lambda () (message "Queue empty.")))) (urls '("https://httpbin.org/get?foo=0" "https://httpbin.org/get?foo=1"))) (plz-run (dolist (url urls queue) (plz-queue queue 'get url :then (lambda (body) (message "%s" body))))))
You may also clear a queue with ~plz-clear~, which cancels any active or queued requests and calls their ~:else~ functions. And ~plz-length~ returns the number of a queue's active and queued requests.
** Tips :PROPERTIES: :TOC: :ignore (this) :END:
You can customize settings in the =plz= group, but this can only be used to adjust a few defaults. It's not intended that changing or binding global variables be necessary for normal operation.
Changelog :PROPERTIES: :TOC: :depth 0 :END:
** 0.9
Compatibility
Changes
Fixes
Development
** 0.8
Additions
** 0.7.3
Fixes
** 0.7.2
Fixes
** 0.7.1
Fixes
** 0.7
Changes
A new error signal, ~plz-error~, is defined. The existing signals, ~plz-curl-error~ and ~plz-http-error~, inherit from it, so handling ~plz-error~ catches both.
NOTE: The existing signals, ~plz-curl-error~ and ~plz-http-error~, are hereby deprecated, and they will be removed in v0.8. Applications should be updated while using v0.7 to only expect ~plz-error~.
Fixes
Internal
** 0.6
Additions
Fixes
** 0.5.4
Fixes
** 0.5.3
Fixes
** 0.5.2
Fixes
** 0.5.1
Fixes
** 0.5
Additions
** 0.4
Additions
Changes
Fixes
Internal
** 0.3
Additions
Fixes
** 0.2.1
Fixes
** 0.2
Added
** 0.1
Initial release.
Credits
Thanks to [[https://github.com/skeeto][Chris Wellons]], author of the [[https://github.com/skeeto/elfeed][Elfeed]] feed reader and the popular blog [[https://nullprogram.com/][null program]], for his invaluable advice, review, and encouragement.
Development
Bug reports, feature requests, suggestions — /oh my/!
Note that ~plz~ is a young library, and its only client so far is [[https://github.com/alphapapa/ement.el][Ement.el]]. There are a variety of HTTP and ~curl~ features it does not yet support, since they have not been needed by the author. Patches are welcome, as long as they include passing tests.
** Copyright assignment
This package is part of [[https://www.gnu.org/software/emacs/][GNU Emacs]], being distributed in [[https://elpa.gnu.org/][GNU ELPA]]. Contributions to this project must follow GNU guidelines, which means that, as with other parts of Emacs, patches of more than a few lines must be accompanied by having assigned copyright for the contribution to the FSF. Contributors who wish to do so may contact [[mailto:emacs-devel@gnu.org][emacs-devel@gnu.org]] to request the assignment form.
GPLv3
** Info export options
** File-local variables