Annotation of libwww/Library/src/HTNet.c, revision 2.32
2.23 frystyk 1: /* HTNet.c
2: ** ASYNCRONOUS SOCKET MANAGEMENT
2.1 frystyk 3: **
2.10 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.4 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
7: ** This is the implementation of the internal library multithreading
2.1 frystyk 8: ** functions. This includes an interrupt handler and a event loop.
9: **
10: ** History:
2.14 frystyk 11: ** 12 June 94 Written by Henrik Frystyk, frystyk@w3.org
2.17 frystyk 12: ** 31 May 95 Charlie Brooks cbrooks@osf.org
13: **
2.1 frystyk 14: */
15:
2.9 frystyk 16: /* Implemention dependent include files */
17: #include "tcp.h"
18:
2.1 frystyk 19: /* Library include files */
20: #include "HTUtils.h"
2.16 frystyk 21: #include "HTProt.h"
2.1 frystyk 22: #include "HTError.h"
2.25 frystyk 23: #include "HTAlert.h"
2.23 frystyk 24: #include "HTReqMan.h"
2.17 frystyk 25: #include "HTEvntrg.h"
2.23 frystyk 26: #include "HTStream.h"
2.24 frystyk 27: #include "HTNetMan.h" /* Implemented here */
2.1 frystyk 28:
2.9 frystyk 29: #ifdef WIN32
30: #include <io.h>
31: #endif
32:
2.23 frystyk 33: #ifndef HT_MAX_SOCKETS
34: #define HT_MAX_SOCKETS 6
35: #endif
36:
37: typedef struct _CBFInfo {
2.30 frystyk 38: HTNetCallback * cbf;
2.23 frystyk 39: int status; /* Status associated with this callback */
40: } CBFInfo;
41:
42: struct _HTStream {
43: CONST HTStreamClass * isa;
44: /* ... */
45: };
46:
47: PRIVATE int HTMaxActive = HT_MAX_SOCKETS; /* Max active requests */
2.24 frystyk 48: PRIVATE HTList *HTNetCBF = NULL; /* List of call back functions */
49:
2.23 frystyk 50: PRIVATE HTList *HTNetActive = NULL; /* List of active requests */
51: PRIVATE HTList *HTNetPending = NULL; /* List of pending requests */
2.24 frystyk 52: PRIVATE HTList *HTNetPersistent = NULL; /* List of persistent connections */
2.1 frystyk 53:
54: /* ------------------------------------------------------------------------- */
55:
2.23 frystyk 56: /*
57: ** Set the max number of simultanous sockets. Default is HT_MAX_SOCKETS
2.1 frystyk 58: */
2.23 frystyk 59: PUBLIC BOOL HTNet_setMaxSocket (int newmax)
60: {
61: if (newmax > 0) {
62: HTMaxActive = newmax;
63: return YES;
64: }
65: return NO;
66: }
67:
68: PUBLIC int HTNet_maxSocket (void)
2.1 frystyk 69: {
2.23 frystyk 70: return HTMaxActive;
71: }
72:
73: /* ------------------------------------------------------------------------- */
74: /* Termination Call Back Functions */
75: /* ------------------------------------------------------------------------- */
76:
77: /* HTNet_Register
78: ** --------------
79: ** Register a call back function that is to be called on every
80: ** termination of a request. Several call back functions can be registered
81: ** in which case all of them are called in the order of which they
82: ** were registered.
83: **
84: ** The status signifies which call back function to call depending of the
85: ** result of the request. This can be
86: **
87: ** HT_ERROR An error occured
88: ** HT_LOADED The document was loaded
89: ** HT_NO_DATA OK, but no data
90: ** HT_RETRY Retry request after at a later time
91: ** HT_ALL All of above
92: */
2.30 frystyk 93: PUBLIC BOOL HTNet_register (HTNetCallback *cbf, int status)
2.23 frystyk 94: {
95: if (THD_TRACE)
2.31 frystyk 96: TTYPrint(TDEST, "Net register HTNetCallback %p\n", (void *) cbf);
2.23 frystyk 97: if (cbf) {
98: CBFInfo *cbfinfo = (CBFInfo *) calloc(1, sizeof(CBFInfo));
99: if (!cbfinfo) outofmem(__FILE__, "HTNet_register");
100: cbfinfo->cbf = cbf;
101: cbfinfo->status = status;
102: if (!HTNetCBF) HTNetCBF = HTList_new();
103: return HTList_addObject(HTNetCBF, (void *) cbfinfo);
104: }
105: return NO;
106: }
107:
108: /* HTNet_unregister
109: ** --------------
110: ** Unregister a call back function that is to be called on every
111: ** termination of a request.
112: */
2.30 frystyk 113: PUBLIC BOOL HTNet_unregister (HTNetCallback *cbf)
2.23 frystyk 114: {
115: if (THD_TRACE)
2.31 frystyk 116: TTYPrint(TDEST, "Net unreg.. HTNetCallback %p\n", (void *) cbf);
2.23 frystyk 117: if (HTNetCBF && cbf) {
118: HTList *cur = HTNetCBF;
119: CBFInfo *pres;
120: while ((pres = (CBFInfo *) HTList_nextObject(cur))) {
121: if (pres->cbf == cbf) {
122: HTList_removeObject(HTNetCBF, (void *) pres);
123: free(pres);
124: return YES;
125: }
126: }
127: }
128: return NO;
129: }
2.1 frystyk 130:
2.23 frystyk 131: /* HTNet_unregisterAll
132: ** -------------------
133: ** Unregisters all call back functions
134: */
135: PUBLIC BOOL HTNet_unregisterAll (void)
136: {
137: if (THD_TRACE)
2.31 frystyk 138: TTYPrint(TDEST, "Net unreg.. All callback functions\n");
2.23 frystyk 139: if (HTNetCBF) {
140: HTList *cur = HTNetCBF;
141: CBFInfo *pres;
142: while ((pres = (CBFInfo *) HTList_nextObject(cur))) {
143: HTList_removeObject(HTNetCBF, (void *) pres);
144: free(pres);
145: }
146: HTList_delete(HTNetCBF);
147: HTNetCBF = NULL;
148: return YES;
149: }
150: return NO;
151: }
152:
153: /* HTNet_callback
154: ** --------------
155: ** Call all the call back functions registered in the list IF not the
156: ** status is HT_IGNORE.
157: ** The callback functions are called in the order of which they
158: ** were registered. At the moment an application callback function is
159: ** called, it can free the request object - it is no longer used by the
160: ** Library.
161: ** Returns YES if OK, else NO.
162: */
163: PUBLIC BOOL HTNet_callback (HTRequest * request, int status)
164: {
165: if (HTNetCBF && request && status != HT_IGNORE) {
166: int cnt = HTList_count(HTNetCBF);
167: while (--cnt >= 0) {
2.26 frystyk 168: CBFInfo *pres = (CBFInfo *) HTList_objectAt(HTNetCBF, cnt);
2.23 frystyk 169: if (pres && (pres->status == status || pres->status == HT_ALL)) {
170: if (THD_TRACE)
2.31 frystyk 171: TTYPrint(TDEST, "Net callback %p (request=%p, status=%d)\n",
2.23 frystyk 172: (void *) pres->cbf, request, status);
173: (*(pres->cbf))(request, status);
174: }
175: }
176: return YES;
2.1 frystyk 177: }
2.23 frystyk 178: return NO;
2.1 frystyk 179: }
180:
2.23 frystyk 181: /* ------------------------------------------------------------------------- */
182: /* Request Queue */
183: /* ------------------------------------------------------------------------- */
2.1 frystyk 184:
2.23 frystyk 185: /* HTNet_activeQueue
186: ** -----------------
187: ** Returns the list of active requests that are currently having an open
188: ** connection.
189: ** Returns list of HTNet objects or NULL if error
2.1 frystyk 190: */
2.23 frystyk 191: PUBLIC HTList *HTNet_activeQueue (void)
192: {
193: return HTNetActive;
194: }
2.17 frystyk 195:
2.28 frystyk 196: /* HTNet_activeCount
197: ** ----------------
198: ** Returns the number of active requests
199: */
2.29 frystyk 200: PUBLIC BOOL HTNet_idle (void)
2.28 frystyk 201: {
2.29 frystyk 202: return HTList_isEmpty(HTNetActive);
2.28 frystyk 203: }
204:
2.23 frystyk 205: /* HTNet_pendingQueue
206: ** ------------------
207: ** Returns the list of pending requests that are waiting to become active
208: ** Returns list of HTNet objects or NULL if error
209: */
210: PUBLIC HTList *HTNet_pendingQueue (void)
2.1 frystyk 211: {
2.23 frystyk 212: return HTNetPending;
2.1 frystyk 213: }
214:
2.23 frystyk 215: /* ------------------------------------------------------------------------- */
216: /* Creation and deletion methods */
217: /* ------------------------------------------------------------------------- */
218:
2.27 frystyk 219: /* HTNet_duplicate
220: ** ---------------
221: ** Creates a new HTNet object as a duplicate of the same request.
222: ** Returns YES if OK, else NO
223: ** BUG: We do not check if we have a socket free!
224: */
225: PUBLIC BOOL HTNet_dup (HTNet *src, HTNet **dest)
226: {
227: *dest = NULL;
228: if (!src) return NO;
229: if ((*dest = (HTNet *) calloc(1, sizeof(HTNet))) == NULL)
230: outofmem(__FILE__, "HTNet_dup");
231: memcpy(*dest, src, sizeof(HTNet));
232: return YES;
233: }
234:
2.30 frystyk 235: /* HTNet_priority
236: ** --------------
237: ** Get the current priority of the Net object
238: */
239: PUBLIC HTPriority HTNet_priority (HTNet * net)
240: {
241: return (net ? net->priority : -1);
242: }
243:
244: /* HTNet_setPriority
245: ** -----------------
246: ** Set the current priority of the Net object
247: ** This will change the priority next time the thread is blocked
248: */
249: PUBLIC BOOL HTNet_setPriority (HTNet * net, HTPriority priority)
250: {
251: if (net) {
252: net->priority = priority;
253: return YES;
254: }
255: return NO;
256: }
257:
258: /* HTNet_new
259: ** ---------
2.23 frystyk 260: ** Create a new HTNet object as a new request to be handled. If we have
261: ** more than HTMaxActive connections already then put this into the
262: ** pending queue, else start the request by calling the call back
263: ** function registered with this access method.
264: ** Returns YES if OK, else NO
265: */
2.30 frystyk 266: PUBLIC BOOL HTNet_new (HTRequest * request)
2.23 frystyk 267: {
268: HTNet *me;
269: HTProtocol *prot;
2.27 frystyk 270: if (!request) return NO;
2.23 frystyk 271: if (!HTNetActive) HTNetActive = HTList_new();
2.26 frystyk 272: prot = (HTProtocol *) HTAnchor_protocol(request->anchor);
2.23 frystyk 273:
274: /* Create new net object and bind it to the request object */
275: if ((me = (HTNet *) calloc(1, sizeof(HTNet))) == NULL)
276: outofmem(__FILE__, "HTNet_new");
277: me->request = request;
278: request->net = me;
279: me->preemtive = (HTProtocol_preemtive(prot) || request->preemtive);
2.30 frystyk 280: me->priority = request->priority;
2.23 frystyk 281: me->sockfd = INVSOC;
282: if (!(me->cbf = HTProtocol_callback(prot))) {
283: if (THD_TRACE)
2.31 frystyk 284: TTYPrint(TDEST, "HTNet_new... NO CALL BACK FUNCTION!\n");
2.23 frystyk 285: free(me);
286: return NO;
287: }
2.25 frystyk 288: request->retrys++;
2.23 frystyk 289:
290: /*
291: ** Check if we can start the request, else put it into pending queue
292: ** If so then call the call back function associated with the anchor.
293: ** We use the INVSOC as we don't have a valid socket yet!
294: */
295: if (HTList_count(HTNetActive) < HTMaxActive) {
296: HTList_addObject(HTNetActive, (void *) me);
297: if (THD_TRACE)
2.31 frystyk 298: TTYPrint(TDEST, "HTNet_new... starting request %p (retry=%d)\n",
2.25 frystyk 299: request, request->retrys);
300: (*(me->cbf))(me->sockfd, request, FD_NONE);
2.23 frystyk 301: } else {
302: if (!HTNetPending) HTNetPending = HTList_new();
303: if (THD_TRACE)
2.31 frystyk 304: TTYPrint(TDEST, "HTNet_new... request %p registered as pending\n",
2.25 frystyk 305: request);
306: HTProgress(request, HT_PROG_WAIT, NULL);
307: HTList_addObject(HTNetPending, (void *) me);
2.23 frystyk 308: }
309: return YES;
310: }
311:
312: /* delete_object
313: ** -------------
314: ** Deletes an HTNet object
2.15 frystyk 315: */
2.23 frystyk 316: PRIVATE BOOL delete_object (HTNet *net, int status)
2.15 frystyk 317: {
2.23 frystyk 318: if (THD_TRACE)
2.31 frystyk 319: TTYPrint(TDEST, "HTNet_delete Remove net object %p\n", net);
2.23 frystyk 320: if (net) {
321: int status = 0;
322:
323: /* Free stream with data FROM network to application */
324: if (net->target) {
325: if (status == HT_INTERRUPTED)
326: (*net->target->isa->abort)(net->target, NULL);
327: else
328: (*net->target->isa->_free)(net->target);
329: }
330:
331: /* Close socket */
332: if (net->sockfd != INVSOC) {
2.24 frystyk 333: if (HTDNS_socket(net->dns) == INVSOC) {
334: if ((status = NETCLOSE(net->sockfd)) < 0)
2.32 ! frystyk 335: HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO,
2.24 frystyk 336: "NETCLOSE");
337: if (THD_TRACE)
2.31 frystyk 338: TTYPrint(TDEST, "HTNet_delete closing %d\n", net->sockfd);
2.25 frystyk 339: HTEvent_UnRegister(net->sockfd, (SockOps) FD_ALL);
2.24 frystyk 340: } else {
341: if (THD_TRACE)
2.31 frystyk 342: TTYPrint(TDEST, "HTNet_delete keeping %d\n", net->sockfd);
2.25 frystyk 343: HTDNS_clearActive(net->dns);
344: /* Here we should probably use a low priority */
2.27 frystyk 345: HTEvent_UnRegister(net->sockfd, (SockOps) FD_ALL);
2.25 frystyk 346: HTEvent_Register(net->sockfd, net->request, (SockOps) FD_READ,
347: HTDNS_closeSocket, net->priority);
2.24 frystyk 348: }
2.23 frystyk 349: }
350: if (net->isoc)
351: HTInputSocket_free(net->isoc);
352: if (net->request)
353: net->request->net = NULL; /* Break link to request */
354: free(net);
355: return status ? NO : YES;
356: }
357: return NO;
358: }
359:
360: /* HTNet_delete
361: ** ------------
362: ** Deletes the HTNet object from the list of active requests and calls
363: ** any registered call back functions IF not the status is HT_IGNORE.
364: ** This is used if we have internal requests that the app doesn't know
365: ** about. We also see if we have pending requests that can be started
366: ** up now when we have a socket free.
367: ** The callback functions are called in the reverse order of which they
368: ** were registered (last one first)
369: */
370: PUBLIC BOOL HTNet_delete (HTNet * net, int status)
371: {
372: if (THD_TRACE)
2.31 frystyk 373: TTYPrint(TDEST,"HTNetDelete. Net Object and call callback functions\n");
2.23 frystyk 374: if (HTNetActive && net) {
2.25 frystyk 375: SOCKFD cs = net->sockfd; /* Current sockfd */
2.23 frystyk 376:
377: /* Remove object and call callback functions */
378: HTRequest *request = net->request;
379: HTList_removeObject(HTNetActive, (void *) net);
380: delete_object(net, status);
381: HTNet_callback(request, status);
382:
2.25 frystyk 383: /*
384: ** See first if we have a persistent request queued up for this socket
385: ** If not then see if there is a pending request
386: */
387: if (HTNetPersistent) {
388: HTList *cur = HTNetPersistent;
389: HTNet *next;
390: while ((next = (HTNet *) HTList_nextObject(cur))) {
391: if (next->sockfd == cs) {
392: if (THD_TRACE)
2.31 frystyk 393: TTYPrint(TDEST, "HTNet delete launch WARM request %p\n",
2.25 frystyk 394: next->request);
2.28 frystyk 395: HTList_addObject(HTNetActive, (void *) next);
396: HTList_removeObject(HTNetPersistent, (void *) next);
2.25 frystyk 397: (*(next->cbf))(next->sockfd, next->request, FD_NONE);
398: break;
399: }
400: }
401: } else if (HTList_count(HTNetActive) < HTMaxActive &&
402: HTList_count(HTNetPending)) {
2.23 frystyk 403: HTNet *next = (HTNet *) HTList_removeFirstObject(HTNetPending);
404: if (next) {
405: HTList_addObject(HTNetActive, (void *) next);
406: if (THD_TRACE)
2.31 frystyk 407: TTYPrint(TDEST,"HTNet delete launch PENDING request %p\n",
2.23 frystyk 408: next->request);
409: (*(next->cbf))(INVSOC, next->request, FD_NONE);
410: }
411: }
412: return YES;
413: }
414: return NO;
415: }
416:
417: /* HTNet_deleteAll
418: ** ---------------
419: ** Deletes all HTNet object that might either be active or pending
2.25 frystyk 420: ** We DO NOT call the call back functions - A crude way of saying goodbye!
2.23 frystyk 421: */
422: PUBLIC BOOL HTNet_deleteAll (void)
423: {
424: if (THD_TRACE)
2.31 frystyk 425: TTYPrint(TDEST, "HTNetDelete. Remove all Net objects, NO callback\n");
2.25 frystyk 426: if (HTNetPersistent) {
427: HTList *cur = HTNetPersistent;
428: HTNet *pres;
429: while ((pres = (HTNet *) HTList_nextObject(cur))) {
430: pres->sockfd = INVSOC; /* Don't close it more than once */
431: delete_object(pres, HT_INTERRUPTED);
432: }
433: HTList_delete(HTNetPersistent);
434: HTNetPersistent = NULL;
435: }
2.23 frystyk 436: if (HTNetPending) {
437: HTList *cur = HTNetPending;
438: HTNet *pres;
439: while ((pres = (HTNet *) HTList_nextObject(cur)))
440: delete_object(pres, HT_INTERRUPTED);
441: HTList_delete(HTNetPending);
442: HTNetPending = NULL;
443: }
444: if (HTNetActive) {
445: HTList *cur = HTNetActive;
446: HTNet *pres;
447: while ((pres = (HTNet *) HTList_nextObject(cur)))
448: delete_object(pres, HT_INTERRUPTED);
449: HTList_delete(HTNetActive);
450: HTNetActive = NULL;
451: }
452: return NO;
2.15 frystyk 453: }
454:
2.25 frystyk 455: /* HTNet_wait
456: ** ----------
457: ** Let a net object wait for a persistent socket. It will be launched
458: ** from the HTNet_delete() function
459: */
460: PUBLIC BOOL HTNet_wait (HTNet *net)
461: {
462: if (net) {
463: if (THD_TRACE)
2.31 frystyk 464: TTYPrint(TDEST,"HTNet_wait.. request %p is waiting for socket %d\n",
2.25 frystyk 465: net->request, net->sockfd);
466: if (!HTNetPersistent) HTNetPersistent = HTList_new();
467: HTList_addObject(HTNetPersistent, (void *) net);
468: return YES;
469: }
470: return NO;
471: }
472:
2.23 frystyk 473: /* ------------------------------------------------------------------------- */
474: /* Killing requests */
475: /* ------------------------------------------------------------------------- */
476:
477: /* HTNet_kill
478: ** ----------
479: ** Kill the request by calling the call back function with a request for
480: ** closing the connection. Does not remove the object. This is done by
481: ** HTNet_delete() function which is called by the load routine.
482: ** Returns OK if success, NO on error
483: */
484: PUBLIC BOOL HTNet_kill (HTNet * me)
485: {
486: if (HTNetActive && me) {
2.25 frystyk 487: HTList *cur = HTNetActive;
2.23 frystyk 488: HTNet *pres;
2.25 frystyk 489: while ((pres = (HTNet *) HTList_nextObject(cur))) {
2.23 frystyk 490: if (pres == me) {
2.25 frystyk 491: (*(pres->cbf))(pres->sockfd, pres->request, FD_CLOSE);
2.23 frystyk 492: return YES;
493: }
494: }
495: }
496: if (THD_TRACE)
2.31 frystyk 497: TTYPrint(TDEST, "HTNet_kill.. object %p is not registered\n", me);
2.23 frystyk 498: return NO;
499: }
500:
501: /* HTNet_killAll
502: ** -------------
503: ** Kills all registered (active+pending) requests by calling the call
504: ** back function with a request for closing the connection. We do not
505: ** remove the HTNet object as it is done by HTNet_delete().
506: ** Returns OK if success, NO on error
507: */
508: PUBLIC BOOL HTNet_killAll (void)
509: {
510: HTNet *pres;
511: if (THD_TRACE)
2.31 frystyk 512: TTYPrint(TDEST, "HTNet_kill.. ALL registered requests!!!\n");
2.23 frystyk 513:
2.25 frystyk 514: /* We start off in persistent queue so we avoid racing */
515: if (HTNetPersistent) {
516: while ((pres = (HTNet *) HTList_lastObject(HTNetPersistent)) != NULL) {
517: pres->sockfd = INVSOC;
518: (*(pres->cbf))(pres->sockfd, pres->request, FD_CLOSE);
519: HTList_removeObject(HTNetPersistent, pres);
520: }
521: }
2.23 frystyk 522: if (HTNetPending) {
523: while ((pres = (HTNet *) HTList_lastObject(HTNetPending)) != NULL) {
2.25 frystyk 524: (*(pres->cbf))(pres->sockfd, pres->request, FD_CLOSE);
2.23 frystyk 525: HTList_removeObject(HTNetPending, pres);
526: }
527: }
528: if (HTNetActive) {
529: while ((pres = (HTNet *) HTList_lastObject(HTNetActive)) != NULL)
2.25 frystyk 530: (*(pres->cbf))(pres->sockfd, pres->request, FD_CLOSE);
2.23 frystyk 531: }
532: return YES;
533: }
Webmaster