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

Last change on this file since 36706 was 36706, checked in by sjamaan, 6 months ago

Update http-client C5 changelog with 1.1

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