rev 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +102 -0
- data/README +5 -5
- data/Rakefile +70 -0
- data/examples/echo_server.rb +24 -0
- data/ext/http11_client/http11_parser.rl +173 -0
- data/ext/libev/Changes +40 -0
- data/ext/libev/LICENSE +25 -0
- data/ext/libev/README +130 -0
- data/ext/libev/README.embed +3 -0
- data/ext/libev/update_ev_wrap +19 -0
- data/ext/rev/extconf.rb +24 -11
- data/ext/rev/rev.h +7 -0
- data/ext/rev/rev_buffer.c +17 -8
- data/ext/rev/rev_ext.c +5 -4
- data/ext/rev/rev_io_watcher.c +7 -6
- data/ext/rev/rev_loop.c +48 -6
- data/ext/rev/rev_ssl.c +255 -0
- data/ext/rev/rev_timer_watcher.c +3 -6
- data/ext/rev/rev_utils.c +108 -0
- data/ext/rev/rev_watcher.c +0 -3
- data/lib/rev.rb +2 -1
- data/lib/rev/async_watcher.rb +38 -0
- data/lib/rev/dns_resolver.rb +6 -8
- data/lib/rev/http_client.rb +35 -32
- data/lib/rev/io.rb +47 -43
- data/lib/rev/io_watcher.rb +0 -2
- data/lib/rev/listener.rb +0 -1
- data/lib/rev/loop.rb +11 -4
- data/lib/rev/server.rb +2 -4
- data/lib/rev/socket.rb +1 -2
- data/lib/rev/ssl.rb +184 -0
- data/lib/rev/timer_watcher.rb +0 -2
- data/lib/rev/watcher.rb +0 -2
- data/rev.gemspec +27 -0
- metadata +69 -61
- data/lib/http11_client.bundle +0 -0
- data/lib/rev_ext.bundle +0 -0
- data/spec/rev_spec.rb +0 -26
data/CHANGES
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
0.2.0:
|
2
|
+
|
3
|
+
* Initial Ruby 1.8.6 support
|
4
|
+
|
5
|
+
* Omit Rev::LIBEV_VERSION constant
|
6
|
+
|
7
|
+
* Catch Errno::ECONNRESET when writing to sockets
|
8
|
+
|
9
|
+
* SSL support via Rev::SSL, with a small C extension subclassing Ruby's
|
10
|
+
OpenSSL::SSL::SSLSocket allowing for non-blocking SSL handshakes
|
11
|
+
|
12
|
+
* Initial Rev::Utils implementation with #ncpus and methods to query and
|
13
|
+
change the maximum number of file descriptors for the current process.
|
14
|
+
|
15
|
+
* Initial Rev::AsyncWatcher implementation for cross-thread signaling
|
16
|
+
|
17
|
+
* Handle unspecified Content-Length when encoding is identity in HttpClient
|
18
|
+
|
19
|
+
* Fix bug in HttpClient processing zero Content-Length
|
20
|
+
|
21
|
+
* Get rid of method_missing stuff in Rev::HttpClient
|
22
|
+
|
23
|
+
* Have Rev::HttpClient close the connection on error
|
24
|
+
|
25
|
+
* Allow Rev::TCPSocket#on_connect to be private when accepting connections
|
26
|
+
from a Rev::TCPServer
|
27
|
+
|
28
|
+
0.1.4:
|
29
|
+
|
30
|
+
* Calibrate Rev::TimerWatchers against ev_time() and ev_now() when the watcher
|
31
|
+
is attached to the loop to ensure that the timeout interval is correct.
|
32
|
+
|
33
|
+
* Add check to ensure that a Rev::Loop cannot be run from within a callback
|
34
|
+
|
35
|
+
* Store Rev::Loop.default in a Thread-specific instance variable
|
36
|
+
|
37
|
+
* Upgrade libev to 0.3.0
|
38
|
+
|
39
|
+
* Rename BufferedIO to IO
|
40
|
+
|
41
|
+
* Fixed bug in BufferedIO#write_output_buffer causing it to spin endlessly on
|
42
|
+
an empty buffer.
|
43
|
+
|
44
|
+
* Added has_active_watchers? to Rev::Loop to check for active watchers
|
45
|
+
|
46
|
+
0.1.3:
|
47
|
+
|
48
|
+
* Fixed bug in Rev::Buffer read_from and write_to: now rb_sys_fail on failed
|
49
|
+
reads/writes.
|
50
|
+
|
51
|
+
* Change Rev::Buffer memory pools to purge on a periodic interval, rather than
|
52
|
+
whenever the GC marks the object.
|
53
|
+
|
54
|
+
* Fix bug in tracking the active watcher count. Factor shared watcher behavior
|
55
|
+
from rev_watcher.h to rev_watcher.c.
|
56
|
+
|
57
|
+
0.1.2:
|
58
|
+
|
59
|
+
* Commit initial specs
|
60
|
+
|
61
|
+
* Improve RDoc for the library
|
62
|
+
|
63
|
+
* Eliminate "zero copy" writes as they bypass the event loop
|
64
|
+
|
65
|
+
* Added Rev::Buffer C extension to provide high speed buffered writes
|
66
|
+
|
67
|
+
* Implement Rev::TCPSocket#peeraddr to improve compatibility with Ruby sockets
|
68
|
+
|
69
|
+
* Added Rev::Listener.close for clean shutdown of a listener
|
70
|
+
|
71
|
+
* Rev::Loop.default used to call ev_loop_default() (in C). However, this
|
72
|
+
registers signal handlers which conflict with Ruby's own. Now the behavior
|
73
|
+
has been changed to return a thread-local singleton of Rev::Loop.
|
74
|
+
|
75
|
+
* Creating a new Rev::TCPListener will disable reverse lookups in BasicSocket
|
76
|
+
|
77
|
+
* Made backlog for Rev::TCPListener user-definable
|
78
|
+
|
79
|
+
* Rev::TCPSocket now implements an on_resolve_failed callback for failed DNS
|
80
|
+
resolution. By default it's aliased to on_connect_failed.
|
81
|
+
|
82
|
+
* Changed event_callbacks to use instance_exec rather than passing the
|
83
|
+
watcher object as an argument. Documented use of defining an event
|
84
|
+
callback as a block
|
85
|
+
|
86
|
+
* Subsecond precision for Rev::TimerWatchers
|
87
|
+
|
88
|
+
0.1.1:
|
89
|
+
|
90
|
+
* Added Rev::HttpClient, an asynchronous HTTP/1.1 client written on top of
|
91
|
+
the Rev::TCPSocket class
|
92
|
+
|
93
|
+
* Imported HTTP response parser from the RFuzz project
|
94
|
+
|
95
|
+
* Added exception handling for Errno::ECONNRESET and Errno::EAGAIN
|
96
|
+
|
97
|
+
* Fixed bugs in buffered writer which resulted in exceptions if all data
|
98
|
+
couldn't be written with a nonblocking write.
|
99
|
+
|
100
|
+
0.1.0:
|
101
|
+
|
102
|
+
* Initial public release
|
data/README
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
= Rev
|
2
2
|
|
3
|
-
Rev is
|
4
|
-
|
5
|
-
epoll system call for Linux, the kqueue system call for BSDs and
|
6
|
-
completion ports interface for Solaris.
|
3
|
+
Rev is an event library for Ruby, built on the libev event library which
|
4
|
+
provides a cross-platform interface to high performance system calls . This
|
5
|
+
includes the epoll system call for Linux, the kqueue system call for BSDs and
|
6
|
+
OS X, and the completion ports interface for Solaris.
|
7
7
|
|
8
8
|
Rev also binds asynchronous wrappers to Ruby's core socket classes so you can
|
9
9
|
use them in conjunction with Rev to build asynchronous event-driven
|
@@ -120,7 +120,7 @@ haven't overridden them in a subclass). This is especially useful for small
|
|
120
120
|
one off programs or just experimenting with the API.
|
121
121
|
|
122
122
|
Any callback (methods prefixed with on_*) can be set on the fly by passing it
|
123
|
-
a block.
|
123
|
+
a block. (NOTE: Ruby 1.9 only)
|
124
124
|
|
125
125
|
Below is an example of using this syntax. It implements an echo server
|
126
126
|
identical to the one above:
|
data/Rakefile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'fileutils'
|
6
|
+
include FileUtils
|
7
|
+
|
8
|
+
# Load Rev Gemspec
|
9
|
+
load 'rev.gemspec'
|
10
|
+
|
11
|
+
# Default Rake task is compile
|
12
|
+
task :default => :compile
|
13
|
+
|
14
|
+
# RDoc
|
15
|
+
Rake::RDocTask.new(:rdoc) do |task|
|
16
|
+
task.rdoc_dir = 'doc'
|
17
|
+
task.title = 'Rev'
|
18
|
+
task.options = %w(--title Revactor --main README --line-numbers)
|
19
|
+
task.rdoc_files.include(['ext/rev/*.c', 'lib/**/*.rb'])
|
20
|
+
task.rdoc_files.include(['README', 'LICENSE'])
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gem
|
24
|
+
Rake::GemPackageTask.new(GEMSPEC) do |pkg|
|
25
|
+
pkg.need_tar = true
|
26
|
+
end
|
27
|
+
|
28
|
+
def make(makedir)
|
29
|
+
Dir.chdir(makedir) { sh 'make' }
|
30
|
+
end
|
31
|
+
|
32
|
+
def extconf(dir)
|
33
|
+
Dir.chdir(dir) { ruby "extconf.rb" }
|
34
|
+
end
|
35
|
+
|
36
|
+
def setup_extension(dir, extension)
|
37
|
+
ext = "ext/#{dir}"
|
38
|
+
ext_so = "#{ext}/#{extension}.#{Config::CONFIG['DLEXT']}"
|
39
|
+
ext_files = FileList[
|
40
|
+
"#{ext}/*.c",
|
41
|
+
"#{ext}/*.h",
|
42
|
+
"#{ext}/extconf.rb",
|
43
|
+
"#{ext}/Makefile",
|
44
|
+
"lib"
|
45
|
+
]
|
46
|
+
|
47
|
+
task "lib" do
|
48
|
+
directory "lib"
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "Builds just the #{extension} extension"
|
52
|
+
task extension.to_sym => ["#{ext}/Makefile", ext_so ]
|
53
|
+
|
54
|
+
file "#{ext}/Makefile" => ["#{ext}/extconf.rb"] do
|
55
|
+
extconf "#{ext}"
|
56
|
+
end
|
57
|
+
|
58
|
+
file ext_so => ext_files do
|
59
|
+
make "#{ext}"
|
60
|
+
cp ext_so, "lib"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
setup_extension("rev", "rev_ext")
|
65
|
+
setup_extension("http11_client", "http11_client")
|
66
|
+
|
67
|
+
task :compile => [:rev_ext, :http11_client]
|
68
|
+
|
69
|
+
CLEAN.include ['build/*', '**/*.o', '**/*.so', '**/*.a', '**/*.log', 'pkg']
|
70
|
+
CLEAN.include ['ext/rev/Makefile', 'lib/rev_ext.*', 'lib/http11_client.*']
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/rev'
|
2
|
+
|
3
|
+
ADDR = '127.0.0.1'
|
4
|
+
PORT = 4321
|
5
|
+
|
6
|
+
class EchoServerConnection < Rev::TCPSocket
|
7
|
+
def on_connect
|
8
|
+
puts "#{remote_addr}:#{remote_port} connected"
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_close
|
12
|
+
puts "#{remote_addr}:#{remote_port} disconnected"
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_read(data)
|
16
|
+
write data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
event_loop = Rev::Loop.default
|
21
|
+
Rev::TCPServer.new(ADDR, PORT, EchoServerConnection).attach(event_loop)
|
22
|
+
|
23
|
+
puts "Echo server listening on #{ADDR}:#{PORT}"
|
24
|
+
event_loop.run
|
@@ -0,0 +1,173 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2005 Zed A. Shaw
|
3
|
+
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#include "http11_parser.h"
|
7
|
+
#include <stdio.h>
|
8
|
+
#include <assert.h>
|
9
|
+
#include <stdlib.h>
|
10
|
+
#include <ctype.h>
|
11
|
+
#include <string.h>
|
12
|
+
|
13
|
+
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
14
|
+
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
15
|
+
#define PTR_TO(F) (buffer + parser->F)
|
16
|
+
#define L(M) fprintf(stderr, "" # M "\n");
|
17
|
+
|
18
|
+
|
19
|
+
/** machine **/
|
20
|
+
%%{
|
21
|
+
machine httpclient_parser;
|
22
|
+
|
23
|
+
action mark {MARK(mark, fpc); }
|
24
|
+
|
25
|
+
action start_field { MARK(field_start, fpc); }
|
26
|
+
|
27
|
+
action write_field {
|
28
|
+
parser->field_len = LEN(field_start, fpc);
|
29
|
+
}
|
30
|
+
|
31
|
+
action start_value { MARK(mark, fpc); }
|
32
|
+
|
33
|
+
action write_value {
|
34
|
+
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
35
|
+
}
|
36
|
+
|
37
|
+
action reason_phrase {
|
38
|
+
parser->reason_phrase(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
39
|
+
}
|
40
|
+
|
41
|
+
action status_code {
|
42
|
+
parser->status_code(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
43
|
+
}
|
44
|
+
|
45
|
+
action http_version {
|
46
|
+
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
47
|
+
}
|
48
|
+
|
49
|
+
action chunk_size {
|
50
|
+
parser->chunk_size(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
51
|
+
}
|
52
|
+
|
53
|
+
action last_chunk {
|
54
|
+
parser->last_chunk(parser->data, NULL, 0);
|
55
|
+
}
|
56
|
+
|
57
|
+
action done {
|
58
|
+
parser->body_start = fpc - buffer + 1;
|
59
|
+
if(parser->header_done != NULL)
|
60
|
+
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
|
61
|
+
fbreak;
|
62
|
+
}
|
63
|
+
|
64
|
+
# line endings
|
65
|
+
CRLF = "\r\n";
|
66
|
+
|
67
|
+
# character types
|
68
|
+
CTL = (cntrl | 127);
|
69
|
+
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
70
|
+
|
71
|
+
# elements
|
72
|
+
token = (ascii -- (CTL | tspecials));
|
73
|
+
|
74
|
+
Reason_Phrase = (any -- CRLF)+ >mark %reason_phrase;
|
75
|
+
Status_Code = digit+ >mark %status_code;
|
76
|
+
http_number = (digit+ "." digit+) ;
|
77
|
+
HTTP_Version = ("HTTP/" http_number) >mark %http_version ;
|
78
|
+
Status_Line = HTTP_Version " " Status_Code " " Reason_Phrase :> CRLF;
|
79
|
+
|
80
|
+
field_name = token+ >start_field %write_field;
|
81
|
+
field_value = any* >start_value %write_value;
|
82
|
+
message_header = field_name ":" " "* field_value :> CRLF;
|
83
|
+
|
84
|
+
Response = Status_Line (message_header)* (CRLF @done);
|
85
|
+
|
86
|
+
chunk_ext_val = token+;
|
87
|
+
chunk_ext_name = token+;
|
88
|
+
chunk_extension = (";" chunk_ext_name >start_field %write_field %start_value ("=" chunk_ext_val >start_value)? %write_value )*;
|
89
|
+
last_chunk = "0"? chunk_extension :> (CRLF @last_chunk @done);
|
90
|
+
chunk_size = xdigit+;
|
91
|
+
chunk = chunk_size >mark %chunk_size chunk_extension :> (CRLF @done);
|
92
|
+
Chunked_Header = (chunk | last_chunk);
|
93
|
+
|
94
|
+
main := Response | Chunked_Header;
|
95
|
+
}%%
|
96
|
+
|
97
|
+
/** Data **/
|
98
|
+
%% write data;
|
99
|
+
|
100
|
+
int httpclient_parser_init(httpclient_parser *parser) {
|
101
|
+
int cs = 0;
|
102
|
+
%% write init;
|
103
|
+
parser->cs = cs;
|
104
|
+
parser->body_start = 0;
|
105
|
+
parser->content_len = 0;
|
106
|
+
parser->mark = 0;
|
107
|
+
parser->nread = 0;
|
108
|
+
parser->field_len = 0;
|
109
|
+
parser->field_start = 0;
|
110
|
+
|
111
|
+
return(1);
|
112
|
+
}
|
113
|
+
|
114
|
+
|
115
|
+
/** exec **/
|
116
|
+
size_t httpclient_parser_execute(httpclient_parser *parser, const char *buffer, size_t len, size_t off) {
|
117
|
+
const char *p, *pe;
|
118
|
+
int cs = parser->cs;
|
119
|
+
|
120
|
+
assert(off <= len && "offset past end of buffer");
|
121
|
+
|
122
|
+
p = buffer+off;
|
123
|
+
pe = buffer+len;
|
124
|
+
|
125
|
+
assert(*pe == '\0' && "pointer does not end on NUL");
|
126
|
+
assert(pe - p == len - off && "pointers aren't same distance");
|
127
|
+
|
128
|
+
|
129
|
+
%% write exec;
|
130
|
+
|
131
|
+
parser->cs = cs;
|
132
|
+
parser->nread += p - (buffer + off);
|
133
|
+
|
134
|
+
assert(p <= pe && "buffer overflow after parsing execute");
|
135
|
+
assert(parser->nread <= len && "nread longer than length");
|
136
|
+
assert(parser->body_start <= len && "body starts after buffer end");
|
137
|
+
assert(parser->mark < len && "mark is after buffer end");
|
138
|
+
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
139
|
+
assert(parser->field_start < len && "field starts after buffer end");
|
140
|
+
|
141
|
+
if(parser->body_start) {
|
142
|
+
/* final \r\n combo encountered so stop right here */
|
143
|
+
%%write eof;
|
144
|
+
parser->nread++;
|
145
|
+
}
|
146
|
+
|
147
|
+
return(parser->nread);
|
148
|
+
}
|
149
|
+
|
150
|
+
int httpclient_parser_finish(httpclient_parser *parser)
|
151
|
+
{
|
152
|
+
int cs = parser->cs;
|
153
|
+
|
154
|
+
%%write eof;
|
155
|
+
|
156
|
+
parser->cs = cs;
|
157
|
+
|
158
|
+
if (httpclient_parser_has_error(parser) ) {
|
159
|
+
return -1;
|
160
|
+
} else if (httpclient_parser_is_finished(parser) ) {
|
161
|
+
return 1;
|
162
|
+
} else {
|
163
|
+
return 0;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
int httpclient_parser_has_error(httpclient_parser *parser) {
|
168
|
+
return parser->cs == httpclient_parser_error;
|
169
|
+
}
|
170
|
+
|
171
|
+
int httpclient_parser_is_finished(httpclient_parser *parser) {
|
172
|
+
return parser->cs == httpclient_parser_first_final;
|
173
|
+
}
|
data/ext/libev/Changes
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Revision history for libev, a high-performance and full-featured event loop
|
2
|
+
|
3
|
+
3.0 Mon Jan 28 13:14:47 CET 2008
|
4
|
+
- API/ABI bump to version 3.0.
|
5
|
+
- ev++.h includes "ev.h" by default now, not <ev.h>.
|
6
|
+
- slightly improved documentation.
|
7
|
+
- speed up signal detection after a fork.
|
8
|
+
- only optionally return trace status changed in ev_child
|
9
|
+
watchers.
|
10
|
+
- experimental (and undocumented) loop wrappers for ev++.h.
|
11
|
+
|
12
|
+
2.01 Tue Dec 25 08:04:41 CET 2007
|
13
|
+
- separate Changes file.
|
14
|
+
- fix ev_path_set => ev_stat_set typo.
|
15
|
+
- remove event_compat.h from the libev tarball.
|
16
|
+
- change how include files are found.
|
17
|
+
- doc updates.
|
18
|
+
- update licenses, explicitly allow for GPL relicensing.
|
19
|
+
|
20
|
+
2.0 Sat Dec 22 17:47:03 CET 2007
|
21
|
+
- new ev_sleep, ev_set_(io|timeout)_collect_interval.
|
22
|
+
- removed epoll from embeddable fd set.
|
23
|
+
- fix embed watchers.
|
24
|
+
- renamed ev_embed.loop to other.
|
25
|
+
- added exported Symbol tables.
|
26
|
+
- undefine member wrapper macros at the end of ev.c.
|
27
|
+
- respect EV_H in ev++.h.
|
28
|
+
|
29
|
+
1.86 Tue Dec 18 02:36:57 CET 2007
|
30
|
+
- fix memleak on loop destroy (not relevant for perl).
|
31
|
+
|
32
|
+
1.85 Fri Dec 14 20:32:40 CET 2007
|
33
|
+
- fix some aliasing issues w.r.t. timers and periodics
|
34
|
+
(not relevant for perl).
|
35
|
+
|
36
|
+
(for historic versions refer to EV/Changes, found in the Perl interface)
|
37
|
+
|
38
|
+
0.1 Wed Oct 31 21:31:48 CET 2007
|
39
|
+
- original version; hacked together in <24h.
|
40
|
+
|
data/ext/libev/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
All files in libev are Copyright (C)2007 Marc Alexander Lehmann.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions are
|
5
|
+
met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above
|
11
|
+
copyright notice, this list of conditions and the following
|
12
|
+
disclaimer in the documentation and/or other materials provided
|
13
|
+
with the distribution.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
16
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
17
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
18
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
19
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
20
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
21
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
22
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
23
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|