source: project/wiki/http @ 8469

Last change on this file since 8469 was 8469, checked in by svnwiki, 12 years ago

Changes applied for elf (66.92.69.84) through svnwiki:

format

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