eventmachine 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/RELEASE_NOTES +5 -1
- data/ext/binder.cpp +125 -0
- data/ext/binder.h +54 -0
- data/ext/ed.cpp +528 -0
- data/ext/ed.h +152 -0
- data/ext/em.cpp +475 -0
- data/ext/em.h +90 -0
- data/ext/extconf.rb +31 -0
- data/ext/libmain.cpp +312 -0
- data/ext/project.h +63 -0
- data/ext/sigs.cpp +60 -0
- data/ext/sigs.h +35 -0
- data/lib/eventmachine.rb +39 -1
- metadata +18 -80
- data/doc/classes/Echo.html +0 -180
- data/doc/classes/Echo.src/M000033.html +0 -23
- data/doc/classes/Echo.src/M000034.html +0 -19
- data/doc/classes/Echo.src/M000035.html +0 -19
- data/doc/classes/Echo.src/M000036.html +0 -18
- data/doc/classes/EventMachine.html +0 -369
- data/doc/classes/EventMachine.src/M000002.html +0 -24
- data/doc/classes/EventMachine.src/M000003.html +0 -24
- data/doc/classes/EventMachine.src/M000004.html +0 -26
- data/doc/classes/EventMachine.src/M000005.html +0 -21
- data/doc/classes/EventMachine.src/M000006.html +0 -23
- data/doc/classes/EventMachine.src/M000007.html +0 -28
- data/doc/classes/EventMachine.src/M000008.html +0 -20
- data/doc/classes/EventMachine.src/M000009.html +0 -19
- data/doc/classes/EventMachine.src/M000010.html +0 -18
- data/doc/classes/EventMachine.src/M000011.html +0 -18
- data/doc/classes/EventMachine/Connection.html +0 -341
- data/doc/classes/EventMachine/Connection.src/M000020.html +0 -19
- data/doc/classes/EventMachine/Connection.src/M000021.html +0 -17
- data/doc/classes/EventMachine/Connection.src/M000022.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000023.html +0 -17
- data/doc/classes/EventMachine/Connection.src/M000024.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000025.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000026.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000027.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000028.html +0 -17
- data/doc/classes/EventMachine/Connection.src/M000029.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000030.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000031.html +0 -18
- data/doc/classes/EventMachine/Connection.src/M000032.html +0 -18
- data/doc/classes/EventMachine/ConnectionAlreadyBound.html +0 -111
- data/doc/classes/EventMachine/ConnectionNotBound.html +0 -111
- data/doc/classes/EventMachine/Connections.html +0 -292
- data/doc/classes/EventMachine/Connections.src/M000012.html +0 -20
- data/doc/classes/EventMachine/Connections.src/M000013.html +0 -23
- data/doc/classes/EventMachine/Connections.src/M000014.html +0 -23
- data/doc/classes/EventMachine/Connections.src/M000015.html +0 -24
- data/doc/classes/EventMachine/Connections.src/M000016.html +0 -19
- data/doc/classes/EventMachine/Connections.src/M000017.html +0 -21
- data/doc/classes/EventMachine/Connections.src/M000018.html +0 -19
- data/doc/classes/EventMachine/Connections.src/M000019.html +0 -20
- data/doc/classes/EventMachine/EventCodes.html +0 -133
- data/doc/classes/EventMachine/NoConnectionMade.html +0 -111
- data/doc/classes/EventMachine/NoHandlerForAcceptedConnection.html +0 -111
- data/doc/classes/EventMachine/NoServerCreated.html +0 -111
- data/doc/classes/EventMachine/TimerNotInstalled.html +0 -111
- data/doc/classes/EventMachine/TooManyAcceptors.html +0 -111
- data/doc/classes/EventMachine/TooManyTimersPending.html +0 -111
- data/doc/classes/EventMachine/UnknownTimerFired.html +0 -111
- data/doc/classes/Zzz.html +0 -131
- data/doc/classes/Zzz.src/M000001.html +0 -18
- data/doc/created.rid +0 -1
- data/doc/files/binder_cpp.html +0 -101
- data/doc/files/ed_cpp.html +0 -101
- data/doc/files/em_cpp.html +0 -101
- data/doc/files/event_machine_rb.html +0 -118
- data/doc/files/g_rb.html +0 -108
- data/doc/files/lib/eventmachine_rb.html +0 -114
- data/doc/files/libmain_cpp.html +0 -101
- data/doc/files/sigs_cpp.html +0 -101
- data/doc/files/tests/testem_rb.html +0 -110
- data/doc/fr_class_index.html +0 -41
- data/doc/fr_file_index.html +0 -35
- data/doc/fr_method_index.html +0 -62
- data/doc/index.html +0 -24
- data/ext/libeventmachine.so +0 -0
data/RELEASE_NOTES
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
$Id: RELEASE_NOTES
|
1
|
+
$Id: RELEASE_NOTES 2283 2006-04-13 10:17:37Z francis $
|
2
2
|
|
3
3
|
RUBY/EventMachine RELEASE NOTES
|
4
4
|
|
5
|
+
--------------------------------------------------
|
6
|
+
Version: 0.3.2, released 12Apr06
|
7
|
+
Added support for a user-supplied block in EventMachine#connect.
|
8
|
+
|
5
9
|
--------------------------------------------------
|
6
10
|
Version: 0.3.1, released 11Apr06
|
7
11
|
Fixed bug that prevented EventMachine from being run multiple
|
data/ext/binder.cpp
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
/*****************************************************************************
|
2
|
+
|
3
|
+
$Id: binder.cpp 2291 2006-04-14 03:56:18Z francis $
|
4
|
+
|
5
|
+
File: binder.cpp
|
6
|
+
Date: 07Apr06
|
7
|
+
|
8
|
+
Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
9
|
+
Gmail: garbagecat20
|
10
|
+
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
12
|
+
it under the terms of the GNU General Public License as published by
|
13
|
+
the Free Software Foundation; either version 2 of the License, or
|
14
|
+
(at your option) any later version.
|
15
|
+
|
16
|
+
This program is distributed in the hope that it will be useful,
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
19
|
+
GNU General Public License for more details.
|
20
|
+
|
21
|
+
You should have received a copy of the GNU General Public License
|
22
|
+
along with this program; if not, write to the Free Software
|
23
|
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
24
|
+
|
25
|
+
*****************************************************************************/
|
26
|
+
|
27
|
+
#include "project.h"
|
28
|
+
|
29
|
+
#define DEV_URANDOM "/dev/urandom"
|
30
|
+
#define DO_NOT_REQUIRE_UUID
|
31
|
+
|
32
|
+
|
33
|
+
map<string, Bindable_t*> Bindable_t::BindingBag;
|
34
|
+
|
35
|
+
|
36
|
+
/********************************
|
37
|
+
STATIC Bindable_t::CreateBinding
|
38
|
+
********************************/
|
39
|
+
|
40
|
+
string Bindable_t::CreateBinding()
|
41
|
+
{
|
42
|
+
static int index = 0;
|
43
|
+
static string seed;
|
44
|
+
|
45
|
+
if ((index >= 1000000) || (seed.length() == 0)) {
|
46
|
+
#ifdef REQUIRE_UUID
|
47
|
+
uuid_t u;
|
48
|
+
uuid_generate (u);
|
49
|
+
#endif
|
50
|
+
|
51
|
+
#ifdef DO_NOT_REQUIRE_UUID
|
52
|
+
int fd = open (DEV_URANDOM, O_RDONLY);
|
53
|
+
if (fd < 0)
|
54
|
+
throw std::runtime_error ("No entropy device");
|
55
|
+
|
56
|
+
unsigned char u[16];
|
57
|
+
size_t r = read (fd, u, sizeof(u));
|
58
|
+
if (r < sizeof(u))
|
59
|
+
throw std::runtime_error ("Unable to read entropy device");
|
60
|
+
#endif
|
61
|
+
|
62
|
+
unsigned char *u1 = (unsigned char*)u;
|
63
|
+
char u2 [sizeof(u) * 2 + 1];
|
64
|
+
|
65
|
+
for (size_t i=0; i < sizeof(u); i++)
|
66
|
+
sprintf (u2 + (i * 2), "%02x", u1[i]);
|
67
|
+
|
68
|
+
seed = string (u2);
|
69
|
+
index = 0;
|
70
|
+
|
71
|
+
|
72
|
+
}
|
73
|
+
|
74
|
+
stringstream ss;
|
75
|
+
ss << seed << (++index);
|
76
|
+
return ss.str();
|
77
|
+
}
|
78
|
+
|
79
|
+
|
80
|
+
/*****************************
|
81
|
+
STATIC: Bindable_t::GetObject
|
82
|
+
*****************************/
|
83
|
+
|
84
|
+
Bindable_t *Bindable_t::GetObject (const char *binding)
|
85
|
+
{
|
86
|
+
string s (binding ? binding : "");
|
87
|
+
return GetObject (s);
|
88
|
+
}
|
89
|
+
|
90
|
+
/*****************************
|
91
|
+
STATIC: Bindable_t::GetObject
|
92
|
+
*****************************/
|
93
|
+
|
94
|
+
Bindable_t *Bindable_t::GetObject (const string &binding)
|
95
|
+
{
|
96
|
+
map<string, Bindable_t*>::const_iterator i = BindingBag.find (binding);
|
97
|
+
if (i != BindingBag.end())
|
98
|
+
return i->second;
|
99
|
+
else
|
100
|
+
return NULL;
|
101
|
+
}
|
102
|
+
|
103
|
+
|
104
|
+
/**********************
|
105
|
+
Bindable_t::Bindable_t
|
106
|
+
**********************/
|
107
|
+
|
108
|
+
Bindable_t::Bindable_t()
|
109
|
+
{
|
110
|
+
Binding = Bindable_t::CreateBinding();
|
111
|
+
BindingBag [Binding] = this;
|
112
|
+
}
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
/***********************
|
117
|
+
Bindable_t::~Bindable_t
|
118
|
+
***********************/
|
119
|
+
|
120
|
+
Bindable_t::~Bindable_t()
|
121
|
+
{
|
122
|
+
BindingBag.erase (Binding);
|
123
|
+
}
|
124
|
+
|
125
|
+
|
data/ext/binder.h
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
/*****************************************************************************
|
2
|
+
|
3
|
+
$Id: binder.h 2291 2006-04-14 03:56:18Z francis $
|
4
|
+
|
5
|
+
File: binder.h
|
6
|
+
Date: 07Apr06
|
7
|
+
|
8
|
+
Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
9
|
+
Gmail: garbagecat20
|
10
|
+
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
12
|
+
it under the terms of the GNU General Public License as published by
|
13
|
+
the Free Software Foundation; either version 2 of the License, or
|
14
|
+
(at your option) any later version.
|
15
|
+
|
16
|
+
This program is distributed in the hope that it will be useful,
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
19
|
+
GNU General Public License for more details.
|
20
|
+
|
21
|
+
You should have received a copy of the GNU General Public License
|
22
|
+
along with this program; if not, write to the Free Software
|
23
|
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
24
|
+
|
25
|
+
*****************************************************************************/
|
26
|
+
|
27
|
+
#ifndef __ObjectBindings__H_
|
28
|
+
#define __ObjectBindings__H_
|
29
|
+
|
30
|
+
|
31
|
+
class Bindable_t
|
32
|
+
{
|
33
|
+
public:
|
34
|
+
static string CreateBinding();
|
35
|
+
static Bindable_t *GetObject (const string&);
|
36
|
+
static Bindable_t *GetObject (const char*);
|
37
|
+
static map<string, Bindable_t*> BindingBag;
|
38
|
+
|
39
|
+
public:
|
40
|
+
Bindable_t();
|
41
|
+
virtual ~Bindable_t();
|
42
|
+
|
43
|
+
string GetBinding() {return Binding;}
|
44
|
+
|
45
|
+
private:
|
46
|
+
string Binding;
|
47
|
+
};
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
#endif // __ObjectBindings__H_
|
54
|
+
|
data/ext/ed.cpp
ADDED
@@ -0,0 +1,528 @@
|
|
1
|
+
/*****************************************************************************
|
2
|
+
|
3
|
+
$Id: ed.cpp 2291 2006-04-14 03:56:18Z francis $
|
4
|
+
|
5
|
+
File: ed.cpp
|
6
|
+
Date: 06Apr06
|
7
|
+
|
8
|
+
Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
9
|
+
Gmail: garbagecat20
|
10
|
+
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
12
|
+
it under the terms of the GNU General Public License as published by
|
13
|
+
the Free Software Foundation; either version 2 of the License, or
|
14
|
+
(at your option) any later version.
|
15
|
+
|
16
|
+
This program is distributed in the hope that it will be useful,
|
17
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
19
|
+
GNU General Public License for more details.
|
20
|
+
|
21
|
+
You should have received a copy of the GNU General Public License
|
22
|
+
along with this program; if not, write to the Free Software
|
23
|
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
24
|
+
|
25
|
+
*****************************************************************************/
|
26
|
+
|
27
|
+
#include "project.h"
|
28
|
+
|
29
|
+
|
30
|
+
/****************************************
|
31
|
+
EventableDescriptor::EventableDescriptor
|
32
|
+
****************************************/
|
33
|
+
|
34
|
+
EventableDescriptor::EventableDescriptor (int sd):
|
35
|
+
EventCallback (NULL),
|
36
|
+
LastRead (0),
|
37
|
+
LastWritten (0),
|
38
|
+
MySocket (sd),
|
39
|
+
bCloseNow (false),
|
40
|
+
bCloseAfterWriting (false)
|
41
|
+
{
|
42
|
+
/* There are three ways to close a socket, all of which should
|
43
|
+
* automatically signal to the event machine that this object
|
44
|
+
* should be removed from the polling scheduler.
|
45
|
+
* First is a hard close, intended for bad errors or possible
|
46
|
+
* security violations. It immediately closes the connection
|
47
|
+
* and puts this object into an error state.
|
48
|
+
* Second is to set bCloseNow, which will cause the event machine
|
49
|
+
* to delete this object (and thus close the connection in our
|
50
|
+
* destructor) the next chance it gets. bCloseNow also inhibits
|
51
|
+
* the writing of new data on the socket (but not necessarily
|
52
|
+
* the reading of new data).
|
53
|
+
* The third way is to set bCloseAfterWriting, which inhibits
|
54
|
+
* the writing of new data and converts to bCloseNow as soon
|
55
|
+
* as everything in the outbound queue has been written.
|
56
|
+
* bCloseAfterWriting is really for use only by protocol handlers
|
57
|
+
* (for example, HTTP writes an HTML page and then closes the
|
58
|
+
* connection). All of the error states we generate internally
|
59
|
+
* cause an immediate close to be scheduled, which may have the
|
60
|
+
* effect of discarding outbound data.
|
61
|
+
*/
|
62
|
+
|
63
|
+
if (sd == -1)
|
64
|
+
throw std::runtime_error ("bad eventable descriptor");
|
65
|
+
CreatedAt = gCurrentLoopTime;
|
66
|
+
}
|
67
|
+
|
68
|
+
|
69
|
+
/*****************************************
|
70
|
+
EventableDescriptor::~EventableDescriptor
|
71
|
+
*****************************************/
|
72
|
+
|
73
|
+
EventableDescriptor::~EventableDescriptor()
|
74
|
+
{
|
75
|
+
if (EventCallback)
|
76
|
+
(*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_UNBOUND, NULL, 0);
|
77
|
+
Close();
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
/*************************************
|
82
|
+
EventableDescriptor::SetEventCallback
|
83
|
+
*************************************/
|
84
|
+
|
85
|
+
void EventableDescriptor::SetEventCallback (void(*cb)(const char*, int, const char*, int))
|
86
|
+
{
|
87
|
+
EventCallback = cb;
|
88
|
+
}
|
89
|
+
|
90
|
+
|
91
|
+
/**************************
|
92
|
+
EventableDescriptor::Close
|
93
|
+
**************************/
|
94
|
+
|
95
|
+
void EventableDescriptor::Close()
|
96
|
+
{
|
97
|
+
// Close the socket right now. Intended for emergencies.
|
98
|
+
if (MySocket != -1) {
|
99
|
+
shutdown (MySocket, 1);
|
100
|
+
close (MySocket);
|
101
|
+
MySocket = -1;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
/*********************************
|
107
|
+
EventableDescriptor::ShouldDelete
|
108
|
+
*********************************/
|
109
|
+
|
110
|
+
bool EventableDescriptor::ShouldDelete()
|
111
|
+
{
|
112
|
+
/* For use by a socket manager, which needs to know if this object
|
113
|
+
* should be removed from scheduling events and deleted.
|
114
|
+
* Has an immediate close been scheduled, or are we already closed?
|
115
|
+
* If either of these are the case, return true. In theory, the manager will
|
116
|
+
* then delete us, which in turn will make sure the socket is closed.
|
117
|
+
* Note, if bCloseAfterWriting is true, we check a virtual method to see
|
118
|
+
* if there is outbound data to write, and only request a close if there is none.
|
119
|
+
*/
|
120
|
+
|
121
|
+
return ((MySocket == -1) || bCloseNow || (bCloseAfterWriting && (GetOutboundDataSize() <= 0)));
|
122
|
+
}
|
123
|
+
|
124
|
+
|
125
|
+
/******************************************
|
126
|
+
ConnectionDescriptor::ConnectionDescriptor
|
127
|
+
******************************************/
|
128
|
+
|
129
|
+
ConnectionDescriptor::ConnectionDescriptor (int sd):
|
130
|
+
EventableDescriptor (sd),
|
131
|
+
bConnectPending (false),
|
132
|
+
OutboundDataSize (0)
|
133
|
+
{
|
134
|
+
}
|
135
|
+
|
136
|
+
|
137
|
+
/*******************************************
|
138
|
+
ConnectionDescriptor::~ConnectionDescriptor
|
139
|
+
*******************************************/
|
140
|
+
|
141
|
+
ConnectionDescriptor::~ConnectionDescriptor()
|
142
|
+
{
|
143
|
+
// Run down any stranded outbound data.
|
144
|
+
for (size_t i=0; i < OutboundPages.size(); i++)
|
145
|
+
OutboundPages[i].Free();
|
146
|
+
}
|
147
|
+
|
148
|
+
|
149
|
+
/**************************************************
|
150
|
+
STATIC: ConnectionDescriptor::SendDataToConnection
|
151
|
+
**************************************************/
|
152
|
+
|
153
|
+
int ConnectionDescriptor::SendDataToConnection (const char *binding, const char *data, int data_length)
|
154
|
+
{
|
155
|
+
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
|
156
|
+
return cd ? cd->SendOutboundData (data, data_length) : -1;
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
/*********************************************
|
161
|
+
STATIC: ConnectionDescriptor::CloseConnection
|
162
|
+
*********************************************/
|
163
|
+
|
164
|
+
void ConnectionDescriptor::CloseConnection (const char *binding, bool after_writing)
|
165
|
+
{
|
166
|
+
ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
|
167
|
+
if (cd) {
|
168
|
+
if (after_writing)
|
169
|
+
cd->bCloseAfterWriting = true;
|
170
|
+
else
|
171
|
+
cd->bCloseNow = true;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
/**************************************
|
178
|
+
ConnectionDescriptor::SendOutboundData
|
179
|
+
**************************************/
|
180
|
+
|
181
|
+
int ConnectionDescriptor::SendOutboundData (const char *data, int length)
|
182
|
+
{
|
183
|
+
// Highly naive and incomplete implementation.
|
184
|
+
// There's no throttle for runaways (which should abort only this connection
|
185
|
+
// and not the whole process), and no coalescing of small pages.
|
186
|
+
|
187
|
+
if (bCloseNow || bCloseAfterWriting)
|
188
|
+
return 0;
|
189
|
+
|
190
|
+
if (!data && (length > 0))
|
191
|
+
throw std::runtime_error ("bad outbound data");
|
192
|
+
char *buffer = (char *) malloc (length + 1);
|
193
|
+
if (!buffer)
|
194
|
+
throw std::runtime_error ("no allocation for outbound data");
|
195
|
+
memcpy (buffer, data, length);
|
196
|
+
buffer [length] = 0;
|
197
|
+
OutboundPages.push_back (OutboundPage (buffer, length));
|
198
|
+
OutboundDataSize += length;
|
199
|
+
return length;
|
200
|
+
}
|
201
|
+
|
202
|
+
|
203
|
+
|
204
|
+
/***********************************
|
205
|
+
ConnectionDescriptor::SelectForRead
|
206
|
+
***********************************/
|
207
|
+
|
208
|
+
bool ConnectionDescriptor::SelectForRead()
|
209
|
+
{
|
210
|
+
/* A connection descriptor is always scheduled for read,
|
211
|
+
* UNLESS it's in a pending-connect state.
|
212
|
+
* On Linux, unlike Unix, a nonblocking socket on which
|
213
|
+
* connect has been called, does NOT necessarily select
|
214
|
+
* both readable and writable in case of error.
|
215
|
+
* The socket will select writable when the disposition
|
216
|
+
* of the connect is known. On the other hand, a socket
|
217
|
+
* which successfully connects and selects writable may
|
218
|
+
* indeed have some data available on it, so it will
|
219
|
+
* select readable in that case, violating expectations!
|
220
|
+
* So we will not poll for readability until the socket
|
221
|
+
* is known to be in a connected state.
|
222
|
+
*/
|
223
|
+
|
224
|
+
return bConnectPending ? false : true;
|
225
|
+
}
|
226
|
+
|
227
|
+
|
228
|
+
/************************************
|
229
|
+
ConnectionDescriptor::SelectForWrite
|
230
|
+
************************************/
|
231
|
+
|
232
|
+
bool ConnectionDescriptor::SelectForWrite()
|
233
|
+
{
|
234
|
+
/* Cf the notes under SelectForRead.
|
235
|
+
* In a pending-connect state, we ALWAYS select for writable.
|
236
|
+
* In a normal state, we only select for writable when we
|
237
|
+
* have outgoing data to send.
|
238
|
+
*/
|
239
|
+
|
240
|
+
if (bConnectPending)
|
241
|
+
return true;
|
242
|
+
else {
|
243
|
+
return (GetOutboundDataSize() > 0);
|
244
|
+
}
|
245
|
+
}
|
246
|
+
|
247
|
+
|
248
|
+
/**************************
|
249
|
+
ConnectionDescriptor::Read
|
250
|
+
**************************/
|
251
|
+
|
252
|
+
void ConnectionDescriptor::Read()
|
253
|
+
{
|
254
|
+
/* Read and dispatch data on a socket that has selected readable.
|
255
|
+
* It's theoretically possible to get and dispatch incoming data on
|
256
|
+
* a socket that has already been scheduled for closing or close-after-writing.
|
257
|
+
* In those cases, we'll leave it up the to protocol handler to "do the
|
258
|
+
* right thing" (which probably means to ignore the incoming data).
|
259
|
+
*/
|
260
|
+
|
261
|
+
int sd = GetSocket();
|
262
|
+
assert (sd != -1);
|
263
|
+
|
264
|
+
int total_bytes_read = 0;
|
265
|
+
char readbuffer [16 * 1024];
|
266
|
+
|
267
|
+
for (int i=0; i < 10; i++) {
|
268
|
+
// Don't read just one buffer and then move on. This is faster
|
269
|
+
// if there is a lot of incoming.
|
270
|
+
// But don't read indefinitely. Give other sockets a chance to run.
|
271
|
+
|
272
|
+
int r = recv (sd, readbuffer, sizeof(readbuffer) - 1, 0);
|
273
|
+
//cerr << "<R:" << r << ">";
|
274
|
+
|
275
|
+
if (r > 0) {
|
276
|
+
total_bytes_read += r;
|
277
|
+
LastRead = gCurrentLoopTime;
|
278
|
+
|
279
|
+
readbuffer [r] = 0; // Impute a null-terminator, just for convenience.
|
280
|
+
|
281
|
+
if (EventCallback)
|
282
|
+
(*EventCallback)(GetBinding().c_str(), EventMachine_t::CONNECTION_READ, readbuffer, r);
|
283
|
+
|
284
|
+
}
|
285
|
+
else if (r == 0) {
|
286
|
+
break;
|
287
|
+
}
|
288
|
+
else {
|
289
|
+
// Basically a would-block, meaning we've everything there is to read.
|
290
|
+
break;
|
291
|
+
}
|
292
|
+
|
293
|
+
}
|
294
|
+
|
295
|
+
|
296
|
+
if (total_bytes_read == 0) {
|
297
|
+
// If we read no data on a socket that selected readable,
|
298
|
+
// it generally means the other end closed the connection gracefully.
|
299
|
+
bCloseNow = true;
|
300
|
+
}
|
301
|
+
|
302
|
+
}
|
303
|
+
|
304
|
+
|
305
|
+
/***************************
|
306
|
+
ConnectionDescriptor::Write
|
307
|
+
***************************/
|
308
|
+
|
309
|
+
void ConnectionDescriptor::Write()
|
310
|
+
{
|
311
|
+
/* A socket which is in a pending-connect state will select
|
312
|
+
* writable when the disposition of the connect is known.
|
313
|
+
* At that point, check to be sure there are no errors,
|
314
|
+
* and if none, then promote the socket out of the pending
|
315
|
+
* state.
|
316
|
+
*/
|
317
|
+
|
318
|
+
if (bConnectPending) {
|
319
|
+
int error;
|
320
|
+
socklen_t len;
|
321
|
+
len = sizeof(error);
|
322
|
+
int o = getsockopt (MySocket, SOL_SOCKET, SO_ERROR, &error, &len);
|
323
|
+
if ((o == 0) && (error == 0))
|
324
|
+
bConnectPending = false;
|
325
|
+
else
|
326
|
+
bCloseNow = true;
|
327
|
+
}
|
328
|
+
else {
|
329
|
+
_WriteOutboundData();
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
|
334
|
+
/****************************************
|
335
|
+
ConnectionDescriptor::_WriteOutboundData
|
336
|
+
****************************************/
|
337
|
+
|
338
|
+
void ConnectionDescriptor::_WriteOutboundData()
|
339
|
+
{
|
340
|
+
/* This is a helper function called by ::Write.
|
341
|
+
* It's possible for a socket to select writable and then no longer
|
342
|
+
* be writable by the time we get around to writing. The kernel might
|
343
|
+
* have used up its available output buffers between the select call
|
344
|
+
* and when we get here. So this condition is not an error.
|
345
|
+
*/
|
346
|
+
|
347
|
+
int sd = GetSocket();
|
348
|
+
assert (sd != -1);
|
349
|
+
|
350
|
+
char output_buffer [16 * 1024];
|
351
|
+
size_t nbytes = 0;
|
352
|
+
|
353
|
+
while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
|
354
|
+
OutboundPage *op = &(OutboundPages[0]);
|
355
|
+
if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
|
356
|
+
memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
|
357
|
+
nbytes += (op->Length - op->Offset);
|
358
|
+
op->Free();
|
359
|
+
OutboundPages.pop_front();
|
360
|
+
}
|
361
|
+
else {
|
362
|
+
int len = sizeof(output_buffer) - nbytes;
|
363
|
+
memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
|
364
|
+
op->Offset += len;
|
365
|
+
nbytes += len;
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
// We should never have gotten here if there were no data to write,
|
370
|
+
// so assert that as a sanity check.
|
371
|
+
// Don't bother to make sure nbytes is less than output_buffer because
|
372
|
+
// if it were we probably would have crashed already.
|
373
|
+
assert (nbytes > 0);
|
374
|
+
|
375
|
+
assert (GetSocket() != -1);
|
376
|
+
int bytes_written = send (GetSocket(), output_buffer, nbytes, 0);
|
377
|
+
|
378
|
+
if (bytes_written > 0) {
|
379
|
+
OutboundDataSize -= bytes_written;
|
380
|
+
if ((size_t)bytes_written < nbytes) {
|
381
|
+
int len = nbytes - bytes_written;
|
382
|
+
char *buffer = (char*) malloc (len + 1);
|
383
|
+
if (!buffer)
|
384
|
+
throw std::runtime_error ("bad alloc throwing back data");
|
385
|
+
memcpy (buffer, output_buffer + bytes_written, len);
|
386
|
+
buffer [len] = 0;
|
387
|
+
OutboundPages.push_front (OutboundPage (buffer, len));
|
388
|
+
}
|
389
|
+
}
|
390
|
+
else {
|
391
|
+
if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR))
|
392
|
+
Close();
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
|
397
|
+
|
398
|
+
/*******************************
|
399
|
+
ConnectionDescriptor::Heartbeat
|
400
|
+
*******************************/
|
401
|
+
|
402
|
+
void ConnectionDescriptor::Heartbeat()
|
403
|
+
{
|
404
|
+
/* Only allow a certain amount of time to go by while waiting
|
405
|
+
* for a pending connect. If it expires, then kill the socket.
|
406
|
+
*/
|
407
|
+
|
408
|
+
if (bConnectPending) {
|
409
|
+
if ((gCurrentLoopTime - CreatedAt) >= PendingConnectTimeout)
|
410
|
+
bCloseNow = true;
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
|
415
|
+
/**************************************
|
416
|
+
AcceptorDescriptor::AcceptorDescriptor
|
417
|
+
**************************************/
|
418
|
+
|
419
|
+
AcceptorDescriptor::AcceptorDescriptor (EventMachine_t *parent_em, int sd):
|
420
|
+
EventableDescriptor (sd),
|
421
|
+
MyEventMachine (parent_em)
|
422
|
+
{
|
423
|
+
/* This is really bad and ugly. Change someday if possible.
|
424
|
+
* We have to know about an event-machine (probably the one that owns us),
|
425
|
+
* so we can pass newly-created connections to it.
|
426
|
+
*/
|
427
|
+
|
428
|
+
if (!MyEventMachine)
|
429
|
+
throw std::runtime_error ("bad event-machine passed to acceptor");
|
430
|
+
}
|
431
|
+
|
432
|
+
|
433
|
+
/***************************************
|
434
|
+
AcceptorDescriptor::~AcceptorDescriptor
|
435
|
+
***************************************/
|
436
|
+
|
437
|
+
AcceptorDescriptor::~AcceptorDescriptor()
|
438
|
+
{
|
439
|
+
}
|
440
|
+
|
441
|
+
|
442
|
+
/************************
|
443
|
+
AcceptorDescriptor::Read
|
444
|
+
************************/
|
445
|
+
|
446
|
+
void AcceptorDescriptor::Read()
|
447
|
+
{
|
448
|
+
/* Accept up to a certain number of sockets on the listening connection.
|
449
|
+
* Don't try to accept all that are present, because this would allow a DoS attack
|
450
|
+
* in which no data were ever read or written. We should accept more than one,
|
451
|
+
* if available, to keep the partially accepted sockets from backing up in the kernel.
|
452
|
+
*/
|
453
|
+
|
454
|
+
/* Make sure we use non-blocking i/o on the acceptor socket, since we're selecting it
|
455
|
+
* for readability. According to Stevens UNP, it's possible for an acceptor to select readable
|
456
|
+
* and then block when we call accept. For example, the other end resets the connection after
|
457
|
+
* the socket selects readable and before we call accept. The kernel will remove the dead
|
458
|
+
* socket from the accept queue. If the accept queue is now empty, accept will block.
|
459
|
+
*/
|
460
|
+
|
461
|
+
|
462
|
+
struct sockaddr_in pin;
|
463
|
+
socklen_t addrlen = sizeof (pin);
|
464
|
+
|
465
|
+
for (int i=0; i < 10; i++) {
|
466
|
+
int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
|
467
|
+
if (sd == -1) {
|
468
|
+
// This breaks the loop when we've accepted everything on the kernel queue,
|
469
|
+
// up to 10 new connections. But what if the *first* accept fails?
|
470
|
+
// Does that mean anything serious is happening, beyond the situation
|
471
|
+
// described in the note above?
|
472
|
+
break;
|
473
|
+
}
|
474
|
+
|
475
|
+
// Set the newly-accepted socket non-blocking.
|
476
|
+
// On Windows, this may fail because, weirdly, Windows inherits the non-blocking
|
477
|
+
// attribute that we applied to the acceptor socket into the accepted one.
|
478
|
+
int val = fcntl (sd, F_GETFL, 0);
|
479
|
+
if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) {
|
480
|
+
shutdown (sd, 1);
|
481
|
+
close (sd);
|
482
|
+
continue;
|
483
|
+
}
|
484
|
+
|
485
|
+
|
486
|
+
// Disable slow-start (Nagle algorithm). Eventually make this configurable.
|
487
|
+
int one = 1;
|
488
|
+
setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof(one));
|
489
|
+
|
490
|
+
|
491
|
+
ConnectionDescriptor *cd = new ConnectionDescriptor (sd);
|
492
|
+
if (!cd)
|
493
|
+
throw std::runtime_error ("no newly accepted connection");
|
494
|
+
if (EventCallback) {
|
495
|
+
(*EventCallback) (GetBinding().c_str(), EventMachine_t::CONNECTION_ACCEPTED, cd->GetBinding().c_str(), cd->GetBinding().size());
|
496
|
+
}
|
497
|
+
assert (MyEventMachine);
|
498
|
+
MyEventMachine->Add (cd);
|
499
|
+
}
|
500
|
+
|
501
|
+
}
|
502
|
+
|
503
|
+
|
504
|
+
/*************************
|
505
|
+
AcceptorDescriptor::Write
|
506
|
+
*************************/
|
507
|
+
|
508
|
+
void AcceptorDescriptor::Write()
|
509
|
+
{
|
510
|
+
// Why are we here?
|
511
|
+
throw std::runtime_error ("bad code path in acceptor");
|
512
|
+
}
|
513
|
+
|
514
|
+
|
515
|
+
/*****************************
|
516
|
+
AcceptorDescriptor::Heartbeat
|
517
|
+
*****************************/
|
518
|
+
|
519
|
+
void AcceptorDescriptor::Heartbeat()
|
520
|
+
{
|
521
|
+
// No-op
|
522
|
+
}
|
523
|
+
|
524
|
+
|
525
|
+
|
526
|
+
|
527
|
+
|
528
|
+
|