diff -r -C2 --exclude=CVS libwww-current/Library/src/HTChannl.c libwww-5.3.1/Library/src/HTChannl.c *** libwww-current/Library/src/HTChannl.c Fri Jun 23 16:27:34 2000 --- libwww-5.3.1/Library/src/HTChannl.c Sun Sep 3 18:22:25 2000 *************** *** 6,12 **** --- 6,16 ---- ** @(#) $Id: HTChannl.c,v 2.26 2000/06/23 14:27:34 kahan Exp $ ** + ** Authors: + ** PS Peter Stamfest ** ** HISTORY: ** April 96 HFN Written + ** Sept 2000 PS + Added protocol context handling + ** + Fixed some bugs */ *************** *** 52,55 **** --- 56,67 ---- int semaphore; /* On channel use */ HTHost * host; /* Zombie connections */ + + /* PS: a context to be used by non-stateless protocols to keep + important, connection related, information for persistent + connections. The free_func is called during the deletion of the + HTChannel object in order to free the memory used by the + protocolContext */ + void * protocolContext; /* protocol specific data */ + HTChannel_protocolContext_free_func * protocolContext_free_func; }; *************** *** 143,146 **** --- 155,161 ---- ch->fp = NULL; } + + /* Free protocol context, if any */ + HTChannel_deleteProtocolContext(ch); HT_FREE(ch); } *************** *** 177,180 **** --- 192,199 ---- ch->channelIStream.channel = ch; ch->channelOStream.channel = ch; + /* */ + ch->protocolContext = NULL; + ch->protocolContext_free_func = NULL; + /* */ HTList_addObject(list, (void *) ch); *************** *** 237,243 **** ** delete it and free memory. */ ! if (channel->semaphore <= 0 && channels && ( ! channel->sockfd != INVSOC || channel->fp != NULL)) { ! int hash = HASH(channel->sockfd); HTList * list = channels[hash]; if (list) { --- 256,274 ---- ** delete it and free memory. */ ! if (channel->semaphore <= 0 && channels) { ! /* PS (2000-09-03): removed the following code: && ( ! channel->sockfd != INVSOC || channel->fp != NULL)) { ! ! Reason: In HTChannel_setSocket it is totally legal to set ! the socket to -1 (INVSOC). The hash there is forced to be ! 0 for those "sockets" (As now done below as well). ! Because libwww ITSELF uses to call the setSocket method ! with INVSOC (from HTDoClose), it would be impossible to ! delete such Channels using HTChannel_delete. They would ! only be deleted throuch HTChannel_deleteAll (most ! probably from HTLibTerminate or an application specific ! variant of it) which - more often than not - will ! reference objects that got deleted already. */ ! int hash = channel->sockfd < 0 ? 0 : HASH(channel->sockfd); HTList * list = channels[hash]; if (list) { *************** *** 487,490 **** --- 518,551 ---- return &ch->channelOStream; return NULL; + } + + /* + ** Protocol Context pointer to be used as a user defined context + */ + PUBLIC void + HTChannel_setProtocolContext(HTChannel * me, + void * protocolContext, + HTChannel_protocolContext_free_func * free_func) + { + if (me) { + me->protocolContext = protocolContext; + me->protocolContext_free_func = free_func; + } + } + + PUBLIC void * HTChannel_protocolContext (HTChannel * me) + { + return me ? me->protocolContext : NULL; + } + + PUBLIC void HTChannel_deleteProtocolContext(HTChannel * me) + { + if (me) { + if (me->protocolContext_free_func) { + (me->protocolContext_free_func)(me, me->protocolContext); + me->protocolContext = NULL; + me->protocolContext_free_func = NULL; + } + } } diff -r -C2 --exclude=CVS libwww-current/Library/src/HTChannl.html libwww-5.3.1/Library/src/HTChannl.html *** libwww-current/Library/src/HTChannl.html Wed Jul 7 17:43:28 1999 --- libwww-5.3.1/Library/src/HTChannl.html Sun Sep 3 18:00:22 2000 *************** *** 89,92 **** --- 89,114 ----

+ Protocol Defined Contexts +

+

+ This is used for anything the user of a HTChannel object would like to + keep tabs on. A free function can be associated with the function as + well, in order to free up memory assiciated with the context. This + function is called when the channel gets destroyed. It gets passed in + a pointer to the HTChannel and the protocol Context itself. The + protocol context should only be used within libwww code + + NOTE: The HTChannel_setProtocolContext function does NOT call the + free_func for a previously registered protocol context. Use the + HTChannel_deleteProtocolContext function to do this. + +

+ typedef void HTChannel_protocolContext_free_func(HTChannel *, void *);
+ 
+ extern void HTChannel_setProtocolContext (HTChannel * me, void * protocolContext, HTChannel_protocolContext_free_func * free_func);
+ extern void * HTChannel_protocolContext  (HTChannel * me);
+ extern void HTChannel_deleteProtocolContext(HTChannel * me);
+ 
+

The Host Object

diff -r -C2 --exclude=CVS libwww-current/Library/src/HTFTP.c libwww-5.3.1/Library/src/HTFTP.c *** libwww-current/Library/src/HTFTP.c Wed Aug 9 12:43:08 2000 --- libwww-5.3.1/Library/src/HTFTP.c Sun Sep 3 20:35:21 2000 *************** *** 28,31 **** --- 28,32 ---- ** HF Henrik Frystyk ** AL Ari Luotonen + ** PS Peter Stamfest ** ** History: *************** *** 56,59 **** --- 57,76 ---- ** after the CERT advisory warning on wu. Added code ** to do an incremental streaming of FTP data. + ** Aug 2000 PS Peter Stamfest + ** + Fixed a problem with missed return codes. + ** + Persistant connection now work - taking into + ** account user information + ** + Different event priorities fot the data and control + ** connection - this avoids a problem with persistent + ** connections that were closed too early. + ** Sep 2000 PS + Changed the handling of the data connection: The Net + ** object is not "dup"ped anymore, because the HTNet_dup + ** seems dubious (although it probably isn't). The data + ** connection HTNet object is now only created + ** as soon as it is needed + ** + Cleaned up the handling of the ftp_data and ftp_ctrl + ** objects. + ** + Now the listing of the root directory is consistent + ** with how an FTP proxy would do it (squid) ** ** Notes: *************** *** 99,102 **** --- 116,140 ---- } HTFTPState; + /* PS: reversed def/decl of _ftp_data and _ftp_ctrl + structures. ftp_data is now used within ftp_ctrl to get rid of the + strange handling of ftp_data objects - BTW: why are there TWO data + types if they are so tightly coupled that the one doesn't make + sense without the other? - IMHO, they should be collapsed into one + type + + Wherever the ftp_data object is needed, it is referenced through + the ftp_ctrl object from now on. */ + + typedef struct _ftp_data { + char host[30]; /* Host to contact for data */ + char * file; /* File or dir name */ + char * offset; /* offset into file */ + BOOL pasv; /* Active or passive */ + char type; /* 'A', 'I', 'L'(IST), 'N'(LST) */ + int complete; /* Check if both ctrl and data is loaded */ + BOOL stream_error; + } ftp_data; + + typedef struct _ftp_ctrl { HTChunk * cmd; *************** *** 115,129 **** HTNet * dnet; /* Data connection */ BOOL alreadyLoggedIn; } ftp_ctrl; ! typedef struct _ftp_data { ! char host[30]; /* Host to contact for data */ ! char * file; /* File or dir name */ ! char * offset; /* offset into file */ ! BOOL pasv; /* Active or passive */ ! char type; /* 'A', 'I', 'L'(IST), 'N'(LST) */ ! int complete; /* Check if both ctrl and data is loaded */ ! BOOL stream_error; ! } ftp_data; struct _HTStream { --- 153,179 ---- HTNet * dnet; /* Data connection */ BOOL alreadyLoggedIn; + ftp_data * data; } ftp_ctrl; ! ! /* PS: A HTChannel protocol context for FTP - used to select a ! persistent channel compatible with the carried out FTP request. ! Before this was introduced, the code selected channels that might ! have been set up with different authentication information than the ! "current" URL specifies. This is a security problem. ! ! The protocol context could also be used to keep track of the ! current directory, the set transfer mode, or other things, to ! further speed things up and to keep the number of commands sent ! small. */ ! ! typedef struct _ftp_protocol_context { ! char *uid; ! char *passwd; ! } ftp_protocol_context; ! ! /* PS: decl of the _ftp_protocol_context deletion function needed by ! HTChannel_setProtocolContext */ ! PRIVATE void delete_ftp_protocol_context(HTChannel *ch, void *p); struct _HTStream { *************** *** 198,202 **** if (cnet && ctrl) { HTNet * dnet = ctrl->dnet; ! ftp_data * data = (ftp_data *) HTNet_context(dnet); HTChunk_delete(ctrl->cmd); HT_FREE(ctrl->reply); --- 248,252 ---- if (cnet && ctrl) { HTNet * dnet = ctrl->dnet; ! ftp_data * data = (ftp_data *) ctrl->data; /*HTNet_context(dnet);*/ HTChunk_delete(ctrl->cmd); HT_FREE(ctrl->reply); *************** *** 205,209 **** HT_FREE(ctrl->account); HT_FREE(ctrl); ! if (dnet && data) { HT_FREE(data->file); HT_FREE(data); --- 255,261 ---- HT_FREE(ctrl->account); HT_FREE(ctrl); ! /* PS: removed the check for dnet - no longer needed since ! data is only referenced through ctrl */ ! if (/*dnet && */data) { HT_FREE(data->file); HT_FREE(data); *************** *** 211,222 **** /* See if we got a content length */ ! if (status == HT_LOADED) HTAnchor_setLength(HTRequest_anchor(request), HTNet_bytesRead(dnet)); ! ! /* Delete the data net object */ ! HTChannel_deleteInput(HTNet_channel(dnet), status); ! HTChannel_deleteOutput(HTNet_channel(dnet), status); ! HTNet_delete(dnet, HT_IGNORE); ! } HTNet_delete(cnet, status); --- 263,291 ---- /* See if we got a content length */ ! if (dnet && (status == HT_LOADED)) HTAnchor_setLength(HTRequest_anchor(request), HTNet_bytesRead(dnet)); ! if (dnet) { ! HTHost *host = HTNet_host(dnet); ! HTChannel *ch = HTNet_channel(dnet); ! ! /* PS: Handling changed a little bit: calling ! HTHost_clearChannel (could be that this is ! redundant, but logically it is correct: FTP data ! connections are not persistent by definition (RFC). ! ! Delete the data net object */ ! HTChannel_deleteInput(ch, status); ! HTChannel_deleteOutput(ch, status); ! /* Setting the request to NULL keeps HTNet_delete from ! calling the per request after filters - this should ! be done when deleting the control Net object, ! actually wouldn't be done anyway, because we use ! HT_IGNORE */ ! HTNet_setRequest(dnet, NULL); ! HTNet_delete(dnet, HT_IGNORE); ! ! /* Delete the channel object: again: not persistent */ ! HTHost_clearChannel(host, status); ! } } HTNet_delete(cnet, status); *************** *** 277,280 **** --- 346,369 ---- { int status; + /* : check to see if there are multiple lines - there was a bug + that caused status lines on the FTP control connection to be + lost: If multiple such lines were passed into this function in + one call, only the first was processed properly, the rest was + lost. This sometimes blocked the code, because the code waited + for a lost status code */ + const char *c = b; + int found = 0; + + for (c = b ; *c ; c++) { + if (*c == CR || *c == LF) { + found = 1; + continue; + } + if (found) { + l = c - b; + break; + } + } + /* */ HTHost_setConsumed(me->host, l); while (l-- > 0) { *************** *** 410,413 **** --- 499,504 ---- char *path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION); char *ptr = strchr(login, '@'); + char *c; /* used to save a little time */ + if (ptr) { /* Uid and/or passwd specified */ char *passwd; *************** *** 446,451 **** else if (*(ptr-1) == '/') data->type = 'D'; ! } else if (*(path+strlen(path)-1) == '/') { ! *(path+strlen(path)-1) = '\0'; data->type = 'D'; } --- 537,543 ---- else if (*(ptr-1) == '/') data->type = 'D'; ! } else if (*(c = (path+strlen(path)-1)) == '/') { ! /* only strip last / if the path is != "/" !! */ ! if (c != path) *c = '\0'; data->type = 'D'; } *************** *** 481,484 **** --- 573,578 ---- return YES; } + /* PS: needed in AcceptDataSocket */ + PRIVATE int FTPEvent (SOCKET soc, void * pVoid, HTEventType type); /* Open a Data socket for listening on *************** *** 487,495 **** ** Returns YES if OK, else NO */ ! PRIVATE BOOL AcceptDataSocket (HTNet *cnet, HTNet *dnet, ftp_data *data) { if (HTHost_listen(NULL, dnet, "ftp://localhost:0") == HT_ERROR) return NO; /* ** Now we must find out who we are to tell the other guy --- 581,620 ---- ** Returns YES if OK, else NO */ ! PRIVATE BOOL AcceptDataSocket (HTNet *cnet, HTNet *dnet, ftp_ctrl *ctrl) { + /* Create the HTNet object for the data connection */ + /* Get a host object ... Any will do, because HTHost_listen + replaces it anyway */ + HTHost *h = HTNet_host(cnet); + + dnet = ctrl->dnet = HTNet_new(h); + /* HTNet_setProtocol(dnet, HTProtocol_find(HTNet_request(cnet), "ftp")); */ + /* HTNet_setTransport(dnet, HTTransport_find(HTNet_request(cnet), "tcp")); */ + HTNet_setProtocol(dnet, HTNet_protocol(cnet)); + HTNet_setTransport(dnet, HTNet_transport(cnet)); + HTNet_setRequest(dnet, HTNet_request(cnet)); + + /* PS: moved here from HTFTPLoad + slight modifications */ + /* for now, the dnet comes back to the same place + ** - vestigial from when the callback was from the request object + */ + HTNet_setRawBytesCount(dnet, YES); + HTNet_setContext(dnet, ctrl); + HTNet_setEventCallback(dnet, FTPEvent); + HTNet_setEventParam(dnet, ctrl); + /* */ + if (HTHost_listen(NULL, dnet, "ftp://localhost:0") == HT_ERROR) return NO; + /* PS: The data connection gets assigned a lower prio, this is to + avoid that the last read event of the data connection channel + is called after an event of the control connection has done the + final read & close on the data connection. - This lead to the + situation that the data connection event handling closed the + (to be persistent) control connection... */ + HTHost_setEventPriority(HTNet_host(dnet), HT_PRIORITY_MAX - 1); + /* */ + /* ** Now we must find out who we are to tell the other guy *************** *** 514,518 **** u_long addr = local_host.sin_addr.s_addr; u_short port = local_port.sin_port; ! sprintf(data->host, "%d,%d,%d,%d,%d,%d", (int)*((unsigned char *)(&addr)+0), (int)*((unsigned char *)(&addr)+1), --- 639,643 ---- u_long addr = local_host.sin_addr.s_addr; u_short port = local_port.sin_port; ! sprintf(ctrl->data->host, "%d,%d,%d,%d,%d,%d", (int)*((unsigned char *)(&addr)+0), (int)*((unsigned char *)(&addr)+1), *************** *** 813,819 **** if (FTPMode & FTP_DATA_PASV && !data->pasv) ctrl->substate = NEED_PASV; ! else if (AcceptDataSocket(cnet, dnet, data)) ctrl->substate = NEED_PORT; ! else ctrl->substate = SUB_ERROR; break; --- 938,945 ---- if (FTPMode & FTP_DATA_PASV && !data->pasv) ctrl->substate = NEED_PASV; ! else if (AcceptDataSocket(cnet, dnet, ctrl)) { ctrl->substate = NEED_PORT; ! dnet = ctrl->dnet; ! } else ctrl->substate = SUB_ERROR; break; *************** *** 856,861 **** } } else { ! ctrl->substate = AcceptDataSocket(cnet, dnet, data) ? NEED_PORT : SUB_ERROR; } } else --- 982,988 ---- } } else { ! ctrl->substate = AcceptDataSocket(cnet, dnet, ctrl) ? NEED_PORT : SUB_ERROR; + dnet = ctrl->dnet; } } else *************** *** 1102,1105 **** --- 1229,1234 ---- HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_CONNECT\n"); status = HTHost_connect(HTNet_host(dnet), dnet, data->host); + HTHost_setEventPriority(HTNet_host(dnet), HT_PRIORITY_MAX - 1); + if (status == HT_WOULD_BLOCK) return HT_WOULD_BLOCK; *************** *** 1279,1283 **** continue; } else { ! status = HTHost_read(HTNet_host(dnet), dnet); } if (status == HT_WOULD_BLOCK) --- 1408,1412 ---- continue; } else { ! status = HTHost_read(HTNet_host(dnet), dnet); } if (status == HT_WOULD_BLOCK) *************** *** 1364,1372 **** ctrl->state = FTP_BEGIN; ctrl->server = FTP_UNSURE; ! ctrl->dnet = HTNet_dup(cnet); ctrl->cnet = cnet; HTNet_setContext(cnet, ctrl); HTNet_setEventCallback(cnet, FTPEvent); HTNet_setEventParam(cnet, ctrl); HTNet_setRawBytesCount(ctrl->dnet, YES); --- 1493,1503 ---- ctrl->state = FTP_BEGIN; ctrl->server = FTP_UNSURE; ! ctrl->dnet = NULL; /* HTNet_dup(cnet); */ ctrl->cnet = cnet; + ctrl->data = data; HTNet_setContext(cnet, ctrl); HTNet_setEventCallback(cnet, FTPEvent); HTNet_setEventParam(cnet, ctrl); + #if 0 HTNet_setRawBytesCount(ctrl->dnet, YES); *************** *** 1374,1387 **** ** - vestigial from when the callback was from the request object */ HTNet_setContext(ctrl->dnet, data); HTNet_setEventCallback(ctrl->dnet, FTPEvent); HTNet_setEventParam(ctrl->dnet, ctrl); return FTPEvent(soc, ctrl, HTEvent_BEGIN); } PRIVATE int FTPEvent (SOCKET soc, void * pVoid, HTEventType type) { ftp_ctrl * ctrl = (ftp_ctrl *) pVoid; ! ftp_data * data = (ftp_data *) HTNet_context(ctrl->dnet); int status = HT_ERROR; HTNet * cnet = ctrl->cnet; --- 1505,1549 ---- ** - vestigial from when the callback was from the request object */ + HTNet_setRawBytesCount(ctrl->dnet, YES); HTNet_setContext(ctrl->dnet, data); HTNet_setEventCallback(ctrl->dnet, FTPEvent); HTNet_setEventParam(ctrl->dnet, ctrl); + #endif return FTPEvent(soc, ctrl, HTEvent_BEGIN); } + /* The channel compatibility check function to be used with the + *_with_check routines in HTHost */ + PRIVATE int FTPControlCheck(HTHost *host, void *data) + { + ftp_ctrl *ctrl = (ftp_ctrl *) data; + ftp_protocol_context *ctx = 0; + HTChannel *channel; + + if (!host || !data) return YES; + channel = HTHost_channel(host); + + if (channel == NULL) return YES; + ctx = HTChannel_protocolContext(channel); + + HTTRACE(PROT_TRACE, "FTP Check... channel: %s, wanted: %s\n" _ (ctx ? ctx->uid : "null") _ ctrl->uid ); + + if (ctx == NULL) return YES; + + if ((strcmp(ctx->uid, ctrl->uid) == 0) && + (strcmp(ctx->passwd, ctrl->passwd) == 0)) { + HTTRACE(PROT_TRACE, "FTP Check... compatible channel!\n"); + return 1; + } + + HTTRACE(PROT_TRACE, "FTP Check... incompatible channel!\n"); + + return 0; + } + PRIVATE int FTPEvent (SOCKET soc, void * pVoid, HTEventType type) { ftp_ctrl * ctrl = (ftp_ctrl *) pVoid; ! ftp_data * data = ctrl->data; /* (ftp_data *) HTNet_context(ctrl->dnet);*/ int status = HT_ERROR; HTNet * cnet = ctrl->cnet; *************** *** 1413,1417 **** } else { ctrl = (ftp_ctrl *) HTNet_context(cnet); /* Get existing copy */ ! data = (ftp_data *) HTNet_context(ctrl->dnet); } --- 1575,1579 ---- } else { ctrl = (ftp_ctrl *) HTNet_context(cnet); /* Get existing copy */ ! data = (ftp_data *) ctrl->data; /*HTNet_context(ctrl->dnet);*/ } *************** *** 1472,1482 **** case FTP_NEED_CCON: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_NEED_CONN\n"); ! status = HTHost_connect(host, cnet, url); host = HTNet_host(cnet); if (status == HT_OK) { /* ** Check the protocol class to see if we have connected to a ! ** the right class of server, in this case HTTP. */ { --- 1634,1648 ---- case FTP_NEED_CCON: HTTRACE(PROT_TRACE, "FTP Event... now in state FTP_NEED_CONN\n"); ! status = HTHost_connect_with_check(host, cnet, url, ! FTPControlCheck, ctrl); host = HTNet_host(cnet); if (status == HT_OK) { + ftp_protocol_context *ctx; + HTChannel *channel; + int next_state = FTP_NEED_LOGIN; /* ** Check the protocol class to see if we have connected to a ! ** the right class of server, in this case FTP. */ { *************** *** 1491,1494 **** --- 1657,1675 ---- } + /* PS: Record protocol context with the channel */ + channel = HTHost_channel(host); + if (HTChannel_protocolContext(channel) == NULL) { + ctx = (ftp_protocol_context *) HT_MALLOC(sizeof(ftp_protocol_context)); + if (ctx == NULL) { + HT_OUTOFMEM("ftp_protocol_context "); + } + ctx->uid = ctx->passwd = 0; + HTSACopy(&ctx->uid, ctrl->uid); + HTSACopy(&ctx->passwd, ctrl->passwd); + + HTChannel_setProtocolContext(channel, ctx, + delete_ftp_protocol_context); + } + /* Check persistent connection */ if (HTNet_persistent(cnet)) { *************** *** 1497,1500 **** --- 1678,1683 ---- ctrl->server); ctrl->reset = 1; + ctrl->substate = 0; + next_state = FTP_NEED_DCON; } else HTNet_setPersistent(cnet, YES, HT_TP_SINGLE); *************** *** 1531,1535 **** } ! ctrl->state = FTP_NEED_LOGIN; } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; --- 1714,1718 ---- } ! ctrl->state = next_state; } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; *************** *** 1613,1615 **** --- 1796,1810 ---- { return g_FTPControlMode; + } + + /* PS: protocol context deletion function */ + PRIVATE void delete_ftp_protocol_context(HTChannel *ch, void *p) + { + ftp_protocol_context *ctx = (ftp_protocol_context *) p; + if (ctx) { + if (ctx->uid) HT_FREE(ctx->uid); + if (ctx->passwd) HT_FREE(ctx->passwd); + ctx->uid = ctx->passwd = NULL; + HT_FREE(ctx); + } } diff -r -C2 --exclude=CVS libwww-current/Library/src/HTHost.c libwww-5.3.1/Library/src/HTHost.c *** libwww-current/Library/src/HTHost.c Fri Jul 28 15:56:08 2000 --- libwww-5.3.1/Library/src/HTHost.c Sun Sep 3 18:37:25 2000 *************** *** 10,14 **** --- 10,23 ---- ** it is using. We also keep track of persistent connections ** + ** Authors + ** PS Peter Stamfest + ** ** April 96 HFN Written + ** + ** Sep 2000 PS Changed many HTHost functions to allow for a + ** "HTChannel" compatibility check as needed to + ** support non-stateless protocols over + ** persistent connections + ** */ *************** *** 263,278 **** } ! /* ! ** Search the host info cache for a host object or create a new one ! ** and add it. Examples of host names are ** ! ** www.w3.org ! ** www.foo.com:8000 ! ** 18.52.0.18 ** - ** Returns Host object or NULL if error. You may get back an already - ** existing host object - you're not guaranteed a new one each time. */ ! PUBLIC HTHost * HTHost_new (char * host, u_short u_port) { HTList * list = NULL; /* Current list in cache */ --- 272,289 ---- } ! ! /* PS: ** ! ** Extended version of HTHost_new. This version provides for a ! ** "channel compatibility check" and is intended to be used for ! ** Non-stateless persistent connections. A protocol handler can ! ** use this function to test for the compatibility of a channel ! ** (as associated with a HTHost) with the protocol (due to state ! ** of the connection using the channel) ** */ ! PUBLIC HTHost * HTHost_new_with_check (char * host, u_short u_port, ! HTHost_new_with_check_callback *ccbf, ! void * cbdata) { HTList * list = NULL; /* Current list in cache */ *************** *** 307,339 **** delete_object(list, pres); pres = NULL; } - break; } } } ! ! /* If not found then create new Host object, else use existing one */ ! if (pres) { ! if (pres->channel) { ! ! /* ! ** If we have a TTL for this TCP connection then ! ** check that we haven't passed it. ! */ ! if (pres->expires > 0) { ! time_t t = time(NULL); ! if (HTHost_isIdle(pres) && pres->expires < t) { ! HTTRACE(CORE_TRACE, "Host info... Persistent channel %p gotten cold\n" _ ! pres->channel); ! HTHost_clearChannel(pres, HT_OK); ! } else { ! pres->expires = t + HTPassiveTimeout; ! HTTRACE(CORE_TRACE, "Host info... REUSING CHANNEL %p\n" _ pres->channel); ! } ! } ! } else { ! HTTRACE(CORE_TRACE, "Host info... Found Host %p with no active channel\n" _ pres); ! } ! } else { if ((pres = (HTHost *) HT_CALLOC(1, sizeof(HTHost))) == NULL) HT_OUTOFMEM("HTHost_add"); --- 318,373 ---- delete_object(list, pres); pres = NULL; + } else { + if (pres->channel) { + time_t t = time(NULL); + /* + ** If we have a TTL for this TCP connection then + ** check that we haven't passed it. + */ + if (pres->expires > 0) { + if (HTHost_isIdle(pres) && pres->expires < t) { + HTTRACE(CORE_TRACE, "Host info... Persistent channel %p gotten cold\n" _ + pres->channel); + HTHost_clearChannel(pres, HT_OK); + } + } + if (pres && pres->channel) { + /* We might move this check to embrace + this if ... */ + + /* Check if the channel is compatible + with what the caller wants ... */ + int candidate_ok = 1; + + if (ccbf) { + candidate_ok = ccbf(pres, cbdata); + } + + if (candidate_ok) { + pres->expires = t + HTPassiveTimeout; + HTTRACE(CORE_TRACE, + "Host info... REUSING CHANNEL %p\n" + _ pres->channel); + } else { + /* pres is not wanted by the caller ... */ + HTTRACE(CORE_TRACE, "Host info... Candidate channel refused by checking callback %p\n" _ pres->channel); + pres = 0; + } + } + } else { + HTTRACE(CORE_TRACE, "Host info... Found Host %p with no active channel\n" _ pres); + } + } + if (pres) { + break; } } } } ! ! /* If not found then create new Host object, else ! use existing one */ ! ! if (!pres) { if ((pres = (HTHost *) HT_CALLOC(1, sizeof(HTHost))) == NULL) HT_OUTOFMEM("HTHost_add"); *************** *** 357,361 **** } ! PUBLIC HTHost * HTHost_newWParse (HTRequest * request, char * url, u_short u_port) { char * port; --- 391,419 ---- } ! /* ! ** Search the host info cache for a host object or create a new one ! ** and add it. Examples of host names are ! ** ! ** www.w3.org ! ** www.foo.com:8000 ! ** 18.52.0.18 ! ** ! ** Returns Host object or NULL if error. You may get back an already ! ** existing host object - you're not guaranteed a new one each time. ! */ ! PUBLIC HTHost * HTHost_new (char * host, u_short u_port) ! { ! return HTHost_new_with_check(host, u_port, NULL, NULL); ! } ! ! ! ! ! ! PUBLIC HTHost *HTHost_newWParse_with_check(HTRequest * request, ! char * url, ! u_short u_port, ! HTHost_new_with_check_callback *ccbf, ! void * cbdata) { char * port; *************** *** 391,397 **** /* Find information about this host */ ! if ((me = HTHost_new(parsedHost, u_port)) == NULL) { HTTRACE(PROT_TRACE, "HTHost parse Can't get host info\n"); ! me->tcpstate = TCP_ERROR; return NULL; } --- 449,456 ---- /* Find information about this host */ ! if ((me = HTHost_new_with_check(parsedHost, u_port, ccbf, cbdata)) == NULL) { HTTRACE(PROT_TRACE, "HTHost parse Can't get host info\n"); ! /* Removed, because it would cause a SIGSEGV !!! */ ! /* me->tcpstate = TCP_ERROR; */ return NULL; } *************** *** 409,412 **** --- 468,476 ---- } + PUBLIC HTHost * HTHost_newWParse (HTRequest * request, char * url, u_short u_port) + { + return HTHost_newWParse_with_check(request, url, u_port, NULL, NULL); + } + /* ** Search the host info cache for a host object. Examples of host names: *************** *** 1289,1293 **** ** (HostEvent) with the event manager. */ ! PUBLIC int HTHost_connect (HTHost * host, HTNet * net, char * url) { HTRequest * request = HTNet_request(net); --- 1353,1359 ---- ** (HostEvent) with the event manager. */ ! PUBLIC int HTHost_connect_with_check (HTHost * host, HTNet * net, char * url, ! HTHost_new_with_check_callback *ccbf, ! void *cbdata) { HTRequest * request = HTNet_request(net); *************** *** 1295,1299 **** if (!host) { HTProtocol * protocol = HTNet_protocol(net); ! if ((host = HTHost_newWParse(request, url, HTProtocol_id(protocol))) == NULL) return HT_ERROR; --- 1361,1367 ---- if (!host) { HTProtocol * protocol = HTNet_protocol(net); ! if ((host = HTHost_newWParse_with_check(request, url, ! HTProtocol_id(protocol), ! ccbf, cbdata)) == NULL) return HT_ERROR; *************** *** 1345,1348 **** --- 1413,1420 ---- return HT_ERROR; /* @@@ - some more deletion and stuff here? */ } + PUBLIC int HTHost_connect (HTHost * host, HTNet * net, char * url) + { + return HTHost_connect_with_check(host, net, url, NULL, NULL); + } PUBLIC int HTHost_listen (HTHost * host, HTNet * net, char * url) *************** *** 1794,1795 **** --- 1866,1878 ---- } + + /* PS: Change the priority of all the events registered for the host. + Added to fix a race condition / bug with FTP persistent connections */ + + PUBLIC void HTHost_setEventPriority(HTHost *host, int prio) + { + int i; + for (i = 0; i < HTEvent_TYPES; i++) { + HTEvent_setPriority(host->events[i], prio); + } + } diff -r -C2 --exclude=CVS libwww-current/Library/src/HTHost.html libwww-5.3.1/Library/src/HTHost.html *** libwww-current/Library/src/HTHost.html Wed Jul 7 17:43:28 1999 --- libwww-5.3.1/Library/src/HTHost.html Tue Aug 29 23:16:37 2000 *************** *** 54,57 **** --- 54,65 ---- www.foo.com:8000 +

+ Check callback for HTHost_new_with_check_callback +

+

+ Callback function type to be used with HTHost_new_with_check +

+ typedef int HTHost_new_with_check_callback(HTHost *, void *data);
+ 

Add a Host Object *************** *** 59,63 **** --- 67,78 ----
  extern HTHost * HTHost_new (char * host, u_short u_port);
+ extern HTHost * HTHost_new_with_check (char * host, u_short u_port,
+ 				       HTHost_new_with_check_callback *ccbf,
+ 				       void *cbdata);
  extern HTHost * HTHost_newWParse(HTRequest * request, char * url, u_short u_port);
+ extern HTHost * HTHost_newWParse_with_check(HTRequest * request, 
+ 					    char * url, u_short u_port,
+ 					    HTHost_new_with_check_callback *ccbf,
+ 					    void *cbdata);
  extern int HTHost_hash (HTHost * host);
  
*************** *** 216,222 **** As a Net Object doesn't necessarily know whether there is a channel up and running and whether that channel can be reused, ! it must do an explicit connect the the host.
  extern int HTHost_connect (HTHost * host, HTNet * net, char * url);
  
  extern int HTHost_accept  (HTHost * host, HTNet * net, char * url);
--- 231,240 ----
  As a Net Object doesn't necessarily know whether
  there is a channel up and running and whether that channel can be reused,
! it must do an explicit connect to the host.
  
  extern int HTHost_connect (HTHost * host, HTNet * net, char * url);
+ extern int HTHost_connect_with_check (HTHost * host, HTNet * net, char * url,
+ 				      HTHost_new_with_check_callback *ccbf,
+ 				      void *cbdata);
  
  extern int HTHost_accept  (HTHost * host, HTNet * net, char * url);
diff -r -C2 --exclude=CVS libwww-current/Library/src/HTLib.c libwww-5.3.1/Library/src/HTLib.c
*** libwww-current/Library/src/HTLib.c	Wed Jun 23 00:40:48 1999
--- libwww-5.3.1/Library/src/HTLib.c	Sun Sep  3 18:21:07 2000
***************
*** 190,194 ****
      HT_FREE(HTAppVersion);
  
-     HTAtom_deleteAll();					 /* Remove the atoms */
      HTDNS_deleteAll();				/* Remove the DNS host cache */
      HTAnchor_deleteAll(NULL);		/* Delete anchors and drop hyperdocs */
--- 190,193 ----
***************
*** 199,202 ****
--- 198,202 ----
  
      HTUTree_deleteAll();			     /* Delete all URL Trees */
+     HTAtom_deleteAll();					 /* Remove the atoms */
  
      initialized = NO;