source: project/wiki/http @ 8467

Last change on this file since 8467 was 8467, checked in by elf, 13 years ago

Changes applied for elf (66.92.69.84) through svnwiki:

formatting, again.

File size: 23.7 KB
Line 
1[[tags: egg]]
2
3== http
4
5[[toc:]]
6
7=== Description
8
9An easy to use HTTP client and server package. The server is fully multithreaded and
10supports persistent connections.
11
12=== Author
13
14[[Felix Winkelmann]]
15
16=== Requirements
17
18Requires the [[http://www.call-with-current-continuation.org/eggs/regex-case.html|regex-case]] egg at compile-time.
19
20{{http-client}} requires the [[http://www.call-with-current-continuation.org/eggs/url.html|url]] egg at runtime.
21
22Both {{http-client}} and {{http-server}} require the {{http-utils}} module at runtime.
23
24=== Documentation
25
26What follows is a description of the {{http}} extension, which is separated into three
27sub-extensions: {{http-client}} (HTTP client functionality), {{http-server}} (serving
28HTTP requests) and {{http-utils}} (utility functions common to client and server code).
29
30If you want to run a web-server, you should also take a look at the more featureful [[spiffy]] extension.
31
32==== http-client
33
34===== Usage
35
36{{(require-extension http-client)}}
37
38===== http:send-request
39
40 [procedure] (http:send-request REQUEST [INPUT-PORT OUTPUT-PORT])
41
42Sends an HTTP request represented by {{REQUEST}}, which may either be a HTTP
43request object, or a string that specifies an URL. The request is sent to it's destination
44and four values are returned: a string containing the first line of the response received
45from the server, an a-list that maps HTTP headers to values (strings) and the input-
46and output-port of the connection. Note that the connection is still open, and the returned
47ports can be passed in subsequent invocations of {{http:send-request}} to
48achieve a persistent connection. The ports are closed automatically, if no longer
49referenced.
50
51If an URL is given instead of a request record, a request is sent of the form
52
53 GET url HTTP/1.0
54 Connection: close
55
56===== http:GET
57
58 [procedure] (http:GET REQUEST)
59
60Sends a ''GET'' request represented by {{REQUEST}}, which may be a string (an URL)
61or a HTTP request object, and returns a string containing the body of the servers response.
62The {{REQUEST}} keeps a {{Cookie}} header that is set by {{Set-Cookie}} headers in a response,
63unsafely (hack), regardless of the storing policy. Be aware of it when reusing {{REQUEST}}.
64
65===== http:POST
66
67 [procedure] (http:POST REQUEST [ARGUMENTS] #!key [headers: LIST]
68  [type: CTYPE] [delim: DELIM])
69
70Sends a '''POST''' request represented by {{REQUEST}}, which may be a string (URL)
71or a HTTP request object, and returns a string containing the body of the server response.
72{{ARGUMENTS}} may be one of several forms depending on the value of the {{Content-Type}} attribute. If {{ARGUMENTS}} is unspecified, the body will be blank (i.e., a headers-only request).
73{{DELIM}} is an arbitrary separator string defaulting to the null-string.  The behaviour of {{DELIM}} is dependent on {{Content-Type}}.
74{{HEADERS}} should be either null or a list composed of {{(ATTRIBUTE . VALUE)}} pairs.  {{Connection}} and {{Content-Type}}, if unspecified, will be added automatically.  Attributes are not case sensitive.  Any attributes explicitly given in {{HEADERS}} are used, even if normally autogenerated. 
75{{CTYPE}} is the {{Content-Type}} header attribute, and defaults to {{application/x-www-form-urlencoded}}.  {{http:POST}} has special handlers for several values of {{Content-Type}} to simplify creation of the body, as given below:
76
77<scheme>
78(display "<table style=\"table-layout: auto; vertical-align: baseline\">")
79(display "<tr><th><tt>Content-Type</tt></th><th>Handler</th></tr>")
80(display "<tr><td><tt>application/x-www-form-urlencoded</tt></td>")
81(display "<td><tt>ARGUMENTS</tt> may be a string or a list. The list may ")
82(display "contain either <tt>(NAME . VALUE)</tt> pairs or strings of the ") (display "form <tt>\"name=value\"</tt>.  The message body is generated ")
83(display "as <tt>\"name1=val1&amp;name2=val2...\"</tt>.  <tt>DELIM</tt> ")
84(display "is ignored.</td></tr>")
85(display "<tr><td><tt>multipart/form-data</tt></td>")
86(display "<td><p><tt>ARGUMENTS</tt> may be a string or a list.  ")
87(display "<tt>DELIM</tt> is used as the boundary between multipart ")
88(display "sections, or defaults to <tt>----chicken-scheme----</tt> ")
89(display "if absent.  <tt>DELIM</tt> is automatically added to the ")
90(display "<tt>Content-Type</tt> header as the value of the ")
91(display "<tt>boundary</tt> attribute.  Each element of the ")
92(display "<tt>ARGUMENTS</tt> top-level list generates a single multipart ")
93(display "segment, and must be composed of elements of the following ")
94(display "types:\n")
95(display "<table style=\"inline-table; vertical-align: baseline; table-layout: auto\"><tr><td><tt>NAME</tt></td>")
96(display "<td>name attribute set to <tt>NAME</tt>.  Body is empty.")
97(display "</td></tr>")
98(display "<tr><td><tt>(NAME . VALUE)</tt></td>")
99(display "<td>name attribute set to <tt>NAME</tt>.  Body set to ")
100(display "<tt>VALUE</tt>.</td></tr>")
101(display "<tr><td><tt>(NAME (ATTRIB . AVAL)... VALUE)</tt></td>")
102(display "<td>name attribute set to <tt>NAME</tt>.  Every ")
103(display "<tt>(ATTRIB . AVAL)</tt> pair is added to the segment header ")
104(display "as <tt>ATTRIB=\"AVAL\"</tt>, separated by semicolons.  If ")
105(display "<tt>ATTRIB</tt> ends with a colon, it is added to the body ")
106(display "to allow metadata for file transmission.  <tt>VALUE</tt> ")
107(display "is appended to the body after attributes are processed, and ")
108(display "must NOT be a pair.</td></tr></table></p></td></tr>")
109(display "<tr><td>everything else</td>")
110(display "<td><tt>ARGUMENTS</tt> may be a string or a list of strings.  ")
111(display "Lists of strings are concatenated with <tt>DELIM</tt> as a ")
112(display "separator.</td></tr></table>")
113</scheme>
114
115The above alterations are performed only when {{ARGUMENTS}} is a list.  If given as a string, the body is set to the string value without any alteration.  All values other than strings or lists generate an error.
116
117The {{REQUEST}} keeps a Cookie header, as with {{http:GET}}.
118
119
120
121===== http:close-all-connections!
122
123 [procedure] (http:close-all-connections!)
124
125Close all persistent connections kept in the current thread.
126
127===== http:read-body
128
129 [procedure] (http:read-body ARGUMENTS PORT)
130
131Read the HTTP Body from the input port {{PORT}},
132according to the {{Content-Length}} header or the {{Transfer-Encoding}} header.
133
134===== http:add-proxy!
135
136 [procedure] (http:add-proxy! PROXY-HOST PROXY-PORT [SERV-PATTERN] [HOST-PATTERN] [PORT-PATTERN] [PATH-PATTERN])
137
138Add a http proxy. {{PATTERN}}s are either a string,
139a regex, or {{#t}}. The first added proxy has the least priority.
140
141Note: {{CONNECT}} method for SSL is not supported (yet).
142
143===== http:remove-all-proxies!
144
145 [procedure] (http:remove-all-proxies!)
146
147Remove all http proxies.
148
149
150==== http-server
151
152===== Usage
153
154{{(require-extension http-server)}}
155
156===== General operation
157
158The server maps URLs to ''resource-handlers'', which are procedures that process
159incoming client-requests. A resource-handler is responsible for generating a ''response''
160by writing output to the value of {{(current-output-port)}}.
161
162The data contained in a client-request is parsed by a so-called ''content-parser'',
163which is a procedure that reads the request-body from the port given by {{(current-input-port)}}.
164Content-types are encoded as symbols.
165
166A parser for {{application/x-www-form-urlencoded}} is predefined, other content-parsers
167have to be defined by application-code using the procedure {{http:content-parser}}, or the
168default parser will be invoked (which reads the content as a plain string).
169
170The content-parser for text returns the request-body as a string. The content-parser for urlencoded
171data returns the request-body as an a-list that maps variables to strings.
172
173===== http:make-server
174
175 [procedure] (http:make-server PORTNUMBER #!key NAME PROTOCOL BACKLOG ACCEPT INIT)
176
177Creates and returns a server-procedure. {{NAME}} defaults to something silly,
178{{PROTOCOL}} to {{#f}} (ignored), {{BACKLOG}} to {{40}} and {{ACCEPT}} to {{#f}}.
179{{INIT}} should be a procedure of no arguments that will be called after the
180networking initialisation has taken place (specifically, after the invocation of
181{{tcp-listen}}).
182
183To run the server-loop, invoke the returned procedure, which takes an optional boolean argument
184(passing {{#t}} will generate debugging output). {{PORTNUMBER}}, {{BACKLOG}}, and {{ACCEPT}}
185are directly passed on to {{tcp-listen}} (see [[Unit tcp]] for more information about those parameters).
186
187===== http:content-parser
188
189 [procedure] (http:content-parser CONTENTTYPE [PROC])
190
191Returns or sets the parser-procedure (a procedure of three arguments: the size of the content
192(may be {{#f}}), the a-list of request headers and an input port) for {{CONTENTTYPE}}, which
193should be a symbol.
194
195The content-parser procedure {{PROC}} should return two values: the parsed and the unparsed (raw) request body.
196
197===== http:write-response-header
198
199 [procedure] (http:write-response-header REQ [CODE MSG [ALIST [PORT [PROTOCOL]]]])
200
201Writes a HTTP response header for request {{REQ}} to {{PORT}}, containing the server-name.
202{{CODE}} and {{MSG}} default to {{200}} and {{OK}}, respectively.
203The optional {{ALIST}} may contain pairs with header-names and -values.
204If given, {{PROTOCOL}} specifes the HTTP protocol to use for the reply, which
205should be a symbol (either {{HTTP/1.0}} or {{HTTP/1.1}}) and defaults to the
206protocol given in {{http:make-server}}.
207
208===== http:write-error-response
209
210 [procedure] (http:write-error-response CODE MESSAGE [PORT])
211
212Writes a HTTP error-response to {{PORT}}, which defaults to the value of
213{{(current-output-port)}}. Uses the result returned by the value of
214{{(http:error-response-handler)}}.
215
216===== http:request-method-handler
217
218 [procedure] (http:request-method-handler METHOD [PROC])
219
220Reads ot sets the handler procedure {{PROC}} for the request-method {{METHOD}} (which should be a symbol).
221{{PROC}} should accept one argument, a request-object. During execution of the handler,
222the current input- and output ports are bound to ports connected to the client.
223
224Method-handlers for {{GET}} and {{POST}} requests are predefined.
225
226===== http:add-resource
227
228 [procedure] (http:add-resource URL HANDLER)
229
230Defines a new resource for {{URL}} (which may be a string, a symbol or a list of strings/symbols).
231{{HANDLER}} should be a procedure of two arguments: a request structure, and an a-list
232mapping urlencoded arguments to values.
233
234During execution of the handler the current input- and an output-ports are bound to ports communicating
235with the client.
236
237===== http:remove-resource
238
239 [procedure] (http:remove-resource URL)
240
241Removes the resource defined under {{URL}} (string, symbol or list).
242
243===== http:find-resource
244
245 [procedure] (http:find-resource URL)
246
247Returns the handler-procedure for the resource defined under {{URL}} or {{#f}}
248if no such resource is registered.
249
250===== http:fallback-handler
251
252 [parameter] http:fallback-handler
253
254Contains a procedure that is is invoked on requests for resources that could not be found.
255This procedure is then called with the original request object. The default handler generates a 404 response.
256
257===== http:error-response-handler
258
259 [parameter] http:error-response-handler
260
261Contains a procedure that will be called when an HTTP error-response should be generated.
262The procedure is called with the error-code and message and should return a string containing HTML
263that will be sent in the body of the error-response.
264
265===== http:current-request-count
266
267 [procedure] (http:current-request-count)
268
269Returns the number of requests handled since startup.
270
271===== http:request-count-limit
272
273 [parameter] http:request-count-limit
274
275Maximum number of concurrently handled requests.
276
277===== http:startup-hook
278
279 [parameter] http:startup-hook
280
281A procedure that will be called on server startup, before accepting first request.
282The default procedure does nothing.
283
284===== http:error-hook
285
286 [parameter] http:error-hook
287
288A procedure that will be called when a thread triggers an unhandled exception.
289The exception is passed as an argument to the hook procedure.
290
291===== http:log-hook
292
293 [parameter] http:log-hook
294
295A procedure that will be called upon completion of a HTTP request. The procedure will be called
296with two arguments: the request object and the IP address of the client. The default value of this
297parameter does nothing.
298
299===== http:url-transformation
300
301 [parameter] http:url-transformation
302
303A procedure that allows arbitrary transformations of the URL part of each incoming request. The procedure
304is called with a single argument (the URL string) and should return the same URL, or a transformed one.
305The default transformation returns the original url unchanged.
306
307===== http:listen-procedure
308
309 [parameter] http:listen-procedure
310
311Holds a procedure that will be used to create a socket-listener - defaults to {{tcp-listen}}.
312
313===== http:accept-procedure
314
315 [parameter] http:accept-procedure
316
317Holds a procedure that will be used to accept a socket-connection - defaults to {{tcp-accept}}.
318
319===== http:get-addresses-procedure
320
321 [parameter] http:get-addresses-procedure
322
323Holds a procedure that will be used to obtain peer addresses - defaults to {{tcp-addresses}}.
324
325===== http:hard-close-procedure
326
327 [parameter] http:hard-close-procedure
328
329Holds a procedure that will be used to close a connection prematurely - defaults to {{tcp-abandon-port}}.
330
331==== http-utils
332
333
334===== Usage
335
336{{(require-extension http-utils)}}
337
338
339===== http:decode-url
340
341 [procedure] (http:decode-url URL)
342
343Canonicalizes the string passed in {{URL}} and returns two values:
344the location-path and an alist containing argument <-> value pairs.
345
346
347===== http:make-request
348
349 [procedure] (http:make-request METHOD URL [ATTRIBUTES [BODY [PROTOCOL [IP]]]])
350
351Returns a freshly created HTTP request object (see below for the meaning of the arguments).
352
353===== http:request?
354
355 [procedure] (http:request? X)
356
357Returns {{#t}} if {{X}} is a request object, or {{#f}} otherwise.
358
359
360===== http-request accessor methods
361
362 [procedure] (http:request-url REQUEST) -> URL
363 [procedure] (http:request-protocol REQUEST) -> PROTOCOL
364 [procedure] (http:request-attributes REQUEST) -> ATTRIBUTES
365 [procedure] (http:request-body REQUEST) -> X
366 [procedure] (http:request-method REQUEST) -> METHOD
367 [procedure] (http:request-ip REQUEST) -> STRING
368 [procedure] (http:request-sslctx REQUEST) -> <ssl-client-context>
369 [procedure] (http:request-url-set! REQUEST URL)
370 [procedure] (http:request-protocol-set! REQUEST PROTOCOL)
371 [procedure] (http:request-attributes-set! REQUEST ATTRIBUTES)
372 [procedure] (http:request-body-set! REQUEST X)
373 [procedure] (http:request-method-set! REQUEST METHOD)
374 [procedure] (http:request-ip-set! REQUEST STRING)
375 [procedure] (http:request-sslctx-set! REQUEST <ssl-client-context>)
376
377Accessor procedures for the components of a HTTP request structure. {{URL}} is a string,
378{{METHOD}} and {{PROTOCOL}} are symbols, {{BODY}} is either {{#f}}
379or a string and {{ATTRIBUTES}} is an a-list where each pair contains an attribute
380string and a value string.
381
382
383
384===== http:request attribute methods
385
386 [procedure] (http:request-attribute-get REQUEST ATTRIB [DEFAULT {{#f}}])
387
388Accessor procedure to get the value of {{ATTRIB}} in {{REQUEST}}.  If {{ATTRIB}} is not present in request, {{DEFAULT}} is returned.  The search is case-insensitive.  Note that this only returns the value; the attribute name is NOT returned.
389
390 [procedure] (http:request-attribute-add! REQUEST ATTRIB AVAL)
391
392Adds {{ATTRIB}} to {{REQUEST}}'s attribute list with its value set to {{AVAL}}.  If {{ATTRIB}} already appears in the list (case-insensitive), its value is set to {{AVAL}} and it retains its position in the list; otherwise, the {{(ATTRIB . AVAL)}} pair is added to the end.
393
394
395 [procedure] (http:request-attribute-del! REQUEST ATTRIB)
396
397Removes {{ATTRIB}} from the attribute list in {{REQUEST}}, if it exists (case-insensitive search).  The order of the attribute list is not altered  aside from the removal.  It is not an error for {{ATTRIB}} to not appear in the list.
398
399
400===== http:read-line-limit
401
402 [parameter] http:read-line-limit
403
404The maximum length of a header-line.
405
406===== http:read-request-attributes
407
408 [procedure] (http:read-request-attributes PORT)
409
410Reads MIME type headers from {{PORT}} until end of file is reached or a line
411is not a valid MIME header and returns an a-list where each pair holds the header
412(converted to lowercase) and the value (both strings).
413
414===== http:canonicalize-string
415
416 [procedure] (http:canonicalize-string STRING)
417
418Canonicalizes {{STRING}} by substituting {{%XX}} and {{+}} sequences.
419
420
421=== Examples
422
423==== Server example
424
425A simple "Hello, world" server:
426
427<enscript highlight=scheme>
428(require-extension http-server)
429
430(http:add-resource '("/" "/index.html")
431  (lambda (r a)
432    (let ([msg "&lt;h1>Hello, world!&lt;/h1>"])
433      (http:write-response-header
434        r
435        `(("Content-type" . "test/html")
436          ("Content-length" . ,(string-length msg))))
437      (display msg) ) ) )
438
439((http:make-server 4242) #t)
440</enscript>
441
442To try it out, simply load the code into the interpreter and point your browser to
443[[http://localhost:4242/|localhost:4242]].
444
445==== Client example
446
447<enscript highlight=scheme>
448(require-extension http-client)
449
450(define-values (h a i o) (http:send-request "localhost:4242/"))
451
452(pretty-print (read-lines i))
453(close-input-port i)
454(close-output-port o)
455</enscript>
456
457Loading this into the interpreter will print
458
459 ("<h1>Hello, world!</h1>")
460
461(provided the hello-world server is running)
462
463
464=== Changelog
465* 2.4 Changed POST method; added multipart handling; added utility functions for query and modification of attributes in request objects, changed the build order. (elf)
466* 2.3 Yet another .setup fix related to the make macro (related to SVN revision 7145).
467* 2.2 Another .setup fix (related to the make macro).
468* 2.1 .setup fix to work with chicken from SVN trunk.
469* 2.0 Move http keep-alive header management from [[spiffy]] to http. This breaks backwards-compatibility because {{http:write-response-header}} needs the request object passed to it. ([[http://trac.callcc.org/ticket/311|Ticket #311]])
470* 1.58 Compatibility upgrade: removed deprecated Chicken functions.
471* 1.57 Added keep-alive support to http-server [Daishi Kato]
472* 1.56 Fixed bug in request content processing with POST forms
473* 1.55 Remove silly default "location" header ([[http://trac.callcc.org/ticket/339|ticket #339]]) [Daishi Kato]
474* 1.54 Fix passing of POST arguments to handlers defined with {{http:add-resource}}
475* 1.53 Generation of debugging info from {{get/post-handler}} ([[http://trac.callcc.org/ticket/253|ticket #253]]) [Mario Goulart]
476* 1.52 Added requirement of [[url]] egg to meta info [thanks to Mario Goulart]
477* 1.51 Client fix for handling closed keep-alive connections [Daishi Kato]
478* 1.50 Client API for http proxies [Daishi Kato]
479* 1.49 Client API flushes output before reading to be able to work with buffered ports
480* 1.48 Bugfix in http-client.scm [Daishi Kato]
481* 1.47 Removed multipart support, added unparsed request body field, content-parsers return additional raw body
482* 1.46 Bugfix in {{url-parser}}
483* 1.45 Cookie hack for http:GET and http:POST [Daishi Kato]
484* 1.44 {{http:POST}} now handles www-form-urlencoded content-type, which is the default [Daishi Kato]
485* 1.43 Client support for chunked transfer encoding [Daishi Kato]
486* 1.42 Client support for persistent connections [Daishi Kato]
487* 1.41 Client support for https (requires openssl egg) [Daishi Kato]
488* 1.40 Client requests did finalize ports in the wrong situation [Thanks to Tim Reid]
489* 1.39 URL canonicaliation fix #2 [Thanks to Zbigniew Szadkowski]
490* 1.38 URL-canonicalization fix by Peter Busser
491* 1.37 Added hidden slot to request-structure
492* 1.36 Fixed bug in {{http:write-error-response}} [Thanks to Peter Bex]
493* 1.35 Any error-response forces closing of client connection [Suggested by Diashi Kato]
494* 1.34 Added {{-lws2_32}} to build-command for {{http-client}} on Windows [Thanks to Daishi Kato]
495* 1.33 Added parameterized socket operations to server
496* 1.32 Adapted to SRFI-69 compatible hash-tables
497* 1.31 The ports returned by {{http:send-request}}, {{http:GET}} and {{http:POST}} are finalized [Thanks to Reed Sheridan]
498* 1.30 Added {{ip}} field to request object
499* 1.29 Added {{http:url-transformation}}, debug output includes response headers
500* 1.28 Replaced use of {{(end-of-file)}} with {{#!eof}}
501* 1.27 Added {{http:request-limit}}; fixed {{http:POST}} (which didn't
502handle the second argument always correctly)
503* 1.26 Special-cased regex strings to work around pregexp bugs [Thanks to Peter Bex]
504* 1.25 Retries on failed {{tcp-accept}}, error-message output is limited
505* 1.24 Fixed bug in server example [Thanks to Graham Fawcett]
506* 1.23 Added note about SIGPIPE [Thanks Graham Fawcett]
507* 1.22 Fixed regex bug in {{http-utils.scm}} [Thanks to Patrick Brannan]
508* 1.21 Added {{http:log-hook}}
509* 1.20 Content-parser got a third argument (attribute a-list); support for {{multipart/form-data}} content type; default handler for unknown content types
510* 1.19 Added documentation for {{http:read-request-attributes}}
511* 1.18 Added {{http:GET}} and {{http:POST}}
512* 1.17 URL parsing of requests and header-generation is somewhat more robust [Thanks to Peter Bex]
513* 1.16 {{http:send-request}} assumed old meaning of request body
514* 1.15 If the content-type is not known, a request-body has the empty list ({{()}}) as the body [Thanks to Peter Bex]
515* 1.14 Urlencoded arguments without argument are handled better and escapes in the argument name are processed correctly
516* 1.13 URL parsing regex fix #394493849 [Thanks to Lars Rustemeier]
517* 1.12 Added {{http:current-request-count}}
518* 1.11 {{http:make-server}} takes keyword arguments now and accepts two additional arguments
519* 1.10 {{http:write-response-header}} accepts yet another optional argument (protocol)
520* 1.9 Fixed bug that required {{uint32_t}} which wasn't necessarily everywhere available [Thanks to Mikel Evins]
521* 1.8 Adapted to new setup scheme
522* 1.7 URL parsing in {{(http-client)}} again. Will it ever stop?
523* 1.6 URL parsing in {{(http-client)}} works now with PCRE
524* 1.5 Fixed bug in regular expression for URL parsing and another one in the regexp for HTTP headers
525* 1.4 The URL parsing in {{(http-client)}} didn't handle {{XXX.XXX.XXX.XXX}}-style URLs [Thanks to Lars Rustemeier]
526* 1.3 {{http:write-response-header}} doesn't override headers given in the alist.
527* 1.2 {{http:write-response-header}} accepts more optional arguments.
528Added default content-parser for {{application/x-www-form-urlencoded}} bodies.
529{{http:canonicalize-string}}.
530* 1.1 The {{content-type}} from a request may be followed by optional parameters.
531Fixed bug in {{canonicalize-string}}. The argument to the fallback-handler
532is now a request object.
533* 1.0 Initial release
534
535=== License
536
537 Copyright (c) 2003, Felix L. Winkelmann
538 All rights reserved.
539 
540 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
541 conditions are met:
542 
543   Redistributions of source code must retain the above copyright notice, this list of conditions and the following
544     disclaimer.
545   Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
546     disclaimer in the documentation and/or other materials provided with the distribution.
547   Neither the name of the author nor the names of its contributors may be used to endorse or promote
548     products derived from this software without specific prior written permission.
549 
550 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
551 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
552 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
553 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
554 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
555 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
556 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
557 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
558 POSSIBILITY OF SUCH DAMAGE.
Note: See TracBrowser for help on using the repository browser.