Skip to content

Commit

Permalink
Peter Sylvester brought code that now allows a callback to modified t…
Browse files Browse the repository at this point in the history
…he URL

even when the multi interface is used, and then libcurl will simulate a
"follow location" to that new URL. Test 509 was added to test this feature.
  • Loading branch information
bagder committed Jan 12, 2004
1 parent 5173bab commit 3a61c98
Show file tree
Hide file tree
Showing 7 changed files with 623 additions and 177 deletions.
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
Changelog Changelog


Daniel (12 January 2004) Daniel (12 January 2004)
- Peter Sylvester brought code that now allows a callback to modified the URL
even when the multi interface is used, and then libcurl will simulate a
"follow location" to that new URL. Test 509 was added to test this feature.

- Extended the time we retry servers in the test script, and I also made it - Extended the time we retry servers in the test script, and I also made it
retry the https and ftps servers before they are considered bad. I believe retry the https and ftps servers before they are considered bad. I believe
the previous approach could turn problematic on really slow hosts. the previous approach could turn problematic on really slow hosts.
Expand Down
318 changes: 169 additions & 149 deletions lib/multi.c
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -320,45 +320,62 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)


easy=multi->easy.next; easy=multi->easy.next;
while(easy) { while(easy) {

#ifdef CURLDEBUG #ifdef CURLDEBUG
fprintf(stderr, "HANDLE %p: State: %x\n", fprintf(stderr, "HANDLE %p: State: %x\n",
(char *)easy, easy->state); (char *)easy, easy->state);
#endif #endif
do {
if (CURLM_STATE_WAITCONNECT <= easy->state &&
easy->state <= CURLM_STATE_DO &&
easy->easy_handle->change.url_changed) {
char *gotourl;
Curl_posttransfer(easy->easy_handle);


switch(easy->state) { gotourl = strdup(easy->easy_handle->change.url);
case CURLM_STATE_INIT: easy->easy_handle->change.url_changed = FALSE;
/* init this transfer. */ easy->result = Curl_follow(easy->easy_handle, gotourl);
easy->result=Curl_pretransfer(easy->easy_handle); if(CURLE_OK == easy->result)

easy->state = CURLM_STATE_CONNECT;
if(CURLE_OK == easy->result) { else
/* after init, go CONNECT */ free(gotourl);
easy->state = CURLM_STATE_CONNECT;
result = CURLM_CALL_MULTI_PERFORM;

easy->easy_handle->state.used_interface = Curl_if_multi;
} }
break;
easy->easy_handle->change.url_changed = FALSE;


case CURLM_STATE_CONNECT: switch(easy->state) {
/* Connect. We get a connection identifier filled in. */ case CURLM_STATE_INIT:
Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); /* init this transfer. */
easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, &async); easy->result=Curl_pretransfer(easy->easy_handle);


if(CURLE_OK == easy->result) { if(CURLE_OK == easy->result) {
if(async) /* after init, go CONNECT */
/* We're now waiting for an asynchronous name lookup */ easy->state = CURLM_STATE_CONNECT;
easy->state = CURLM_STATE_WAITRESOLVE; result = CURLM_CALL_MULTI_PERFORM;
else {
/* after the connect has been sent off, go WAITCONNECT */ easy->easy_handle->state.used_interface = Curl_if_multi;
easy->state = CURLM_STATE_WAITCONNECT;
result = CURLM_CALL_MULTI_PERFORM;
} }
} break;
break;


case CURLM_STATE_WAITRESOLVE: case CURLM_STATE_CONNECT:
/* awaiting an asynch name resolve to complete */ /* Connect. We get a connection identifier filled in. */
Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE);
easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn,
&async);

if(CURLE_OK == easy->result) {
if(async)
/* We're now waiting for an asynchronous name lookup */
easy->state = CURLM_STATE_WAITRESOLVE;
else {
/* after the connect has been sent off, go WAITCONNECT */
easy->state = CURLM_STATE_WAITCONNECT;
result = CURLM_CALL_MULTI_PERFORM;
}
}
break;

case CURLM_STATE_WAITRESOLVE:
/* awaiting an asynch name resolve to complete */
{ {
struct Curl_dns_entry *dns; struct Curl_dns_entry *dns;


Expand Down Expand Up @@ -387,146 +404,149 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
} }
break; break;


case CURLM_STATE_WAITCONNECT: case CURLM_STATE_WAITCONNECT:
/* awaiting a completion of an asynch connect */ /* awaiting a completion of an asynch connect */
easy->result = Curl_is_connected(easy->easy_conn, easy->result = Curl_is_connected(easy->easy_conn,
easy->easy_conn->sock[FIRSTSOCKET], easy->easy_conn->sock[FIRSTSOCKET],
&connected); &connected);
if(connected) if(connected)
easy->result = Curl_protocol_connect(easy->easy_conn, NULL); easy->result = Curl_protocol_connect(easy->easy_conn, NULL);

if(CURLE_OK != easy->result) {
/* failure detected */
Curl_disconnect(easy->easy_conn); /* close the connection */
easy->easy_conn = NULL; /* no more connection */
break;
}


if(connected) { if(CURLE_OK != easy->result) {
/* after the connect has completed, go DO */ /* failure detected */
easy->state = CURLM_STATE_DO; Curl_disconnect(easy->easy_conn); /* close the connection */
result = CURLM_CALL_MULTI_PERFORM; easy->easy_conn = NULL; /* no more connection */
} break;
break; }


case CURLM_STATE_DO: if(connected) {
/* Do the fetch or put request */ /* after the connect has completed, go DO */
easy->result = Curl_do(&easy->easy_conn); easy->state = CURLM_STATE_DO;
if(CURLE_OK == easy->result) { result = CURLM_CALL_MULTI_PERFORM;

/* after do, go PERFORM... or DO_MORE */
if(easy->easy_conn->bits.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 { break;
/* we're done with the DO, now PERFORM */
easy->result = Curl_readwrite_init(easy->easy_conn); case CURLM_STATE_DO:
if(CURLE_OK == easy->result) { /* Do the fetch or put request */
easy->state = CURLM_STATE_PERFORM; easy->result = Curl_do(&easy->easy_conn);
result = CURLM_CALL_MULTI_PERFORM; if(CURLE_OK == easy->result) {

/* after do, go PERFORM... or DO_MORE */
if(easy->easy_conn->bits.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;
break;


case CURLM_STATE_DO_MORE: 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->sock[SECONDARYSOCKET],
&connected);
if(connected) {
/* /*
* When we are connected, DO MORE and then go PERFORM * First, check if we really are ready to do more.
*/ */
easy->result = Curl_do_more(easy->easy_conn); easy->result =

Curl_is_connected(easy->easy_conn,
if(CURLE_OK == easy->result) easy->easy_conn->sock[SECONDARYSOCKET],
easy->result = Curl_readwrite_init(easy->easy_conn); &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) { if(CURLE_OK == easy->result) {
easy->state = CURLM_STATE_PERFORM; easy->state = CURLM_STATE_PERFORM;
result = CURLM_CALL_MULTI_PERFORM; result = CURLM_CALL_MULTI_PERFORM;
}
} }
} break;
break;


case CURLM_STATE_PERFORM: case CURLM_STATE_PERFORM:
/* read/write data if it is ready to do so */ /* read/write data if it is ready to do so */
easy->result = Curl_readwrite(easy->easy_conn, &done); easy->result = Curl_readwrite(easy->easy_conn, &done);


if(easy->result) { if(easy->result) {
/* The transfer phase returned error, we mark the connection to get /* The transfer phase returned error, we mark the connection to get
* closed to prevent being re-used. This is becasue we can't * closed to prevent being re-used. This is becasue we can't
* possibly know if the connection is in a good shape or not now. */ * possibly know if the connection is in a good shape or not now. */
easy->easy_conn->bits.close = TRUE; easy->easy_conn->bits.close = TRUE;


if(-1 !=easy->easy_conn->sock[SECONDARYSOCKET]) { if(-1 !=easy->easy_conn->sock[SECONDARYSOCKET]) {
/* if we failed anywhere, we must clean up the secondary socket if /* if we failed anywhere, we must clean up the secondary socket if
it was used */ it was used */
sclose(easy->easy_conn->sock[SECONDARYSOCKET]); sclose(easy->easy_conn->sock[SECONDARYSOCKET]);
easy->easy_conn->sock[SECONDARYSOCKET]=-1; easy->easy_conn->sock[SECONDARYSOCKET]=-1;
}
Curl_posttransfer(easy->easy_handle);
Curl_done(easy->easy_conn);
} }
Curl_posttransfer(easy->easy_handle);
Curl_done(easy->easy_conn);
}


/* after the transfer is done, go DONE */ /* after the transfer is done, go DONE */
else if(TRUE == done) { else if(TRUE == done) {


/* call this even if the readwrite function returned error */ /* call this even if the readwrite function returned error */
Curl_posttransfer(easy->easy_handle); Curl_posttransfer(easy->easy_handle);


/* When we follow redirects, must to go back to the CONNECT state */ /* When we follow redirects, must to go back to the CONNECT state */
if(easy->easy_conn->newurl) { if(easy->easy_conn->newurl) {
char *newurl = easy->easy_conn->newurl; char *newurl = easy->easy_conn->newurl;
easy->easy_conn->newurl = NULL; easy->easy_conn->newurl = NULL;
easy->result = Curl_follow(easy->easy_handle, newurl); easy->result = Curl_follow(easy->easy_handle, newurl);
if(CURLE_OK == easy->result) { if(CURLE_OK == easy->result) {
easy->state = CURLM_STATE_CONNECT; easy->state = CURLM_STATE_CONNECT;
result = CURLM_CALL_MULTI_PERFORM; result = CURLM_CALL_MULTI_PERFORM;
}
}
else {
easy->state = CURLM_STATE_DONE;
result = CURLM_CALL_MULTI_PERFORM;
} }
} }
else { break;
easy->state = CURLM_STATE_DONE; case CURLM_STATE_DONE:
result = CURLM_CALL_MULTI_PERFORM; /* post-transfer command */
} easy->result = Curl_done(easy->easy_conn);
}
break;
case CURLM_STATE_DONE:
/* post-transfer command */
easy->result = Curl_done(easy->easy_conn);


/* after we have DONE what we're supposed to do, go COMPLETED, and /* after we have DONE what we're supposed to do, go COMPLETED, and
it doesn't matter what the Curl_done() returned! */ it doesn't matter what the Curl_done() returned! */
easy->state = CURLM_STATE_COMPLETED; easy->state = CURLM_STATE_COMPLETED;
break; break;


case CURLM_STATE_COMPLETED: case CURLM_STATE_COMPLETED:
/* this is a completed transfer, it is likely to still be connected */ /* this is a completed transfer, it is likely to still be connected */


/* This node should be delinked from the list now and we should post /* This node should be delinked from the list now and we should post
an information message that we are complete. */ an information message that we are complete. */
break; break;
default: default:
return CURLM_INTERNAL_ERROR; return CURLM_INTERNAL_ERROR;
} }


if(CURLM_STATE_COMPLETED != easy->state) { if(CURLM_STATE_COMPLETED != easy->state) {
if(CURLE_OK != easy->result) { if(CURLE_OK != easy->result) {
/* /*
* If an error was returned, and we aren't in completed state now, * If an error was returned, and we aren't in completed state now,
* then we go to completed and consider this transfer aborted. */ * then we go to completed and consider this transfer aborted. */
easy->state = CURLM_STATE_COMPLETED; easy->state = CURLM_STATE_COMPLETED;
}
else
/* this one still lives! */
(*running_handles)++;
} }
else
/* this one still lives! */ } while (easy->easy_handle->change.url_changed);
(*running_handles)++;
}


if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) {
/* clear out the usage of the shared DNS cache */ /* clear out the usage of the shared DNS cache */
Expand Down
2 changes: 1 addition & 1 deletion tests/data/Makefile.am
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ test62 test63 test64 test65 test66 test144 test145 test67 test68 test41 \
test40 test42 test69 test70 test71 test72 test73 test146 test505 \ test40 test42 test69 test70 test71 test72 test73 test146 test505 \
test74 test75 test76 test77 test78 test147 test148 test506 test79 test80 \ test74 test75 test76 test77 test78 test147 test148 test506 test79 test80 \
test81 test82 test83 test84 test85 test86 test87 test507 test149 test88 \ test81 test82 test83 test84 test85 test86 test87 test507 test149 test88 \
test89 test90 test508 test91 test92 test203 test93 test94 test95 test89 test90 test508 test91 test92 test203 test93 test94 test95 test509
Loading

0 comments on commit 3a61c98

Please sign in to comment.