source: project/wiki/eggref/5/http-client @ 37991

Last change on this file since 37991 was 37991, checked in by Kooda, 2 months ago

eggref/5/http-client: add a note about importing openssl when statically linking

File size: 29.2 KB
Line 
1[[tags: egg]]
2
3== http-client
4
5[[toc:]]
6
7=== Description
8
9Http-client is a highlevel HTTP client library.
10
11=== Author
12
13[[/users/peter-bex|Peter Bex]]
14
15=== Requirements
16
17Requires the following extensions:
18
19* [[srfi-1]]
20* [[srfi-13]]
21* [[srfi-18]]
22* [[srfi-69]]
23* [[intarweb]]
24* [[sendfile]]
25* [[uri-common]]
26* [[simple-md5]]
27
28The [[openssl]] extension is optional; if it's not installed you'll
29get an error when trying to access a HTTPS URI.
30
31Additionnaly, when statically linking the egg into an executable, HTTPS
32support will only be available if you also import the [[openssl]] module
33in your own program '''before''' importing http-client.
34
35=== Documentation
36
37==== Main request procedures
38
39<procedure>(call-with-response request writer reader)</procedure>
40
41This is the core http-client procedure, but it is also pretty
42low-level.  It is only necessary to use this when you want the most
43control over the request/response cycle.  Otherwise, you should use
44{{with-input-from-request}}, {{call-with-input-request}} or
45{{call-with-input-request*}}.
46
47{{request}} is the request object that contains information about the
48request to perform.  {{reader}} is a procedure that receives the
49response object and should read the ''entire'' request body (any
50leftover data will cause errors on subsequent requests with keepalive
51connections), {{writer}} is a procedure that receives the request
52object and should write the request body.
53
54The {{writer}} should be prepared to be called several times; if the
55response is a redirect or some other status that indicates the server
56wants the client to perform a new request, the writer should be ready
57to write a request body for this new request. In case digest
58authentication with message integrity checking is used, {{writer}} is
59always invoked at least twice, once to determine the message digest of
60the response and once to actually write the response.
61
62Returns three values: The result of the call to {{reader}} (or {{#f}}
63if there is no message body in the response), the request-uri of
64the last request and the response object. The request-uri is useful
65because this is to be used as the base uri of the document. This can
66differ from the initial request in the presence of redirects.
67
68If there is no response body to read (as determined by intarweb's
69{{response-has-message-body-for-request?}}), the {{reader}} procedure
70is not invoked at all.
71
72If successive requests cause more than {{max-redirect-depth}} redirect
73responses to occur, a condition of type
74{{(exn http redirect-depth-exceeded)}} is raised.
75
76If the request's URI or the URI of a used proxy is of an unsupported
77type, a condition of type {{(exn http unsupported-uri-scheme)}} is
78raised (this can of course also occur when the initial URI is correct,
79but the server redirects to an URI with an unsupported scheme).
80
81When the request requires authentication of an unsupported type, a
82condition of type {{(exn http unknown-authtype)}} is raised.
83
84<procedure>(call-with-input-request uri-or-request writer reader)</procedure>
85
86This procedure is a convenience wrapper around {{call-with-response}}.
87
88It is much less strict - {{uri-or-request}} can be an [[intarweb]]
89request object, but also an [[uri-common]] object or even a string
90with the URI in it, in which case a request object will be
91automatically constructed around the URI, using the {{GET}} method
92when {{writer}} is {{#f}} or the {{POST}} method when {{writer}} is
93not {{#f}}.
94
95{{writer}} can be either {{#f}} (in which case nothing is written and
96the {{GET}} method chosen), a string containing the raw data to send,
97an alist, or a procedure that accepts a port and writes the
98response data to it.  If you supply a procedure, do not forget to set
99the {{content-length}} header!  In the other cases, whenever possible,
100the length is calculated and the header automatically set for you.
101
102If you supplied an alist, the {{content-type}} header is automatically
103set to {{application/x-www-form-urlencoded}} unless there's an alist
104entry whose value is a list starting with the keyword {{file:}}, in
105which case {{multipart/form-data}} is used.  See the examples for
106{{with-input-from-request}} below.  If the data cannot be form-encoded,
107a condition of type {{(exn http formdata-error)}} is raised.
108
109{{reader}} is either {{#f}} or a procedure which accepts a port and
110reads out the data.  If there is data left in the port when the reader
111returns (or {{#f}} was supplied), this will be automatically discarded
112to avoid problems.
113
114Returns three values: The result of the call to {{reader}} (or {{#f}}
115if there is no message body in the response), the request-uri of the
116last request and the response object.  If the response code is not in
117the 200 class, it will raise a condition of type
118{{(exn http client-error)}}, {{(exn http server-error)}} or
119{{(exn http unexpected-server-response)}}, depending on the response
120code.  This includes {{404 not found}} (which is a {{client-error}}).
121
122If there is no response body to read (as determined by intarweb's
123{{response-has-message-body-for-request?}}), the {{reader}} procedure
124is not invoked at all.
125
126When posting multipart form data, the value of a file entry is a list
127of keyword-value pairs.  The following keywords are recognised:
128
129; {{file:}} : This indicates the file to read from.  Can be either a string or a port. This ''must'' be specified, everything else is optional.
130; {{filename:}} : This indicates the filename to pass on to the server.  If not specified or {{#f}}, the {{file:}}'s string (or port-name in case of a port) will be used.
131; {{headers:}} : Additional headers to send for this entry (an [[intarweb]] headers-object).
132
133If the URI argument is not a valid URI, a condition of type
134{{(exn http client-error bad-uri)}} will be raised.
135
136If the writer is a list it is taken to be form-data, but if the
137encoding fails, a condition of type
138{{(exn http client-error form-data-error)}} will be raised.
139
140
141<procedure>(call-with-input-request* uri-or-request writer reader)</procedure>
142
143As {{call-with-input-request}}, except {{reader}} is passed two
144arguments: the input port and the complete intarweb response object
145(useful for when you want to inspect headers or other aspects of the
146response).
147
148Please note that the port is '''not''' the same as the
149{{response-port}} from the response object: the port is delimited so
150that you can read until {{EOF}}.  The {{response-port}} is the
151original underlying, unbounded port.  If you do want to read from it,
152you must make sure to read no more than what's in the
153{{Content-Length}} header, if present.  If the header is not present,
154it will either be a chunked port (which is implicitly delimited by
155intarweb) or the port will be closed by the remote end after it is
156consumed, so you can read until EOF in that case.
157
158<procedure>(with-input-from-request uri-or-request writer-thunk reader-thunk)</procedure>
159
160Same as {{call-with-input-request}}, except when you pass a procedure
161as {{reader-thunk}} or {{writer-thunk}} it has to be a thunk (lambda
162of no arguments) instead of a procedure of one argument.  These thunks
163will be executed with the current input (or output) port to the
164request or response port, respectively.
165
166You can still pass {{#f}} for both or an alist or string for
167{{writer-thunk}}.
168
169===== Examples
170
171<enscript highlight="scheme">
172(import http-client (chicken io))
173
174;; Start with a simple GET request:
175(with-input-from-request "http://wiki.call-cc.org/" #f read-string)
176 => ;; [the chicken wiki page HTML contents]
177
178;; Perform a POST of the key "test" with value "value" to an echo service:
179(with-input-from-request "http://localhost/echo-service"
180                         '((test . "value")) read-string)
181 => "You posted: test=value"
182
183;; Performing a PUT request (a less commonly used method) requires
184;; constructing your request object manually:
185
186(import intarweb uri-common (chicken io))  ; uri-common gives us "make-request" and "uri-reference"
187
188(with-input-from-request
189  (make-request method: 'PUT
190                uri: (uri-reference "http://example.com/blabla"))
191  (lambda () (print "Page contents"))
192  read-string)
193
194;; Performing a JSON PUT request furthermore requires you to
195;; pass custom headers:
196(let* ((uri (uri-reference "http://www.example.com/some/document"))
197       (req (make-request method: 'PUT
198                          uri: uri
199                          headers: (headers '((content-type application/json))))))
200  (with-input-from-request req "Contents of the document" read-string))
201
202;; Finally, an example where we need to send an "attachment" (file)
203;; We post a file to the echo-service from the first example.
204;; This results in a multi-part POST request, for which we set
205;; custom headers on the file (but not the main request)
206(with-input-from-request "http://localhost/echo-service"
207                         '((test . "value")
208                           (test-file file: "/tmp/myfile" filename: "hello.txt"
209                                      headers: ((content-type text/plain))))
210                         read-string)
211 => "You posted: test=value and a file named \"hello.txt\""
212</enscript>
213
214
215==== Request handling parameters
216
217<parameter>(max-retry-attempts [number])</parameter>
218
219When a request fails because of an I/O or network problem (or simply
220because the remote end closed a persistent connection while we were
221doing something else), the library will try to establish a new
222connection and perform the request again.  This parameter controls how
223many times this is allowed to be done.  If {{#f}}, it will never give up.
224
225Defaults to 1.
226
227<parameter>(retry-request? [predicate])</parameter>
228
229This procedure is invoked when a retry should take place, to determine
230if it should take place at all.  It should be a procedure accepting a
231request object and returning {{#f}} or a true value.  If the value is
232true, the new request will be sent.  Otherwise, the error that caused
233the retry attempt will be re-raised.
234
235Defaults to {{idempotent?}}, from [[intarweb]].  This is because
236non-idempotent requests cannot be safely retried when it is unknown
237whether the previous request reached the server or not.
238
239<parameter>(max-redirect-depth [number])</parameter>
240
241The maximum number of allowed redirects, or {{#f}} if there is no
242limit.  Currently there's no automatic redirect loop detection
243algorithm implemented.  If zero, no redirects will be followed at all.
244
245Defaults to 5.
246
247When the redirect limit is reached, {{call-with-response}} raises a
248condition of type {{(exn http redirect-depth-exceeded)}}.
249
250<parameter>(client-software [software-spec])</parameter>
251
252This is the names, versions and comments of the software packages that
253the client is using, for use in the {{user-agent}} header which is
254automatically added to each request by {{prepare-request}}.
255
256Defaults to {{(("Chicken Scheme HTTP-client" VERSION #f))}}, where
257{{VERSION}} is the version of this egg.
258
259==== Customising requests
260
261Given that http-client tries to do a lot "automagically", there will
262be occasions where you want more control.  One way to do that is to
263use {{call-with-response}}, but it's very low-level, so you'll end up
264reimplementing a lot of the functionality that
265{{call-with-input-request}} and {{with-input-from-request}} offer.
266
267Instead, http-client offers a parameter that allows you to tweak the
268request just before a connection is made:
269
270<parameter>(prepare-request [preparer])</parameter>
271
272Here, {{preparer}} is a procedure which will be called every time a
273connection is made.  This includes every request in a redirect chain,
274so you can decide for every URL in the chain what to do with the
275request.  The procedure receives and must return an intarweb request
276object.  The default implementation is {{default-prepare-request}}.
277
278'''CAVEAT''': If you decide to change the request-uri last-minute, you
279must also set the {{host}} header, because it will have already been
280initialized to the request-uri's host attribute.
281
282<procedure>(default-prepare-request req)</procedure>
283
284This is the default implementation of the {{prepare-request}}
285parameter.  It sets the {{User-Agent}} header from the
286{{client-software}} parameter and adds {{set-cookie}} headers which
287belong to the server and path from the request-uri.
288
289
290==== Connection management
291
292This egg tries to re-use connections that are marked as keep-alive, to
293avoid unnecessary overhead in establishing new connections when making
294multiple requests to the same server.  This is handled through a pool
295of idle connections from which the request procedures take the oldest
296active connection.
297
298<parameter>(max-idle-connections [count])</parameter>
299
300This controls the maximum allowed idle connections at any given time.
301When a connection would be returned to the pool, the connection will
302be discarded instead, if the maximum is exceeded.
303
304This value should always be well below the maximum number of available
305file descriptors for your operating system.
306
307Defaults to {{32}}.
308
309
310<procedure>(close-connection! uri)</procedure>
311
312Close the connection to the server associated with the URI.
313
314<procedure>(close-idle-connections!)</procedure>
315
316Close all remaining idle connections.  Note that connections that are
317currently in use will still be returned to the connection pool after
318their requests finish!
319
320<procedure>(close-all-connections!)</procedure>
321
322Deprecated alias for {{close-idle-connections!}}.
323
324==== Setting up custom server connections
325
326<procedure>(default-server-connector uri proxy)</procedure>
327
328The default value of the {{server-connector}} parameter.  This
329procedure creates a connection to the remote end for the given {{uri}}
330(an [[uri-common]] object) and returns two values: an input port and
331an output port.
332
333If {{proxy}} is not {{#f}} but an [[uri-common]] object, it will
334connect to that, instead.
335
336This connector supports plain {{http}} connections, and {{https}} if
337the {{openssl}} egg can be loaded (which it attempts to do on the
338fly).
339
340<parameter>(server-connector [connector])</parameter>
341
342This parameter holds a procedure which is invoked to establish a
343connection for an URI.
344
345The procedure should accept two [[uri-common]] objects as arguments:
346the first indicates the URI for which the connection is to be made and
347the second indicates the proxy through which the connection should be
348made, or {{#f}} if a direct connection should be made to the first
349URI's host and port.  It should return two values: an input port and
350an output port corresponding to the connection.
351
352This can be used for nonstandard or complex connections, like for
353example connecting to UNIX domain sockets or for supplying SSL/TLS
354client certificates.
355
356===== SSL client certificate authentication example
357
358This is how you would make a connection to an HTTPS server while
359supplying a client certificate.  Many thanks to Ryan Senior for the
360initial code.
361
362<enscript highlight="scheme">
363(import http-client uri-common openssl)
364
365(define (make-ssl-context/client-cert ca-cert-path cert-path key-path)
366  (let ((ssl-ctx (ssl-make-client-context 'tls)))
367
368    ;; Set up so the server's certificate can and will be verified
369    (ssl-load-suggested-certificate-authorities! ssl-ctx ca-cert-path)
370    (ssl-load-verify-root-certificates! ssl-ctx ca-cert-path)
371    (ssl-set-verify! ssl-ctx #t)
372
373    ;; Now load the client certificate
374    (ssl-load-certificate-chain! ssl-ctx cert-path)
375    (ssl-load-private-key! ssl-ctx key-path)
376
377    ;; Return the object we created
378    ssl-ctx))
379
380;; This creates server connectors associated with an SSL context
381(define (make-ssl-server-connector/context ssl-ctx)
382  (lambda (uri proxy)
383    (let ((remote-end (or proxy uri)))
384      (if (eq? 'https (uri-scheme remote-end))
385          ;; Only use ssl-connect for HTTPS connections
386          (ssl-connect (uri-host remote-end)
387                       (uri-port remote-end)
388                       ssl-ctx)
389          ;; Use http-client's default otherwise
390          (default-server-connector uri proxy)))))
391
392;; Now, make a context and matching connector, and register it
393(let ((ssl-ctx (make-ssl-context/client-cert
394                 "/etc/ssl/certs/ca.crt"
395                 "/etc/ssl/certs/my-client-cert.crt"
396                 "/etc/ssl/private/my-client-cert.key")))
397  (server-connector (make-ssl-server-connector/context ssl-ctx)))
398</enscript>
399
400Now, all requests made with any of the http-client procedures would
401authenticate with a server using the configured client certificate.
402
403==== Cookie management
404
405http-client's cookie management is supposed to be as automatic and
406DWIMmy as possible.  This means it will write any cookie as instructed
407by a server and all stored cookies are automatically sent back to the
408server upon a new request.
409
410However, in some cases you may want to take control of how cookies are
411stored.
412
413The API described here should be considered unstable and it may change
414dramatically when someone comes up with a better way to handle cookies.
415
416<procedure>(get-cookies-for-uri uri)</procedure>
417
418Fetch a list of all cookies which ought to be sent to the given URI.
419Cookies are vectors of two elements: a name/value pair and an alist of
420attributes.  In other words, these are the exact same values you can
421put in a {{cookie}} header.
422
423<procedure>(store-cookie! cookie-info set-cookie)</procedure>
424
425Store a cookie in the cookiejar corresponding to the Set-Cookie header
426given by {{set-cookie}}.  This overwrites any cookie that is equal to
427this cookie, as defined by RFC 2965, section 3.3.3.  Practically, this
428means that when the cookie's name, domain and path are equal to an
429existant one, it will be overwritten by the new one.  These attributes
430are taken from the {{cookie-info}} alist and expected to be there.
431
432Generally, attributes should be taken from {{set-cookie}}, but if
433missing they ought to be taken from the request URI that responded
434with the {{set-cookie}}.
435
436<enscript highlight="scheme">
437(store-cookie! `((path . ,(make-uri path: '(/ "")))
438                 (domain . "some.host.com")
439                 (secure . #t))
440               `#(("COOKIE_NAME" . "cookie-value")
441                  ((path . ,(make-uri path: '(/ ""))))))
442</enscript>
443
444<procedure>(delete-cookie! cookie-name cookie-info)</procedure>
445
446Removes any cookie from the cookiejar that is equal to the given
447cookie (again, in the sense of RFC 2965, section 3.3.3).
448The {{cookie-name}} must match and the {{path}} and {{domain}} values for
449the {{cookie-info}} alist must match.
450
451==== Authentication support
452
453When a 401 Unauthorized response is received, in most interactive
454clients, the user is normally asked to authenticate.  To support this
455type of interaction, http-client offers the following parameter:
456
457<parameter>(determine-username/password [HANDLER])</parameter>
458
459The procedure in this parameter is called whenever the remote
460host requests authentication via a 401 Unauthorized response.
461
462The {{HANDLER}} is a procedure of two arguments; the URI for the
463resource currently being requested and the realm (a string) which
464wants credentials.  The procedure should return two string values:
465the username and the password to use for authentication.
466
467The default value is a procedure which extracts the username and
468password components from the URI.
469
470For proxy authentication support, see {{determine-proxy-username/password}}
471in the next section.
472
473<parameter>(http-authenticators [AUTHENTICATORS])</parameter>
474
475This parameter allows for pluggable authentication schemes.
476{{AUTHENTICATORS}} is an alist mapping authentication scheme name
477to a procedure of 7 arguments:
478
479{{(lambda (response response-header new-request request-header uri realm writer) ...)}}
480
481Here, {{response}} is the response object, {{response-header}} is the
482name of the response header which required authentication - a symbol
483which is either {{www-authenticate}} or {{proxy-authenticate}}.
484
485{{new-request}} is the request that will be sent next, to be populated
486with additional headers by the authenticator procedure, and
487{{request-header}} is the name of the request header which is expected
488to be provided and supplied with extra details by the authenticator -
489also a symbol, which is either {{authorization}} or
490{{proxy-authorization}}.
491
492{{uri}} is the URI which was requested when the authorization was
493demanded (in case of {{www-authenticate}}, the protected resource) and
494{{realm}} is the authentication realm (a string).
495
496Finally {{writer}} is the writer procedure passed by the user or
497fabricated by {{call-with-input-request}} based on the user's form
498arguments.  It's always a procedure accepting a request object.
499This is only needed when full-request authentication is desired, to
500obtain a request body.
501
502==== Proxy support
503
504http-client has support for sending requests through proxy servers.
505
506<parameter>(determine-proxy [HANDLER])</parameter>
507
508Whenever a request is sent, the library invokes the procedure stored
509in this parameter to determine through what proxy to send the request,
510if any.
511
512The {{HANDLER}} procedure receives one argument, the URI about to be
513requested, and returns either an [[uri-common]] absolute URI object
514representing the proxy or {{#f}} if no proxy should be used.
515
516The URI's path and query, if present, are ignored; only the scheme
517and authority (host, port, username, password) are used.
518
519The default value of this parameter is {{determine-proxy-from-environment}}.
520
521<enscript highlight="scheme">
522(determine-proxy
523 (lambda (url)
524   (uri-reference "http://127.0.0.1:8888/")))
525</enscript>
526
527
528If you just want to disable proxy support, you can do:
529
530<enscript highlight="scheme">
531(determine-proxy (constantly #f))   ; From unit data-structures
532</enscript>
533
534<procedure>(determine-proxy-from-environment URI)</procedure>
535
536This procedure implements the common behaviour of HTTP software under
537UNIX:
538
539* First it checks if the requested URI's host (or an asterisk) is listed in the {{NO_PROXY}} environment variable (if suffixed with a port number, the port is also compared).  If a match is found, no proxy is used.
540* Then it will check if the {{$(protocol)_proxy}} or the {{$(PROTOCOL)_PROXY}} variable (in that order) are set.  If so, that's used.  {{protocol}} here actually means "scheme", so the URI's scheme is used, suffixed with {{_proxy}}. This means {{http_proxy}} is used for HTTP requests and {{https_proxy}} is used for HTTPS requests, but see the next point.
541* If the scheme is {{http}} and the environment variable {{REQUEST_METHOD}} is present, {{CGI_HTTP_PROXY}} is used instead of {{HTTP_PROXY}} to prevent a "[[https://httpoxy.org|httpoxy]]" attack.  This makes the assumption that {{REQUEST_METHOD}} is set because the library is being used in a CGI script.
542* If there's still no match, it looks for {{all_proxy}} or {{ALL_PROXY}}, in that order. If one of these environment variables are set, that value is used as a fallback proxy.
543* Finally, if none of these checks resulted in a proxy URI, no proxy will be used.
544
545Some UNIX software expects plain hostnames or hostname port
546combinations separated by colons, but (currently) this library expects
547full URIs, like most modern UNIX programs.
548
549<parameter>(determine-proxy-username/password [HANDLER])</parameter>
550
551The procedure in this parameter is called whenever the proxy requests
552authentication via a 407 Proxy Authentication Required response. This
553basically works the same as authentication against an origin server.
554
555The {{HANDLER}} is a procedure of two arguments; the URI for the
556''proxy'' currently being used and the realm (a string) which wants
557credentials.  The procedure should return two string values: the
558username and the password to use for authentication.
559
560The default value is a procedure which extracts the username and
561password components from the proxy's URI.
562
563
564=== Changelog
565
566* 1.1.1 Fix tests on CHICKEN 5.0.2 and higher.
567* 1.1 Add response object to condition object raised when redirect limit is exceeded (thanks to Norman Gray).
568* 1.0 Port to CHICKEN 5
569* 0.17 Preserve parsed URI path for requests, by avoiding re-encoding.  Fixes #1448 (thanks to [[/users/caolan-mcmahon|Caolan McMahon]])
570* 0.16 Replace heavy dependencies md5, message-digest and string-utils with simple-md5.
571* 0.15 Fix file descriptor leak when reader would raise exception (which also happened on 404 responses!)
572* 0.14 Fix off-by-one error in retry-attempts (thanks to "semarie").
573* 0.13 Enable SNI support for newly released openssl egg 1.9.0, use saner defaults like actually checking certificates. Add {{prepare-request}} procedure (thanks to [[/users/caolan-mcmahon|Caolan McMahon]])
574* 0.12.2 Tweak test timeouts some more.
575* 0.12.1 Tweak test timeouts so they hopefully won't fail as fast on Salmonella runs.
576* 0.12 Fix an endless loop in {{close-idle-connections!}} and a bug when {{max-idle-connections}} was zero (thanks to [[/users/mario-domenech-goulart|Mario Goulart]] for pointing out the endless loop).
577* 0.11 Add {{max-idle-connections}} to avoid FD exhaustion (thanks to [[/users/alaric-blagrave-snellpym|Alaric]] for pointing out this issue).  Add type and value check for uri argument (thanks to Lemonman for pointing this out).  Fix multipart sending of port-based files.  Add basic test suite.  Fix 303 redirect switch to {{GET}} method.  Use chunked encoding when using a custom writer procedure and there's no content-length header.
578* 0.10 Do not read {{HTTP_PROXY}} if {{REQUEST_METHOD}} is present (running in a CGI script), to prevent "[[https://httpoxy.org|httpoxy]]" attack (CVE-2016-6287).
579* 0.9 Add support for custom connector procedures.  Thanks to Ryan Senior for suggesting support for https client certificates, which this makes possible.
580* 0.8 Fix bug in multipart/form-data file uploads with non-file components in the form data causing a crash.  Thanks to Ryan Senior for reporting the bug and testing the fix.
581* 0.7.2 Add {{call-with-input-request*}}. Thanks to [[/users/mario-domenech-goulart|Mario Goulart]] for suggesting this.
582* 0.7.1 Fix delimited port handling of {{peek-char}} which caused mysterious openssl errors.  Thanks to [[/users/mario-domenech-goulart|Mario Goulart]] for a reproducible test case.
583* 0.7 Reduce CPU usage by implementing custom {{read-string!}} and {{read-line}} procedures in {{make-delimited-input-port}}. Improved error reporting (show URI as string, and always include it in error messages). Gracefully handle premature disconnection by retrying (as per RFC2616, 8.2.4).  Make openssl an optional dependency to make it easier to install on Windows.
584* 0.6.1 Work around a bug in {{read-string!}} in CHICKEN core which caused random errors.
585* 0.6 Provide a proper condition when encountering unsupported URI schemes (thanks to [[/users/christian-kellermann|Christian Kellermann]]).  Fix response body reading in error situations (thanks to [[/users/andyjpb|Andy Bennett]]).  Update request writer to use new {{finish-request-body}} from intarweb 1.0.
586* 0.5.1 Restore compatibility with message-digest and string-utils egg.
587* 0.5 Improve detection of dropped connections (prevents unneccessary "connection reset" exceptions to propagate into the program). Simplify interface by switching to {{POST}} when a {{writer}} is given to {{with-input-from-request}} and {{call-with-input-request}}.  Add support for multipart forms (file upload). Fix error in case of missing username when authorization was required (introduced by version 0.4.2). Put loop call in tail position (thanks to [[/users/felix-winkelmann|Felix]]) Automatically discard remaining data on the input port, if any, to avoid problems on subsequent requests. Add rudimentary support for parameterizable authentication schemes.
588* 0.4.2 Allow missing passwords in URIs for authentication
589* 0.4.1 Fix connection status check so when the remote end closed the connection we don't try to read from it anymore (thanks to Daishi Kato and Thomas Hintz)
590* 0.4 Fix redirection code on 303, and off-by-1 mistake in redirects count (thanks to Moritz Heidkamp). Add arguments to exn objects (thanks to Christian Kellermann). Also accept an empty alist for POSTdata. Fix URI path comparisons in cookies (thanks to Daishi Kato)
591* 0.3 Fixed handling of missing Path parameters in set-cookie headers. Reported by Hugo Arregui. Improve set-cookie handling by only passing Path and Domain when matching Set-Cookie header included those parameters.
592* 0.2 Added proxy support and many many bugfixes
593* 0.1 Initial version
594
595=== License
596
597  Copyright (c) 2008-2019, Peter Bex
598  Parts copyright (c) 2000-2004, Felix L. Winkelmann
599  All rights reserved.
600 
601  Redistribution and use in source and binary forms, with or without
602  modification, are permitted provided that the following conditions are
603  met:
604 
605  Redistributions of source code must retain the above copyright
606  notice, this list of conditions and the following disclaimer.
607 
608  Redistributions in binary form must reproduce the above copyright
609  notice, this list of conditions and the following disclaimer in the
610  documentation and/or other materials provided with the distribution.
611 
612  Neither the name of the author nor the names of its contributors may
613  be used to endorse or promote products derived from this software
614  without specific prior written permission.
615 
616  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
617  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
618  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
619  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
620  COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
621  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
622  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
623  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
624  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
625  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
626  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
627  OF THE POSSIBILITY OF SUCH DAMAGE.
Note: See TracBrowser for help on using the repository browser.