Annotation of libwww/Library/src/HTMuxCh.c, revision 2.1
2.1 ! frystyk 1: /*
! 2: ** MUX CHANNEL, SESSION AND PROTOCOL MANAGEMENT
! 3: **
! 4: ** (c) COPYRIGHT MIT 1995.
! 5: ** Please first read the full copyright statement in the file COPYRIGH.
! 6: ** @(#) $Id: HTMuxCh.c,v 1.1.2.6 1996/11/08 19:49:45 eric Exp $
! 7: **
! 8: ** Handles a MUX Channel with sessions and protocols
! 9: **
! 10: ** Authors
! 11: ** HFN Henrik Frystyk Nielsen <frystyk@w3.org>
! 12: **
! 13: ** HISTORY:
! 14: ** Oct 96 HFN Written
! 15: */
! 16:
! 17: /* Library Include files */
! 18: #include "sysdep.h"
! 19: #include "WWWUtil.h"
! 20: #include "WWWCore.h"
! 21: #include "WWWTrans.h"
! 22: #include "WWWStream.h"
! 23: #include "HTMuxTx.h"
! 24: #include "HTMuxHeader.h"
! 25: #include "HTDemux.h"
! 26: #include "HTMuxCh.h" /* Implemented here */
! 27:
! 28: #define MAX_SESSIONS 0xFF /* Max 256 sessions */
! 29:
! 30: #define SID_BASE 2
! 31: #define RECEIVER_OFFSET 0 /* Client control session */
! 32: #define SENDER_OFFSET 1 /* Server control session */
! 33:
! 34: struct _HTStream {
! 35: const HTStreamClass * isa;
! 36: /* ... */
! 37: };
! 38:
! 39: struct _HTOutputStream {
! 40: const HTOutputStreamClass * isa;
! 41: /* ... */
! 42: };
! 43:
! 44: #define PUTBLOCK(b,l) (*me->isa->put_block)(me,(b),(l))
! 45:
! 46: struct _HTMuxProtocol {
! 47: HTAtom * name;
! 48: HTProtocolId pid;
! 49: };
! 50:
! 51: struct _HTMuxSession {
! 52: HTMuxSessionId sid;
! 53: HTProtocolId pid;
! 54: HTNet * net;
! 55:
! 56: /* State */
! 57: HTMuxClose close; /* State of the connection */
! 58: int credit; /* Available credit */
! 59: int fragment; /* Fragment size */
! 60: int read; /* Data read sine last credit */
! 61:
! 62: /* Input */
! 63: HTStream * buffer; /* If we have to buffer data */
! 64: BOOL buffering;
! 65: };
! 66:
! 67: struct _HTMuxChannel {
! 68: int hash;
! 69: HTHost * host;
! 70: int max_sid; /* A la max_sockfd in select */
! 71: HTNet * net;
! 72: HTList * protocols; /* List of defined protocols */
! 73: HTMuxSession * sessions[MAX_SESSIONS];
! 74: };
! 75:
! 76: PRIVATE HTList ** muxchs = NULL; /* List of mux muxchs */
! 77:
! 78: /* ------------------------------------------------------------------------- */
! 79:
! 80: PRIVATE HTMuxSession * session_new (void)
! 81: {
! 82: HTMuxSession * me;
! 83: if ((me = (HTMuxSession *) HT_CALLOC(1, sizeof(HTMuxSession))) == NULL)
! 84: HT_OUTOFMEM("HTMuxSession_new");
! 85: me->credit = DEFAULT_CREDIT;
! 86: return me;
! 87: }
! 88:
! 89: PRIVATE BOOL session_delete (HTMuxSession * session)
! 90: {
! 91: if (session) {
! 92: HT_FREE(session);
! 93: return YES;
! 94: }
! 95: return NO;
! 96: }
! 97:
! 98: PUBLIC HTMuxSession * HTMuxSession_register (HTMuxChannel * muxch,
! 99: HTMuxSessionId sid, HTProtocolId pid)
! 100: {
! 101: if (muxch) {
! 102: HTMuxSession * session = muxch->sessions[sid];
! 103: if (session == NULL) {
! 104: session = session_new();
! 105: session->sid = sid;
! 106: session->pid = pid;
! 107: muxch->sessions[sid] = session;
! 108: if (MUX_TRACE)
! 109: HTTrace("Mux Channel. Registered session %d on channel %p\n",
! 110: sid, muxch);
! 111: }
! 112: return session;
! 113: }
! 114: if (MUX_TRACE) HTTrace("Mux Channel. Can't register new session\n");
! 115: return NULL;
! 116: }
! 117:
! 118: PUBLIC HTMuxSessionId HTMuxSession_accept (HTMuxChannel * muxch, HTNet * net,
! 119: HTProtocolId pid)
! 120: {
! 121: if (muxch && net) {
! 122: HTMuxSession * session;
! 123: HTMuxSessionId sid = SID_BASE + RECEIVER_OFFSET;
! 124: for (; sid<MAX_SESSIONS; sid+=2) {
! 125: if ((session = muxch->sessions[sid]) &&
! 126: session->net == NULL && session->pid == pid) {
! 127: if (MUX_TRACE)
! 128: HTTrace("Mux Channel. Accepting session %d on channel %p\n",
! 129: sid, muxch);
! 130: return sid;
! 131: }
! 132: }
! 133: }
! 134: if (MUX_TRACE) HTTrace("Mux Channel. Can't accept new session\n");
! 135: return INVSID;
! 136: }
! 137:
! 138: PUBLIC HTMuxSessionId HTMuxSession_connect (HTMuxChannel * muxch, HTNet * net,
! 139: HTProtocolId pid)
! 140: {
! 141: if (muxch && net) {
! 142: HTMuxSessionId sid = SID_BASE + SENDER_OFFSET;
! 143: for (; sid<MAX_SESSIONS; sid+=2) {
! 144: if (muxch->sessions[sid] == NULL) {
! 145: HTMuxSession * session = session_new();
! 146: session->sid = sid;
! 147: session->pid = pid;
! 148: session->net = net;
! 149: muxch->sessions[sid] = session;
! 150: if (MUX_TRACE)
! 151: HTTrace("Mux Channel. Creating session %d on channel %p\n",
! 152: sid, muxch);
! 153: return sid;
! 154: }
! 155: }
! 156: }
! 157: if (MUX_TRACE) HTTrace("Mux Channel. Can't create new session\n");
! 158: return INVSID;
! 159: }
! 160:
! 161: PUBLIC int HTMuxSession_close (HTMuxChannel * muxch, HTMuxSessionId sid)
! 162: {
! 163: if (muxch) {
! 164: HTMuxSession * session = muxch->sessions[sid];
! 165: HTMuxSession_setClose(muxch, session, MUX_S_END_WRITE);
! 166: return YES;
! 167: }
! 168: return NO;
! 169: }
! 170:
! 171: PUBLIC HTMuxSessionId HTMuxSession_id (HTMuxSession * session)
! 172: {
! 173: return session ? session->sid : INVSID;
! 174: }
! 175:
! 176: PUBLIC HTProtocolId HTMuxSession_pid (HTMuxSession * session)
! 177: {
! 178: return session ? session->pid : INVPID;
! 179: }
! 180:
! 181: PUBLIC HTNet * HTMuxSession_net (HTMuxSession * session)
! 182: {
! 183: return session ? session->net : NULL;
! 184: }
! 185:
! 186: PUBLIC BOOL HTMuxSession_setClose (HTMuxChannel * muxch,
! 187: HTMuxSession * session, HTMuxClose close)
! 188: {
! 189: if (muxch && session) {
! 190: session->close |= close;
! 191:
! 192: /*
! 193: ** If both directions are closed down then we can put the session
! 194: ** to sleep.
! 195: */
! 196: if (session->close == MUX_S_END) {
! 197: if (MUX_TRACE)
! 198: HTTrace("Mux Channel. Closing session %d on channel %p\n",
! 199: session->sid, muxch);
! 200: muxch->sessions[session->sid] = NULL;
! 201: session_delete(session);
! 202: }
! 203: return YES;
! 204: }
! 205: return NO;
! 206: }
! 207:
! 208: PUBLIC int HTMuxSession_credit (HTMuxSession * session)
! 209: {
! 210: return session ? session->credit : -1;
! 211: }
! 212:
! 213: PUBLIC BOOL HTMuxSession_setCredit (HTMuxChannel * muxch,
! 214: HTMuxSessionId sid, int credit)
! 215: {
! 216: HTMuxSession * session;
! 217: if (muxch && (session = muxch->sessions[sid])) {
! 218: session->credit = credit;
! 219: return YES;
! 220: }
! 221: return NO;
! 222: }
! 223:
! 224: PUBLIC int HTMuxSession_fragment (HTMuxSession * session)
! 225: {
! 226: return session ? session->fragment : -1;
! 227: }
! 228:
! 229: PUBLIC BOOL HTMuxSession_setFragment (HTMuxChannel * muxch,
! 230: HTMuxSessionId sid, int fragment)
! 231: {
! 232: HTMuxSession * session;
! 233: if (muxch && (session = muxch->sessions[sid])) {
! 234: session->fragment = fragment;
! 235: return YES;
! 236: }
! 237: return NO;
! 238: }
! 239:
! 240: /*
! 241: ** Tries really hard to get rid of the data.
! 242: ** Returns:
! 243: ** -1 Error
! 244: ** 0 Buffered the data
! 245: ** 1 Got rid of the data
! 246: */
! 247: PUBLIC int HTMuxSession_disposeData (HTMuxSession * me, const char * buf, int len)
! 248: {
! 249: if (MUX_TRACE)
! 250: HTTrace("Mux Channel. Writing %d bytes to session %p\n", len, me);
! 251:
! 252: /*
! 253: ** There are two situations that can occur: Either we have an accepted session
! 254: ** with a Net object or we have an unaccepted session with no Net object. In
! 255: ** the former case we try to get rid of the data by pushing it directly to the
! 256: ** read stream of the Net object. In the latter case we buffer as much as we
! 257: ** can.
! 258: */
! 259: if (me) {
! 260: HTNet * net;
! 261: HTStream * sink;
! 262: int status;
! 263: if ((net = me->net) && (sink = HTNet_readStream(net))) {
! 264:
! 265: /*
! 266: ** Look first to see if we have old data that we can dispose down
! 267: ** the sink. We keep the buffer stream so that we can reuse it later.
! 268: */
! 269: if (me->buffer && me->buffering) {
! 270: if ((*me->buffer->isa->flush)(me->buffer) == HT_OK) {
! 271: if (MUX_TRACE) HTTrace("Mux Channel. Flushed buffered data\n");
! 272: me->buffering = NO;
! 273: } else if ((*me->buffer->isa->put_block)(me->buffer, buf, len) >= 0) {
! 274: if (MUX_TRACE) HTTrace("Mux Channel. Buffer accepted data\n");
! 275: return 0;
! 276: }
! 277: if (MUX_TRACE) HTTrace("Mux Channel. Can't buffer data\n");
! 278: return (-1);
! 279: }
! 280:
! 281: /*
! 282: ** See if we can get rid of the new data. If not then try to buffer it.
! 283: ** If this also fails then we reset the channel. A positive return code
! 284: ** from the stream means that we got rid of the data successfully.
! 285: */
! 286: if ((status = (*sink->isa->put_block)(sink, buf, len)) >= 0) {
! 287: if (MUX_TRACE) HTTrace("Mux Channel. Stream returned %d\n", status);
! 288:
! 289: /*
! 290: ** If we get back a HT_LOADED then we have all the data we need
! 291: ** and we can terminate the request
! 292: */
! 293: if (status == HT_LOADED) {
! 294: HTNet_execute (net, HTEvent_END);
! 295: return 0;
! 296: }
! 297:
! 298: /*
! 299: ** Decide whether we should send a credit message
! 300: ** MORE TO COME
! 301: */
! 302: me->read += len;
! 303: if (me->read >= DEFAULT_CREDIT / 2) {
! 304: me->read = 0;
! 305: return 1;
! 306: }
! 307: return 0;
! 308: }
! 309: }
! 310:
! 311: /*
! 312: ** The stream is not ready and we try to buffer the data in
! 313: ** the meantime.
! 314: */
! 315: if (!me->buffer) {
! 316: me->buffer = HTPipeBuffer(sink, DEFAULT_CREDIT);
! 317: me->buffering = YES;
! 318: }
! 319: status = (*me->buffer->isa->put_block)(me->buffer, buf, len);
! 320: if (status >= 0) {
! 321: if (MUX_TRACE) HTTrace("Mux Channel. Buffer accepted data\n");
! 322: return 0;
! 323: }
! 324: if (MUX_TRACE) HTTrace("Mux Channel. Buffer returned %d\n", status);
! 325: }
! 326: return (-1);
! 327: }
! 328:
! 329: /* ------------------------------------------------------------------------- */
! 330:
! 331: PRIVATE BOOL channel_delete (HTMuxChannel * ch)
! 332: {
! 333: if (ch) {
! 334: HT_FREE(ch);
! 335: return YES;
! 336: }
! 337: return NO;
! 338: }
! 339:
! 340: PUBLIC HTMuxChannel * HTMuxChannel_new (HTHost * host)
! 341: {
! 342: if (host) {
! 343: HTMuxChannel * me = NULL;
! 344:
! 345: /* Create new object */
! 346: if ((me = (HTMuxChannel *) HT_CALLOC(1, sizeof(HTMuxChannel))) == NULL)
! 347: HT_OUTOFMEM("HTMuxChannel_new");
! 348: me->hash = HTHost_hash(host);
! 349: me->host = host;
! 350:
! 351: /*
! 352: ** Make sure that we are in interleave mode
! 353: */
! 354: HTHost_setMode(host, HT_TP_INTERLEAVE);
! 355:
! 356: /*
! 357: ** Get a special MUX Net object that we keep to our selves. We don't
! 358: ** associate a request object as the Net object lives longer.
! 359: */
! 360: me->net = HTNet_new(NULL);
! 361: HTNet_setReadStream(me->net, HTDemux_new(host, me));
! 362:
! 363: /* Insert into hash table */
! 364: if (!muxchs) {
! 365: if ((muxchs=(HTList **) HT_CALLOC(HOST_HASH_SIZE, sizeof(HTList *))) == NULL)
! 366: HT_OUTOFMEM("HTMuxChannel_new");
! 367: }
! 368: if (!muxchs[me->hash]) muxchs[me->hash] = HTList_new();
! 369: HTList_addObject(muxchs[me->hash], (void *) me);
! 370: if (MUX_TRACE)
! 371: HTTrace("Mux Channel. %p created with hash %d\n",me, me->hash);
! 372: return me;
! 373: }
! 374: return NULL;
! 375: }
! 376:
! 377: PUBLIC HTMuxChannel * HTMuxChannel_find (HTHost * host)
! 378: {
! 379: if (muxchs) {
! 380: int hash = HTHost_hash(host);
! 381: HTList * list = muxchs[hash];
! 382: if (list) {
! 383: HTMuxChannel * pres = NULL;
! 384: while ((pres = (HTMuxChannel *) HTList_nextObject(list)))
! 385: if (pres->host == host) return pres;
! 386: }
! 387: }
! 388: return NULL;
! 389: }
! 390:
! 391: PUBLIC BOOL HTMuxChannel_delete (HTMuxChannel * me)
! 392: {
! 393: if (me) {
! 394: HTList * list = NULL;
! 395: if (MUX_TRACE) HTTrace("Mux Channel. Deleting %p\n", me);
! 396: if (muxchs && (list = muxchs[me->hash])) {
! 397: HTList_removeObject(list, (void *) me);
! 398: channel_delete(me);
! 399: return YES;
! 400: }
! 401: }
! 402: return NO;
! 403: }
! 404:
! 405: PUBLIC BOOL HTMuxChannel_deleteAll (void)
! 406: {
! 407: if (muxchs) {
! 408: HTList * cur;
! 409: int cnt;
! 410: for (cnt=0; cnt<HOST_HASH_SIZE; cnt++) {
! 411: if ((cur = muxchs[cnt])) {
! 412: HTMuxChannel * pres;
! 413: while ((pres = (HTMuxChannel *) HTList_nextObject(cur)))
! 414: channel_delete(pres);
! 415: }
! 416: HTList_delete(muxchs[cnt]);
! 417: }
! 418: HT_FREE(muxchs);
! 419: }
! 420: return YES;
! 421: }
! 422:
! 423: PUBLIC HTNet * HTMuxChannel_net (HTMuxChannel * me)
! 424: {
! 425: return me ? me->net : NULL;
! 426: }
! 427:
! 428: PUBLIC HTMuxSession * HTMuxChannel_findSession (HTMuxChannel * me, HTMuxSessionId sid)
! 429: {
! 430: return (me) ? me->sessions[sid] : NULL;
! 431: }
! 432:
! 433: #if 0
! 434: PUBLIC HTMuxSession * HTMuxChannel_findSessionFromNet (HTMuxChannel * me, HTNet * net)
! 435: {
! 436: if (me && net) {
! 437: int cnt = 0;
! 438: HTMuxSession **session = me->sessions;
! 439: while (cnt<MAX_SESSIONS) {
! 440: if (**session->net == net) return *session;
! 441: session++, cnt++;
! 442: }
! 443: }
! 444: return NULL;
! 445: }
! 446: #endif
! 447:
! 448: PUBLIC HTHost * HTMuxChannel_host (HTMuxChannel * muxch)
! 449: {
! 450: return muxch ? muxch->host : NULL;
! 451: }
! 452:
! 453: PUBLIC int HTMuxChannel_sendControl (HTMuxChannel * muxch, HTMuxSessionId sid,
! 454: HTMuxHeader opcode, int value,
! 455: const void * param, int param_size)
! 456: {
! 457: if (muxch && muxch->host) {
! 458: HTOutputStream * me = HTChannel_output(HTHost_channel(muxch->host));
! 459: HTMuxHeader header[2];
! 460: switch (opcode) {
! 461: case MUX_STRING:
! 462: if (param && param_size) {
! 463: header[0] = HT_WORDSWAP(MUX_CONTROL | MUX_LONG_LENGTH | MUX_SET_LEN(value));
! 464: header[1] = HT_WORDSWAP(param_size);
! 465: PUTBLOCK((const char *) header, 8);
! 466: PUTBLOCK((const char *) param, MUX_LONG_ALIGN(param_size));
! 467: }
! 468: break;
! 469: case MUX_STACK:
! 470: if (param && param_size) {
! 471: header[0] = HT_WORDSWAP(MUX_CONTROL | MUX_LONG_LENGTH | MUX_SET_LEN(value));
! 472: header[1] = HT_WORDSWAP(param_size);
! 473: PUTBLOCK((const char *) header, 8);
! 474: PUTBLOCK((const char *) param, MUX_LONG_ALIGN(param_size));
! 475: }
! 476: break;
! 477: case MUX_FRAGMENT:
! 478: header[0] = HT_WORDSWAP(MUX_CONTROL | MUX_SET_SID(sid) | MUX_SET_LEN(value));
! 479: PUTBLOCK((const char *) header, 4);
! 480: break;
! 481: case MUX_CREDIT:
! 482: header[0] = HT_WORDSWAP(MUX_CONTROL | MUX_LONG_LENGTH | MUX_SET_SID(sid));
! 483: header[1] = HT_WORDSWAP(value);
! 484: PUTBLOCK((const char *) header, 8);
! 485: break;
! 486: default:
! 487: if (MUX_TRACE) HTTrace("Mux Channel. UNKNOWN OPCODE %d\n", opcode);
! 488: return HT_ERROR;
! 489: }
! 490:
! 491: /* Flush for now */
! 492: #if 1
! 493: return (*me->isa->flush)(me);
! 494: #else
! 495: return HT_OK;
! 496: #endif
! 497: }
! 498: return HT_ERROR;
! 499: }
! 500:
! 501: /* ------------------------------------------------------------------------- */
! 502:
! 503: PUBLIC BOOL HTMuxProtocol_add (HTMuxChannel * muxch,
! 504: HTProtocolId pid, const char * protocol)
! 505: {
! 506: if (muxch && protocol) {
! 507: HTMuxProtocol * ms;
! 508: if ((ms = (HTMuxProtocol *) HT_CALLOC(1, sizeof(HTMuxProtocol))) == NULL)
! 509: HT_OUTOFMEM("HTMuxProtocol_new");
! 510: ms->name = HTAtom_caseFor(protocol);
! 511: ms->pid = pid;
! 512: if (!muxch->protocols) muxch->protocols = HTList_new();
! 513: return HTList_addObject(muxch->protocols, ms);
! 514: }
! 515: return NO;
! 516: }
! 517:
! 518: PUBLIC BOOL HTMuxProtocol_delete (HTMuxChannel * muxch, HTProtocolId pid)
! 519: {
! 520: if (muxch && muxch->protocols) {
! 521: HTList * cur = muxch->protocols;
! 522: HTMuxProtocol * pres;
! 523: while ((pres = (HTMuxProtocol *) HTList_nextObject(cur))) {
! 524: if (pres->pid == pid) {
! 525: HTList_removeObject(muxch->protocols, pres);
! 526: HT_FREE(pres);
! 527: return YES;
! 528: }
! 529: }
! 530: }
! 531: return NO;
! 532: }
Webmaster