Re: Kannel & blocking connect again.

Vjacheslav Chekushin <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Organization LMT
Message-ID <[email protected]>
Hi, Stipe.

Stipe Tolj wrote:

> Hi Vjacheslav,
> 
> thanks a lot for the submission of the patch. 
> 
> Could you please be so kind to use the unified diff format which is
> invoked with the -u switch, like 
> 
>   $ diff -u <oldfile> <newfile>
> 
> and attach the output to the mail, please no inline. Could you resend
> it that way and I will commit it to cvs today. Thanks in advance.


I don't use inline, may be mail program do it for me.
So I send 2 attachments, diff and gzipped diff.

> 
> Stipe
> 
> [email protected]
> -------------------------------------------------------------------
>

-- 
Vjacheslav Chekushin                                mailto:[email protected]
Latvian Mobile Phone Company                        http://www.lmt.lv
nonblocking.patch (text/plain, 16.2 KB)
diff -ubr --exclude=CVS gwlib/conn.c /home/wapgw/kannel/develcvs2/gateway/gwlib/conn.c
--- gwlib/conn.c	Tue Jun 11 23:59:52 2002
+++ /home/wapgw/kannel/develcvs2/gateway/gwlib/conn.c	Tue Nov 12 09:23:07 2002
@@ -62,6 +62,9 @@
     /* fd value is read-only and is not locked */
     int fd;
 
+  /* socket state */
+  enum {yes,no} connected;
+
     /* Protected by outlock */
     Octstr *outbuf;
     long outbufpos;   /* start of unwritten data in outbuf */
@@ -460,6 +463,51 @@
     return conn_open_tcp_with_port(host, port, 0, our_host);
 }
 
+Connection *conn_open_tcp_nb(Octstr *host, int port, Octstr *our_host)
+{
+  return conn_open_tcp_nb_with_port(host, port, 0, our_host);
+}
+
+Connection *conn_open_tcp_nb_with_port(Octstr *host, int port, int our_port,
+				       Octstr *our_host)
+{
+  int sockfd;
+  int done = -1;
+  Connection *c;
+  
+  sockfd = tcpip_connect_nb_to_server_with_port(octstr_get_cstr(host), port,
+						our_port, our_host == NULL ?
+						NULL : octstr_get_cstr(our_host), &done);
+  if (sockfd < 0)
+    return NULL;
+  c = conn_wrap_fd(sockfd, 0);
+  if (done != 0) {
+    c->connected = no;
+  }
+  return c;
+}
+
+int conn_is_connected(Connection *conn) 
+{
+  if (conn->connected == yes) return 0;
+  return -1;
+}
+
+int conn_get_connect_result(Connection *conn) 
+{
+  int err,len;
+  len = sizeof(len);
+  if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
+    return -1;
+  }
+  
+  if (err) {
+    return -1;
+  }
+  
+  conn->connected = yes;
+  return 0;
+}
 
 Connection *conn_open_tcp_with_port(Octstr *host, int port, int our_port,
 		Octstr *our_host)
@@ -493,6 +541,7 @@
     conn->inbufpos = 0;
 
     conn->fd = fd;
+    conn->connected = yes;
     conn->read_eof = 0;
     conn->read_error = 0;
     conn->output_buffering = DEFAULT_OUTPUT_BUFFERING;
@@ -746,6 +795,13 @@
         return;
     }
 
+    /* Get result of nonblocking connect, before any reads and writes
+     * we must check result (it must be handled in initial callback) */
+    if (conn->connected == no) {
+      conn->callback(conn, conn->callback_data);
+      return;
+    }
+
     /* If unlocked_write manages to write all pending data, it will
      * tell the fdset to stop listening for POLLOUT. */
     if (revents & POLLOUT) {
@@ -794,10 +850,16 @@
         result = -1;
     } else {
         events = 0;
+	/* For nonconnected socket we must lesten both directions */
+        if (conn->connected == yes) {
         if (conn->read_eof == 0 && conn->read_error == 0)
             events |= POLLIN;
         if (unlocked_outbuf_len(conn) > 0)
             events |= POLLOUT;
+        } else {
+          events |= POLLIN;
+          events |= POLLOUT;
+        }
 
         conn->registered = fdset;
         conn->callback = callback;
diff -ubr --exclude=CVS gwlib/conn.h /home/wapgw/kannel/develcvs2/gateway/gwlib/conn.h
--- gwlib/conn.h	Thu Aug  8 19:24:43 2002
+++ /home/wapgw/kannel/develcvs2/gateway/gwlib/conn.h	Tue Nov 12 09:23:07 2002
@@ -75,6 +75,23 @@
 Connection *conn_open_tcp_with_port(Octstr *host, int port, int our_port,
 		Octstr *our_host);
 
+/* Open a TCP/IP connection to the given host and port.  Return NULL in case of
+ * error. Overwise return new Connection. */
+Connection *conn_open_tcp_nb(Octstr *host, int port, Octstr *our_host);
+
+/* As above, but binds our end to 'our_port'. If 'our_port' is 0, uses
+ * any port like conn_open_tcp. */
+Connection *conn_open_tcp_nb_with_port(Octstr *host, int port, int our_port,
+				       Octstr *our_host);
+
+/* Returns 0 if socket is connected, -1 overwise */
+int conn_is_connected(Connection *conn);
+
+/* If socket is in the 'connecting' state, it must be listen by poller.
+ * After poller returns, connection must be checked for connection 
+ * procedure's result. Return 0 if connection done successfully */
+int conn_get_connect_result(Connection *conn);
+
 /* Create a Connection structure around the given file descriptor.
  * The file descriptor must not be used for anything else after this;
  * it must always be accessed via the Connection operations.  This
diff -ubr --exclude=CVS gwlib/http.c /home/wapgw/kannel/develcvs2/gateway/gwlib/http.c
--- gwlib/http.c	Thu Oct 17 12:22:39 2002
+++ /home/wapgw/kannel/develcvs2/gateway/gwlib/http.c	Tue Nov 12 09:34:33 2002
@@ -550,7 +550,6 @@
     "GET", "POST", "HEAD"
 };
 
-
 /*
  * Information about a server we've connected to.
  */
@@ -562,6 +561,7 @@
     List *request_headers;
     Octstr *request_body;   /* NULL for GET or HEAD, non-NULL for POST */
     enum {
+      connecting,
 	request_not_sent,
 	reading_status,
 	reading_entity,
@@ -582,6 +582,8 @@
 } HTTPServer;
 
 
+static int send_request(HTTPServer *trans);
+
 static HTTPServer *server_create(HTTPCaller *caller, int method, Octstr *url,
                                  List *headers, Octstr *body, int follow_remaining,
                                  Octstr *certkeyfile)
@@ -698,7 +700,7 @@
 	    conn = conn_open_ssl(host, port, certkeyfile, our_host);
 	else
 #endif /* HAVE_LIBSSL */
-	    conn = conn_open_tcp(host, port, our_host);
+	    conn = conn_open_tcp_nb(host, port, our_host);
 	debug("gwlib.http", 0, "HTTP: Opening connection to `%s:%d' (fd=%d).",
 	      octstr_get_cstr(host), port, conn_get_id(conn));
     } else {
@@ -851,6 +853,8 @@
     HTTPServer *trans;
     int ret;
     Octstr *h;
+    int rc;
+    char buf[128];
     
     trans = data;
 
@@ -861,6 +865,43 @@
 
     while (trans->state != transaction_done) {
 	switch (trans->state) {
+	case connecting:
+	  debug("gwlib.http", 0, "Get info about connecting socket");
+          if (conn_get_connect_result(trans->conn) != 0) {
+            debug("gwlib.http", 0, "Socket not connected");
+            conn_unregister(conn);
+            goto error;
+          }
+
+          if (trans->method == HTTP_METHOD_POST) {
+            /* 
+             * Add a Content-Length header.  Override an existing one, if
+             * necessary.  We must have an accurate one in order to use the
+             * connection for more than a single request.
+             */
+            http_header_remove_all(trans->request_headers, "Content-Length");
+            sprintf(buf, "%ld", octstr_len(trans->request_body));
+            http_header_add(trans->request_headers, "Content-Length", buf);
+          } 
+          /* 
+           * ok, this has to be an GET or HEAD request method then,
+           * if it contains a body, then this is not HTTP conform, so at
+           * least warn the user 
+           */
+          else if (trans->request_body != NULL) {
+            warning(0, "HTTP: GET or HEAD method request contains body:");
+            octstr_dump(trans->request_body, 0);
+          }
+
+          if ((rc = send_request(trans)) == 0) {
+            trans->state = reading_status;
+            conn_register(trans->conn, client_fdset, handle_transaction, 
+                          trans);
+          } else {
+            list_produce(trans->caller, trans);
+          }
+          break;
+
 	case reading_status:
 	    ret = client_read_status(trans);
 	    if (ret < 0) {
@@ -1120,84 +1161,110 @@
     return 0;
 }
 
-
-/*
- * Build and send the HTTP request. Return socket from which the
- * response can be read or -1 for error.
- */
-
-static Connection *send_request(HTTPServer *trans)
+static Connection *get_connection(HTTPServer *trans) 
 {
-    Octstr *path, *request;
+  Octstr *path;
     Connection *conn;
     Octstr *host, *our_host = NULL;
     int port;
 
-    path = NULL;
-    request = NULL;
     conn = NULL;
+  path = NULL;
 
     /* May not be NULL if we're retrying this transaction. */
     octstr_destroy(trans->host);
     trans->host = NULL;
 
-    if(trans->request_headers == NULL)
-    	trans->request_headers = http_create_empty_headers();
-
     if (parse_url(trans->url, &trans->host, &trans->port, &path, &trans->ssl,
                   &trans->username, &trans->password) == -1)
         goto error;
-    if (trans->username != NULL)
-        http_add_basic_auth(trans->request_headers, trans->username,
-                            trans->password);
+
+
     if (proxy_used_for_host(trans->host)) {
-        proxy_add_authentication(trans->request_headers);
-        request = build_request(http_method2name(trans->method), 
-                                trans->url, trans->host, trans->port, 
-                                trans->request_headers, trans->request_body);
         host = proxy_hostname;
         port = proxy_port;
     } else {
-        request = build_request(http_method2name(trans->method), path, trans->host, 
-                                trans->port, trans->request_headers,
-                                trans->request_body);
         host = trans->host;
         port = trans->port;
     }
 
     if (trans->retrying) {
 #ifdef HAVE_LIBSSL
-        if (trans->ssl) 
-            conn = conn_open_ssl(host, port, trans->certkeyfile, our_host);
+    if (trans->ssl) conn = conn_open_ssl(host, port, trans->certkeyfile, our_host);
         else
 #endif /* HAVE_LIBSSL */
-            conn = conn_open_tcp(host, port, our_host);
+      conn = conn_open_tcp_nb(host, port, our_host);
             debug("gwlib.http", 0, "HTTP: Opening NEW connection to `%s:%d' (fd=%d).",
                   octstr_get_cstr(host), port, conn_get_id(conn));
     } else
-        conn = conn_pool_get(host, port, trans->ssl, trans->certkeyfile, our_host);
+    conn = conn_pool_get(host, port, trans->ssl, trans->certkeyfile,
+                         our_host);
     if (conn == NULL)
         goto error;
 
+  octstr_destroy(path);
+
+  return conn;
+
+ error:
+  conn_destroy(conn);
+  octstr_destroy(path);
+  error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));
+  return NULL;
+}
+
+/*
+ * Build and send the HTTP request. Return socket from which the
+ * response can be read or -1 for error.
+ */
+
+static int send_request(HTTPServer *trans)
+{
+  Octstr *path, *request;
+
+  path = NULL;
+  request = NULL;
+
+  if (parse_url(trans->url, &trans->host, &trans->port, &path, &trans->ssl,
+		&trans->username, &trans->password) == -1)
+    goto error;
+
+  if (trans->username != NULL)
+    http_add_basic_auth(trans->request_headers, trans->username,
+			trans->password);
+
+  if (proxy_used_for_host(trans->host)) {
+    proxy_add_authentication(trans->request_headers);
+    request = build_request(http_method2name(trans->method),
+			    trans->url, trans->host, trans->port, 
+			    trans->request_headers, 
+			    trans->request_body);
+  } else {
+    request = build_request(http_method2name(trans->method),path, 
+			    trans->host, trans->port,
+			    trans->request_headers,
+			    trans->request_body);
+  }
+  
     debug("wsp.http", 0, "HTTP: Sending request:");
     octstr_dump(request, 0);
-    if (conn_write(conn, request) == -1)
+  if (conn_write(trans->conn, request) == -1)
         goto error;
 
     octstr_destroy(path);
     octstr_destroy(request);
 
-    return conn;
+  return 0;
 
-error:
-    conn_destroy(conn);
+ error:
+  conn_destroy(trans->conn);
+  trans->conn = NULL;
     octstr_destroy(path);
     octstr_destroy(request);
     error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));
-    return NULL;
+  return -1;
 }
 
-
 /*
  * This thread starts the transaction: it connects to the server and sends
  * the request. It then sends the transaction to the read_response_thread
@@ -1207,6 +1274,7 @@
 {
     HTTPServer *trans;
     char buf[128];    
+    int rc;
 
     while (run_status == running) {
         trans = list_consume(pending_requests);
@@ -1215,6 +1283,14 @@
 
         gw_assert(trans->state == request_not_sent);
 
+	trans->conn = get_connection(trans);
+
+	if (trans->conn == NULL)
+	  list_produce(trans->caller, trans);
+        else {
+          if (conn_is_connected(trans->conn) == 0) {
+	    debug("gwlib.http", 0, "Socket connected at once");
+
         if (trans->method == HTTP_METHOD_POST) {
             /* 
              * Add a Content-Length header.  Override an existing one, if
@@ -1234,14 +1310,21 @@
             warning(0, "HTTP: GET or HEAD method request contains body:");
             octstr_dump(trans->request_body, 0);
         }
-
-        trans->conn = send_request(trans);
-        if (trans->conn == NULL)
-            list_produce(trans->caller, trans);
-        else {
+	    if ((rc = send_request(trans)) == 0) {
             trans->state = reading_status;
+              conn_register(trans->conn, client_fdset, handle_transaction, 
+                            trans);
+            } else {
+              list_produce(trans->caller, trans);
+            }
+
+          } else { /* Socket not connected, wait for connection */
+            debug("gwlib.http", 0, "Socket connecting");
+            trans->state = connecting;
             conn_register(trans->conn, client_fdset, handle_transaction, trans);
         }
+	  
+	}
     }
 }
 
diff -ubr --exclude=CVS gwlib/socket.c /home/wapgw/kannel/develcvs2/gateway/gwlib/socket.c
--- gwlib/socket.c	Thu May  2 17:44:54 2002
+++ /home/wapgw/kannel/develcvs2/gateway/gwlib/socket.c	Tue Nov 12 09:23:07 2002
@@ -160,6 +160,92 @@
     return -1;
 }
 
+int tcpip_connect_nb_to_server(char *hostname, int port, const char *interface_name, int *done)
+{
+  return tcpip_connect_nb_to_server_with_port(hostname, port, 0, interface_name, done);
+}
+
+int tcpip_connect_nb_to_server_with_port(char *hostname, int port, int our_port, const char *interface_name, int *done) 
+{
+  struct sockaddr_in addr;
+  struct sockaddr_in o_addr;
+  struct hostent hostinfo;
+  struct hostent o_hostinfo;
+  int s;
+  int flags,rc;
+
+  *done = 1;
+  
+  s = socket(PF_INET, SOCK_STREAM, 0);
+  if (s == -1) {
+    error(errno, "Couldn't create new socket.");
+    goto error;
+  }
+  
+  if (gw_gethostbyname(&hostinfo, hostname) == -1) {
+    error(errno, "gethostbyname failed");
+    goto error;
+  }
+  
+  addr = empty_sockaddr_in;
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(port);
+  addr.sin_addr = *(struct in_addr *) hostinfo.h_addr;
+  
+  if (our_port > 0 || (interface_name != NULL && strcmp(interface_name, "*") != 0)) {
+    int reuse;
+    
+    o_addr = empty_sockaddr_in;
+    o_addr.sin_family = AF_INET;
+    o_addr.sin_port = htons(our_port);
+    if (interface_name == NULL || strcmp(interface_name, "*") == 0)
+      o_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    else {
+      if (gw_gethostbyname(&o_hostinfo, interface_name) == -1) {
+	error(errno, "gethostbyname failed");
+	goto error;
+      }
+      o_addr.sin_addr = *(struct in_addr *) o_hostinfo.h_addr;
+    }
+    
+    reuse = 1;
+    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse,
+		   sizeof(reuse)) == -1) {
+      error(errno, "setsockopt failed before bind");
+      goto error;
+    }
+    if (bind(s, (struct sockaddr *) &o_addr, sizeof(o_addr)) == -1) {
+      error(errno, "bind to local port %d failed", our_port);
+      goto error;
+    }
+  }
+
+  flags = fcntl(s, F_GETFL, 0);
+  fcntl(s, F_SETFL, flags | O_NONBLOCK);
+
+  if ((rc = connect(s, (struct sockaddr *) &addr, sizeof(addr))) < 0) {
+    if (errno != EINPROGRESS) {
+      error(errno, "nonblocking connect failed");
+      goto error;
+    }
+  }
+  
+  /* May be connected immediatly
+   * (if we connecting to localhost for example) */
+  if (rc == 0) { 
+    *done = 0;
+  }
+
+  return s;
+  
+ error:
+  error(0, "error connecting to server `%s' at port `%d'",
+	hostname, port);
+  if (s >= 0)
+    close(s);
+  return -1;
+}
+
 
 int write_to_socket(int socket, char *str)
 {
diff -ubr --exclude=CVS gwlib/socket.h /home/wapgw/kannel/develcvs2/gateway/gwlib/socket.h
--- gwlib/socket.h	Mon Oct  8 22:43:04 2001
+++ /home/wapgw/kannel/develcvs2/gateway/gwlib/socket.h	Tue Nov 12 09:23:07 2002
@@ -38,6 +38,15 @@
 int tcpip_connect_to_server_with_port(char *hostname, int port, int our_port,
 		const char *interface_name);
 
+/* Open a client socket in nonblocking mode, done is 0 if socket 
+   connected  immediatly, overwise done is 1 */
+int tcpip_connect_nb_to_server(char *hostname, int port, const char *interface_name,
+                               int *done);
+
+/* As above, but binds our end to 'our_port' */
+int tcpip_connect_nb_to_server_with_port(char *hostname, int port, int our_port,
+                                         const char *interface_name, int *done);
+
 /* Write string to socket. */
 int write_to_socket(int socket, char *str);
nonblocking.patch.gz (application/x-gzip, 4.6 KB) - not displayed
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.