Curl_pgrsSetUploadSize(data, 0); /* nothing */
}
- Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
conn->datastream = Curl_hyper_stream;
/* clear userpwd and proxyuserpwd to avoid reusing old credentials
unsigned char buf[1024];
(void)sread(ctx->sock, buf, sizeof(buf));
}
- cf_socket_close(cf, data);
}
*done = TRUE;
return CURLE_OK;
if(cf) {
cf->cft->do_close(cf, data);
}
+ Curl_shutdown_clear(data, index);
}
-CURLcode Curl_conn_shutdown_blocking(struct Curl_easy *data, int sockindex)
+CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
{
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
+ timediff_t timeout_ms;
+ struct curltime now;
DEBUGASSERT(data->conn);
/* it is valid to call that without filters being present */
cf = data->conn->cfilter[sockindex];
- if(cf) {
- timediff_t timeout_ms;
- bool done = FALSE;
- int what;
+ if(!cf) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ *done = FALSE;
+ now = Curl_now();
+ if(!Curl_shutdown_started(data, sockindex)) {
DEBUGF(infof(data, "shutdown start on%s connection",
sockindex? " secondary" : ""));
- Curl_shutdown_start(data, sockindex, NULL);
- while(cf) {
- while(!done && !result) {
- result = cf->cft->do_shutdown(cf, data, &done);
- if(!result && !done) {
- timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, NULL);
- if(timeout_ms < 0) {
- failf(data, "SSL shutdown timeout");
- result = CURLE_OPERATION_TIMEDOUT;
- goto out;
- }
-
- what = Curl_conn_cf_poll(cf, data, timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- result = CURLE_RECV_ERROR;
- goto out;
- }
- else if(0 == what) {
- failf(data, "SSL shutdown timeout");
- result = CURLE_OPERATION_TIMEDOUT;
- goto out;
- }
- }
- }
- if(result)
- break;
- CURL_TRC_CF(data, cf, "shut down successfully");
- cf = cf->next;
- done = FALSE;
+ Curl_shutdown_start(data, sockindex, &now);
+ }
+ else {
+ timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);
+ if(timeout_ms < 0) {
+ failf(data, "SSL shutdown timeout");
+ return CURLE_OPERATION_TIMEDOUT;
}
- Curl_shutdown_clear(data, sockindex);
- DEBUGF(infof(data, "shutdown done on%s connection -> %d",
- sockindex? " secondary" : "", result));
}
-out:
+
+ while(cf) {
+ bool cfdone = FALSE;
+ result = cf->cft->do_shutdown(cf, data, &cfdone);
+ if(result) {
+ CURL_TRC_CF(data, cf, "shut down failed with %d", result);
+ return result;
+ }
+ else if(!cfdone) {
+ CURL_TRC_CF(data, cf, "shut down not done yet");
+ return CURLE_OK;
+ }
+ CURL_TRC_CF(data, cf, "shut down successfully");
+ cf = cf->next;
+ }
+ *done = (!result);
return result;
}
void Curl_conn_close(struct Curl_easy *data, int sockindex);
/**
- * Shutdown the connection at `sockindex` blocking with timeout
- * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS
+ * Shutdown the connection at `sockindex` non-blocking, using timeout
+ * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS.
+ * Will return CURLE_OK and *done == FALSE if not finished.
*/
-CURLcode Curl_conn_shutdown_blocking(struct Curl_easy *data, int sockindex);
+CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done);
/**
* Return if data is pending in some connection filter at chain
memset(pt, 0, sizeof(*pt));
}
+bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
+{
+ struct curltime *pt = &data->conn->shutdown.start[sockindex];
+ return (pt->tv_sec > 0) || (pt->tv_usec > 0);
+}
+
/* Copies connection info into the transfer handle to make it available when
the transfer handle is no longer associated with the connection. */
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
void Curl_shutdown_clear(struct Curl_easy *data, int sockindex);
+/* TRUE iff shutdown has been started */
+bool Curl_shutdown_started(struct Curl_easy *data, int sockindex);
+
/*
* Used to extract socket and connectdata struct for the most recent
* transfer on the given Curl_easy.
if(data->state.upload) {
Curl_pgrsSetUploadSize(data, data->state.infilesize);
- Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
}
else
- Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
*done = TRUE;
return CURLE_OK;
}
failf(data, "Failed sending DICT request");
goto error;
}
- Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); /* no upload */
}
else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
failf(data, "Failed sending DICT request");
goto error;
}
- Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
}
else {
goto error;
}
- Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
}
}
};
#endif
-static void close_secondarysocket(struct Curl_easy *data, bool premature)
+static void close_secondarysocket(struct Curl_easy *data)
{
- if(!premature) {
- CURL_TRC_FTP(data, "[%s] shutting down DATA connection", FTP_DSTATE(data));
- Curl_conn_shutdown_blocking(data, SECONDARYSOCKET);
- }
CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_DSTATE(data));
Curl_conn_close(data, SECONDARYSOCKET);
Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET);
Curl_set_in_callback(data, false);
if(error) {
- close_secondarysocket(data, TRUE);
+ close_secondarysocket(data);
return CURLE_ABORTED_BY_CALLBACK;
}
}
/* set the SO_SNDBUF for the secondary socket for those who need it */
Curl_sndbuf_init(conn->sock[SECONDARYSOCKET]);
- Curl_xfer_setup(data, -1, -1, FALSE, SECONDARYSOCKET);
+ Curl_xfer_setup2(data, CURL_XFER_SEND, -1, TRUE);
}
else {
/* FTP download: */
- Curl_xfer_setup(data, SECONDARYSOCKET,
- conn->proto.ftpc.retr_size_saved, FALSE, -1);
+ Curl_xfer_setup2(data, CURL_XFER_RECV,
+ conn->proto.ftpc.retr_size_saved, TRUE);
}
conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
infof(data, "File already completely uploaded");
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
/* Set ->transfer so that we won't get any error in
* ftp_done() because we didn't transfer anything! */
if(ftp->downloadsize == 0) {
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
/* Set ->transfer so that we won't get any error in ftp_done()
}
}
- close_secondarysocket(data, result != CURLE_OK);
+ close_secondarysocket(data);
}
if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
}
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
if(!ftpc->wait_data_conn) {
/* no waiting for the data connection so this is now complete */
CURLcode result = ftp_do_more(data, &completed);
if(result) {
- close_secondarysocket(data, TRUE);
+ close_secondarysocket(data);
return result;
}
}
if(ftp->transfer != PPTRANSFER_BODY)
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
else if(!connected)
/* since we didn't connect now, we want do_more to get called */
conn->bits.do_more = TRUE;
if(result)
return result;
- Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
return CURLE_OK;
}
#endif /* CURL_DISABLE_GOPHER */
out:
if(!result) {
/* setup variables for the upcoming transfer */
- Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
}
return result;
}
if(data->req.bytecount == size)
/* The entire data is already transferred! */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
else {
/* IMAP download */
data->req.maxdownload = size;
/* force a recv/send check of this connection, as the data might've been
read off the socket already */
data->state.select_bits = CURL_CSELECT_IN;
- Curl_xfer_setup(data, FIRSTSOCKET, size, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, size, FALSE);
}
}
else {
Curl_pgrsSetUploadSize(data, data->state.infilesize);
/* IMAP upload */
- Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* End of DO phase */
imap_state(data, IMAP_STOP);
if(imap->transfer != PPTRANSFER_BODY)
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
return CURLE_OK;
}
FREE_ON_WINLDAP(host);
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
connclose(conn, "LDAP connection always disable reuse");
return result;
else {
lr->msgid = msgid;
data->req.p.ldap = lr;
- Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
*done = TRUE;
}
}
if(pop3->transfer == PPTRANSFER_BODY) {
/* POP3 download */
- Curl_xfer_setup(data, FIRSTSOCKET, -1, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
if(pp->overflow) {
/* The recv buffer contains data that is actually body content so send
req->upload_done = FALSE;
req->download_done = FALSE;
req->ignorebody = FALSE;
+ req->shutdown = FALSE;
req->bytecount = 0;
req->writebytecount = 0;
req->header = TRUE; /* assume header */
req->getheader = FALSE;
req->no_body = data->set.opt_no_body;
req->authneg = FALSE;
+ req->shutdown = FALSE;
}
void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
if(!data->req.upload_done && data->req.eos_read &&
Curl_bufq_is_empty(&data->req.sendbuf)) {
+ if(data->req.shutdown) {
+ bool done;
+ result = Curl_xfer_send_shutdown(data, &done);
+ if(result)
+ return result;
+ if(!done)
+ return CURLE_AGAIN;
+ }
return req_set_upload_done(data);
}
return CURLE_OK;
but it is not the final request in the auth
negotiation. */
BIT(sendbuf_init); /* sendbuf is initialized */
+ BIT(shutdown); /* request end will shutdown connection */
};
/**
}
if(rtspreq == RTSPREQ_RECEIVE) {
- Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, TRUE);
goto out;
}
if(result)
goto out;
- Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
/* issue the request */
result = Curl_req_send(data, &req_buffer);
Curl_pgrsSetUploadSize(data, data->state.infilesize);
/* SMTP upload */
- Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* End of DO phase */
smtp_state(data, SMTP_STOP);
if(smtp->transfer != PPTRANSFER_BODY)
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
return CURLE_OK;
}
}
#endif
/* mark this as "no further transfer wanted" */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
return result;
}
*done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
if(*done)
/* Tell curl we're done */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
}
else {
/* no timeouts to handle, check our socket */
*done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE;
if(*done)
/* Tell curl we're done */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
}
/* if rc == 0, then select() timed out */
}
return TRUE;
}
+static CURLcode xfer_recv_shutdown(struct Curl_easy *data, bool *done)
+{
+ int sockindex;
+
+ if(!data || !data->conn)
+ return CURLE_FAILED_INIT;
+ if(data->conn->sockfd == CURL_SOCKET_BAD)
+ return CURLE_FAILED_INIT;
+ sockindex = (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]);
+ return Curl_conn_shutdown(data, sockindex, done);
+}
+
+static bool xfer_recv_shutdown_started(struct Curl_easy *data)
+{
+ int sockindex;
+
+ if(!data || !data->conn)
+ return CURLE_FAILED_INIT;
+ if(data->conn->sockfd == CURL_SOCKET_BAD)
+ return CURLE_FAILED_INIT;
+ sockindex = (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]);
+ return Curl_shutdown_started(data, sockindex);
+}
+
/**
* Receive raw response data for the transfer.
* @param data the transfer
else if(totalleft < (curl_off_t)blen)
blen = (size_t)totalleft;
}
+ else if(xfer_recv_shutdown_started(data)) {
+ /* we already reveived everything. Do not try more. */
+ blen = 0;
+ }
if(!blen) {
- /* want nothing - continue as if read nothing. */
- DEBUGF(infof(data, "readwrite_data: we're done"));
+ /* want nothing more */
*err = CURLE_OK;
- return 0;
+ nread = 0;
+ }
+ else {
+ *err = Curl_xfer_recv(data, buf, blen, &nread);
}
- *err = Curl_xfer_recv(data, buf, blen, &nread);
if(*err)
return -1;
+ if(nread == 0) {
+ if(data->req.shutdown) {
+ bool done;
+ *err = xfer_recv_shutdown(data, &done);
+ if(*err)
+ return -1;
+ if(!done) {
+ *err = CURLE_AGAIN;
+ return -1;
+ }
+ }
+ DEBUGF(infof(data, "readwrite_data: we're done"));
+ }
DEBUGASSERT(nread >= 0);
return nread;
}
}
/*
- * Curl_xfer_setup() is called to setup some basic properties for the
- * upcoming transfer.
+ * xfer_setup() is called to setup basic properties for the transfer.
*/
-void Curl_xfer_setup(
+static void xfer_setup(
struct Curl_easy *data, /* transfer */
int sockindex, /* socket index to read from or -1 */
curl_off_t size, /* -1 if unknown at this point */
bool getheader, /* TRUE if header parsing is wanted */
- int writesockindex /* socket index to write to, it may very well be
+ int writesockindex, /* socket index to write to, it may very well be
the same we read from. -1 disables */
+ bool shutdown /* shutdown connection at transfer end. Only
+ * supported when sending OR receiving. */
)
{
struct SingleRequest *k = &data->req;
DEBUGASSERT(conn != NULL);
DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1));
+ DEBUGASSERT(!shutdown || (sockindex == -1) || (writesockindex == -1));
if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) {
/* when multiplexing, the read/write sockets need to be the same! */
conn->writesockfd = writesockindex == -1 ?
CURL_SOCKET_BAD:conn->sock[writesockindex];
}
- k->getheader = getheader;
+ k->getheader = getheader;
k->size = size;
+ k->shutdown = shutdown;
/* The code sequence below is placed in this function just because all
necessary input is not always known in do_complete() as this function may
}
+void Curl_xfer_setup_nop(struct Curl_easy *data)
+{
+ xfer_setup(data, -1, -1, FALSE, -1, FALSE);
+}
+
+void Curl_xfer_setup1(struct Curl_easy *data,
+ int send_recv,
+ curl_off_t recv_size,
+ bool getheader)
+{
+ int recv_index = (send_recv & CURL_XFER_RECV)? FIRSTSOCKET : -1;
+ int send_index = (send_recv & CURL_XFER_SEND)? FIRSTSOCKET : -1;
+ DEBUGASSERT((recv_index >= 0) || (recv_size == -1));
+ xfer_setup(data, recv_index, recv_size, getheader, send_index, FALSE);
+}
+
+void Curl_xfer_setup2(struct Curl_easy *data,
+ int send_recv,
+ curl_off_t recv_size,
+ bool shutdown)
+{
+ int recv_index = (send_recv & CURL_XFER_RECV)? SECONDARYSOCKET : -1;
+ int send_index = (send_recv & CURL_XFER_SEND)? SECONDARYSOCKET : -1;
+ DEBUGASSERT((recv_index >= 0) || (recv_size == -1));
+ xfer_setup(data, recv_index, recv_size, FALSE, send_index, shutdown);
+}
+
CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
const char *buf, size_t blen,
bool is_eos)
Curl_conn_ev_data_done_send(data);
return CURLE_OK;
}
+
+CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done)
+{
+ int sockindex;
+
+ if(!data || !data->conn)
+ return CURLE_FAILED_INIT;
+ if(data->conn->writesockfd == CURL_SOCKET_BAD)
+ return CURLE_FAILED_INIT;
+ sockindex = (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]);
+ return Curl_conn_shutdown(data, sockindex, done);
+}
CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data,
const char *hd0, size_t hdlen, bool is_eos);
-/* This sets up a forthcoming transfer */
-void Curl_xfer_setup(struct Curl_easy *data,
- int sockindex, /* socket index to read from or -1 */
- curl_off_t size, /* -1 if unknown at this point */
- bool getheader, /* TRUE if header parsing is wanted */
- int writesockindex /* socket index to write to. May be
- the same we read from. -1
- disables */
- );
+#define CURL_XFER_NOP (0)
+#define CURL_XFER_RECV (1<<(0))
+#define CURL_XFER_SEND (1<<(1))
+#define CURL_XFER_SENDRECV (CURL_XFER_RECV|CURL_XFER_SEND)
+
+/**
+ * The transfer is neither receiving nor sending now.
+ */
+void Curl_xfer_setup_nop(struct Curl_easy *data);
+
+/**
+ * The transfer will use socket 1 to send/recv. `recv_size` is
+ * the amount to receive or -1 if unknown. `getheader` indicates
+ * response header processing is expected.
+ */
+void Curl_xfer_setup1(struct Curl_easy *data,
+ int send_recv,
+ curl_off_t recv_size,
+ bool getheader);
+
+/**
+ * The transfer will use socket 2 to send/recv. `recv_size` is
+ * the amount to receive or -1 if unknown. With `shutdown` being
+ * set, the transfer is only allowed to either send OR receive
+ * and the socket 2 connection will be shutdown at the end of
+ * the transfer. An unclean shutdown will fail the transfer.
+ */
+void Curl_xfer_setup2(struct Curl_easy *data,
+ int send_recv,
+ curl_off_t recv_size,
+ bool shutdown);
/**
* Multi has set transfer to DONE. Last chance to trigger
ssize_t *pnrcvd);
CURLcode Curl_xfer_send_close(struct Curl_easy *data);
+CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done);
#endif /* HEADER_CURL_TRANSFER_H */
(void)conn->handler->done(data, result, FALSE);
goto out;
}
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
}
/* since we skip do_init() */
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
/* upload data */
- Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->sockfd = conn->writesockfd;
sshc->sftp_dir = NULL;
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
state(data, SSH_STOP);
break;
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
state(data, SSH_STOP);
break;
}
- Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->writesockfd = conn->sockfd;
}
/* upload data */
- Curl_xfer_setup(data, -1, data->req.size, FALSE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->sockfd = conn->writesockfd;
/* download data */
bytecount = ssh_scp_request_get_size(sshc->scp_session);
data->req.maxdownload = (curl_off_t) bytecount;
- Curl_xfer_setup(data, FIRSTSOCKET, bytecount, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->writesockfd = conn->sockfd;
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
/* upload data */
- Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->sockfd = conn->writesockfd;
Curl_safefree(sshp->readdir_longentry);
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
state(data, SSH_STOP);
break;
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
state(data, SSH_STOP);
break;
}
- Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->writesockfd = conn->sockfd;
/* upload data */
data->req.size = data->state.infilesize;
Curl_pgrsSetUploadSize(data, data->state.infilesize);
- Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->sockfd = conn->writesockfd;
/* download data */
bytecount = (curl_off_t)sb.st_size;
data->req.maxdownload = (curl_off_t)sb.st_size;
- Curl_xfer_setup(data, FIRSTSOCKET, bytecount, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->writesockfd = conn->sockfd;
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
/* upload data */
- Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->sockfd = conn->writesockfd;
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
- Curl_xfer_setup(data, -1, -1, FALSE, -1);
+ Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
state(data, SSH_STOP);
break;
}
- Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1);
+ Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
/* not set by Curl_xfer_setup to preserve keepon bits */
conn->writesockfd = conn->sockfd;
backend->gtls.sent_shutdown = TRUE;
if(send_shutdown) {
int ret = gnutls_bye(backend->gtls.session, GNUTLS_SHUT_RDWR);
+ if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
+ CURL_TRC_CF(data, cf, "SSL shutdown, gnutls_bye EAGAIN");
+ connssl->io_need = gnutls_record_get_direction(backend->gtls.session)?
+ CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
+ result = CURLE_OK;
+ goto out;
+ }
if(ret != GNUTLS_E_SUCCESS) {
CURL_TRC_CF(data, cf, "SSL shutdown, gnutls_bye error: '%s'(%d)",
gnutls_strerror((int)ret), (int)ret);
connssl->peer.hostname, connssl->peer.port);
}
- if(backend->cred && backend->ctxt) {
+ if(!backend->ctxt || connssl->shutdown) {
+ *done = TRUE;
+ goto out;
+ }
+
+ if(backend->cred && backend->ctxt && !backend->sent_shutdown) {
SecBufferDesc BuffDesc;
SecBuffer Buffer;
SECURITY_STATUS sspi_status;
outbuf.pvBuffer, outbuf.cbBuffer,
&result);
s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
- if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
- infof(data, "schannel: failed to send close msg: %s"
- " (bytes written: %zd)", curl_easy_strerror(result), written);
- result = CURLE_SEND_ERROR;
+ if(!result) {
+ if(written < (ssize_t)outbuf.cbBuffer) {
+ /* TODO: handle partial sends */
+ infof(data, "schannel: failed to send close msg: %s"
+ " (bytes written: %zd)", curl_easy_strerror(result), written);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ backend->sent_shutdown = TRUE;
+ *done = TRUE;
+ }
+ else if(result == CURLE_AGAIN) {
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ result = CURLE_OK;
+ goto out;
}
+ else {
+ if(!backend->recv_connection_closed) {
+ infof(data, "schannel: error sending close msg: %d", result);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ /* Looks like server already closed the connection.
+ * An error to send our close notify is not a failure. */
+ *done = TRUE;
+ result = CURLE_OK;
+ }
+ }
+ }
+
+ /* If the connection seems open and we have not seen the close notify
+ * from the server yet, try to receive it. */
+ if(backend->cred && backend->ctxt &&
+ !backend->recv_sspi_close_notify && !backend->recv_connection_closed) {
+ char buffer[1024];
+ ssize_t nread;
+
+ nread = schannel_recv(cf, data, buffer, sizeof(buffer), &result);
+ if(nread > 0) {
+ /* still data coming in? */
+ }
+ else if(nread == 0) {
+ /* We got the close notify alert and are done. */
+ backend->recv_connection_closed = TRUE;
+ *done = TRUE;
+ }
+ else if(nread < 0 && result == CURLE_AGAIN) {
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ }
+ else {
+ CURL_TRC_CF(data, cf, "SSL shutdown, error %d", result);
+ result = CURLE_RECV_ERROR;
}
}
#ifdef HAS_MANUAL_VERIFY_API
bool use_manual_cred_validation; /* true if manual cred validation is used */
#endif
+ BIT(sent_shutdown);
};
/* key to use at `multi->proto_hash` */