From 8023f94ecb26116e00e1fcd25a6bd821e998ea4a Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Mon, 9 Jun 2014 18:26:03 -0400 Subject: Remove special treatment of + + -- cgit v1.2.3 From 2bd5faedfe7464c9f4cc6bd36084d487c7c86b2a Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Tue, 19 Aug 2014 11:08:25 -0400 Subject: New release --- CHANGELOG | 7 +++++++ configure.ac | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b80c8d9f..e56e24db 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +======== +20140819 +======== + +- Improvements to HTML model +- Bug fixes and optimization improvements + ======== 20140807 ======== diff --git a/configure.ac b/configure.ac index 25698b7f..e0f795e1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT([urweb], [20140807]) -WORKING_VERSION=1 +AC_INIT([urweb], [20140819]) +WORKING_VERSION=0 AC_USE_SYSTEM_EXTENSIONS # automake 1.12 requires this, but automake 1.11 doesn't recognize it -- cgit v1.2.3 From 277913f2ae39e159b1a639fe1d869fd00ce1a2c7 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Tue, 19 Aug 2014 11:17:39 -0400 Subject: and type fixes (grandfathered into release) --- lib/ur/basis.urs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/ur/basis.urs b/lib/ur/basis.urs index 9d58ee66..30271ce9 100644 --- a/lib/ur/basis.urs +++ b/lib/ur/basis.urs @@ -1019,9 +1019,8 @@ val button : cformTag ([Value = string] ++ boxAttrs) [] val ccheckbox : cformTag ([Value = bool, Size = int, Source = source bool, Onchange = transaction unit] ++ boxAttrs) [] -con cselect = [Cselect] -val cselect : cformTag ([Source = source string, Onchange = transaction unit] ++ boxAttrs) cselect -val coption : unit -> tag [Value = string, Selected = bool] cselect [] [] [] +val cselect : cformTag ([Source = source string, Onchange = transaction unit] ++ boxAttrs) [Cselect] +val coption : unit -> tag [Value = string, Selected = bool] [Cselect, Body] [] [] [] val ctextarea : cformTag ([Value = string, Rows = int, Cols = int, Source = source string, Onchange = transaction unit, Ontext = transaction unit] ++ boxAttrs) [] -- cgit v1.2.3 From 9515ba96f8f48b17888267c15b3181893cca6be3 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Tue, 19 Aug 2014 11:23:26 -0400 Subject: Return to working version mode --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e0f795e1..074c26cd 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_INIT([urweb], [20140819]) -WORKING_VERSION=0 +WORKING_VERSION=1 AC_USE_SYSTEM_EXTENSIONS # automake 1.12 requires this, but automake 1.11 doesn't recognize it -- cgit v1.2.3 From cb0fdb4d2b0682d67fe57dc755b574d1867216b5 Mon Sep 17 00:00:00 2001 From: Sergey Mironov Date: Mon, 7 Jul 2014 10:05:04 +0400 Subject: Add 'role' data attribute. Note, that 'role' attribute is a part of reach ARIA API described here: http://www.w3.org/TR/wai-aria/ Among 'role', it defines lots of aria-* attributes --- lib/ur/basis.urs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ur/basis.urs b/lib/ur/basis.urs index 30271ce9..5d0a0c8a 100644 --- a/lib/ur/basis.urs +++ b/lib/ur/basis.urs @@ -848,7 +848,7 @@ con scrollEvents = [Onscroll = transaction unit] con boxEvents = focusEvents ++ mouseEvents ++ keyEvents ++ resizeEvents ++ scrollEvents con tableEvents = focusEvents ++ mouseEvents ++ keyEvents -con boxAttrs = [Data = data_attr, Id = id, Title = string] ++ boxEvents +con boxAttrs = [Data = data_attr, Id = id, Title = string, Role = string] ++ boxEvents con tableAttrs = [Data = data_attr, Id = id, Title = string] ++ tableEvents val span : bodyTag boxAttrs -- cgit v1.2.3 From cbe0e1aeceefc7db712230a070ddcf757cfe1981 Mon Sep 17 00:00:00 2001 From: Sergey Mironov Date: Sat, 23 Aug 2014 11:59:34 +0000 Subject: Check realloc's return code to prevent segfault on out of memoty condition --- src/c/request.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/c/request.c b/src/c/request.c index f212655f..9dc6aa59 100644 --- a/src/c/request.c +++ b/src/c/request.c @@ -444,8 +444,12 @@ request_result uw_request(uw_request_context rc, uw_context ctx, int len = strlen(inputs); if (len+1 > rc->queryString_size) { - rc->queryString_size = len+1; rc->queryString = realloc(rc->queryString, len+1); + if(rc->queryString == NULL) { + log_error(logger_data, "queryString is too long (not enough memory)\n"); + return FAILED; + } + rc->queryString_size = len+1; } strcpy(rc->queryString, inputs); @@ -480,8 +484,12 @@ request_result uw_request(uw_request_context rc, uw_context ctx, on_success(ctx); if (path_len + 1 > rc->path_copy_size) { + rc->path_copy = realloc(rc->path_copy, path_len + 1); + if(rc->path_copy == NULL) { + log_error(logger_data, "Path is too long (not enough memory)\n"); + return FAILED; + } rc->path_copy_size = path_len + 1; - rc->path_copy = realloc(rc->path_copy, rc->path_copy_size); } strcpy(rc->path_copy, path); -- cgit v1.2.3 From 0f24f4667d4571834c552d9a6ca3dbe180296bb0 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Sun, 24 Aug 2014 11:43:49 -0400 Subject: Extend ScriptCheck to take RPCs into account --- src/scriptcheck.sml | 101 ++++++++++++++++++++++++++++++++++++++++++++------- tests/DynChannel.ur | 29 +++++++++++++++ tests/DynChannel.urp | 6 +++ tests/rpchan.ur | 18 +++++++++ tests/rpchan.urs | 1 + 5 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 tests/DynChannel.ur create mode 100644 tests/DynChannel.urp create mode 100644 tests/rpchan.ur create mode 100644 tests/rpchan.urs diff --git a/src/scriptcheck.sml b/src/scriptcheck.sml index 4bc2a4cf..0d30ebcb 100644 --- a/src/scriptcheck.sml +++ b/src/scriptcheck.sml @@ -1,4 +1,4 @@ -(* Copyright (c) 2009, Adam Chlipala +(* Copyright (c) 2009, 2014, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,10 @@ structure ScriptCheck :> SCRIPT_CHECK = struct open Mono +structure SM = BinaryMapFn(struct + type ord_key = string + val compare = String.compare + end) structure SS = BinarySetFn(struct type ord_key = string val compare = String.compare @@ -39,37 +43,108 @@ val pushBasis = SS.addList (SS.empty, ["new_channel", "self"]) +datatype rpcmap = + Rpc of int (* ID of function definition *) + | Module of rpcmap SM.map + +fun lookup (r : rpcmap, k : string) = + let + fun lookup' (r, ks) = + case r of + Rpc x => SOME x + | Module m => + case ks of + [] => NONE + | k :: ks' => + case SM.find (m, k) of + NONE => NONE + | SOME r' => lookup' (r', ks') + in + lookup' (r, String.tokens (fn ch => ch = #"/") k) + end + +fun insert (r : rpcmap, k : string, v) = + let + fun insert' (r, ks) = + case r of + Rpc _ => Rpc v + | Module m => + case ks of + [] => Rpc v + | k :: ks' => + let + val r' = case SM.find (m, k) of + NONE => Module SM.empty + | SOME r' => r' + in + Module (SM.insert (m, k, insert' (r', ks'))) + end + in + insert' (r, String.tokens (fn ch => ch = #"/") k) + end + +fun dump (r : rpcmap) = + case r of + Rpc _ => print "ROOT\n" + | Module m => (print "\n"; + SM.appi (fn (k, r') => (print (k ^ ":\n"); + dump r')) m; + print "\n") + fun classify (ds, ps) = let val proto = Settings.currentProtocol () fun inString {needle, haystack} = String.isSubstring needle haystack - fun hasClient {basis, funcs, push} = + fun hasClient {basis, rpcs, funcs, push} = MonoUtil.Exp.exists {typ = fn _ => false, exp = fn ERecv _ => push | EFfiApp ("Basis", x, _) => SS.member (basis, x) | EJavaScript _ => not push | ENamed n => IS.member (funcs, n) + | EServerCall (e, _, _, _) => + let + fun head (e : exp) = + case #1 e of + EStrcat (e1, _) => head e1 + | EPrim (Prim.String (_, s)) => SOME s + | _ => NONE + in + case head e of + NONE => true + | SOME fcall => + case lookup (rpcs, fcall) of + NONE => true + | SOME n => IS.member (funcs, n) + end | _ => false} + fun decl ((d, _), rpcs) = + case d of + DExport (Mono.Rpc _, fcall, n, _, _, _) => + insert (rpcs, fcall, n) + | _ => rpcs + + val rpcs = foldl decl (Module SM.empty) ds + fun decl ((d, _), (pull_ids, push_ids)) = let - val hasClientPull = hasClient {basis = SS.empty, funcs = pull_ids, push = false} - val hasClientPush = hasClient {basis = pushBasis, funcs = push_ids, push = true} + val hasClientPull = hasClient {basis = SS.empty, rpcs = rpcs, funcs = pull_ids, push = false} + val hasClientPush = hasClient {basis = pushBasis, rpcs = rpcs, funcs = push_ids, push = true} in case d of DVal (_, n, _, e, _) => (if hasClientPull e then - IS.add (pull_ids, n) - else - pull_ids, - if hasClientPush e then - IS.add (push_ids, n) - else - push_ids) + IS.add (pull_ids, n) + else + pull_ids, + if hasClientPush e then + IS.add (push_ids, n) + else + push_ids) | DValRec xes => (if List.exists (fn (_, _, _, e, _) => hasClientPull e) xes then - foldl (fn ((_, n, _, _, _), pull_ids) => IS.add (pull_ids, n)) - pull_ids xes + foldl (fn ((_, n, _, _, _), pull_ids) => IS.add (pull_ids, n)) + pull_ids xes else pull_ids, if List.exists (fn (_, _, _, e, _) => hasClientPush e) xes then diff --git a/tests/DynChannel.ur b/tests/DynChannel.ur new file mode 100644 index 00000000..d3688781 --- /dev/null +++ b/tests/DynChannel.ur @@ -0,0 +1,29 @@ +table channels : {Id : int, Channel:channel xbody} + +fun dosend (s:string) : transaction unit = + c <- oneRow1 (SELECT * FROM channels); + debug ("Sending " ^ s ^ " through the channel..."); + send c.Channel {[s]} + +fun mkchannel {} : transaction xbody = + c <- channel; + s <- source ; + dml( DELETE FROM channels WHERE Id >= 0); + dml( INSERT INTO channels(Id, Channel) VALUES(0, {[c]}) ); + return + + +
+ +
diff --git a/tests/rpchan.urs b/tests/rpchan.urs new file mode 100644 index 00000000..6ac44e0b --- /dev/null +++ b/tests/rpchan.urs @@ -0,0 +1 @@ +val main : unit -> transaction page -- cgit v1.2.3 From 4e4c43ff335cd6c2f1ec6bf359f69e9b09047572 Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Sat, 30 Aug 2014 08:28:59 -0400 Subject: New release --- CHANGELOG | 7 +++++++ configure.ac | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e56e24db..979f4d87 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +======== +20140830 +======== + +- New HTML attribute: 'role' +- Bug fixes + ======== 20140819 ======== diff --git a/configure.ac b/configure.ac index 074c26cd..85ce1da5 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ -AC_INIT([urweb], [20140819]) -WORKING_VERSION=1 +AC_INIT([urweb], [20140830]) +WORKING_VERSION=0 AC_USE_SYSTEM_EXTENSIONS # automake 1.12 requires this, but automake 1.11 doesn't recognize it -- cgit v1.2.3 From 4fe4c6868a10db99ddadf4cd60576178df29c8cd Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Sat, 30 Aug 2014 08:48:41 -0400 Subject: Return to working version mode --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 85ce1da5..2ff25580 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_INIT([urweb], [20140830]) -WORKING_VERSION=0 +WORKING_VERSION=1 AC_USE_SYSTEM_EXTENSIONS # automake 1.12 requires this, but automake 1.11 doesn't recognize it -- cgit v1.2.3 From a894904947777bbc797a69b1d55ca4008375acaf Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Thu, 4 Sep 2014 08:40:14 -0400 Subject: In computing command lines, put filenames inside of quotes, to support spaces and other funky characters nicely --- src/compiler.sml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/compiler.sml b/src/compiler.sml index 716cc3d3..b46643ff 100644 --- a/src/compiler.sml +++ b/src/compiler.sml @@ -1,4 +1,4 @@ -(* Copyright (c) 2008-2012, Adam Chlipala +(* Copyright (c) 2008-2012, 2014, Adam Chlipala * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1483,7 +1483,10 @@ val sqlify = { val toSqlify = transform sqlify "sqlify" o toMono_opt2 -val escapeFilename = String.translate (fn #" " => "\\ " | #"\"" => "\\\"" | #"'" => "\\'" | ch => str ch) +fun escapeFilename s = + "\"" + ^ String.translate (fn #"\"" => "\\\"" | #"\\" => "\\\\" | ch => str ch) s + ^ "\"" val beforeC = ref (fn () => ()) -- cgit v1.2.3 From 1bbc50639256f0a04b1866ad23a3945c17130068 Mon Sep 17 00:00:00 2001 From: Sergey Mironov Date: Sun, 24 Aug 2014 11:56:41 +0400 Subject: Check realloc's return code to prevent segfault on out of memory condition (Part 2) --- src/c/request.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/c/request.c b/src/c/request.c index 9dc6aa59..d621aea7 100644 --- a/src/c/request.c +++ b/src/c/request.c @@ -444,11 +444,12 @@ request_result uw_request(uw_request_context rc, uw_context ctx, int len = strlen(inputs); if (len+1 > rc->queryString_size) { - rc->queryString = realloc(rc->queryString, len+1); - if(rc->queryString == NULL) { + char *qs = realloc(rc->queryString, len+1); + if(qs == NULL) { log_error(logger_data, "queryString is too long (not enough memory)\n"); return FAILED; } + rc->queryString = qs; rc->queryString_size = len+1; } strcpy(rc->queryString, inputs); @@ -484,11 +485,12 @@ request_result uw_request(uw_request_context rc, uw_context ctx, on_success(ctx); if (path_len + 1 > rc->path_copy_size) { - rc->path_copy = realloc(rc->path_copy, path_len + 1); - if(rc->path_copy == NULL) { + char *pc = realloc(rc->path_copy, path_len + 1); + if(pc == NULL) { log_error(logger_data, "Path is too long (not enough memory)\n"); return FAILED; } + rc->path_copy = pc; rc->path_copy_size = path_len + 1; } strcpy(rc->path_copy, path); -- cgit v1.2.3 From c833454d70e5ae0436ed495f886209f95e07329e Mon Sep 17 00:00:00 2001 From: Sergey Mironov Date: Tue, 2 Sep 2014 17:36:14 +0000 Subject: Replace common "if(!quiet) printf(...)" pattern with a macro --- src/c/http.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/c/http.c b/src/c/http.c index 32dd1dd1..2e419f05 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -23,6 +23,9 @@ extern uw_app uw_application; int uw_backlog = SOMAXCONN; static int keepalive = 0, quiet = 0; +#define qfprintf(f, fmt, args...) do { if(!quiet) fprintf(f, fmt, ##args); } while(0) +#define qprintf(fmt, args...) do { if(!quiet) printf(fmt, ##args); } while(0) + static char *get_header(void *data, const char *h) { char *s = data; int len = strlen(h); @@ -86,8 +89,7 @@ static void *worker(void *data) { sock = uw_dequeue(); } - if (!quiet) - printf("Handling connection with thread #%d.\n", me); + qprintf("Handling connection with thread #%d.\n", me); while (1) { int r; @@ -107,16 +109,14 @@ static void *worker(void *data) { r = recv(sock, back, buf_size - 1 - (back - buf), 0); if (r < 0) { - if (!quiet) - fprintf(stderr, "Recv failed\n"); + qfprintf(stderr, "Recv failed while receiving header\n"); close(sock); sock = 0; break; } if (r == 0) { - if (!quiet) - printf("Connection closed.\n"); + qprintf("Connection closed.\n"); close(sock); sock = 0; break; @@ -159,16 +159,14 @@ static void *worker(void *data) { r = recv(sock, back, buf_size - 1 - (back - buf), 0); if (r < 0) { - if (!quiet) - fprintf(stderr, "Recv failed\n"); + qfprintf(stderr, "Recv failed\n"); close(sock); sock = 0; goto done; } if (r == 0) { - if (!quiet) - fprintf(stderr, "Connection closed.\n"); + qfprintf(stderr, "Connection closed.\n"); close(sock); sock = 0; goto done; @@ -236,8 +234,7 @@ static void *worker(void *data) { uw_set_headers(ctx, get_header, headers); uw_set_env(ctx, get_env, NULL); - if (!quiet) - printf("Serving URI %s....\n", path); + qprintf("Serving URI %s....\n", path); rr = uw_request(rc, ctx, method, path, query_string, body, back - body, on_success, on_failure, NULL, log_error, log_debug, @@ -405,8 +402,7 @@ int main(int argc, char *argv[]) { sin_size = sizeof their_addr; - if (!quiet) - printf("Listening on port %d....\n", uw_port); + qprintf("Listening on port %d....\n", uw_port); { pthread_t thread; @@ -434,11 +430,9 @@ int main(int argc, char *argv[]) { int new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); if (new_fd < 0) { - if (!quiet) - fprintf(stderr, "Socket accept failed\n"); + qfprintf(stderr, "Socket accept failed\n"); } else { - if (!quiet) - printf("Accepted connection.\n"); + qprintf("Accepted connection.\n"); if (keepalive) { int flag = 1; -- cgit v1.2.3 From af2946386863bcb273aa2677a3d6e235d1660b16 Mon Sep 17 00:00:00 2001 From: Sergey Mironov Date: Tue, 2 Sep 2014 17:37:22 +0000 Subject: Check realloc's return code to prevent segfault on out of memory condition (Part 3) --- src/c/http.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/c/http.c b/src/c/http.c index 2e419f05..2a8b7e94 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -97,8 +97,15 @@ static void *worker(void *data) { if (back - buf == buf_size - 1) { char *new_buf; - buf_size *= 2; - new_buf = realloc(buf, buf_size); + size_t new_buf_size = buf_size*2; + new_buf = realloc(buf, new_buf_size); + if(!new_buf) { + qfprintf(stderr, "Realloc failed while receiving header\n"); + close(sock); + sock = 0; + break; + } + buf_size = new_buf_size; back = new_buf + (back - buf); buf = new_buf; } @@ -146,9 +153,16 @@ static void *worker(void *data) { while (back - body < clen) { if (back - buf == buf_size - 1) { char *new_buf; - buf_size *= 2; - new_buf = realloc(buf, buf_size); - + size_t new_buf_size = buf_size * 2; + new_buf = realloc(buf, new_buf_size); + if(!new_buf) { + qfprintf(stderr, "Realloc failed while receiving content\n"); + close(sock); + sock = 0; + goto done; + } + + buf_size = new_buf_size; back = new_buf + (back - buf); body = new_buf + (body - buf); s = new_buf + (s - buf); -- cgit v1.2.3 From 5d2d4930568267b0e205ece3d4908cdc7ff715a1 Mon Sep 17 00:00:00 2001 From: Sergey Mironov Date: Tue, 2 Sep 2014 17:42:10 +0000 Subject: Introduce recv timeout controlled by '-T' option in http.c This should prevent a DDoS attack where attacker and keeps the connection open but send no data. --- src/c/http.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/c/http.c b/src/c/http.c index 2a8b7e94..9651a216 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -116,7 +116,7 @@ static void *worker(void *data) { r = recv(sock, back, buf_size - 1 - (back - buf), 0); if (r < 0) { - qfprintf(stderr, "Recv failed while receiving header\n"); + qfprintf(stderr, "Recv failed while receiving header, retcode %d errno %m\n", r); close(sock); sock = 0; break; @@ -173,7 +173,7 @@ static void *worker(void *data) { r = recv(sock, back, buf_size - 1 - (back - buf), 0); if (r < 0) { - qfprintf(stderr, "Recv failed\n"); + qfprintf(stderr, "Recv failed while receiving content, retcode %d errno %m\n", r); close(sock); sock = 0; goto done; @@ -312,7 +312,7 @@ static void *worker(void *data) { } static void help(char *cmd) { - printf("Usage: %s [-p ] [-a ] [-t ] [-k] [-q]\nThe '-k' option turns on HTTP keepalive.\nThe '-q' option turns off some chatter on stdout.\n", cmd); + printf("Usage: %s [-p ] [-a ] [-t ] [-k] [-q] [-T SEC]\nThe '-k' option turns on HTTP keepalive.\nThe '-q' option turns off some chatter on stdout.\nThe -T option sets socket recv timeout (0 disables timeout, default is 5 sec)", cmd); } static void sigint(int signum) { @@ -327,6 +327,7 @@ int main(int argc, char *argv[]) { struct sockaddr_in their_addr; // connector's address information socklen_t sin_size; int yes = 1, uw_port = 8080, nthreads = 1, i, *names, opt; + int recv_timeout_sec = 5; signal(SIGINT, sigint); signal(SIGPIPE, SIG_IGN); @@ -334,7 +335,7 @@ int main(int argc, char *argv[]) { my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero); - while ((opt = getopt(argc, argv, "hp:a:t:kq")) != -1) { + while ((opt = getopt(argc, argv, "hp:a:t:kqT:")) != -1) { switch (opt) { case '?': fprintf(stderr, "Unknown command-line option\n"); @@ -375,6 +376,15 @@ int main(int argc, char *argv[]) { keepalive = 1; break; + case 'T': + recv_timeout_sec = atoi(optarg); + if (recv_timeout_sec < 0) { + fprintf(stderr, "Invalid recv timeout\n"); + help(argv[0]); + return 1; + } + break; + case 'q': quiet = 1; break; @@ -453,6 +463,17 @@ int main(int argc, char *argv[]) { setsockopt(new_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); } + if(recv_timeout_sec>0) { + int ret; + struct timeval tv; + memset(&tv, 0, sizeof(struct timeval)); + tv.tv_sec = recv_timeout_sec; + ret = setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); + if(ret != 0) { + qfprintf(stderr, "Timeout setting failed, errcode %d errno '%m'\n", ret); + } + } + uw_enqueue(new_fd); } } -- cgit v1.2.3