1 /*
2 **	CONTAINS STREAMS FOR READING AND WRITING TO AND FROM A TRANSPORT
3 **
4 **	(c) COPYRIGHT MIT 1995.
5 **	Please first read the full copyright statement in the file COPYRIGH.
6 **	@(#) $Id$
7 **
8 **
9 ** HISTORY:
10 **	April 96  HFN	Written
11 */
12 
13 /* Library Include files */
14 #include "wwwsys.h"
15 #include "WWWUtil.h"
16 #include "HTAlert.h"
17 #include "HTHost.h"
18 #include "HTError.h"
19 
20 #ifdef HT_MUX
21 #include "WWWMux.h"
22 #endif
23 
24 #include "HTChannl.h"					 /* Implemented here */
25 
26 #define HASH(s)		((s) % (HT_M_HASH_SIZE))
27 
28 struct _HTInputStream {
29     const HTInputStreamClass *	isa;
30     HTChannel *			channel;
31 };
32 
33 struct _HTOutputStream {
34     const HTOutputStreamClass *	isa;
35     HTChannel *			channel;
36 };
37 
38 struct _HTChannel {
39     /* what media do we talk to? */
40     SOCKET		sockfd;					   /* Socket */
41     FILE *		fp;				  /* File descriptor */
42 
43     /* what streams handle the IO */
44     HTInputStream *	input;				     /* Input stream */
45     HTOutputStream *	output;				    /* Output stream */
46 
47     /* proxy streams to dereference the above streams */
48     HTInputStream	channelIStream;
49     HTOutputStream	channelOStream;
50 
51     BOOL		active;			/* Active or passive channel */
52     int			semaphore;			   /* On channel use */
53     HTHost *		host;			       /* Zombie connections */
54 };
55 
56 PRIVATE HTList	** channels = NULL;			 /* List of channels */
57 
58 /* ------------------------------------------------------------------------- */
59 
60 /*
61 **	Skinny stream objects to pass the IO requests to the channels current IO streams.
62 **	This was needed because the channel's IO streams could go away after the IO streams
63 **	were set up for multiple requests.
64 */
65 
ChannelIStream_flush(HTInputStream * me)66 PRIVATE int ChannelIStream_flush (HTInputStream * me)
67 {return me->channel->input ? (*me->channel->input->isa->flush)(me->channel->input) : HT_ERROR;}
ChannelIStream_free(HTInputStream * me)68 PRIVATE int ChannelIStream_free (HTInputStream * me)
69 {return me->channel->input ? (*me->channel->input->isa->_free)(me->channel->input) : HT_ERROR;}
ChannelIStream_abort(HTInputStream * me,HTList * e)70 PRIVATE int ChannelIStream_abort (HTInputStream * me, HTList * e)
71 {return me->channel->input ? (*me->channel->input->isa->abort)(me->channel->input, e) : HT_ERROR;}
ChannelIStream_read(HTInputStream * me)72 PRIVATE int ChannelIStream_read (HTInputStream * me)
73 {return me->channel->input ? (*me->channel->input->isa->read)(me->channel->input) : HT_ERROR;}
ChannelIStream_close(HTInputStream * me)74 PRIVATE int ChannelIStream_close (HTInputStream * me)
75 {return me->channel->input ? (*me->channel->input->isa->close)(me->channel->input) : HT_ERROR;}
ChannelIStream_consumed(HTInputStream * me,size_t bytes)76 PUBLIC int ChannelIStream_consumed (HTInputStream * me, size_t bytes)
77 {return me->channel->input ? (*me->channel->input->isa->consumed)(me->channel->input, bytes) : HT_ERROR;}
78 PRIVATE const HTInputStreamClass ChannelIStreamIsa =
79 {
80     "ChannelInput",
81     ChannelIStream_flush,
82     ChannelIStream_free,
83     ChannelIStream_abort,
84     ChannelIStream_read,
85     ChannelIStream_close,
86     ChannelIStream_consumed
87 };
88 
ChannelOStream_flush(HTOutputStream * me)89 PRIVATE int ChannelOStream_flush (HTOutputStream * me)
90 {return me->channel->output ? (*me->channel->output->isa->flush)(me->channel->output) : HT_ERROR;}
ChannelOStream_free(HTOutputStream * me)91 PRIVATE int ChannelOStream_free (HTOutputStream * me)
92 {return me->channel->output ? (*me->channel->output->isa->_free)(me->channel->output) : HT_ERROR;}
ChannelOStream_abort(HTOutputStream * me,HTList * e)93 PRIVATE int ChannelOStream_abort (HTOutputStream * me, HTList * e)
94 {return me->channel->output ? (*me->channel->output->isa->abort)(me->channel->output, e) : HT_ERROR;}
ChannelOStream_put_character(HTOutputStream * me,char c)95 PRIVATE int ChannelOStream_put_character (HTOutputStream * me, char c)
96 {return me->channel->output ? (*me->channel->output->isa->put_character)(me->channel->output, c) : HT_ERROR;}
ChannelOStream_put_string(HTOutputStream * me,const char * s)97 PRIVATE int ChannelOStream_put_string (HTOutputStream * me, const char * s)
98 {return me->channel->output ? (*me->channel->output->isa->put_string)(me->channel->output, s) : HT_ERROR;}
ChannelOStream_put_block(HTOutputStream * me,const char * buf,int len)99 PRIVATE int ChannelOStream_put_block (HTOutputStream * me, const char * buf, int len)
100 {return me->channel->output ? (*me->channel->output->isa->put_block)(me->channel->output, buf, len) : HT_ERROR;}
ChannelOStream_close(HTOutputStream * me)101 PRIVATE int ChannelOStream_close (HTOutputStream * me)
102 {return me->channel->output ? (*me->channel->output->isa->close)(me->channel->output) : HT_ERROR;}
103 PRIVATE const HTOutputStreamClass ChannelOStreamIsa =
104 {
105     "ChannelOutput",
106     ChannelOStream_flush,
107     ChannelOStream_free,
108     ChannelOStream_abort,
109     ChannelOStream_put_character,
110     ChannelOStream_put_string,
111     ChannelOStream_put_block,
112     ChannelOStream_close,
113 };
114 
115 /* ------------------------------------------------------------------------- */
116 
free_channel(HTChannel * ch)117 PRIVATE void free_channel (HTChannel * ch)
118 {
119     if (ch) {
120 
121 	/* Close the input and output stream */
122 	if (ch->input) {
123 	    (*ch->input->isa->close)(ch->input);
124 	    ch->input = NULL;
125 	}
126 	if (ch->output) {
127 	    (*ch->output->isa->close)(ch->output);
128 	    ch->output = NULL;
129 	}
130 
131 	/* Close the socket */
132 	if (ch->sockfd != INVSOC) {
133 	    NETCLOSE(ch->sockfd);
134    	    HTNet_decreaseSocket();
135 	    HTTRACE(PROT_TRACE, "Channel..... Deleted %p, socket %d\n" _ ch _ ch->sockfd);
136 	    ch->sockfd = INVSOC;
137 	}
138 
139 	/* Close the file */
140 	if (ch->fp) {
141 	    fclose(ch->fp);
142 	    HTTRACE(PROT_TRACE, "Channel..... Deleted %p, file %p\n" _ ch _ ch->fp);
143 	    ch->fp = NULL;
144 	}
145 	HT_FREE(ch);
146     }
147 }
148 
149 /*
150 **	A channel is uniquely identified by a socket.
151 **	Note that we don't create the input and output stream - they are
152 **	created later.
153 **
154 **	We only keep a hash on sockfd's as we don't have to look for channels
155 **	for ANSI file descriptors.
156 */
HTChannel_new(SOCKET sockfd,FILE * fp,BOOL active)157 PUBLIC HTChannel * HTChannel_new (SOCKET sockfd, FILE * fp, BOOL active)
158 {
159     HTList * list = NULL;
160     HTChannel * ch = NULL;
161     int hash = sockfd < 0 ? 0 : HASH(sockfd);
162     HTTRACE(PROT_TRACE, "Channel..... Hash value is %d\n" _ hash);
163     if (!channels) {
164 	if (!(channels = (HTList **) HT_CALLOC(HT_M_HASH_SIZE,sizeof(HTList*))))
165 	    HT_OUTOFMEM("HTChannel_new");
166     }
167     if (!channels[hash]) channels[hash] = HTList_new();
168     list = channels[hash];
169     if ((ch = (HTChannel *) HT_CALLOC(1, sizeof(HTChannel))) == NULL)
170 	HT_OUTOFMEM("HTChannel_new");
171     ch->sockfd = sockfd;
172     ch->fp = fp;
173     ch->active = active;
174     ch->semaphore = 1;
175     ch->channelIStream.isa = &ChannelIStreamIsa;
176     ch->channelOStream.isa = &ChannelOStreamIsa;
177     ch->channelIStream.channel = ch;
178     ch->channelOStream.channel = ch;
179     HTList_addObject(list, (void *) ch);
180 
181 #ifdef HT_MUX
182 	    /*
183 	    **  Create a MUX channel and do a connect on this channel with a
184 	    **  new session.
185 	    */
186 	    {
187 		HTProtocol * protocol = HTNet_protocol(net);
188 		HTMuxChannel * muxch = HTMuxChannel_new(me);
189 		net->session = HTMuxSession_connect(muxch, net, HTProtocol_id(protocol));
190 	    }
191 #endif /* HT_MUX */
192 
193     HTTRACE(PROT_TRACE, "Channel..... Added %p to list %p\n" _ ch _ list);
194     return ch;
195 }
196 
197 /*
198 **	Look for a channel object if we for some reason should have lost it
199 **	Returns NULL if nothing found
200 */
HTChannel_find(SOCKET sockfd)201 PUBLIC HTChannel * HTChannel_find (SOCKET sockfd)
202 {
203     if (channels && sockfd != INVSOC) {
204 	int hash = HASH(sockfd);
205 	HTList * list = channels[hash];
206 	if (list) {
207 	    HTChannel * ch = NULL;
208 	    while ((ch = (HTChannel *) HTList_nextObject(list)))
209 		if (ch->sockfd == sockfd) return ch;
210 	}
211     }
212     return NULL;
213 }
214 
215 /*
216 **	When deleting a channel we first look at if there are no more requests
217 **	using the channel (the semaphore is <= 0). Then, if the socket supports
218 **	persistent connections then we register the channel in the Host cache
219 **	and wait until the other end closes it or we get a time out on our side
220 */
HTChannel_delete(HTChannel * channel,int status)221 PUBLIC BOOL HTChannel_delete (HTChannel * channel, int status)
222 {
223     if (channel) {
224 	HTTRACE(PROT_TRACE, "Channel..... Delete %p with semaphore %d, status %d\n" _
225 		channel _ channel->semaphore _ status);
226 	/*
227 	**  We call the free methods on both the input stream and the output
228 	**  stream so that we can free up the stream pipes. However, note that
229 	**  this doesn't mean that we close the input stream and output stream
230 	**  them selves - only the generic streams
231 	*/
232 	HTChannel_deleteInput(channel, status);
233 	HTChannel_deleteOutput(channel, status);
234 
235 	/*
236 	**  Check whether this channel is used by other objects or we can
237 	**  delete it and free memory.
238 	*/
239 	if (channel->semaphore <= 0 && channels && (
240 	    channel->sockfd != INVSOC || channel->fp != NULL)) {
241             int hash = HASH(channel->sockfd);
242 	    HTList * list = channels[hash];
243 	    if (list) {
244 		HTList_removeObject(list, (void *) channel);
245 		free_channel(channel);
246 		return YES;
247 	    }
248 	} else
249 	    HTChannel_downSemaphore(channel);
250     }
251     return NO;
252 }
253 
254 /*	HTChannel_deleteAll
255 **	-------------------
256 **	Destroys all channels. This is called by HTLibTerminate(0
257 */
HTChannel_deleteAll(void)258 PUBLIC BOOL HTChannel_deleteAll (void)
259 {
260     if (channels) {
261 	HTList * cur;
262 	int cnt;
263 	for (cnt=0; cnt<HT_M_HASH_SIZE; cnt++) {
264 	    if ((cur = channels[cnt])) {
265 		HTChannel * pres;
266 		while ((pres = (HTChannel *) HTList_nextObject(cur)) != NULL)
267 		    free_channel(pres);
268 	    }
269 	    HTList_delete(channels[cnt]);
270 	}
271 	HT_FREE(channels);
272     }
273     return YES;
274 }
275 
276 /*      HTChannel_safeDeleteAll
277 **      -------------------
278 **      Destroys all channels. This is called by HTLibTerminate(0
279 */
280 
HTChannel_safeDeleteAll(void)281 PUBLIC BOOL HTChannel_safeDeleteAll (void)
282 {
283     if (channels) {
284         HTList * cur;
285         int cnt;
286         for (cnt=0; cnt<HT_M_HASH_SIZE; cnt++) {
287           if ((cur = channels[cnt])) {
288             HTChannel * pres;
289             while ((pres = (HTChannel *) HTList_nextObject(cur)) != NULL) {
290               HTChannel_delete (pres, HT_TIMEOUT);
291               cur = channels[cnt];
292             }
293 	    HTList_delete (channels[cnt]);
294 	    channels[cnt] = NULL;
295           }
296         }
297         return YES;
298     }
299     return NO;
300 }
301 
302 /*
303 **	Return the socket associated with this channel
304 */
HTChannel_socket(HTChannel * channel)305 PUBLIC SOCKET HTChannel_socket (HTChannel * channel)
306 {
307     return channel ? channel->sockfd : INVSOC;
308 }
309 
HTChannel_setSocket(HTChannel * channel,SOCKET sockfd)310 PUBLIC BOOL HTChannel_setSocket (HTChannel * channel, SOCKET sockfd)
311 {
312     if (channel) {
313 
314 	/*
315 	** As we use the socket number as the hash entry then we have to
316 	** update the hash table as well.
317 	*/
318 	int old_hash = HASH(channel->sockfd);
319 	int new_hash = sockfd < 0 ? 0 : HASH(sockfd);
320 	HTList * list = channels[old_hash];
321 	if (list) HTList_removeObject(list, channel);
322 	if (!channels[new_hash]) channels[new_hash] = HTList_new();
323 	list = channels[new_hash];
324 	HTList_addObject(list, channel);
325 
326 	channel->sockfd = sockfd;
327 	return YES;
328     }
329     return NO;
330 }
331 
332 /*
333 **	Return the file descriptor associated with this channel
334 */
HTChannel_file(HTChannel * channel)335 PUBLIC FILE * HTChannel_file (HTChannel * channel)
336 {
337     return channel ? channel->fp : NULL;
338 }
339 
HTChannel_setFile(HTChannel * channel,FILE * fp)340 PUBLIC BOOL HTChannel_setFile (HTChannel * channel, FILE * fp)
341 {
342     if (channel) {
343 	channel->fp = fp;
344 	return YES;
345     }
346     return NO;
347 }
348 
349 /*
350 **	We keep the associated Host object in case we have a
351 **	sleeping connection.
352 */
HTChannel_setHost(HTChannel * ch,HTHost * host)353 PUBLIC BOOL HTChannel_setHost (HTChannel * ch, HTHost * host)
354 {
355     if (ch) {
356 	ch->host = host;
357 	return YES;
358     }
359     return NO;
360 }
361 
HTChannel_host(HTChannel * ch)362 PUBLIC HTHost * HTChannel_host (HTChannel * ch)
363 {
364     return (ch ? ch->host : NULL);
365 }
366 
367 /*
368 **	Increase the semaphore for this channel
369 */
HTChannel_upSemaphore(HTChannel * channel)370 PUBLIC void HTChannel_upSemaphore (HTChannel * channel)
371 {
372     if (channel) {
373 	channel->semaphore++;
374 	HTTRACE(PROT_TRACE, "Channel..... Semaphore increased to %d for channel %p\n" _
375 		    channel->semaphore _ channel);
376 #ifdef HT_MUX
377 		HTMuxChannel * muxch = HTMuxChannel_find(me);
378 		HTProtocol * protocol = HTNet_protocol(net);
379 		net->session = HTMuxSession_connect(muxch, net, HTProtocol_id(protocol));
380 #endif /* HT_MUX */
381 
382     }
383 }
384 
385 /*
386 **	Decrease the semaphore for this channel
387 */
HTChannel_downSemaphore(HTChannel * channel)388 PUBLIC void HTChannel_downSemaphore (HTChannel * channel)
389 {
390     if (channel) {
391 	channel->semaphore--;
392 	if (channel->semaphore <= 0) channel->semaphore = 0;
393 	HTTRACE(PROT_TRACE, "Channel..... Semaphore decreased to %d for channel %p\n" _
394 		    channel->semaphore _ channel);
395     }
396 }
397 
398 /*
399 **	Explicitly set the semaphore for this channel
400 */
HTChannel_setSemaphore(HTChannel * channel,int semaphore)401 PUBLIC void HTChannel_setSemaphore (HTChannel * channel, int semaphore)
402 {
403     if (channel) {
404 	channel->semaphore = semaphore;
405 	if (channel->semaphore <= 0) channel->semaphore = 0;
406 	HTTRACE(PROT_TRACE, "Channel..... Semaphore set to %d for channel %p\n" _
407 		    channel->semaphore _ channel);
408     }
409 }
410 
411 /*
412 **	Create the input stream and bind it to the channel
413 **	Please read the description in the HTIOStream module on the parameters
414 */
HTChannel_setInput(HTChannel * ch,HTInputStream * input)415 PUBLIC BOOL HTChannel_setInput (HTChannel * ch, HTInputStream * input)
416 {
417     if (ch) {
418 	ch->input = input;
419 	return YES;
420     }
421     return NO;
422 }
423 
HTChannel_input(HTChannel * ch)424 PUBLIC HTInputStream * HTChannel_input (HTChannel * ch)
425 {
426     return ch ? ch->input : NULL;
427 }
428 
HTChannel_deleteInput(HTChannel * channel,int status)429 PUBLIC BOOL HTChannel_deleteInput (HTChannel * channel, int status)
430 {
431     if (channel && channel->input && status != HT_IGNORE) {
432 	HTTRACE(PROT_TRACE,
433 		"Channel..... Delete input stream %p from channel %p\n" _
434 		channel->input _ channel);
435 	if (status==HT_INTERRUPTED || status==HT_TIMEOUT)
436 	    (*channel->input->isa->abort)(channel->input, NULL);
437 	else
438 	    (*channel->input->isa->_free)(channel->input);
439 	return YES;
440     }
441     return NO;
442 }
443 
444 /*
445 **	Create the output stream and bind it to the channel
446 **	Please read the description in the HTIOStream module on the parameters
447 */
HTChannel_setOutput(HTChannel * ch,HTOutputStream * output)448 PUBLIC BOOL HTChannel_setOutput (HTChannel * ch, HTOutputStream * output)
449 {
450     if (ch) {
451 	ch->output = output;
452 	return YES;
453     }
454     return NO;
455 }
456 
HTChannel_output(HTChannel * ch)457 PUBLIC HTOutputStream * HTChannel_output (HTChannel * ch)
458 {
459     return ch ? ch->output : NULL;
460 }
461 
HTChannel_deleteOutput(HTChannel * channel,int status)462 PUBLIC BOOL HTChannel_deleteOutput (HTChannel * channel, int status)
463 {
464     if (channel && channel->output && status != HT_IGNORE) {
465 	HTTRACE(PROT_TRACE,
466 		"Channel..... Delete input stream %p from channel %p\n" _
467 		channel->input _ channel);
468 	if (status==HT_INTERRUPTED || status==HT_TIMEOUT)
469 	    (*channel->output->isa->abort)(channel->output, NULL);
470 	else
471 	    (*channel->output->isa->_free)(channel->output);
472 	return YES;
473     }
474     return NO;
475 }
476 
HTChannel_getChannelIStream(HTChannel * ch)477 PUBLIC HTInputStream * HTChannel_getChannelIStream (HTChannel * ch)
478 {
479     if (ch)
480 	return &ch->channelIStream;
481     return NULL;
482 }
483 
HTChannel_getChannelOStream(HTChannel * ch)484 PUBLIC HTOutputStream * HTChannel_getChannelOStream (HTChannel * ch)
485 {
486     if (ch)
487 	return &ch->channelOStream;
488     return NULL;
489 }
490 
491