return CURLE_OK;
}
-static CURLMcode state_performing(struct Curl_easy *data,
- bool *stream_errorp,
- CURLcode *resultp)
+static CURLMcode multistate_performing(struct Curl_easy *data,
+ bool *stream_errorp,
+ CURLcode *resultp)
{
char *newurl = NULL;
bool retry = FALSE;
return mresult;
}
-static CURLMcode state_do(struct Curl_easy *data,
- bool *stream_errorp,
- CURLcode *resultp)
+static CURLMcode multistate_do(struct Curl_easy *data,
+ bool *stream_errorp,
+ CURLcode *resultp)
{
CURLMcode mresult = CURLM_OK;
CURLcode result = CURLE_OK;
return mresult;
}
-static CURLMcode state_ratelimiting(struct Curl_easy *data,
- CURLcode *resultp)
+static CURLMcode multistate_ratelimiting(struct Curl_easy *data,
+ CURLcode *resultp)
{
CURLcode result = CURLE_OK;
CURLMcode mresult = CURLM_OK;
return mresult;
}
-static CURLMcode state_connect(struct Curl_multi *multi,
- struct Curl_easy *data,
- CURLcode *resultp)
+static CURLMcode multistate_connect(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ CURLcode *resultp)
{
/* Connect. We want to get a connection identifier filled in. This state can
be entered from SETUP and from PENDING. */
multi_assess_wakeup(multi);
}
-static CURLMcode multi_runsingle(struct Curl_multi *multi,
- struct Curl_easy *data,
- struct Curl_sigpipe_ctx *sigpipe_ctx)
+static CURLMcode multistate_init(struct Curl_easy *data, CURLcode *result)
+{
+ *result = Curl_pretransfer(data);
+ if(*result)
+ return CURLM_OK;
+
+ /* after init, go SETUP */
+ multistate(data, MSTATE_SETUP);
+ Curl_pgrsTime(data, TIMER_STARTOP);
+ return CURLM_CALL_MULTI_PERFORM;
+}
+
+static CURLMcode multistate_setup(struct Curl_easy *data)
+{
+ Curl_pgrsTime(data, TIMER_STARTSINGLE);
+ if(data->set.timeout)
+ Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
+ if(data->set.connecttimeout)
+ /* Since a connection might go to pending and back to CONNECT several
+ times before it actually takes off, we need to set the timeout once
+ in SETUP before we enter CONNECT the first time. */
+ Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
+
+ multistate(data, MSTATE_CONNECT);
+ return CURLM_CALL_MULTI_PERFORM;
+}
+
+static CURLMcode multistate_connecting(struct Curl_easy *data,
+ bool *stream_error,
+ CURLcode *result)
{
bool connected;
+
+ if(!data->conn) {
+ DEBUGASSERT(0);
+ *result = CURLE_FAILED_INIT;
+ return CURLM_OK;
+ }
+ if(!Curl_xfer_recv_is_paused(data)) {
+ *result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected);
+ if(connected && !(*result)) {
+ if(!data->conn->bits.reuse &&
+ Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) {
+ /* new connection, can multiplex, wake pending handles */
+ process_pending_handles(data->multi);
+ }
+ multistate(data, MSTATE_PROTOCONNECT);
+ return CURLM_CALL_MULTI_PERFORM;
+ }
+ else if(*result) {
+ /* failure detected */
+ CURL_TRC_M(data, "connect failed -> %d", *result);
+ multi_posttransfer(data);
+ multi_done(data, *result, TRUE);
+ *stream_error = TRUE;
+ return CURLM_OK;
+ }
+ }
+ return CURLM_OK;
+}
+
+static CURLMcode multistate_protoconnect(struct Curl_easy *data,
+ bool *stream_error,
+ CURLcode *result)
+{
+ bool protocol_connected = FALSE;
+
+ if(!(*result) && data->conn->bits.reuse) {
+ /* ftp seems to hang when protoconnect on reused connection since we
+ * handle PROTOCONNECT in general inside the filters, it seems wrong to
+ * restart this on a reused connection.
+ */
+ multistate(data, MSTATE_DO);
+ return CURLM_CALL_MULTI_PERFORM;
+ }
+ if(!(*result))
+ *result = protocol_connect(data, &protocol_connected);
+ if(!(*result) && !protocol_connected) {
+ /* switch to waiting state */
+ multistate(data, MSTATE_PROTOCONNECTING);
+ return CURLM_CALL_MULTI_PERFORM;
+ }
+ else if(!(*result)) {
+ /* protocol connect has completed, go WAITDO or DO */
+ multistate(data, MSTATE_DO);
+ return CURLM_CALL_MULTI_PERFORM;
+ }
+
+ /* failure detected */
+ multi_posttransfer(data);
+ multi_done(data, *result, TRUE);
+ *stream_error = TRUE;
+ return CURLM_OK;
+}
+
+static CURLMcode multistate_protoconnecting(struct Curl_easy *data,
+ bool *stream_error,
+ CURLcode *result)
+{
bool protocol_connected = FALSE;
+
+ /* protocol-specific connect phase */
+ *result = protocol_connecting(data, &protocol_connected);
+ if(!(*result) && protocol_connected) {
+ /* after the connect has completed, go WAITDO or DO */
+ multistate(data, MSTATE_DO);
+ return CURLM_CALL_MULTI_PERFORM;
+ }
+ else if(*result) {
+ /* failure detected */
+ multi_posttransfer(data);
+ multi_done(data, *result, TRUE);
+ *stream_error = TRUE;
+ }
+ return CURLM_OK;
+}
+
+static CURLMcode multistate_doing(struct Curl_easy *data,
+ bool *stream_error,
+ CURLcode *result)
+{
bool dophase_done = FALSE;
+
+ /* we continue DOING until the DO phase is complete */
+ DEBUGASSERT(data->conn);
+ *result = protocol_doing(data, &dophase_done);
+ if(!(*result)) {
+ if(dophase_done) {
+ /* after DO, go DO_DONE or DO_MORE */
+ multistate(data, data->conn->bits.do_more ?
+ MSTATE_DOING_MORE : MSTATE_DID);
+ return CURLM_CALL_MULTI_PERFORM;
+ } /* dophase_done */
+ }
+ else {
+ /* failure detected */
+ multi_posttransfer(data);
+ multi_done(data, *result, FALSE);
+ *stream_error = TRUE;
+ }
+ return CURLM_OK;
+}
+
+static CURLMcode multistate_doing_more(struct Curl_easy *data,
+ bool *stream_error,
+ CURLcode *result)
+{
+ int control;
+
+ /*
+ * When we are connected, DOING MORE and then go DID
+ */
+ DEBUGASSERT(data->conn);
+ *result = multi_do_more(data, &control);
+
+ if(!(*result)) {
+ if(control) {
+ /* if positive, advance to DO_DONE
+ if negative, go back to DOING */
+ multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING);
+ return CURLM_CALL_MULTI_PERFORM;
+ }
+ /* else
+ stay in DO_MORE */
+ }
+ else {
+ /* failure detected */
+ multi_posttransfer(data);
+ multi_done(data, *result, FALSE);
+ *stream_error = TRUE;
+ }
+ return CURLM_OK;
+}
+
+static CURLMcode multistate_did(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ DEBUGASSERT(data->conn);
+ if(data->conn->bits.multiplex)
+ /* Check if we can move pending requests to send pipe */
+ process_pending_handles(multi); /* multiplexed */
+
+ /* Only perform the transfer if there is a good socket to work with.
+ Having both BAD is a signal to skip immediately to DONE */
+ if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) ||
+ CONN_SOCK_IDX_VALID(data->conn->send_idx))
+ multistate(data, MSTATE_PERFORMING);
+ else {
+#ifndef CURL_DISABLE_FTP
+ if(data->state.wildcardmatch &&
+ ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) {
+ data->wildcard->state = CURLWC_DONE;
+ }
+#endif
+ multistate(data, MSTATE_DONE);
+ }
+ return CURLM_CALL_MULTI_PERFORM;
+}
+
+static CURLMcode multistate_done(struct Curl_easy *data, CURLcode *result)
+{
+ if(data->conn) {
+ CURLcode res;
+
+ /* post-transfer command */
+ res = multi_done(data, *result, FALSE);
+
+ /* allow a previously set error code take precedence */
+ if(!(*result))
+ *result = res;
+ }
+
+#ifndef CURL_DISABLE_FTP
+ if(data->state.wildcardmatch) {
+ if(data->wildcard->state != CURLWC_DONE) {
+ /* if a wildcard is set and we are not ending -> lets start again
+ with MSTATE_INIT */
+ multistate(data, MSTATE_INIT);
+ return CURLM_CALL_MULTI_PERFORM;
+ }
+ }
+#endif
+ /* after we have DONE what we are supposed to do, go COMPLETED, and
+ it does not matter what the multi_done() returned! */
+ multistate(data, MSTATE_COMPLETED);
+ return CURLM_CALL_MULTI_PERFORM;
+}
+
+static CURLMcode multi_runsingle(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct Curl_sigpipe_ctx *sigpipe_ctx)
+{
CURLMcode mresult;
CURLcode result = CURLE_OK;
- int control;
if(!GOOD_EASY_HANDLE(data))
return CURLM_BAD_EASY_HANDLE;
case MSTATE_INIT:
/* Transitional state. init this transfer. A handle never comes back to
this state. */
- result = Curl_pretransfer(data);
- if(result)
- break;
-
- /* after init, go SETUP */
- multistate(data, MSTATE_SETUP);
- Curl_pgrsTime(data, TIMER_STARTOP);
- FALLTHROUGH();
+ mresult = multistate_init(data, &result);
+ break;
case MSTATE_SETUP:
/* Transitional state. Setup things for a new transfer. The handle
can come back to this state on a redirect. */
- Curl_pgrsTime(data, TIMER_STARTSINGLE);
- if(data->set.timeout)
- Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
- if(data->set.connecttimeout)
- /* Since a connection might go to pending and back to CONNECT several
- times before it actually takes off, we need to set the timeout once
- in SETUP before we enter CONNECT the first time. */
- Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
-
- multistate(data, MSTATE_CONNECT);
- FALLTHROUGH();
+ mresult = multistate_setup(data);
+ break;
case MSTATE_CONNECT:
- mresult = state_connect(multi, data, &result);
+ mresult = multistate_connect(multi, data, &result);
break;
case MSTATE_CONNECTING:
/* awaiting a completion of an asynch TCP connect */
- if(!data->conn) {
- DEBUGASSERT(0);
- result = CURLE_FAILED_INIT;
- break;
- }
- else if(!Curl_xfer_recv_is_paused(data)) {
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected);
- if(connected && !result) {
- if(!data->conn->bits.reuse &&
- Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) {
- /* new connection, can multiplex, wake pending handles */
- process_pending_handles(data->multi);
- }
- mresult = CURLM_CALL_MULTI_PERFORM;
- multistate(data, MSTATE_PROTOCONNECT);
- }
- else if(result) {
- /* failure detected */
- CURL_TRC_M(data, "connect failed -> %d", result);
- multi_posttransfer(data);
- multi_done(data, result, TRUE);
- stream_error = TRUE;
- break;
- }
- }
+ mresult = multistate_connecting(data, &stream_error, &result);
break;
case MSTATE_PROTOCONNECT:
- if(!result && data->conn->bits.reuse) {
- /* ftp seems to hang when protoconnect on reused connection since we
- * handle PROTOCONNECT in general inside the filers, it seems wrong to
- * restart this on a reused connection.
- */
- multistate(data, MSTATE_DO);
- mresult = CURLM_CALL_MULTI_PERFORM;
- break;
- }
- if(!result)
- result = protocol_connect(data, &protocol_connected);
- if(!result && !protocol_connected) {
- /* switch to waiting state */
- multistate(data, MSTATE_PROTOCONNECTING);
- mresult = CURLM_CALL_MULTI_PERFORM;
- }
- else if(!result) {
- /* protocol connect has completed, go WAITDO or DO */
- multistate(data, MSTATE_DO);
- mresult = CURLM_CALL_MULTI_PERFORM;
- }
- else {
- /* failure detected */
- multi_posttransfer(data);
- multi_done(data, result, TRUE);
- stream_error = TRUE;
- }
+ mresult = multistate_protoconnect(data, &stream_error, &result);
break;
case MSTATE_PROTOCONNECTING:
/* protocol-specific connect phase */
- result = protocol_connecting(data, &protocol_connected);
- if(!result && protocol_connected) {
- /* after the connect has completed, go WAITDO or DO */
- multistate(data, MSTATE_DO);
- mresult = CURLM_CALL_MULTI_PERFORM;
- }
- else if(result) {
- /* failure detected */
- multi_posttransfer(data);
- multi_done(data, result, TRUE);
- stream_error = TRUE;
- }
+ mresult = multistate_protoconnecting(data, &stream_error, &result);
break;
case MSTATE_DO:
- mresult = state_do(data, &stream_error, &result);
+ mresult = multistate_do(data, &stream_error, &result);
break;
case MSTATE_DOING:
/* we continue DOING until the DO phase is complete */
- DEBUGASSERT(data->conn);
- result = protocol_doing(data, &dophase_done);
- if(!result) {
- if(dophase_done) {
- /* after DO, go DO_DONE or DO_MORE */
- multistate(data, data->conn->bits.do_more ?
- MSTATE_DOING_MORE : MSTATE_DID);
- mresult = CURLM_CALL_MULTI_PERFORM;
- } /* dophase_done */
- }
- else {
- /* failure detected */
- multi_posttransfer(data);
- multi_done(data, result, FALSE);
- stream_error = TRUE;
- }
+ mresult = multistate_doing(data, &stream_error, &result);
break;
case MSTATE_DOING_MORE:
/*
* When we are connected, DOING MORE and then go DID
*/
- DEBUGASSERT(data->conn);
- result = multi_do_more(data, &control);
-
- if(!result) {
- if(control) {
- /* if positive, advance to DO_DONE
- if negative, go back to DOING */
- multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING);
- mresult = CURLM_CALL_MULTI_PERFORM;
- }
- /* else
- stay in DO_MORE */
- }
- else {
- /* failure detected */
- multi_posttransfer(data);
- multi_done(data, result, FALSE);
- stream_error = TRUE;
- }
+ mresult = multistate_doing_more(data, &stream_error, &result);
break;
case MSTATE_DID:
- DEBUGASSERT(data->conn);
- if(data->conn->bits.multiplex)
- /* Check if we can move pending requests to send pipe */
- process_pending_handles(multi); /* multiplexed */
-
- /* Only perform the transfer if there is a good socket to work with.
- Having both BAD is a signal to skip immediately to DONE */
- if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) ||
- CONN_SOCK_IDX_VALID(data->conn->send_idx))
- multistate(data, MSTATE_PERFORMING);
- else {
-#ifndef CURL_DISABLE_FTP
- if(data->state.wildcardmatch &&
- ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) {
- data->wildcard->state = CURLWC_DONE;
- }
-#endif
- multistate(data, MSTATE_DONE);
- }
- mresult = CURLM_CALL_MULTI_PERFORM;
+ mresult = multistate_did(multi, data);
break;
case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */
- mresult = state_ratelimiting(data, &result);
+ mresult = multistate_ratelimiting(data, &result);
break;
case MSTATE_PERFORMING:
- mresult = state_performing(data, &stream_error, &result);
+ mresult = multistate_performing(data, &stream_error, &result);
break;
case MSTATE_DONE:
- /* this state is highly transient, so run another loop after this */
- mresult = CURLM_CALL_MULTI_PERFORM;
-
- if(data->conn) {
- CURLcode res;
-
- /* post-transfer command */
- res = multi_done(data, result, FALSE);
-
- /* allow a previously set error code take precedence */
- if(!result)
- result = res;
- }
-
-#ifndef CURL_DISABLE_FTP
- if(data->state.wildcardmatch) {
- if(data->wildcard->state != CURLWC_DONE) {
- /* if a wildcard is set and we are not ending -> lets start again
- with MSTATE_INIT */
- multistate(data, MSTATE_INIT);
- break;
- }
- }
-#endif
- /* after we have DONE what we are supposed to do, go COMPLETED, and
- it does not matter what the multi_done() returned! */
- multistate(data, MSTATE_COMPLETED);
+ mresult = multistate_done(data, &result);
break;
case MSTATE_COMPLETED: