Loading lib/connect.c +88 −3 Original line number Diff line number Diff line Loading @@ -337,6 +337,70 @@ int socketerror(int sockfd) return err; } /* * Curl_is_connected() is used from the multi interface to check if the * firstsocket has connected. */ CURLcode Curl_is_connected(struct connectdata *conn, int sockfd, bool *connected) { int rc; struct SessionHandle *data = conn->data; *connected = FALSE; /* a very negative world view is best */ if(data->set.timeout || data->set.connecttimeout) { /* there is a timeout set */ /* Evaluate in milliseconds how much time that has passed */ long has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); /* subtract the most strict timeout of the ones */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) has_passed -= data->set.timeout*1000; else has_passed -= data->set.connecttimeout*1000; } else if(data->set.timeout) has_passed -= data->set.timeout*1000; else has_passed -= data->set.connecttimeout*1000; if(has_passed > 0 ) { /* time-out, bail out, go home */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEOUTED; } } /* check for connect without timeout as we want to return immediately */ rc = waitconnect(sockfd, 0); if(0 == rc) { int err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ *connected = TRUE; return CURLE_OK; } /* nope, not connected for real */ } /* * If the connection phase is "done" here, we should attempt to connect * to the "next address" in the Curl_hostaddr structure that we resolved * before. But we don't have that struct around anymore and we can't just * keep a pointer since the cache might in fact have gotten pruned by the * time we want to read this... Alas, we don't do this yet. */ return CURLE_OK; } /* * TCP connect to the given host with timeout, proxy or remote doesn't matter. * There might be more than one IP address to try out. Fill in the passed Loading @@ -347,7 +411,8 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ Curl_addrinfo *remotehost, /* use one in here */ int port, /* connect to this */ int *sockconn, /* the connected socket */ Curl_ipconnect **addr) /* the one we used */ Curl_ipconnect **addr, /* the one we used */ bool *connected) /* really connected? */ { struct SessionHandle *data = conn->data; int rc; Loading Loading @@ -437,8 +502,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ case EAGAIN: #endif case EINTR: /* asynchronous connect, wait for connect or timeout */ if(data->state.used_interface == Curl_if_multi) /* don't hang when doing multi */ timeout_ms = 0; rc = waitconnect(sockfd, timeout_ms); break; case ECONNREFUSED: /* no one listening */ Loading @@ -448,6 +516,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ break; } } if(0 == rc) { /* we might be connected, if the socket says it is OK! Ask it! */ int err; Loading @@ -455,11 +524,17 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ *connected = TRUE; /* this is truly a connect */ break; } failf(data, "socket error: %d", err); /* we are _not_ connected, it was a false alert, continue please */ } else if(data->state.used_interface == Curl_if_multi) { /* When running the multi interface, we bail out here */ rc = 0; break; } /* connect failed or timed out */ sclose(sockfd); Loading Loading @@ -542,8 +617,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ */ case EAGAIN: #endif /* asynchronous connect, wait for connect or timeout */ if(data->state.used_interface == Curl_if_multi) /* don't hang when doing multi */ timeout_ms = 0; rc = waitconnect(sockfd, timeout_ms); break; default: Loading @@ -558,6 +636,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ int err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ break; } /* nope, not connected for real */ Loading @@ -565,6 +644,12 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ } if(0 != rc) { if(data->state.used_interface == Curl_if_multi) { /* When running the multi interface, we bail out here */ rc = 0; break; } /* get a new timeout for next attempt */ after = Curl_tvnow(); timeout_ms -= Curl_tvdiff(after, before); Loading lib/connect.h +7 −2 Original line number Diff line number Diff line Loading @@ -26,10 +26,15 @@ int Curl_nonblock(int socket, /* operate on this */ int nonblock /* TRUE or FALSE */); CURLcode Curl_is_connected(struct connectdata *conn, int sockfd, bool *connected); CURLcode Curl_connecthost(struct connectdata *conn, Curl_addrinfo *host, /* connect to this */ int port, /* connect to this port number */ int *sockconn, /* not set if error is returned */ Curl_ipconnect **addr /* the one we used */ ); /* index we used */ Curl_ipconnect **addr, /* the one we used */ bool *connected /* truly connected? */ ); #endif lib/ftp.c +159 −123 Original line number Diff line number Diff line Loading @@ -387,8 +387,10 @@ int Curl_GetFTPResponse(char *buf, return nread; /* total amount of bytes read */ } /* ftp_connect() should do everything that is to be considered a part of the connection phase. */ /* * Curl_ftp_connect() should do everything that is to be considered a part of * the connection phase. */ CURLcode Curl_ftp_connect(struct connectdata *conn) { /* this is FTP and no proxy */ Loading Loading @@ -1321,7 +1323,8 @@ CURLcode ftp_use_port(struct connectdata *conn) */ static CURLcode ftp_use_pasv(struct connectdata *conn) CURLcode ftp_use_pasv(struct connectdata *conn, bool *connected) { struct SessionHandle *data = conn->data; ssize_t nread; Loading Loading @@ -1473,7 +1476,14 @@ CURLcode ftp_use_pasv(struct connectdata *conn) addr, connectport, &conn->secondarysocket, &conninfo); &conninfo, connected); /* * When this is used from the multi interface, this might've returned with * the 'connected' set to FALSE and thus we are now awaiting a non-blocking * connect to connect and we should not be "hanging" here waiting. */ if((CURLE_OK == result) && data->set.verbose) Loading @@ -1494,127 +1504,24 @@ CURLcode ftp_use_pasv(struct connectdata *conn) return CURLE_OK; } /*********************************************************************** * * ftp_perform() /* * Curl_ftp_nextconnect() * * This is the actual DO function for FTP. Get a file/directory according to * the options previously setup. * This function shall be called when the second FTP connection has been * established and is confirmed connected. */ static CURLcode ftp_perform(struct connectdata *conn) CURLcode Curl_ftp_nextconnect(struct connectdata *conn) { /* this is FTP and no proxy */ ssize_t nread; CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; char *buf = data->state.buffer; /* this is our buffer */ CURLcode result; ssize_t nread; int ftpcode; /* for ftp status */ /* the ftp struct is already inited in ftp_connect() */ struct FTP *ftp = conn->proto.ftp; long *bytecountp = ftp->bytecountp; int ftpcode; /* for ftp status */ /* Send any QUOTE strings? */ if(data->set.quote) { if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) return result; } /* This is a re-used connection. Since we change directory to where the transfer is taking place, we must now get back to the original dir where we ended up after login: */ if (conn->bits.reuse) { if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) return result; } /* change directory first! */ if(ftp->dir && ftp->dir[0]) { if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) return result; } /* Requested time of file? */ if(data->set.get_filetime && ftp->file) { result = ftp_getfiletime(conn, ftp->file); if(result) return result; } /* If we have selected NOBODY and HEADER, it means that we only want file information. Which in FTP can't be much more than the file size and date. */ if(data->set.no_body && data->set.include_header && ftp->file) { /* The SIZE command is _not_ RFC 959 specified, and therefor many servers may not support it! It is however the only way we have to get a file's size! */ ssize_t filesize; ftp->no_transfer = TRUE; /* this means no actual transfer is made */ /* Some servers return different sizes for different modes, and thus we must set the proper type before we check the size */ result = ftp_transfertype(conn, data->set.ftp_ascii); if(result) return result; /* failing to get size is not a serious error */ result = ftp_getsize(conn, ftp->file, &filesize); if(CURLE_OK == result) { sprintf(buf, "Content-Length: %d\r\n", filesize); result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); if(result) return result; } /* If we asked for a time of the file and we actually got one as well, we "emulate" a HTTP-style header in our output. */ #ifdef HAVE_STRFTIME if(data->set.get_filetime && data->info.filetime) { struct tm *tm; #ifdef HAVE_LOCALTIME_R struct tm buffer; tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); #else tm = localtime((unsigned long *)&data->info.filetime); #endif /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", tm); result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); if(result) return result; } #endif return CURLE_OK; } if(data->set.no_body) /* doesn't really transfer any data */ ftp->no_transfer = TRUE; /* Get us a second connection up and connected */ else if(data->set.ftp_use_port) { /* We have chosen to use the PORT command */ result = ftp_use_port(conn); if(CURLE_OK == result) /* we have the data connection ready */ infof(data, "Connected the data stream with PORT!\n"); } else { /* We have chosen (this is default) to use the PASV command */ result = ftp_use_pasv(conn); if(CURLE_OK == result) infof(data, "Connected the data stream with PASV!\n"); } if(result) return result; if(data->set.upload) { Loading Loading @@ -1991,6 +1898,128 @@ CURLcode ftp_perform(struct connectdata *conn) return CURLE_OK; } /*********************************************************************** * * ftp_perform() * * This is the actual DO function for FTP. Get a file/directory according to * the options previously setup. */ static CURLcode ftp_perform(struct connectdata *conn, bool *connected) /* for the TCP connect status after PASV / PORT */ { /* this is FTP and no proxy */ CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; char *buf = data->state.buffer; /* this is our buffer */ /* the ftp struct is already inited in ftp_connect() */ struct FTP *ftp = conn->proto.ftp; /* Send any QUOTE strings? */ if(data->set.quote) { if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) return result; } /* This is a re-used connection. Since we change directory to where the transfer is taking place, we must now get back to the original dir where we ended up after login: */ if (conn->bits.reuse) { if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) return result; } /* change directory first! */ if(ftp->dir && ftp->dir[0]) { if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) return result; } /* Requested time of file? */ if(data->set.get_filetime && ftp->file) { result = ftp_getfiletime(conn, ftp->file); if(result) return result; } /* If we have selected NOBODY and HEADER, it means that we only want file information. Which in FTP can't be much more than the file size and date. */ if(data->set.no_body && data->set.include_header && ftp->file) { /* The SIZE command is _not_ RFC 959 specified, and therefor many servers may not support it! It is however the only way we have to get a file's size! */ ssize_t filesize; ftp->no_transfer = TRUE; /* this means no actual transfer is made */ /* Some servers return different sizes for different modes, and thus we must set the proper type before we check the size */ result = ftp_transfertype(conn, data->set.ftp_ascii); if(result) return result; /* failing to get size is not a serious error */ result = ftp_getsize(conn, ftp->file, &filesize); if(CURLE_OK == result) { sprintf(buf, "Content-Length: %d\r\n", filesize); result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); if(result) return result; } /* If we asked for a time of the file and we actually got one as well, we "emulate" a HTTP-style header in our output. */ #ifdef HAVE_STRFTIME if(data->set.get_filetime && data->info.filetime) { struct tm *tm; #ifdef HAVE_LOCALTIME_R struct tm buffer; tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); #else tm = localtime((unsigned long *)&data->info.filetime); #endif /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", tm); result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); if(result) return result; } #endif return CURLE_OK; } if(data->set.no_body) /* doesn't really transfer any data */ ftp->no_transfer = TRUE; /* Get us a second connection up and connected */ else if(data->set.ftp_use_port) { /* We have chosen to use the PORT command */ result = ftp_use_port(conn); if(CURLE_OK == result) { /* we have the data connection ready */ infof(data, "Ordered connect of the data stream with PORT!\n"); *connected = TRUE; /* mark us "still connected" */ } } else { /* We have chosen (this is default) to use the PASV command */ result = ftp_use_pasv(conn, connected); if(connected) infof(data, "Connected the data stream with PASV!\n"); } return result; } /*********************************************************************** * * Curl_ftp() Loading @@ -2003,6 +2032,7 @@ CURLcode ftp_perform(struct connectdata *conn) CURLcode Curl_ftp(struct connectdata *conn) { CURLcode retcode; bool connected; struct SessionHandle *data = conn->data; struct FTP *ftp; Loading Loading @@ -2049,15 +2079,15 @@ CURLcode Curl_ftp(struct connectdata *conn) else ftp->dir = NULL; retcode = ftp_perform(conn); /* clean up here, success or error doesn't matter */ if(ftp->file) free(ftp->file); if(ftp->dir) free(ftp->dir); retcode = ftp_perform(conn, &connected); ftp->file = ftp->dir = NULL; /* zero */ if(CURLE_OK == retcode) { if(connected) retcode = Curl_ftp_nextconnect(conn); else /* since we didn't connect now, we want do_more to get called */ conn->do_more = TRUE; } return retcode; } Loading Loading @@ -2128,6 +2158,12 @@ CURLcode Curl_ftp_disconnect(struct connectdata *conn) free(ftp->entrypath); if(ftp->cache) free(ftp->cache); if(ftp->file) free(ftp->file); if(ftp->dir) free(ftp->dir); ftp->file = ftp->dir = NULL; /* zero */ } return CURLE_OK; } Loading lib/ftp.h +1 −9 Original line number Diff line number Diff line #ifndef __FTP_H #define __FTP_H /***************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | Loading @@ -24,22 +23,15 @@ * $Id$ *****************************************************************************/ /* MN 06/07/02 */ #ifndef CURL_DISABLE_FTP CURLcode Curl_ftp(struct connectdata *conn); CURLcode Curl_ftp_done(struct connectdata *conn); CURLcode Curl_ftp_connect(struct connectdata *conn); CURLcode Curl_ftp_disconnect(struct connectdata *conn); CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...); /* The kerberos stuff needs this: */ int Curl_GetFTPResponse(char *buf, struct connectdata *conn, int *ftpcode); /* MN 06/07/02 */ CURLcode Curl_ftp_nextconnect(struct connectdata *conn); #endif #endif lib/multi.c +102 −9 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ #include "urldata.h" #include "transfer.h" #include "url.h" #include "connect.h" /* The last #include file should be: */ #ifdef MALLOCDEBUG Loading @@ -43,11 +44,13 @@ struct Curl_message { typedef enum { CURLM_STATE_INIT, CURLM_STATE_CONNECT, CURLM_STATE_DO, CURLM_STATE_PERFORM, CURLM_STATE_DONE, CURLM_STATE_COMPLETED, CURLM_STATE_CONNECT, /* connect has been sent off */ CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ CURLM_STATE_DO, /* send off the request (part 1) */ CURLM_STATE_DO_MORE, /* send off the request (part 2) */ CURLM_STATE_PERFORM, /* transfer data */ CURLM_STATE_DONE, /* post data transfer operation */ CURLM_STATE_COMPLETED, /* operation complete */ CURLM_STATE_LAST /* not a true state, never use this */ } CURLMstate; Loading Loading @@ -224,6 +227,32 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, switch(easy->state) { default: break; case CURLM_STATE_WAITCONNECT: case CURLM_STATE_DO_MORE: { /* when we're waiting for a connect, we wait for the socket to become writable */ struct connectdata *conn = easy->easy_conn; int sockfd; if(CURLM_STATE_WAITCONNECT == easy->state) { sockfd = conn->firstsocket; FD_SET(sockfd, write_fd_set); } else { /* When in DO_MORE state, we could be either waiting for us to connect to a remote site, or we could wait for that site to connect to us. It makes a difference in the way: if we connect to the site we wait for the socket to become writable, if the site connects to us we wait for it to become readable */ sockfd = conn->secondarysocket; FD_SET(sockfd, write_fd_set); } if(sockfd > *max_fd) *max_fd = sockfd; } break; case CURLM_STATE_PERFORM: /* This should have a set of file descriptors for us to set. */ /* after the transfer is done, go DONE */ Loading Loading @@ -251,6 +280,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) bool done; CURLMcode result=CURLM_OK; struct Curl_message *msg = NULL; bool connected; *running_handles = 0; /* bump this once for every living handle */ Loading @@ -259,6 +289,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { #ifdef MALLOCDEBUG fprintf(stderr, "HANDLE %p: State: %x\n", (char *)easy, easy->state); #endif switch(easy->state) { case CURLM_STATE_INIT: /* init this transfer. */ Loading Loading @@ -287,23 +323,80 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* Connect. We get a connection identifier filled in. */ easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); /* after connect, go DO */ /* after the connect has been sent off, go WAITCONNECT */ if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_WAITCONNECT; result = CURLM_CALL_MULTI_PERFORM; } break; case CURLM_STATE_WAITCONNECT: { bool connected; easy->result = Curl_is_connected(easy->easy_conn, easy->easy_conn->firstsocket, &connected); if(connected) easy->result = Curl_protocol_connect(easy->easy_conn, NULL); if(CURLE_OK != easy->result) /* failure detected */ break; if(connected) { /* after the connect has completed, go DO */ easy->state = CURLM_STATE_DO; result = CURLM_CALL_MULTI_PERFORM; } } break; case CURLM_STATE_DO: /* Do the fetch or put request */ easy->result = Curl_do(&easy->easy_conn); /* after do, go PERFORM */ if(CURLE_OK == easy->result) { if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) { /* after do, go PERFORM... or DO_MORE */ if(easy->easy_conn->do_more) { /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ easy->state = CURLM_STATE_DO_MORE; result = CURLM_OK; } else { /* we're done with the DO, now PERFORM */ easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } } break; case CURLM_STATE_DO_MORE: /* * First, check if we really are ready to do more. */ easy->result = Curl_is_connected(easy->easy_conn, easy->easy_conn->secondarysocket, &connected); if(connected) { /* * When we are connected, DO MORE and then go PERFORM */ easy->result = Curl_do_more(easy->easy_conn); if(CURLE_OK == easy->result) easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } break; case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); Loading Loading
lib/connect.c +88 −3 Original line number Diff line number Diff line Loading @@ -337,6 +337,70 @@ int socketerror(int sockfd) return err; } /* * Curl_is_connected() is used from the multi interface to check if the * firstsocket has connected. */ CURLcode Curl_is_connected(struct connectdata *conn, int sockfd, bool *connected) { int rc; struct SessionHandle *data = conn->data; *connected = FALSE; /* a very negative world view is best */ if(data->set.timeout || data->set.connecttimeout) { /* there is a timeout set */ /* Evaluate in milliseconds how much time that has passed */ long has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); /* subtract the most strict timeout of the ones */ if(data->set.timeout && data->set.connecttimeout) { if (data->set.timeout < data->set.connecttimeout) has_passed -= data->set.timeout*1000; else has_passed -= data->set.connecttimeout*1000; } else if(data->set.timeout) has_passed -= data->set.timeout*1000; else has_passed -= data->set.connecttimeout*1000; if(has_passed > 0 ) { /* time-out, bail out, go home */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEOUTED; } } /* check for connect without timeout as we want to return immediately */ rc = waitconnect(sockfd, 0); if(0 == rc) { int err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ *connected = TRUE; return CURLE_OK; } /* nope, not connected for real */ } /* * If the connection phase is "done" here, we should attempt to connect * to the "next address" in the Curl_hostaddr structure that we resolved * before. But we don't have that struct around anymore and we can't just * keep a pointer since the cache might in fact have gotten pruned by the * time we want to read this... Alas, we don't do this yet. */ return CURLE_OK; } /* * TCP connect to the given host with timeout, proxy or remote doesn't matter. * There might be more than one IP address to try out. Fill in the passed Loading @@ -347,7 +411,8 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ Curl_addrinfo *remotehost, /* use one in here */ int port, /* connect to this */ int *sockconn, /* the connected socket */ Curl_ipconnect **addr) /* the one we used */ Curl_ipconnect **addr, /* the one we used */ bool *connected) /* really connected? */ { struct SessionHandle *data = conn->data; int rc; Loading Loading @@ -437,8 +502,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ case EAGAIN: #endif case EINTR: /* asynchronous connect, wait for connect or timeout */ if(data->state.used_interface == Curl_if_multi) /* don't hang when doing multi */ timeout_ms = 0; rc = waitconnect(sockfd, timeout_ms); break; case ECONNREFUSED: /* no one listening */ Loading @@ -448,6 +516,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ break; } } if(0 == rc) { /* we might be connected, if the socket says it is OK! Ask it! */ int err; Loading @@ -455,11 +524,17 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ *connected = TRUE; /* this is truly a connect */ break; } failf(data, "socket error: %d", err); /* we are _not_ connected, it was a false alert, continue please */ } else if(data->state.used_interface == Curl_if_multi) { /* When running the multi interface, we bail out here */ rc = 0; break; } /* connect failed or timed out */ sclose(sockfd); Loading Loading @@ -542,8 +617,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ */ case EAGAIN: #endif /* asynchronous connect, wait for connect or timeout */ if(data->state.used_interface == Curl_if_multi) /* don't hang when doing multi */ timeout_ms = 0; rc = waitconnect(sockfd, timeout_ms); break; default: Loading @@ -558,6 +636,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ int err = socketerror(sockfd); if ((0 == err) || (EISCONN == err)) { /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ break; } /* nope, not connected for real */ Loading @@ -565,6 +644,12 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ } if(0 != rc) { if(data->state.used_interface == Curl_if_multi) { /* When running the multi interface, we bail out here */ rc = 0; break; } /* get a new timeout for next attempt */ after = Curl_tvnow(); timeout_ms -= Curl_tvdiff(after, before); Loading
lib/connect.h +7 −2 Original line number Diff line number Diff line Loading @@ -26,10 +26,15 @@ int Curl_nonblock(int socket, /* operate on this */ int nonblock /* TRUE or FALSE */); CURLcode Curl_is_connected(struct connectdata *conn, int sockfd, bool *connected); CURLcode Curl_connecthost(struct connectdata *conn, Curl_addrinfo *host, /* connect to this */ int port, /* connect to this port number */ int *sockconn, /* not set if error is returned */ Curl_ipconnect **addr /* the one we used */ ); /* index we used */ Curl_ipconnect **addr, /* the one we used */ bool *connected /* truly connected? */ ); #endif
lib/ftp.c +159 −123 Original line number Diff line number Diff line Loading @@ -387,8 +387,10 @@ int Curl_GetFTPResponse(char *buf, return nread; /* total amount of bytes read */ } /* ftp_connect() should do everything that is to be considered a part of the connection phase. */ /* * Curl_ftp_connect() should do everything that is to be considered a part of * the connection phase. */ CURLcode Curl_ftp_connect(struct connectdata *conn) { /* this is FTP and no proxy */ Loading Loading @@ -1321,7 +1323,8 @@ CURLcode ftp_use_port(struct connectdata *conn) */ static CURLcode ftp_use_pasv(struct connectdata *conn) CURLcode ftp_use_pasv(struct connectdata *conn, bool *connected) { struct SessionHandle *data = conn->data; ssize_t nread; Loading Loading @@ -1473,7 +1476,14 @@ CURLcode ftp_use_pasv(struct connectdata *conn) addr, connectport, &conn->secondarysocket, &conninfo); &conninfo, connected); /* * When this is used from the multi interface, this might've returned with * the 'connected' set to FALSE and thus we are now awaiting a non-blocking * connect to connect and we should not be "hanging" here waiting. */ if((CURLE_OK == result) && data->set.verbose) Loading @@ -1494,127 +1504,24 @@ CURLcode ftp_use_pasv(struct connectdata *conn) return CURLE_OK; } /*********************************************************************** * * ftp_perform() /* * Curl_ftp_nextconnect() * * This is the actual DO function for FTP. Get a file/directory according to * the options previously setup. * This function shall be called when the second FTP connection has been * established and is confirmed connected. */ static CURLcode ftp_perform(struct connectdata *conn) CURLcode Curl_ftp_nextconnect(struct connectdata *conn) { /* this is FTP and no proxy */ ssize_t nread; CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; char *buf = data->state.buffer; /* this is our buffer */ CURLcode result; ssize_t nread; int ftpcode; /* for ftp status */ /* the ftp struct is already inited in ftp_connect() */ struct FTP *ftp = conn->proto.ftp; long *bytecountp = ftp->bytecountp; int ftpcode; /* for ftp status */ /* Send any QUOTE strings? */ if(data->set.quote) { if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) return result; } /* This is a re-used connection. Since we change directory to where the transfer is taking place, we must now get back to the original dir where we ended up after login: */ if (conn->bits.reuse) { if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) return result; } /* change directory first! */ if(ftp->dir && ftp->dir[0]) { if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) return result; } /* Requested time of file? */ if(data->set.get_filetime && ftp->file) { result = ftp_getfiletime(conn, ftp->file); if(result) return result; } /* If we have selected NOBODY and HEADER, it means that we only want file information. Which in FTP can't be much more than the file size and date. */ if(data->set.no_body && data->set.include_header && ftp->file) { /* The SIZE command is _not_ RFC 959 specified, and therefor many servers may not support it! It is however the only way we have to get a file's size! */ ssize_t filesize; ftp->no_transfer = TRUE; /* this means no actual transfer is made */ /* Some servers return different sizes for different modes, and thus we must set the proper type before we check the size */ result = ftp_transfertype(conn, data->set.ftp_ascii); if(result) return result; /* failing to get size is not a serious error */ result = ftp_getsize(conn, ftp->file, &filesize); if(CURLE_OK == result) { sprintf(buf, "Content-Length: %d\r\n", filesize); result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); if(result) return result; } /* If we asked for a time of the file and we actually got one as well, we "emulate" a HTTP-style header in our output. */ #ifdef HAVE_STRFTIME if(data->set.get_filetime && data->info.filetime) { struct tm *tm; #ifdef HAVE_LOCALTIME_R struct tm buffer; tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); #else tm = localtime((unsigned long *)&data->info.filetime); #endif /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", tm); result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); if(result) return result; } #endif return CURLE_OK; } if(data->set.no_body) /* doesn't really transfer any data */ ftp->no_transfer = TRUE; /* Get us a second connection up and connected */ else if(data->set.ftp_use_port) { /* We have chosen to use the PORT command */ result = ftp_use_port(conn); if(CURLE_OK == result) /* we have the data connection ready */ infof(data, "Connected the data stream with PORT!\n"); } else { /* We have chosen (this is default) to use the PASV command */ result = ftp_use_pasv(conn); if(CURLE_OK == result) infof(data, "Connected the data stream with PASV!\n"); } if(result) return result; if(data->set.upload) { Loading Loading @@ -1991,6 +1898,128 @@ CURLcode ftp_perform(struct connectdata *conn) return CURLE_OK; } /*********************************************************************** * * ftp_perform() * * This is the actual DO function for FTP. Get a file/directory according to * the options previously setup. */ static CURLcode ftp_perform(struct connectdata *conn, bool *connected) /* for the TCP connect status after PASV / PORT */ { /* this is FTP and no proxy */ CURLcode result=CURLE_OK; struct SessionHandle *data=conn->data; char *buf = data->state.buffer; /* this is our buffer */ /* the ftp struct is already inited in ftp_connect() */ struct FTP *ftp = conn->proto.ftp; /* Send any QUOTE strings? */ if(data->set.quote) { if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) return result; } /* This is a re-used connection. Since we change directory to where the transfer is taking place, we must now get back to the original dir where we ended up after login: */ if (conn->bits.reuse) { if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) return result; } /* change directory first! */ if(ftp->dir && ftp->dir[0]) { if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) return result; } /* Requested time of file? */ if(data->set.get_filetime && ftp->file) { result = ftp_getfiletime(conn, ftp->file); if(result) return result; } /* If we have selected NOBODY and HEADER, it means that we only want file information. Which in FTP can't be much more than the file size and date. */ if(data->set.no_body && data->set.include_header && ftp->file) { /* The SIZE command is _not_ RFC 959 specified, and therefor many servers may not support it! It is however the only way we have to get a file's size! */ ssize_t filesize; ftp->no_transfer = TRUE; /* this means no actual transfer is made */ /* Some servers return different sizes for different modes, and thus we must set the proper type before we check the size */ result = ftp_transfertype(conn, data->set.ftp_ascii); if(result) return result; /* failing to get size is not a serious error */ result = ftp_getsize(conn, ftp->file, &filesize); if(CURLE_OK == result) { sprintf(buf, "Content-Length: %d\r\n", filesize); result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); if(result) return result; } /* If we asked for a time of the file and we actually got one as well, we "emulate" a HTTP-style header in our output. */ #ifdef HAVE_STRFTIME if(data->set.get_filetime && data->info.filetime) { struct tm *tm; #ifdef HAVE_LOCALTIME_R struct tm buffer; tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); #else tm = localtime((unsigned long *)&data->info.filetime); #endif /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", tm); result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); if(result) return result; } #endif return CURLE_OK; } if(data->set.no_body) /* doesn't really transfer any data */ ftp->no_transfer = TRUE; /* Get us a second connection up and connected */ else if(data->set.ftp_use_port) { /* We have chosen to use the PORT command */ result = ftp_use_port(conn); if(CURLE_OK == result) { /* we have the data connection ready */ infof(data, "Ordered connect of the data stream with PORT!\n"); *connected = TRUE; /* mark us "still connected" */ } } else { /* We have chosen (this is default) to use the PASV command */ result = ftp_use_pasv(conn, connected); if(connected) infof(data, "Connected the data stream with PASV!\n"); } return result; } /*********************************************************************** * * Curl_ftp() Loading @@ -2003,6 +2032,7 @@ CURLcode ftp_perform(struct connectdata *conn) CURLcode Curl_ftp(struct connectdata *conn) { CURLcode retcode; bool connected; struct SessionHandle *data = conn->data; struct FTP *ftp; Loading Loading @@ -2049,15 +2079,15 @@ CURLcode Curl_ftp(struct connectdata *conn) else ftp->dir = NULL; retcode = ftp_perform(conn); /* clean up here, success or error doesn't matter */ if(ftp->file) free(ftp->file); if(ftp->dir) free(ftp->dir); retcode = ftp_perform(conn, &connected); ftp->file = ftp->dir = NULL; /* zero */ if(CURLE_OK == retcode) { if(connected) retcode = Curl_ftp_nextconnect(conn); else /* since we didn't connect now, we want do_more to get called */ conn->do_more = TRUE; } return retcode; } Loading Loading @@ -2128,6 +2158,12 @@ CURLcode Curl_ftp_disconnect(struct connectdata *conn) free(ftp->entrypath); if(ftp->cache) free(ftp->cache); if(ftp->file) free(ftp->file); if(ftp->dir) free(ftp->dir); ftp->file = ftp->dir = NULL; /* zero */ } return CURLE_OK; } Loading
lib/ftp.h +1 −9 Original line number Diff line number Diff line #ifndef __FTP_H #define __FTP_H /***************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | Loading @@ -24,22 +23,15 @@ * $Id$ *****************************************************************************/ /* MN 06/07/02 */ #ifndef CURL_DISABLE_FTP CURLcode Curl_ftp(struct connectdata *conn); CURLcode Curl_ftp_done(struct connectdata *conn); CURLcode Curl_ftp_connect(struct connectdata *conn); CURLcode Curl_ftp_disconnect(struct connectdata *conn); CURLcode Curl_ftpsendf(struct connectdata *, const char *fmt, ...); /* The kerberos stuff needs this: */ int Curl_GetFTPResponse(char *buf, struct connectdata *conn, int *ftpcode); /* MN 06/07/02 */ CURLcode Curl_ftp_nextconnect(struct connectdata *conn); #endif #endif
lib/multi.c +102 −9 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ #include "urldata.h" #include "transfer.h" #include "url.h" #include "connect.h" /* The last #include file should be: */ #ifdef MALLOCDEBUG Loading @@ -43,11 +44,13 @@ struct Curl_message { typedef enum { CURLM_STATE_INIT, CURLM_STATE_CONNECT, CURLM_STATE_DO, CURLM_STATE_PERFORM, CURLM_STATE_DONE, CURLM_STATE_COMPLETED, CURLM_STATE_CONNECT, /* connect has been sent off */ CURLM_STATE_WAITCONNECT, /* we're awaiting the connect to finalize */ CURLM_STATE_DO, /* send off the request (part 1) */ CURLM_STATE_DO_MORE, /* send off the request (part 2) */ CURLM_STATE_PERFORM, /* transfer data */ CURLM_STATE_DONE, /* post data transfer operation */ CURLM_STATE_COMPLETED, /* operation complete */ CURLM_STATE_LAST /* not a true state, never use this */ } CURLMstate; Loading Loading @@ -224,6 +227,32 @@ CURLMcode curl_multi_fdset(CURLM *multi_handle, switch(easy->state) { default: break; case CURLM_STATE_WAITCONNECT: case CURLM_STATE_DO_MORE: { /* when we're waiting for a connect, we wait for the socket to become writable */ struct connectdata *conn = easy->easy_conn; int sockfd; if(CURLM_STATE_WAITCONNECT == easy->state) { sockfd = conn->firstsocket; FD_SET(sockfd, write_fd_set); } else { /* When in DO_MORE state, we could be either waiting for us to connect to a remote site, or we could wait for that site to connect to us. It makes a difference in the way: if we connect to the site we wait for the socket to become writable, if the site connects to us we wait for it to become readable */ sockfd = conn->secondarysocket; FD_SET(sockfd, write_fd_set); } if(sockfd > *max_fd) *max_fd = sockfd; } break; case CURLM_STATE_PERFORM: /* This should have a set of file descriptors for us to set. */ /* after the transfer is done, go DONE */ Loading Loading @@ -251,6 +280,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) bool done; CURLMcode result=CURLM_OK; struct Curl_message *msg = NULL; bool connected; *running_handles = 0; /* bump this once for every living handle */ Loading @@ -259,6 +289,12 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) easy=multi->easy.next; while(easy) { #ifdef MALLOCDEBUG fprintf(stderr, "HANDLE %p: State: %x\n", (char *)easy, easy->state); #endif switch(easy->state) { case CURLM_STATE_INIT: /* init this transfer. */ Loading Loading @@ -287,23 +323,80 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) /* Connect. We get a connection identifier filled in. */ easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn); /* after connect, go DO */ /* after the connect has been sent off, go WAITCONNECT */ if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_WAITCONNECT; result = CURLM_CALL_MULTI_PERFORM; } break; case CURLM_STATE_WAITCONNECT: { bool connected; easy->result = Curl_is_connected(easy->easy_conn, easy->easy_conn->firstsocket, &connected); if(connected) easy->result = Curl_protocol_connect(easy->easy_conn, NULL); if(CURLE_OK != easy->result) /* failure detected */ break; if(connected) { /* after the connect has completed, go DO */ easy->state = CURLM_STATE_DO; result = CURLM_CALL_MULTI_PERFORM; } } break; case CURLM_STATE_DO: /* Do the fetch or put request */ easy->result = Curl_do(&easy->easy_conn); /* after do, go PERFORM */ if(CURLE_OK == easy->result) { if(CURLE_OK == Curl_readwrite_init(easy->easy_conn)) { /* after do, go PERFORM... or DO_MORE */ if(easy->easy_conn->do_more) { /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ easy->state = CURLM_STATE_DO_MORE; result = CURLM_OK; } else { /* we're done with the DO, now PERFORM */ easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } } break; case CURLM_STATE_DO_MORE: /* * First, check if we really are ready to do more. */ easy->result = Curl_is_connected(easy->easy_conn, easy->easy_conn->secondarysocket, &connected); if(connected) { /* * When we are connected, DO MORE and then go PERFORM */ easy->result = Curl_do_more(easy->easy_conn); if(CURLE_OK == easy->result) easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } break; case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); Loading