first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / octopi / library / tests_sockets / spocket_tester.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : spocket_tester                                                    *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2000-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "spocket_tester.h"
16
17 #include <basis/byte_array.h>
18 #include <basis/functions.h>
19 #include <basis/astring.h>
20 #include <loggers/critical_events.h>
21 #include <loggers/program_wide_logger.h>
22 #include <mathematics/chaos.h>
23 #include <sockets/internet_address.h>
24 #include <sockets/raw_socket.h>
25 #include <sockets/spocket.h>
26 #include <sockets/tcpip_definitions.h>
27 #include <sockets/tcpip_stack.h>
28 #include <timely/time_control.h>
29 #include <timely/time_stamp.h>
30
31 #include <errno.h>
32
33 //using namespace application;
34 using namespace basis;
35 //using namespace filesystem;
36 using namespace loggers;
37 using namespace mathematics;
38 using namespace sockets;
39 using namespace structures;
40 using namespace textual;
41 using namespace timely;
42 //using namespace unit_test;
43
44 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
45
46 const int MAXIMUM_WINSOCK_MTU = 100000;
47   // the largest chunk of bytes we will receive at one time.
48
49 const int MAXIMUM_TRANSFER_WAIT = 40 * SECOND_ms;
50   // the longest amount of time we wait in trying to receive data.
51
52 static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
53   // used for dumping received data into.
54
55 const int PAUSE_TIME = 200;
56   // the snooze interval when we encounter socket underflow or overflow.
57
58 //#define DEBUG_SPOCKET_TESTER
59   // uncomment for noisy version.
60
61 spocket_tester::spocket_tester(const internet_address &where)
62 : _where(new internet_address(where)),
63   _stack(new tcpip_stack),
64   _socket(NIL),
65   _root_server(NIL),
66   _raw(new raw_socket)
67 {
68 }
69
70 spocket_tester::~spocket_tester()
71 {
72   WHACK(_socket);
73   WHACK(_root_server);
74   WHACK(_stack);
75   WHACK(_where);
76   WHACK(_raw);
77 }
78
79 bool spocket_tester::connect()
80 {
81   if (!_socket) {
82     _socket = new spocket(*_where);
83   }
84   outcome ret = spocket::NO_CONNECTION;
85   while (true) {
86     ret = _socket->connect();
87     if (ret == spocket::OKAY) break;
88     if (ret != spocket::NO_CONNECTION) break;
89     time_control::sleep_ms(100);
90   }
91   return ret == spocket::OKAY;
92 }
93
94 bool spocket_tester::accept(bool wait)
95 {
96   if (!_root_server) {
97     _root_server = new spocket(*_where);
98   }
99   if (_socket) {
100     LOG("already have a socket for accept!");
101     return true;
102   }
103   outcome ret = spocket::NO_CONNECTION;
104   while (true) {
105     ret = _root_server->accept(_socket, false);
106     if (ret == spocket::OKAY) break;
107     if (ret != spocket::NO_CONNECTION) break;
108     if (!wait) return true;  // we accepted at least once.
109     time_control::sleep_ms(100);
110   }
111   return ret == spocket::OKAY;
112 }
113
114 bool spocket_tester::do_a_send(abyte *buffer, int size,
115     testing_statistics &stats)
116 {
117   time_stamp start_time;
118   int len_sent;
119   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
120   outcome worked;
121
122 #ifdef DEBUG_SPOCKET_TESTER
123   LOG("into do a send");
124 #endif
125
126   while (time_stamp() < when_to_leave) {
127     worked = _socket->send(buffer, size, len_sent);
128     if (worked == spocket::NONE_READY) {
129 ////      time_control::sleep_ms(PAUSE_TIME);
130       _socket->await_writable(PAUSE_TIME);
131       continue;
132     } else if (worked == spocket::PARTIAL) {
133 //danger danger if we get wrong info.
134       buffer += len_sent;
135       size -= len_sent;
136       stats.bytes_sent += len_sent;
137 ///      time_control::sleep_ms(PAUSE_TIME);
138       _socket->await_writable(PAUSE_TIME);
139       continue;
140     } else break;
141   }
142 #ifdef DEBUG_SPOCKET_TESTER
143   LOG("got out of loop");
144 #endif
145
146   stats.send_time += int(time_stamp().value() - start_time.value());
147   stats.bytes_sent += len_sent;
148
149   if ( (worked != spocket::OKAY) && (worked != spocket::PARTIAL) ) {
150     LOG(astring("No data went out on the socket: ")
151         + spocket::outcome_name(worked));
152     return false;
153   }
154   if (len_sent != size) {
155     LOG(a_sprintf("partial send on socket, %d bytes instead of %d, recurse.", 
156         len_sent, size));
157     return do_a_send(buffer + len_sent, size - len_sent, stats);
158   }
159
160   time_stamp end_time;
161 //  int time_taken = int(end_time.value() - start_time.value());
162
163   return true;
164 }
165
166 bool spocket_tester::do_a_receive(int size_expected, testing_statistics &stats)
167 {
168   time_stamp start_time;
169
170 #ifdef DEBUG_SPOCKET_TESTER
171   LOG("into do a rcv");
172 #endif
173
174   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
175   int full_length = 0;
176   while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) {
177     time_stamp start_of_receive;
178     int len = MAXIMUM_WINSOCK_MTU;
179     outcome ret = _socket->receive(receive_buffer, len);
180     if (ret != spocket::OKAY) {
181       if (ret == spocket::NONE_READY) {
182 if (len != 0) LOG(a_sprintf("supposedly nothing was received (%d bytes)", len));
183 ///        time_control::sleep_ms(PAUSE_TIME);
184         _socket->await_readable(PAUSE_TIME);
185         continue;
186       } else break;
187     }
188     // reset our time if we've gotten good data.
189     if (ret == spocket::OKAY)
190       when_to_leave.reset(MAXIMUM_TRANSFER_WAIT);
191
192     int receive_duration = int(time_stamp().value()
193         - start_of_receive.value());
194     stats.receive_time += receive_duration;
195
196 #ifdef DEBUG_SPOCKET_TESTER
197     LOG(a_sprintf("did recv, len=%d", len));
198 #endif
199
200     if (!len) {
201       LOG("Our socket has been disconnected.");
202       return false;
203     } else if (len < 0) {
204       if (errno == SOCK_EWOULDBLOCK) continue;  // no data.
205       LOG(astring("The receive failed with an error ")
206           + critical_events::system_error_text(errno));
207       return false;
208     }
209     full_length += len;
210     stats.bytes_received += len;
211   }
212
213   if (full_length != size_expected)
214     LOG(a_sprintf("Did not get the full size expected (wanted %d and "
215         "got %d bytes).", size_expected, full_length));
216
217   return true;
218 }
219
220 bool spocket_tester::perform_test(int size, int count,
221     testing_statistics &stats)
222 {
223 #ifdef DEBUG_SPOCKET_TESTER
224   LOG("into perf test");
225 #endif
226
227   // the statics are used to generate our random buffer for sending.
228   static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1];
229   static bool garbage_initialized = false;
230   chaos randomizer;
231
232   // if our static buffer full of random stuff was never initialized, we do
233   // so now.  this supports efficiently re-using the tester if desired.
234   if (!garbage_initialized) {
235     LOG("initializing random send buffer.");
236     // note the less than or equal; we know we have one more byte to fill.
237     for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++)
238       garbage_buffer[i] = randomizer.inclusive(0, 255);
239     garbage_initialized = true;
240     LOG("random send buffer initialized.");
241   }
242
243   // reset the statistical package.
244   stats.total_runs = 0;
245   stats.send_time = 0;
246   stats.receive_time = 0;
247   stats.bytes_sent = 0;
248   stats.bytes_received = 0;
249
250   // check that they aren't trying to do too big of a send.
251   if (size > MAXIMUM_WINSOCK_MTU) {
252     LOG("The size is over our limit.  To fix this, edit the "
253         "send_data function.");
254     return false;
255   }
256
257   // check that our socket is usable.
258   if (!_socket) {
259     LOG("One cannot send data on an uninitialized tester!");
260     return false;
261   }
262
263   int runs_completed = 0;
264     // counts up how many times we've done our test cycle.
265
266   while (runs_completed < count) {
267 #ifdef DEBUG_SPOCKET_TESTER
268     LOG(a_sprintf("iter %d", runs_completed));
269 #endif
270     if (_socket->client()) {
271       // we're doing the client side routine here.
272       time_stamp trip_start;
273 #ifdef DEBUG_SPOCKET_TESTER
274       LOG("client about to send");
275 #endif
276       if (!do_a_send(garbage_buffer, size, stats)) {
277         LOG("We failed on a send.  Now quitting.");
278         return false;
279       }
280 #ifdef DEBUG_SPOCKET_TESTER
281       LOG("client about to rcv");
282 #endif
283       if (!do_a_receive(size, stats)) {
284         LOG("We failed on a receive.  Now quitting.");
285         return false;
286       }
287       stats.round_trip_time += int(time_stamp().value() - trip_start.value());
288     } else {
289       // we're doing the server side routine here.
290       time_stamp trip_start;
291 #ifdef DEBUG_SPOCKET_TESTER
292       LOG("server about to rcv");
293 #endif
294       if (!do_a_receive(size, stats)) {
295         LOG("We failed on a receive.  Now quitting.");
296         return false;
297       }
298 #ifdef DEBUG_SPOCKET_TESTER
299       LOG("server about to send");
300 #endif
301       if (!do_a_send(garbage_buffer, size, stats)) {
302         LOG("We failed on a send.  Now quitting.");
303         return false;
304       }
305       stats.round_trip_time += int(time_stamp().value() - trip_start.value());
306     }
307
308     runs_completed++;  // finished a run.
309     stats.total_runs++;  // count it in the overall stats too.
310     if ( !(runs_completed % 10) )
311       LOG(a_sprintf("Completed test #%d.", runs_completed));
312   }
313
314   return true;
315 }
316