1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
|
type reaction =
Print_file of string
| Expect of string
| End
| Break_and_reconnect
| Close_input
| Close_output
| Sleep of int
| Expect_end
| Reconnect
;;
let read_file fname =
(* read file 'fname' as string *)
let s = ref "" in
let b = String.create 8192 in
let fd = Unix.openfile fname [ Unix.O_RDONLY ] 0 in
let k = ref (Unix.read fd b 0 8192) in
while !k > 0 do
s := !s ^ (String.sub b 0 !k);
k := Unix.read fd b 0 8192;
done;
!s
;;
let prerr_buffer s prefix n0 =
let out_line n k0 k1 =
prerr_string prefix;
prerr_int n;
prerr_string " ";
prerr_string (String.sub s k0 (k1-k0));
prerr_newline();
flush stderr
in
let n = ref n0 in
let k = ref 0 in
let l = String.length s - 1 in
for i = 0 to l do
if s.[i] = '\n' then begin
out_line !n !k i;
incr n;
k := i+1
end else if i = l then begin
out_line !n !k (i+1)
end
done
;;
let pidfile = ref "server.pid";;
let main() =
let line = ref 0 in
let spec = ref [] in
let protocol = ref false in
let portfile = ref "server.port" in
Arg.parse
[ "-portfile", Arg.String
(fun s -> portfile := s),
" <file> Writes the port number to this file (default server.port)";
"-pidfile", Arg.String
(fun s -> pidfile := s),
" <file> Writes the process ID to this file (default server.pid)";
"-line", Arg.Int (fun i ->
if i >= !line then line := i
else failwith "Line numbers must grow"),
" <n> React after the n-th line of input";
"-file", Arg.String
(fun s -> spec := !spec @ [ !line, Print_file s ]),
" <file> Output contents of file";
"-end", Arg.Unit
(fun _ -> spec := !spec @ [ !line, End ]),
" Output EOF marker";
"-break", Arg.Unit
(fun _ -> spec := !spec @ [ !line, Break_and_reconnect ];
line := 0),
" Close the connection immediately; wait for next connection";
"-close-in", Arg.Unit
(fun _ -> spec := !spec @ [ !line, Close_input ] ),
" Close only the input side of the connection; continue";
"-close-out", Arg.Unit
(fun _ -> spec := !spec @ [ !line, Close_output ] ),
" Close only the output side of the connection; continue";
"-expect", Arg.String (fun s -> spec := !spec @ [ !line, Expect s ]),
" <string> Expect that the last input line has these contents";
"-expect-end", Arg.Unit
(fun _ -> spec := !spec @ [ !line, Expect_end ]),
" Ignore input until EOF is read";
"-reconnect", Arg.Unit
(fun _ -> spec := !spec @ [ !line, Reconnect ];
line := 0),
" -expect-end + Allow another connection after EOF";
"-sleep", Arg.Int
(fun i -> spec := !spec @ [ !line, Sleep i ]),
" <n> Sleeps <n> seconds";
"-protocol", Arg.Set protocol,
" turn protocol on stderr on";
]
(fun s -> failwith ("Bad argument: " ^ s))
"Usage: test_server [options]
The test server listens on a TCP port and prints the port number to
the file specified by -portfile. The process ID is printed to the file
specified by -pidfile; this file is removed once the server terminates.
The server accepts per default one connection (and for every -reconnect
option one more connection), and terminates then.
While the test server reads input lines from the client
it checks certain conditions, and reacts on such events. The -line
option specifies after which line of input (0 means: at the beginning)
the next event will happen. -file and -end specify reactions; -file outputs
the contents of the file; and -end outputs an EOF marker. Note that while
a file is being sent to the client the input side of the socket is blocked.
The server terminates also on the SIGTERM signal or after 10 minutes idle time
without connection.
";
let buffsize = 16384 in
let buff = String.create buffsize in
let n0 = ref 0 in (* Line of the first character of 'buff' *)
let lines = ref [] in (* Lines read so far, in reverse order *)
let n_lines = ref 0 in (* length of !lines *)
let this_line = ref "" in (* Current, not yet complete line *)
let s = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
Unix.listen s 10;
let s_name = Unix.getsockname s in
let Unix.ADDR_INET(inetaddr,port) = s_name in
let f_portfile = open_out !portfile in
output_string f_portfile (string_of_int port);
output_string f_portfile "\n";
close_out f_portfile;
let f_pidfile = open_out !pidfile in
output_string f_pidfile (string_of_int (Unix.getpid()));
output_string f_pidfile "\n";
close_out f_pidfile;
let wait_for_connection = ref true in
let thisspec = ref !spec in
while !wait_for_connection do
wait_for_connection := false; (* set to 'true' by Reconnect reaction *)
let l_readable,_,_ = Unix.select [ s ] [] [] 600.0 in
if l_readable = [] then
exit(99); (* timeout *)
let conn, peer = Unix.accept s in
n0 := 1;
if !protocol then
prerr_endline "! ACCEPTED NEW CONNECTION";
let connopen = ref true in
let eof_sent = ref false in (* i.e. write side closed *)
let broken = ref false in (* i.e. read side closed *)
while !connopen do
(* Interpret 'spec' and react *)
while !connopen & !thisspec <> [] & fst(List.hd !thisspec) < !n0 do
let (lineno, react) :: rest_spec = !thisspec in
thisspec := rest_spec;
if !protocol then
prerr_string ("! EVENT ON #" ^ string_of_int lineno ^ ": ");
match react with
Print_file fname ->
if !protocol then
prerr_endline ("SENDING FILE " ^ fname);
let fstring = read_file fname in
if !protocol then
prerr_buffer fstring "> " 1;
let m = ref 0 in
let l = String.length fstring in
begin try
while !m < l do
m := !m + Unix.write conn fstring !m (l - !m)
done
with
Unix.Unix_error(Unix.EPIPE,_,_) ->
if !protocol then
prerr_endline "! BROKEN PIPE";
eof_sent := true;
end
| End ->
if !protocol then
prerr_endline "SENDING EOF";
Unix.shutdown conn Unix.SHUTDOWN_SEND;
eof_sent := true;
| Break_and_reconnect ->
if !protocol then
prerr_endline "BREAKING CONNECTION";
Unix.close conn;
broken := true;
wait_for_connection := true;
connopen := false;
| Close_input ->
if !protocol then
prerr_endline "CLOSING INPUT SIDE";
Unix.shutdown conn Unix.SHUTDOWN_RECEIVE;
| Close_output ->
if !protocol then
prerr_endline "CLOSING OUTPUT SIDE";
Unix.shutdown conn Unix.SHUTDOWN_SEND;
| Expect line ->
let actual_line =
List.nth !lines (!n_lines - lineno) in
if !protocol then
prerr_endline ("EXPECTING LINE '" ^ line ^ "'");
if actual_line = line then begin
if !protocol then
prerr_endline ("! THE LINE MATCHED")
end
else begin
if !protocol then begin
prerr_endline ("! GOT LINE '" ^ actual_line ^ "'");
prerr_endline ("! THE LINE DOES NOT MATCH. SENDING EOF");
end;
Unix.shutdown conn Unix.SHUTDOWN_SEND;
eof_sent := true;
failwith "Test failure";
end
| Sleep i ->
if !protocol then
prerr_endline "SLEEPING";
Unix.sleep i
| (Expect_end | Reconnect) ->
if !protocol then
prerr_endline "! IGNORING INPUT UNTIL GETTING EOF";
let k = ref 1 in
while !k <> 0 do
k := Unix.read conn buff 0 buffsize;
if !k > 0 & !protocol then
prerr_endline ("! IGNORING " ^ string_of_int !k ^ " BYTES");
done;
if react = Reconnect then
wait_for_connection := true;
connopen := false;
done;
(* read as much as immediately possible *)
if not !broken then begin
let _ = Unix.select [ conn ] [] [] (-1.0) in
let k = Unix.read conn buff 0 buffsize in
if k = 0 then begin
(* got EOF *)
if !protocol then
prerr_endline "! GOT EOF.";
connopen := false
end
else begin
if !protocol then
prerr_buffer (String.sub buff 0 k) "< " !n0;
(* count number of lines *)
let i_start = ref 0 in
for i = 0 to k-1 do
if buff.[i] = '\n' then begin
lines :=
(!this_line ^ String.sub buff !i_start (i - !i_start)) :: !lines;
incr n_lines;
this_line := "";
i_start := i+1;
incr n0
end
done;
this_line := !this_line ^ String.sub buff !i_start (k - !i_start);
end
end;
done; (* while !connopen *)
if not !broken then begin
if not !eof_sent then begin
if !protocol then prerr_endline "! IMMEDIATELY REPLYING EOF";
Unix.shutdown conn Unix.SHUTDOWN_SEND;
eof_sent := true;
end;
if !protocol then prerr_endline "! CLOSING SOCKET";
Unix.close conn;
end;
done (* while !wait_for_connection *)
;;
Sys.signal Sys.sigpipe Sys.Signal_ignore;
begin try
main()
with
e ->
prerr_endline ("Exception: " ^ Printexc.to_string e)
end;
Unix.sleep 1;
if Sys.file_exists !pidfile then
Sys.remove !pidfile
;;
|