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