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