multi: make multi_runsingle use sub functions for states
No binary (build failed)
—
(log yok)
| @@ -2000,9 +2000,9 @@ static CURLcode mspeed_check(struct Curl_easy *data) | |
| 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; | |
| @@ -2139,9 +2139,9 @@ static CURLMcode state_performing(struct Curl_easy *data, | |
| 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; | |
| @@ -2276,8 +2276,8 @@ end: | |
| 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; | |
| @@ -2301,9 +2301,9 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data, | |
| 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. */ | |
| @@ -2449,16 +2449,240 @@ static void handle_completed(struct Curl_multi *multi, | |
| 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; | |
| @@ -2517,217 +2741,63 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, | |
| 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: | |