brianmario-eventmachine 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/COPYING +60 -0
  2. data/DEFERRABLES +138 -0
  3. data/EPOLL +141 -0
  4. data/GNU +281 -0
  5. data/KEYBOARD +38 -0
  6. data/LEGAL +25 -0
  7. data/LIGHTWEIGHT_CONCURRENCY +72 -0
  8. data/PURE_RUBY +77 -0
  9. data/README +74 -0
  10. data/RELEASE_NOTES +96 -0
  11. data/SMTP +9 -0
  12. data/SPAWNED_PROCESSES +93 -0
  13. data/TODO +10 -0
  14. data/eventmachine.gemspec +15 -0
  15. data/ext/binder.cpp +126 -0
  16. data/ext/binder.h +48 -0
  17. data/ext/cmain.cpp +553 -0
  18. data/ext/cplusplus.cpp +172 -0
  19. data/ext/ed.cpp +1473 -0
  20. data/ext/ed.h +361 -0
  21. data/ext/em.cpp +1890 -0
  22. data/ext/em.h +170 -0
  23. data/ext/emwin.cpp +300 -0
  24. data/ext/emwin.h +94 -0
  25. data/ext/epoll.cpp +26 -0
  26. data/ext/epoll.h +25 -0
  27. data/ext/eventmachine.h +90 -0
  28. data/ext/eventmachine_cpp.h +94 -0
  29. data/ext/extconf.rb +203 -0
  30. data/ext/files.cpp +94 -0
  31. data/ext/files.h +65 -0
  32. data/ext/kb.cpp +368 -0
  33. data/ext/page.cpp +107 -0
  34. data/ext/page.h +51 -0
  35. data/ext/pipe.cpp +327 -0
  36. data/ext/project.h +119 -0
  37. data/ext/rubymain.cpp +678 -0
  38. data/ext/sigs.cpp +89 -0
  39. data/ext/sigs.h +32 -0
  40. data/ext/ssl.cpp +408 -0
  41. data/ext/ssl.h +86 -0
  42. data/lib/em/deferrable.rb +208 -0
  43. data/lib/em/eventable.rb +39 -0
  44. data/lib/em/future.rb +62 -0
  45. data/lib/em/messages.rb +66 -0
  46. data/lib/em/processes.rb +68 -0
  47. data/lib/em/spawnable.rb +88 -0
  48. data/lib/em/streamer.rb +112 -0
  49. data/lib/eventmachine.rb +1756 -0
  50. data/lib/eventmachine_version.rb +31 -0
  51. data/lib/evma.rb +32 -0
  52. data/lib/evma/callback.rb +32 -0
  53. data/lib/evma/container.rb +75 -0
  54. data/lib/evma/factory.rb +77 -0
  55. data/lib/evma/protocol.rb +87 -0
  56. data/lib/evma/reactor.rb +48 -0
  57. data/lib/jeventmachine.rb +132 -0
  58. data/lib/pr_eventmachine.rb +1011 -0
  59. data/lib/protocols/buftok.rb +127 -0
  60. data/lib/protocols/header_and_content.rb +129 -0
  61. data/lib/protocols/httpcli2.rb +784 -0
  62. data/lib/protocols/httpclient.rb +264 -0
  63. data/lib/protocols/line_and_text.rb +122 -0
  64. data/lib/protocols/linetext2.rb +163 -0
  65. data/lib/protocols/postgres.rb +261 -0
  66. data/lib/protocols/saslauth.rb +179 -0
  67. data/lib/protocols/smtpclient.rb +308 -0
  68. data/lib/protocols/smtpserver.rb +543 -0
  69. data/lib/protocols/stomp.rb +130 -0
  70. data/lib/protocols/tcptest.rb +57 -0
  71. data/setup.rb +1585 -0
  72. metadata +126 -0
@@ -0,0 +1,107 @@
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: page.cpp
6
+ Date: 30Apr06
7
+
8
+ Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9
+ Gmail: blackhedd
10
+
11
+ This program is free software; you can redistribute it and/or modify
12
+ it under the terms of either: 1) the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2 of the
14
+ License, or (at your option) any later version; or 2) Ruby's License.
15
+
16
+ See the file COPYING for complete licensing information.
17
+
18
+ *****************************************************************************/
19
+
20
+
21
+ #include "project.h"
22
+
23
+
24
+ /******************
25
+ PageList::PageList
26
+ ******************/
27
+
28
+ PageList::PageList()
29
+ {
30
+ }
31
+
32
+
33
+ /*******************
34
+ PageList::~PageList
35
+ *******************/
36
+
37
+ PageList::~PageList()
38
+ {
39
+ while (HasPages())
40
+ PopFront();
41
+ }
42
+
43
+
44
+ /***************
45
+ PageList::Front
46
+ ***************/
47
+
48
+ void PageList::Front (const char **page, int *length)
49
+ {
50
+ assert (page && length);
51
+
52
+ if (HasPages()) {
53
+ Page p = Pages.front();
54
+ *page = p.Buffer;
55
+ *length = p.Size;
56
+ }
57
+ else {
58
+ *page = NULL;
59
+ *length = 0;
60
+ }
61
+ }
62
+
63
+
64
+ /******************
65
+ PageList::PopFront
66
+ ******************/
67
+
68
+ void PageList::PopFront()
69
+ {
70
+ if (HasPages()) {
71
+ Page p = Pages.front();
72
+ Pages.pop_front();
73
+ if (p.Buffer)
74
+ free ((void*)p.Buffer);
75
+ }
76
+ }
77
+
78
+
79
+ /******************
80
+ PageList::HasPages
81
+ ******************/
82
+
83
+ bool PageList::HasPages()
84
+ {
85
+ return (Pages.size() > 0) ? true : false;
86
+ }
87
+
88
+
89
+ /**************
90
+ PageList::Push
91
+ **************/
92
+
93
+ void PageList::Push (const char *buf, int size)
94
+ {
95
+ if (buf && (size > 0)) {
96
+ char *copy = (char*) malloc (size);
97
+ if (!copy)
98
+ throw runtime_error ("no memory in pagelist");
99
+ memcpy (copy, buf, size);
100
+ Pages.push_back (Page (copy, size));
101
+ }
102
+ }
103
+
104
+
105
+
106
+
107
+
@@ -0,0 +1,51 @@
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: page.h
6
+ Date: 30Apr06
7
+
8
+ Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9
+ Gmail: blackhedd
10
+
11
+ This program is free software; you can redistribute it and/or modify
12
+ it under the terms of either: 1) the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2 of the
14
+ License, or (at your option) any later version; or 2) Ruby's License.
15
+
16
+ See the file COPYING for complete licensing information.
17
+
18
+ *****************************************************************************/
19
+
20
+
21
+ #ifndef __PageManager__H_
22
+ #define __PageManager__H_
23
+
24
+
25
+ /**************
26
+ class PageList
27
+ **************/
28
+
29
+ class PageList
30
+ {
31
+ struct Page {
32
+ Page (const char *b, size_t s): Buffer(b), Size(s) {}
33
+ const char *Buffer;
34
+ size_t Size;
35
+ };
36
+
37
+ public:
38
+ PageList();
39
+ virtual ~PageList();
40
+
41
+ void Push (const char*, int);
42
+ bool HasPages();
43
+ void Front (const char**, int*);
44
+ void PopFront();
45
+
46
+ private:
47
+ deque<Page> Pages;
48
+ };
49
+
50
+
51
+ #endif // __PageManager__H_
@@ -0,0 +1,327 @@
1
+ /*****************************************************************************
2
+
3
+ $Id$
4
+
5
+ File: pipe.cpp
6
+ Date: 30May07
7
+
8
+ Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9
+ Gmail: blackhedd
10
+
11
+ This program is free software; you can redistribute it and/or modify
12
+ it under the terms of either: 1) the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2 of the
14
+ License, or (at your option) any later version; or 2) Ruby's License.
15
+
16
+ See the file COPYING for complete licensing information.
17
+
18
+ *****************************************************************************/
19
+
20
+ #include "project.h"
21
+
22
+
23
+ #ifdef OS_UNIX
24
+ // THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS.
25
+
26
+ /******************************
27
+ PipeDescriptor::PipeDescriptor
28
+ ******************************/
29
+
30
+ PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em):
31
+ EventableDescriptor (fd, parent_em),
32
+ bReadAttemptedAfterClose (false),
33
+ LastIo (gCurrentLoopTime),
34
+ InactivityTimeout (0),
35
+ OutboundDataSize (0),
36
+ SubprocessPid (subpid)
37
+ {
38
+ #ifdef HAVE_EPOLL
39
+ EpollEvent.events = EPOLLIN;
40
+ #endif
41
+ #ifdef HAVE_KQUEUE
42
+ MyEventMachine->ArmKqueueReader (this);
43
+ #endif
44
+ }
45
+
46
+
47
+ /*******************************
48
+ PipeDescriptor::~PipeDescriptor
49
+ *******************************/
50
+
51
+ PipeDescriptor::~PipeDescriptor()
52
+ {
53
+ // Run down any stranded outbound data.
54
+ for (size_t i=0; i < OutboundPages.size(); i++)
55
+ OutboundPages[i].Free();
56
+
57
+ /* As a virtual destructor, we come here before the base-class
58
+ * destructor that closes our file-descriptor.
59
+ * We have to make sure the subprocess goes down (if it's not
60
+ * already down) and we have to reap the zombie.
61
+ *
62
+ * This implementation is PROVISIONAL and will surely be improved.
63
+ * The intention here is that we never block, hence the highly
64
+ * undesirable sleeps. But if we can't reap the subprocess even
65
+ * after sending it SIGKILL, then something is wrong and we
66
+ * throw a fatal exception, which is also not something we should
67
+ * be doing.
68
+ *
69
+ * Eventually the right thing to do will be to have the reactor
70
+ * core respond to SIGCHLD by chaining a handler on top of the
71
+ * one Ruby may have installed, and dealing with a list of dead
72
+ * children that are pending cleanup.
73
+ *
74
+ * Since we want to have a signal processor integrated into the
75
+ * client-visible API, let's wait until that is done before cleaning
76
+ * this up.
77
+ *
78
+ * Added a very ugly hack to support passing the subprocess's exit
79
+ * status to the user. It only makes logical sense for user code to access
80
+ * the subprocess exit status in the unbind callback. But unbind is called
81
+ * back during the EventableDescriptor destructor. So by that time there's
82
+ * no way to call back this object through an object binding, because it's
83
+ * already been cleaned up. We might have added a parameter to the unbind
84
+ * callback, but that would probably break a huge amount of existing code.
85
+ * So the hack-solution is to define an instance variable in the EventMachine
86
+ * object and stick the exit status in there, where it can easily be accessed
87
+ * with an accessor visible to user code.
88
+ * User code should ONLY access the exit status from within the unbind callback.
89
+ * Otherwise there's no guarantee it'll be valid.
90
+ * This hack won't make it impossible to run multiple EventMachines in a single
91
+ * process, but it will make it impossible to reliably nest unbind calls
92
+ * within other unbind calls. (Not sure if that's even possible.)
93
+ */
94
+
95
+ struct timespec req = {0, 10000000};
96
+ kill (SubprocessPid, SIGTERM);
97
+ nanosleep (&req, NULL);
98
+ assert (MyEventMachine);
99
+ if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) == 0) {
100
+ kill (SubprocessPid, SIGKILL);
101
+ nanosleep (&req, NULL);
102
+ if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) == 0)
103
+ throw std::runtime_error ("unable to reap subprocess");
104
+ }
105
+ }
106
+
107
+
108
+
109
+ /********************
110
+ PipeDescriptor::Read
111
+ ********************/
112
+
113
+ void PipeDescriptor::Read()
114
+ {
115
+ int sd = GetSocket();
116
+ if (sd == INVALID_SOCKET) {
117
+ assert (!bReadAttemptedAfterClose);
118
+ bReadAttemptedAfterClose = true;
119
+ return;
120
+ }
121
+
122
+ LastIo = gCurrentLoopTime;
123
+
124
+ int total_bytes_read = 0;
125
+ char readbuffer [16 * 1024];
126
+
127
+ for (int i=0; i < 10; i++) {
128
+ // Don't read just one buffer and then move on. This is faster
129
+ // if there is a lot of incoming.
130
+ // But don't read indefinitely. Give other sockets a chance to run.
131
+ // NOTICE, we're reading one less than the buffer size.
132
+ // That's so we can put a guard byte at the end of what we send
133
+ // to user code.
134
+ // Use read instead of recv, which on Linux gives a "socket operation
135
+ // on nonsocket" error.
136
+
137
+
138
+ int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
139
+ //cerr << "<R:" << r << ">";
140
+
141
+ if (r > 0) {
142
+ total_bytes_read += r;
143
+ LastRead = gCurrentLoopTime;
144
+
145
+ // Add a null-terminator at the the end of the buffer
146
+ // that we will send to the callback.
147
+ // DO NOT EVER CHANGE THIS. We want to explicitly allow users
148
+ // to be able to depend on this behavior, so they will have
149
+ // the option to do some things faster. Additionally it's
150
+ // a security guard against buffer overflows.
151
+ readbuffer [r] = 0;
152
+ if (EventCallback)
153
+ (*EventCallback)(GetBinding().c_str(), EM_CONNECTION_READ, readbuffer, r);
154
+ }
155
+ else if (r == 0) {
156
+ break;
157
+ }
158
+ else {
159
+ // Basically a would-block, meaning we've read everything there is to read.
160
+ break;
161
+ }
162
+
163
+ }
164
+
165
+
166
+ if (total_bytes_read == 0) {
167
+ // If we read no data on a socket that selected readable,
168
+ // it generally means the other end closed the connection gracefully.
169
+ ScheduleClose (false);
170
+ //bCloseNow = true;
171
+ }
172
+
173
+ }
174
+
175
+ /*********************
176
+ PipeDescriptor::Write
177
+ *********************/
178
+
179
+ void PipeDescriptor::Write()
180
+ {
181
+ int sd = GetSocket();
182
+ assert (sd != INVALID_SOCKET);
183
+
184
+ LastIo = gCurrentLoopTime;
185
+ char output_buffer [16 * 1024];
186
+ size_t nbytes = 0;
187
+
188
+ while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
189
+ OutboundPage *op = &(OutboundPages[0]);
190
+ if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
191
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
192
+ nbytes += (op->Length - op->Offset);
193
+ op->Free();
194
+ OutboundPages.pop_front();
195
+ }
196
+ else {
197
+ int len = sizeof(output_buffer) - nbytes;
198
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
199
+ op->Offset += len;
200
+ nbytes += len;
201
+ }
202
+ }
203
+
204
+ // We should never have gotten here if there were no data to write,
205
+ // so assert that as a sanity check.
206
+ // Don't bother to make sure nbytes is less than output_buffer because
207
+ // if it were we probably would have crashed already.
208
+ assert (nbytes > 0);
209
+
210
+ assert (GetSocket() != INVALID_SOCKET);
211
+ int bytes_written = write (GetSocket(), output_buffer, nbytes);
212
+
213
+ if (bytes_written > 0) {
214
+ OutboundDataSize -= bytes_written;
215
+ if ((size_t)bytes_written < nbytes) {
216
+ int len = nbytes - bytes_written;
217
+ char *buffer = (char*) malloc (len + 1);
218
+ if (!buffer)
219
+ throw std::runtime_error ("bad alloc throwing back data");
220
+ memcpy (buffer, output_buffer + bytes_written, len);
221
+ buffer [len] = 0;
222
+ OutboundPages.push_front (OutboundPage (buffer, len));
223
+ }
224
+ #ifdef HAVE_EPOLL
225
+ EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0));
226
+ assert (MyEventMachine);
227
+ MyEventMachine->Modify (this);
228
+ #endif
229
+ }
230
+ else {
231
+ #ifdef OS_UNIX
232
+ if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
233
+ #endif
234
+ #ifdef OS_WIN32
235
+ if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK))
236
+ #endif
237
+ Close();
238
+ }
239
+ }
240
+
241
+
242
+ /*************************
243
+ PipeDescriptor::Heartbeat
244
+ *************************/
245
+
246
+ void PipeDescriptor::Heartbeat()
247
+ {
248
+ // If an inactivity timeout is defined, then check for it.
249
+ if (InactivityTimeout && ((gCurrentLoopTime - LastIo) >= InactivityTimeout))
250
+ ScheduleClose (false);
251
+ //bCloseNow = true;
252
+ }
253
+
254
+
255
+ /*****************************
256
+ PipeDescriptor::SelectForRead
257
+ *****************************/
258
+
259
+ bool PipeDescriptor::SelectForRead()
260
+ {
261
+ /* Pipe descriptors, being local by definition, don't have
262
+ * a pending state, so this is simpler than for the
263
+ * ConnectionDescriptor object.
264
+ */
265
+ return true;
266
+ }
267
+
268
+ /******************************
269
+ PipeDescriptor::SelectForWrite
270
+ ******************************/
271
+
272
+ bool PipeDescriptor::SelectForWrite()
273
+ {
274
+ /* Pipe descriptors, being local by definition, don't have
275
+ * a pending state, so this is simpler than for the
276
+ * ConnectionDescriptor object.
277
+ */
278
+ return (GetOutboundDataSize() > 0);
279
+ }
280
+
281
+
282
+
283
+
284
+ /********************************
285
+ PipeDescriptor::SendOutboundData
286
+ ********************************/
287
+
288
+ int PipeDescriptor::SendOutboundData (const char *data, int length)
289
+ {
290
+ //if (bCloseNow || bCloseAfterWriting)
291
+ if (IsCloseScheduled())
292
+ return 0;
293
+
294
+ if (!data && (length > 0))
295
+ throw std::runtime_error ("bad outbound data");
296
+ char *buffer = (char *) malloc (length + 1);
297
+ if (!buffer)
298
+ throw std::runtime_error ("no allocation for outbound data");
299
+ memcpy (buffer, data, length);
300
+ buffer [length] = 0;
301
+ OutboundPages.push_back (OutboundPage (buffer, length));
302
+ OutboundDataSize += length;
303
+ #ifdef HAVE_EPOLL
304
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
305
+ assert (MyEventMachine);
306
+ MyEventMachine->Modify (this);
307
+ #endif
308
+ return length;
309
+ }
310
+
311
+ /********************************
312
+ PipeDescriptor::GetSubprocessPid
313
+ ********************************/
314
+
315
+ bool PipeDescriptor::GetSubprocessPid (pid_t *pid)
316
+ {
317
+ bool ok = false;
318
+ if (pid && (SubprocessPid > 0)) {
319
+ *pid = SubprocessPid;
320
+ ok = true;
321
+ }
322
+ return ok;
323
+ }
324
+
325
+
326
+ #endif // OS_UNIX
327
+