━━━━━━━━
				 PLZ.EL
				━━━━━━━━


[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
[httpbin].


[file:http://elpa.gnu.org/packages/plz.svg]
<http://elpa.gnu.org/packages/plz.html>

[httpbin] <https://httpbin.org/>


1 Installation
══════════════

1.1 GNU ELPA
────────────

  `plz' is available in [GNU ELPA].  It may be installed in Emacs using
  the `package-install' command.


[GNU ELPA] <http://elpa.gnu.org/packages/plz.html>


1.2 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)'.


2 Usage
═══════

  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.


2.1 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...")
  └────


2.2 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:

        • `buffer' to pass the response buffer, which will be narrowed
          to the response body and decoded according to `DECODE'.
        • `binary' to pass the response body as an un-decoded string.
        • `string' to pass the response body as a decoded string.
        • `response' to pass a `plz-response' structure.
        • `file' to pass a temporary filename to which the response body
          has been saved without decoding.
        • `(file ~FILENAME)' to pass `FILENAME' after having saved the
          response body to it without decoding.  `FILENAME' must be a
          non-existent file; if it exists, it will not be overwritten,
          and an error will be signaled.
        • A function, which is called in the response buffer with it
          narrowed to the response body (suitable for,
          e.g. `json-read').

        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.


2.3 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.


2.4 Tips
────────

  ⁃ 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.


3 Changelog
═══════════

3.1 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*
  ⁃ Significant improvement in reliability by implementing failsafes and
    workarounds for Emacs's process-handling code.  (See [#3].)
  ⁃ STDERR output from curl processes is not included in response bodies
    (which sometimes happened, depending on Emacs's internal race
    conditions).  (Fixes [#23].)
  ⁃ Use `with-local-quit' for synchronous requests (preventing Emacs
    from complaining sometimes).  (Fixes [#26].)
  ⁃ Various fixes for `:as 'buffer' result type: decode body when
    appropriate; unset multibyte for binary; narrow to body; don't kill
    buffer prematurely.
  ⁃ When clearing a queue, don't try to kill finished processes.

  *Internal*
  ⁃ Response processing now happens outside the process sentinel, so any
    errors (e.g. in user callbacks) are not signaled from inside the
    sentinel.  (This avoids the 2-second pause Emacs imposes in such
    cases.)
  ⁃ Tests run against a local instance of [httpbin] (since the
    `httpbin.org' server is often overloaded).
  ⁃ No buffer-local variables are defined anymore; process properties
    are used instead.


[#3] <https://github.com/alphapapa/plz.el/issues/3>

[#23] <https://github.com/alphapapa/plz.el/issues/23>

[#26] <https://github.com/alphapapa/plz.el/issues/26>

[httpbin] <https://github.com/postmanlabs/httpbin>


3.2 0.6
───────

  *Additions*
  ⁃ Function `plz''s `:body' argument now accepts a list like `(file
    FILENAME)' to upload a file from disk (by passing the filename to
    curl, rather than reading its content into Emacs and sending it to
    curl through the pipe).

  *Fixes*
  ⁃ Function `plz''s docstring now mentions that the `:body' argument
    may also be a buffer (an intentional feature that was accidentally
    undocumented).
  ⁃ Handle HTTP 3xx redirects when using `:as 'response'.


3.3 0.5.4
─────────

  *Fixes*
  ⁃ Only run queue's `finally' function after queue is empty.  (New
    features should not be designed and released on a Friday.)


3.4 0.5.3
─────────

  *Fixes*
  ⁃ Move new slot in `plz-queue' struct to end to prevent invalid
    byte-compiler expansions for already-compiled applications (which
    would require them to be recompiled after upgrading `plz').


3.5 0.5.2
─────────

  *Fixes*
  ⁃ When clearing a queue, only call `plz-queue''s `finally' function
    when specified.


3.6 0.5.1
─────────

  *Fixes*
  ⁃ Only call `plz-queue''s `finally' function when specified.  (Thanks
    to [Dan Oriani] for reporting.)


[Dan Oriani] <https://github.com/redchops>


3.7 0.5
───────

  *Additions*
  ⁃ Struct `plz-queue''s `finally' slot, a function called when the
    queue is finished.


3.8 0.4
───────

  *Additions*
  ⁃ Support for HTTP `HEAD' requests.  (Thanks to [USHIN, Inc.] for
    sponsoring.)

  *Changes*
  ⁃ Allow sending `POST' and `PUT' requests without bodies.  ([#16].
    Thanks to [Joseph Turner] for reporting.  Thanks to [USHIN, Inc.]
    for sponsoring.)

  *Fixes*
  ⁃ All 2xx HTTP status codes are considered successful.  ([#17].
    Thanks to [Joseph Turner] for reporting.  Thanks to [USHIN, Inc.]
    for sponsoring.)
  ⁃ Errors are signaled with error data correctly.

  *Internal*
  ⁃ Test suite explicitly tests with both HTTP/1.1 and HTTP/2.
  ⁃ Test suite also tests with Emacs versions 27.2, 28.1, and 28.2.


[USHIN, Inc.] <https://ushin.org/>

[#16] <https://github.com/alphapapa/plz.el/issues/16>

[Joseph Turner] <https://github.com/josephmturner>

[#17] <https://github.com/alphapapa/plz.el/issues/17>


3.9 0.3
───────

  *Additions*
  ⁃ Handle HTTP proxy headers from Curl. ([#2].  Thanks to [Alan Third]
    and [Sawyer Zheng] for reporting.)

  *Fixes*
  ⁃ Replaced words not in Ispell's default dictionaries (so `checkdoc'
    linting succeeds).


[#2] <https://github.com/alphapapa/plz.el/issues/2>

[Alan Third] <https://github.com/alanthird>

[Sawyer Zheng] <https://github.com/sawyerzheng>


3.10 0.2.1
──────────

  *Fixes*
  ⁃ Handle when Curl process is interrupted.


3.11 0.2
────────

  *Added*
  ⁃ Simple request queueing.


3.12 0.1
────────

  Initial release.


4 Credits
═════════

  ⁃ Thanks to [Chris Wellons], author of the [Elfeed] feed reader and
    the popular blog [null program], for his invaluable advice, review,
    and encouragement.


[Chris Wellons] <https://github.com/skeeto>

[Elfeed] <https://github.com/skeeto/elfeed>

[null program] <https://nullprogram.com/>


5 Development
═════════════

  Bug reports, feature requests, suggestions — /oh my/!

  Note that `plz' is a young library, and its only client so far is
  [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.


[Ement.el] <https://github.com/alphapapa/ement.el>

5.1 Copyright assignment
────────────────────────

  This package is part of [GNU Emacs], being distributed in [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
  [emacs-devel@gnu.org] to request the assignment form.


[GNU Emacs] <https://www.gnu.org/software/emacs/>

[GNU ELPA] <https://elpa.gnu.org/>

[emacs-devel@gnu.org] <mailto:emacs-devel@gnu.org>


6 License
═════════

  GPLv3