Parcourir la source

Merge commit 'origin/andrei/send_flags'

* commit 'origin/andrei/send_flags':
  NEWS: update
  core: new close after send and reuse only script functions
  tcp: send_flags support
  sl: use the msg reply send_flags
  tm: support for send_flags
  core: send flags support
Andrei Pelinescu-Onciul il y a 16 ans
Parent
commit
643491da0b
16 fichiers modifiés avec 199 ajouts et 36 suppressions
  1. 17 0
      NEWS
  2. 17 0
      action.c
  3. 12 0
      cfg.lex
  4. 28 0
      cfg.y
  5. 10 5
      forward.c
  6. 9 0
      ip_addr.h
  7. 6 3
      modules/tm/t_fwd.c
  8. 1 0
      modules/tm/t_lookup.c
  9. 3 3
      modules/tm/uac.c
  10. 12 5
      modules/tm/ut.h
  11. 2 1
      modules_k/sl/sl_funcs.c
  12. 1 0
      modules_s/sl/sl_funcs.c
  13. 2 0
      parser/msg_parser.h
  14. 5 1
      route_struct.h
  15. 6 1
      tcp_conn.h
  16. 68 17
      tcp_main.c

+ 17 - 0
NEWS

@@ -62,13 +62,30 @@ config script changes:
   - support for kamailio style pvars
   - C-like switch()/case (integer only)
   - while()
+  - include file support: include_file "somefile"
+  - event route support: event_route[module_name:eventid]
+
 build system:
   - multiple modules directories are now supported (defined in Makefile.dirs)
 
 new config variables:
   - max_while_loops - maximum iterations allowed for a while  (can be changed
        at runtime). Default 100.
+  - log_name - set the application name used when printing to syslog.
 
+new script commands:
+  add_local_rport() - adds the rport parameter to the added via header
+       (rfc3581).
+  set_forward_no_connect() - the message will be forwarded only if there is
+       already an existing connection to the destination (it applies only to
+       connection oriented protocols like tcp, tls and in the future sctp).
+  set_reply_no_connect() - like set_forward_no_connect(),  but works for
+       replies to the current message.
+  set_forward_close()  - try to close the connection after forwarding the
+       current message (it applies only when the underlying protocol is
+       connection oriented).
+  set_reply_close() - like set_forward_close(), but it works for replies to
+       the current message.
 
 
 

+ 17 - 0
action.c

@@ -50,6 +50,7 @@
  *  2008-12-03  use lvalues/rvalues for assignments (andrei)
  *  2008-12-17  added UDP_MTU_TRY_PROTO_T (andrei)
  *  2009-05-04  switched IF_T to rval_expr (andrei)
+ *  2009-09-15  added SET_{FWD,RPL}_NO_CONNECT, SET_{FWD,RPL}_CLOSE (andrei)
  */
 
 
@@ -1214,6 +1215,22 @@ match_cleanup:
 			else
 				ret=v;
 			break;
+		case SET_FWD_NO_CONNECT_T:
+			msg->fwd_send_flags|= SND_F_FORCE_CON_REUSE;
+			ret=1; /* continue processing */
+			break;
+		case SET_RPL_NO_CONNECT_T:
+			msg->rpl_send_flags|= SND_F_FORCE_CON_REUSE;
+			ret=1; /* continue processing */
+			break;
+		case SET_FWD_CLOSE_T:
+			msg->fwd_send_flags|= SND_F_CON_CLOSE;
+			ret=1; /* continue processing */
+			break;
+		case SET_RPL_CLOSE_T:
+			msg->rpl_send_flags|= SND_F_CON_CLOSE;
+			ret=1; /* continue processing */
+			break;
 /*
 		default:
 			LOG(L_CRIT, "BUG: do_action: unknown type %d\n", a->type);

+ 12 - 0
cfg.lex

@@ -215,6 +215,10 @@ ELSE			"else"
 SET_ADV_ADDRESS	"set_advertised_address"
 SET_ADV_PORT	"set_advertised_port"
 FORCE_SEND_SOCKET	"force_send_socket"
+SET_FWD_NO_CONNECT		"set_forward_no_connect"
+SET_RPL_NO_CONNECT	"set_reply_no_connect"
+SET_FWD_CLOSE	"set_forward_close"
+SET_RPL_CLOSE	"set_reply_close"
 SWITCH			"switch"
 CASE			"case"
 DEFAULT			"default"
@@ -572,6 +576,14 @@ EAT_ABLE	[\ \t\b\r]
 										return SET_ADV_PORT; }
 <INITIAL>{FORCE_SEND_SOCKET}	{	count(); yylval.strval=yytext;
 									return FORCE_SEND_SOCKET; }
+<INITIAL>{SET_FWD_NO_CONNECT}	{ count(); yylval.strval=yytext;
+									return SET_FWD_NO_CONNECT; }
+<INITIAL>{SET_RPL_NO_CONNECT}	{ count(); yylval.strval=yytext;
+									return SET_RPL_NO_CONNECT; }
+<INITIAL>{SET_FWD_CLOSE}		{ count(); yylval.strval=yytext;
+									return SET_FWD_CLOSE; }
+<INITIAL>{SET_RPL_CLOSE}		{ count(); yylval.strval=yytext;
+									return SET_RPL_CLOSE; }
 <INITIAL>{SWITCH}	{ count(); yylval.strval=yytext; return SWITCH; }
 <INITIAL>{CASE}	{ count(); yylval.strval=yytext; return CASE; }
 <INITIAL>{DEFAULT}	{ count(); yylval.strval=yytext; return DEFAULT; }

+ 28 - 0
cfg.y

@@ -319,6 +319,10 @@ extern char *finame;
 %token SET_ADV_ADDRESS
 %token SET_ADV_PORT
 %token FORCE_SEND_SOCKET
+%token SET_FWD_NO_CONNECT
+%token SET_RPL_NO_CONNECT
+%token SET_FWD_CLOSE
+%token SET_RPL_CLOSE
 %token SWITCH
 %token CASE
 %token DEFAULT
@@ -2990,6 +2994,30 @@ cmd:
 		$$=0; yyerror("bad argument, [proto:]host[:port] expected");
 	}
 	| FORCE_SEND_SOCKET error {$$=0; yyerror("missing '(' or ')' ?"); }
+	| SET_FWD_NO_CONNECT LPAREN RPAREN	{
+		$$=mk_action(SET_FWD_NO_CONNECT_T, 0); set_cfg_pos($$);
+	}
+	| SET_FWD_NO_CONNECT	{
+		$$=mk_action(SET_FWD_NO_CONNECT_T, 0); set_cfg_pos($$);
+	}
+	| SET_RPL_NO_CONNECT LPAREN RPAREN	{
+		$$=mk_action(SET_RPL_NO_CONNECT_T, 0); set_cfg_pos($$);
+	}
+	| SET_RPL_NO_CONNECT	{
+		$$=mk_action(SET_RPL_NO_CONNECT_T, 0); set_cfg_pos($$);
+	}
+	| SET_FWD_CLOSE LPAREN RPAREN	{
+		$$=mk_action(SET_FWD_CLOSE_T, 0); set_cfg_pos($$);
+	}
+	| SET_FWD_CLOSE	{
+		$$=mk_action(SET_FWD_CLOSE_T, 0); set_cfg_pos($$);
+	}
+	| SET_RPL_CLOSE LPAREN RPAREN	{
+		$$=mk_action(SET_RPL_CLOSE_T, 0); set_cfg_pos($$);
+	}
+	| SET_RPL_CLOSE	{
+		$$=mk_action(SET_RPL_CLOSE_T, 0); set_cfg_pos($$);
+	}
 	| ID {mod_func_action = mk_action(MODULE_T, 2, MODEXP_ST, NULL, NUMBER_ST,
 			0); } LPAREN func_params RPAREN	{
 		mod_func_action->val[0].u.data = 

+ 10 - 5
forward.c

@@ -384,9 +384,12 @@ int check_self_port(unsigned short port, unsigned short proto)
  *               default port or non srv. lookup is desired, the port must
  *               be !=0 
  *   port      - used only if dst!=0 (else the port in send_info->to is used)
- *   send_info - filled dest_info structure:
- *               if the send_socket member is null, a send_socket will be 
- *               chosen automatically
+ *   send_info - value/result partially filled dest_info structure:
+ *                 - send_info->proto and comp are used
+ *                 - send_info->to will be filled (dns)
+ *                 - send_info->send_flags is filled from the message
+ *                 - if the send_socket member is null, a send_socket will be 
+ *                   chosen automatically
  * WARNING: don't forget to zero-fill all the  unused members (a non-zero 
  * random id along with proto==PROTO_TCP can have bad consequences, same for
  *   a bogus send_socket value)
@@ -438,13 +441,14 @@ int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
 			goto error;
 		}
 	}/* dst */
+	send_info->send_flags=msg->fwd_send_flags;
 	/* calculate branch for outbound request;  if syn_branch is turned off,
 	   calculate is from transaction key, i.e., as an md5 of From/To/CallID/
 	   CSeq exactly the same way as TM does; good for reboot -- than messages
 	   belonging to transaction lost due to reboot will still be forwarded
 	   with the same branch parameter and will be match-able downstream
-
-       if it is turned on, we don't care about reboot; we simply put a simple
+	
+	   if it is turned on, we don't care about reboot; we simply put a simple
 	   value in there; better for performance
 	*/
 	if (syn_branch ) {
@@ -694,6 +698,7 @@ int forward_reply(struct sip_msg* msg)
 	}
 
 	dst.proto=msg->via2->proto;
+	dst.send_flags=msg->fwd_send_flags | msg->rpl_send_flags;
 	if (update_sock_struct_from_via( &dst.to, msg, msg->via2 )==-1) goto error;
 #ifdef USE_COMP
 	dst.comp=msg->via2->comp_no;

+ 9 - 0
ip_addr.h

@@ -34,6 +34,7 @@
  *  2006-04-21  added init_dst_from_rcv (andrei)
  *  2007-06-26  added ip_addr_mk_any() (andrei)
  *  2008-05-21  added su2a(), ip_addr2sbuf(), ip4tosbuf(), ip62sbuf() (andrei)
+ *  2009-09-14  added send flags support to dest_info (andrei)
  */
 
 #ifndef ip_addr_h
@@ -136,11 +137,18 @@ struct receive_info{
 };
 
 
+/* send flags */
+#define SND_F_FORCE_CON_REUSE	1 /* reuse an existing connection or fail */
+#define SND_F_CON_CLOSE			2 /* close the connection after sending */
+
+typedef unsigned char  snd_flags_t;
+
 struct dest_info{
 	struct socket_info* send_sock;
 	union sockaddr_union to;
 	int id; /* tcp stores the connection id here */ 
 	char proto;
+	snd_flags_t send_flags;
 #ifdef USE_COMP
 	short comp;
 #endif
@@ -748,6 +756,7 @@ inline static void init_dst_from_rcv(struct dest_info* dst,
 		dst->to=rcv->src_su;
 		dst->id=rcv->proto_reserved1;
 		dst->proto=rcv->proto;
+		dst->send_flags=0;
 #ifdef USE_COMP
 		dst->comp=rcv->comp;
 #endif

+ 6 - 3
modules/tm/t_fwd.c

@@ -413,6 +413,8 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
 		t->uac[branch].request.dst.send_sock =
 		get_send_socket( request, &t->uac[branch].request.dst.to,
 								t->uac[branch].request.dst.proto);
+		t->uac[branch].request.dst.send_flags=request?
+												request->fwd_send_flags:0;
 	}else {
 #ifdef USE_DNS_FAILOVER
 		if (uri2dst(&t->uac[branch].dns_h, &t->uac[branch].request.dst,
@@ -1083,15 +1085,16 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 	} else try_new=0;
 
 	init_branch_iterator();
-	while((current_uri.s=next_branch( &current_uri.len, &q, &dst_uri.s, &dst_uri.len, &si))) {
+	while((current_uri.s=next_branch( &current_uri.len, &q, &dst_uri.s,
+										&dst_uri.len, &si))) {
 		try_new++;
 		p_msg->force_send_socket = si;
 		getbflagsval(get_branch_iterator(), &bflags);
 		setbflagsval(0, bflags);
 
 		branch_ret=add_uac( t, p_msg, &current_uri, 
-				    (dst_uri.len) ? (&dst_uri) : &current_uri, 
-				    proxy, proto);
+							(dst_uri.len) ? (&dst_uri) : &current_uri, 
+							proxy, proto);
 		/* pick some of the errors in case things go wrong;
 		   note that picking lowest error is just as good as
 		   any other algorithm which picks any other negative

+ 1 - 0
modules/tm/t_lookup.c

@@ -1218,6 +1218,7 @@ int init_rb( struct retr_buf *rb, struct sip_msg *msg)
 #ifdef USE_COMP
 	rb->dst.comp=via->comp_no;
 #endif
+	rb->dst.send_flags=msg->rpl_send_flags;
 	/* turn off mhomed for generating replies -- they are ideally sent to where
 	   request came from to make life with NATs and other beasts easier
 	*/

+ 3 - 3
modules/tm/uac.c

@@ -238,7 +238,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 #ifdef USE_DNS_FAILOVER
 	if (cfg_get(core, core_cfg, use_dns_failover)){
 		dns_srv_handle_init(&dns_h);
-		if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock,
+		if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock, 0,
 							uac_r->dialog->hooks.next_hop, PROTO_NONE)==0)
 				|| (dst.send_sock==0)){
 			dns_srv_handle_put(&dns_h);
@@ -249,7 +249,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 		}
 		dns_srv_handle_put(&dns_h); /* not needed anymore */
 	}else{
-		if ((uri2dst2(0, &dst, uac_r->dialog->send_sock,
+		if ((uri2dst2(0, &dst, uac_r->dialog->send_sock, 0,
 						uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
 				(dst.send_sock==0)){
 			ser_error = E_NO_SOCKET;
@@ -259,7 +259,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
 		}
 	}
 #else /* USE_DNS_FAILOVER */
-	if ((uri2dst2(&dst, uac_r->dialog->send_sock,
+	if ((uri2dst2(&dst, uac_r->dialog->send_sock, 0,
 					uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
 			(dst.send_sock==0)){
 		ser_error = E_NO_SOCKET;

+ 12 - 5
modules/tm/ut.h

@@ -221,6 +221,7 @@ inline static int get_uri_send_info(str* uri, str* host, unsigned short* port,
  *         dst   - will be filled
  *         force_send_sock - if 0 dst->send_sock will be set to the default 
  *                 (see get_send_socket2()) 
+ *         sflags - send flags
  *         uri   - uri in str form
  *         proto - if != PROTO_NONE, this protocol will be forced over the
  *                 uri_proto, otherwise the uri proto will be used if set or
@@ -231,10 +232,12 @@ inline static int get_uri_send_info(str* uri, str* host, unsigned short* port,
 inline static struct dest_info *uri2dst2(struct dns_srv_handle* dns_h,
 										struct dest_info* dst,
 										struct socket_info *force_send_socket,
+										snd_flags_t sflags,
 										str *uri, int proto )
 #else
 inline static struct dest_info *uri2dst2(struct dest_info* dst,
 										struct socket_info *force_send_socket,
+										snd_flags_t sflags,
 										str *uri, int proto )
 #endif
 {
@@ -268,6 +271,7 @@ inline static struct dest_info *uri2dst2(struct dest_info* dst,
 #ifdef USE_COMP
 	dst->comp=parsed_uri.comp;
 #endif
+	dst->send_flags=sflags;
 #ifdef HONOR_MADDR
 	if (parsed_uri.maddr_val.s && parsed_uri.maddr_val.len) {
 		host=&parsed_uri.maddr_val;
@@ -336,9 +340,10 @@ inline static struct dest_info *uri2dst2(struct dest_info* dst,
  *                 null. If null or use_dns_failover==0 normal dns lookup will
  *                 be performed (no failover).
  *         dst   - will be filled
- *         msg   -  sip message used to set dst->send_sock, if 0 dst->send_sock
- *                 will be set to the default w/o using msg->force_send_socket 
- *                 (see get_send_socket()) 
+ *         msg   -  sip message used to set dst->send_sock and dst->send_flags,
+ *                 if 0 dst->send_sock will be set to the default w/o using 
+ *                  msg->force_send_socket (see get_send_socket()) and the 
+ *                  send_flags will be set to 0.
  *         uri   - uri in str form
  *         proto - if != PROTO_NONE, this protocol will be forced over the
  *                 uri_proto, otherwise the uri proto will be used if set or
@@ -351,14 +356,16 @@ inline static struct dest_info *uri2dst(struct dns_srv_handle* dns_h,
 										struct sip_msg *msg, str *uri, 
 											int proto )
 {
-	return uri2dst2(dns_h, dst, msg?msg->force_send_socket:0, uri, proto);
+	return uri2dst2(dns_h, dst, msg?msg->force_send_socket:0,
+						msg?msg->fwd_send_flags:0, uri, proto);
 }
 #else
 inline static struct dest_info *uri2dst(struct dest_info* dst,
 										struct sip_msg *msg, str *uri, 
 											int proto )
 {
-	return uri2dst2(dst, msg?msg->force_send_socket:0, uri, proto);
+	return uri2dst2(dst, msg?msg->force_send_socket:0,
+						msg?msg->fwd_send_flags:0, uri, proto);
 }
 #endif /* USE_DNS_FAILOVER */
 

+ 2 - 1
modules_k/sl/sl_funcs.c

@@ -245,10 +245,11 @@ int sl_send_reply_helper(struct sip_msg *msg ,int code, str *text, str *tag)
 	/* use for sending the received interface -bogdan*/
 	dst.proto=msg->rcv.proto;
 	dst.send_sock=msg->rcv.bind_address;
-	dst.id=msg->rcv.proto_reserved1;	
+	dst.id=msg->rcv.proto_reserved1;
 #ifdef USE_COMP
 	dst.comp=msg->via1->comp_no;
 #endif
+	dst.send_flags=msg->rpl_send_flags;
 	ret = msg_send(&dst, buf.s, buf.len);
 	mhomed=backup_mhomed;
 	pkg_free(buf.s);

+ 1 - 0
modules_s/sl/sl_funcs.c

@@ -168,6 +168,7 @@ int sl_send_reply(struct sip_msg *msg , int code, char* reason)
 #ifdef USE_COMP
 	dst.comp=msg->via1->comp_no;
 #endif
+	dst.send_flags=msg->rpl_send_flags;
 	ret = msg_send(&dst, buf, len);
 	mhomed=backup_mhomed;
 	pkg_free(buf);

+ 2 - 0
parser/msg_parser.h

@@ -246,6 +246,8 @@ typedef struct msg_body {
 
 typedef struct sip_msg {
 	unsigned int id;               /* message id, unique/process*/
+	snd_flags_t fwd_send_flags;    /* send flags for forwarding */
+	snd_flags_t rpl_send_flags;    /* send flags for replies */
 	struct msg_start first_line;   /* Message first line */
 	struct via_body* via1;         /* The first via */
 	struct via_body* via2;         /* The second via */

+ 5 - 1
route_struct.h

@@ -106,7 +106,11 @@ enum action_type{
 		FORCE_SEND_SOCKET_T,
 		ASSIGN_T,
 		ADD_T,
-		UDP_MTU_TRY_PROTO_T
+		UDP_MTU_TRY_PROTO_T,
+		SET_FWD_NO_CONNECT_T,
+		SET_RPL_NO_CONNECT_T,
+		SET_FWD_CLOSE_T,
+		SET_RPL_CLOSE_T
 };
 /* parameter types for actions or types for expression right operands
    (WARNING right operands only, not working for left operands) */

+ 6 - 1
tcp_conn.h

@@ -170,7 +170,8 @@ struct tcp_connection{
 	struct tcp_req req; /* request data */
 	atomic_t refcnt;
 	enum sip_protos type; /* PROTO_TCP or a protocol over it, e.g. TLS */
-	int flags; /* connection related flags */
+	unsigned short flags; /* connection related flags */
+	unsigned short send_flags; /* special send flags */
 	enum tcp_conn_states state; /* connection state */
 	void* extra_data; /* extra data associated to the connection, 0 for tcp*/
 	struct timer_ln timer;
@@ -190,6 +191,10 @@ struct tcp_connection{
 
 /* helper macros */
 
+#define tcpconn_set_send_flags(c, snd_flags) ((c)->send_flags|=(snd_flags))
+
+#define tcpconn_close_after_send(c)	((c)->send_flags & SND_F_CON_CLOSE)
+
 #define TCP_RCV_INFO(c) (&(c)->rcv)
 
 #define TCP_RCV_LADDR(r) (&((r).dst_ip))

+ 68 - 17
tcp_main.c

@@ -100,7 +100,9 @@
  *  2009-02-26  direct blacklist support (andrei)
  *  2009-03-20  s/wq_timeout/send_timeout ; send_timeout is now in ticks
  *              (andrei)
- * 2009-04-09  tcp ev and tcp stats macros added (andrei)
+ *  2009-04-09  tcp ev and tcp stats macros added (andrei)
+ *  2009-09-15  support for force connection reuse and close after send
+ *               send flags (andrei)
  */
 
 
@@ -1762,18 +1764,24 @@ int tcp_send(struct dest_info* dst, union sockaddr_union* from,
 			if (likely(port)){
 				/* try again w/o id */
 				c=tcpconn_get(0, &ip, port, from, con_lifetime);
-				goto no_id;
 			}else{
 				LOG(L_ERR, "ERROR: tcp_send: id %d not found, dropping\n",
 						dst->id);
 				return -1;
 			}
-		}else goto get_fd;
+		}
 	}
-no_id:
-		if (unlikely(c==0)){
+/* no_id: */
+		if (unlikely((c==0) || tcpconn_close_after_send(c))){
+			if (unlikely(c)){
+				/* can't use c if it's marked as close-after-send  =>
+				   release it and try opening new one */
+				tcpconn_chld_put(c); /* release c (dec refcnt & free on 0) */
+				c=0;
+			}
 			/* check if connect() is disabled */
-			if (cfg_get(tcp, tcp_cfg, no_connect))
+			if (unlikely((dst->send_flags & SND_F_FORCE_CON_REUSE) ||
+							cfg_get(tcp, tcp_cfg, no_connect)))
 				return -1;
 			DBG("tcp_send: no open tcp connection found, opening new one\n");
 			/* create tcp connection */
@@ -1814,6 +1822,7 @@ no_id:
 					return -1;
 				}
 				c->flags|=F_CONN_PENDING|F_CONN_FD_CLOSED;
+				tcpconn_set_send_flags(c, dst->send_flags);
 				atomic_set(&c->refcnt, 2); /* ref from here and from main hash
 											 table */
 				/* add it to id hash and aliases */
@@ -1918,6 +1927,14 @@ no_id:
 				}
 				LOG(L_INFO, "tcp_send: quick connect for %p\n", c);
 				TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
+				if (unlikely(dst->send_flags & SND_F_CON_CLOSE)){
+					/* if close-after-send requested, don't bother
+					   sending the fd back to tcp_main, try closing it
+					   immediately (no other tcp_send should use it,
+					   because it is marked as close-after-send before
+					   being added to the hash */
+					goto conn_wait_close;
+				}
 				c->state=S_CONN_OK;
 				/* send to tcp_main */
 				response[0]=(long)c;
@@ -1938,6 +1955,7 @@ no_id:
 								su2a(&dst->to, sizeof(dst->to)));
 				return -1;
 			}
+			tcpconn_set_send_flags(c, dst->send_flags);
 			if (likely(c->state==S_CONN_OK))
 				TCP_STATS_ESTABLISHED(S_CONN_CONNECT);
 			atomic_set(&c->refcnt, 2); /* ref. from here and it will also
@@ -1962,7 +1980,7 @@ no_id:
 			}
 			goto send_it;
 		}
-get_fd:
+/* get_fd: */
 #ifdef TCP_ASYNC
 		/* if data is already queued, we don't need the fd any more */
 		if (unlikely(cfg_get(tcp, tcp_cfg, async) &&
@@ -2048,6 +2066,8 @@ get_fd:
 send_it:
 	DBG("tcp_send: sending...\n");
 	lock_get(&c->write_lock);
+	/* update connection send flags with the current ones */
+	tcpconn_set_send_flags(c, dst->send_flags);
 #ifdef TCP_ASYNC
 	if (likely(cfg_get(tcp, tcp_cfg, async))){
 		if (_wbufq_non_empty(c)
@@ -2203,6 +2223,31 @@ error:
 			TCP_STATS_ESTABLISHED(c->state);
 			c->state=S_CONN_OK;
 	}
+	if (unlikely(dst->send_flags & SND_F_CON_CLOSE)){
+		/* close after write => send EOF request to tcp_main */
+		c->state=S_CONN_BAD;
+		c->timeout=get_ticks_raw();
+		/* tell "main" it should drop this*/
+		response[0]=(long)c;
+		response[1]=CONN_EOF;
+		if (send_all(unix_tcp_sock, response, sizeof(response))<=0){
+			LOG(L_CRIT, "BUG: tcp_send: error return failed (write):%s (%d)\n",
+					strerror(errno), errno);
+			tcpconn_chld_put(c); /* deref. it manually */
+			n=-1;
+		}
+		/* CONN_EOF will auto-dec refcnt => we must not call tcpconn_put 
+		 * if it succeeds */
+#ifdef TCP_FD_CACHE
+		if (unlikely(fd_cache_e)){
+			tcp_fd_cache_rm(fd_cache_e);
+			fd_cache_e=0;
+			close(fd);
+		}else
+#endif /* TCP_FD_CACHE */
+		if (do_close_fd) close(fd);
+		goto end_no_conn;
+	}
 end:
 #ifdef TCP_FD_CACHE
 	if (unlikely((fd_cache_e==0) && use_fd_cache)){
@@ -2216,11 +2261,14 @@ end_no_conn:
 	return n;
 #ifdef TCP_CONNECT_WAIT
 conn_wait_error:
-	/* connect or send failed on newly created connection which was not
-	 * yet sent to tcp_main (but was already hashed) => don't send to main,
-	 * unhash and destroy directly (if refcnt>2 it will be destroyed when the 
-	 * last sender releases the connection (tcpconn_chld_put(c))) or when
-	 * tcp_main receives a CONN_ERROR it*/
+	n=-1;
+conn_wait_close:
+	/* connect or send failed or immediate close-after-send was requested on
+	 * newly created connection which was not yet sent to tcp_main (but was
+	 * already hashed) => don't send to main, unhash and destroy directly
+	 * (if refcnt>2 it will be destroyed when the last sender releases the
+	 * connection (tcpconn_chld_put(c))) or when tcp_main receives a
+	 * CONN_ERROR it*/
 	c->state=S_CONN_BAD;
 	TCPCONN_LOCK;
 		if (c->flags & F_CONN_HASHED){
@@ -2234,7 +2282,7 @@ conn_wait_error:
 			TCPCONN_UNLOCK;
 	/* dec refcnt -> mark it for destruction */
 	tcpconn_chld_put(c);
-	return -1;
+	return n;
 #endif /* TCP_CONNET_WAIT */
 }
 
@@ -3025,11 +3073,12 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
 			LOG(L_ERR, "handle_ser_child: ERROR: received CON_ERROR for %p"
 					" (id %d), refcnt %d\n", 
 					tcpconn, tcpconn->id, atomic_get(&tcpconn->refcnt));
+		case CONN_EOF: /* forced EOF after full send, due to send flags */
 #ifdef TCP_CONNECT_WAIT
 			/* if the connection is pending => it might be on the way of
 			 * reaching tcp_main (e.g. CONN_NEW_COMPLETE or 
 			 *  CONN_NEW_PENDING_WRITE) =>  it cannot be destroyed here */
-			if ( !(tcpconn->flags & F_CONN_PENDING) && 
+			if ( !(tcpconn->flags & F_CONN_PENDING) &&
 					tcpconn_try_unhash(tcpconn) )
 				tcpconn_put(tcpconn);
 #else /* ! TCP_CONNECT_WAIT */
@@ -3155,7 +3204,7 @@ inline static int handle_ser_child(struct process_table* p, int fd_i)
 					}
 				}
 			}else{
-				LOG(L_WARN, "tcp_main: hanlder_ser_child: connection %p"
+				LOG(L_WARN, "tcp_main: handler_ser_child: connection %p"
 							" already watched for write\n", tcpconn);
 			}
 			break;
@@ -3466,8 +3515,10 @@ inline static int handle_tcpconn_ev(struct tcp_connection* tcpconn, short ev,
 	empty_q=0; /* warning fix */
 	if (unlikely((ev & (POLLOUT|POLLERR|POLLHUP)) &&
 					(tcpconn->flags & F_CONN_WRITE_W))){
-		if (unlikely((ev & (POLLERR|POLLHUP)) || 
-					(wbufq_run(tcpconn->s, tcpconn, &empty_q)<0))){
+		if (unlikely((ev & (POLLERR|POLLHUP)) ||
+					(wbufq_run(tcpconn->s, tcpconn, &empty_q)<0) ||
+					(empty_q && tcpconn_close_after_send(tcpconn))
+			)){
 			if (unlikely(io_watch_del(&io_h, tcpconn->s, fd_i, 0)<0)){
 				LOG(L_ERR, "ERROR: handle_tcpconn_ev: io_watch_del(1) failed:"
 							" for %p, fd %d\n", tcpconn, tcpconn->s);