Module: sip-router
Branch: andrei/send_flags
Commit: 340ce466abb2938ba9db1d3cb1d8b10819c1e2aa
URL:
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=340ce46…
Author: Andrei Pelinescu-Onciul <andrei(a)iptel.org>
Committer: Andrei Pelinescu-Onciul <andrei(a)iptel.org>
Date: Tue Sep 15 17:45:41 2009 +0200
tcp: send_flags support
Support for SND_F_FORCE_CON_REUSE and SND_F_CON_CLOSE added to the
tcp code.
---
tcp_conn.h | 7 ++++-
tcp_main.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 74 insertions(+), 18 deletions(-)
diff --git a/tcp_conn.h b/tcp_conn.h
index 9db6308..5338734 100644
--- a/tcp_conn.h
+++ b/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))
diff --git a/tcp_main.c b/tcp_main.c
index c238b2e..a72e3cc 100644
--- a/tcp_main.c
+++ b/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);