3 * $Id: clientStream.cc,v 1.8 2003/04/06 08:23:10 robertc Exp $
5 * DEBUG: section 87 Client-side Stream routines.
6 * AUTHOR: Robert Collins
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with thisObject program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
37 * A client Stream is a uni directional pipe, with the usual non-blocking
38 * asynchronous approach present elsewhere in squid.
40 * Each pipe node has a data push function, and a data request function.
41 * This limits flexability - the data flow is no longer assembled at each
44 * An alternative approach is to pass each node in the pipe the call-
45 * back to use on each IO call. This allows the callbacks to be changed
46 * very easily by a participating node, but requires more maintenance
47 * in each node (store the call back to the msot recent IO request in
48 * the nodes context.) Such an approach also prevents dynamically
49 * changing the pipeline from outside without an additional interface
50 * method to extract the callback and context from the next node.
52 * One important characteristic of the stream is that the readfunc
53 * on the terminating node, and the callback on the first node
54 * will be NULL, and never used.
58 #include "clientStream.h"
59 #include "HttpReply.h"
60 #include "HttpRequest.h"
61 #include "client_side_request.h"
63 CBDATA_TYPE(clientStreamNode
);
66 * TODO: rather than each node undeleting the next, have a clientStreamDelete
71 * clientStream quick notes:
73 * Each node including the HEAD of the clientStream has a cbdataReference
74 * held by the stream. Freeing the stream then removes that reference
75 * and cbdataFrees every node.
76 * Any node with other References, and all nodes downstream will only
77 * free when those references are released.
78 * Stream nodes MAY hold references to the data member of the node.
80 * Specifically - on creation no reference is made.
81 * If you pass a data variable to a node, give it an initial reference.
82 * If the data member is non-null on FREE, cbdataFree WILL be called.
83 * This you must never call cbdataFree on your own context without
84 * explicitly setting the stream node data member to NULL and
85 * cbdataReferenceDone'ing it.
87 * No data member may hold a reference to it's stream node.
88 * The stream guarantees that DETACH will be called before
89 * freeing the node, alowing data members to cleanup.
91 * If a node's data holds a reference to something that needs to
92 * free the stream a circular reference list will occur.
93 * This results no data being freed until that reference is removed.
94 * One way to accomplish thisObject is to explicitly remove the
95 * data from your own node before freeing the stream.
98 * mycontext = thisObject->data;
99 * thisObject->data = NULL;
100 * clientStreamFree (thisObject->head);
105 /* Local functions */
106 static FREE clientStreamFree
;
109 clientStreamNew(CSR
* readfunc
, CSCB
* callback
, CSD
* detach
, CSS
* status
,
110 ClientStreamData data
)
112 clientStreamNode
*temp
;
113 CBDATA_INIT_TYPE_FREECB(clientStreamNode
, clientStreamFree
);
114 temp
= cbdataAlloc(clientStreamNode
);
115 temp
->readfunc
= readfunc
;
116 temp
->callback
= callback
;
117 temp
->detach
= detach
;
118 temp
->status
= status
;
124 * Initialise a client Stream.
126 * func is the read function for the head
127 * callback is the callback for the tail
128 * tailbuf and taillen are the initial buffer and length for the tail.
131 clientStreamInit(dlink_list
* list
, CSR
* func
, CSD
* rdetach
, CSS
* readstatus
,
132 ClientStreamData readdata
, CSCB
* callback
, CSD
* cdetach
, ClientStreamData callbackdata
,
133 StoreIOBuffer tailBuffer
)
135 clientStreamNode
*temp
= clientStreamNew(func
, NULL
, rdetach
, readstatus
,
137 dlinkAdd(temp
, &temp
->node
, list
);
138 cbdataReference(temp
);
140 clientStreamInsertHead(list
, NULL
, callback
, cdetach
, NULL
, callbackdata
);
141 temp
= (clientStreamNode
*)list
->tail
->data
;
142 temp
->readBuffer
= tailBuffer
;
146 * Doesn't actually insert at head. Instead it inserts one *after*
147 * head. This is because HEAD is a special node, as is tail
148 * This function is not suitable for inserting the real HEAD.
151 clientStreamInsertHead(dlink_list
* list
, CSR
* func
, CSCB
* callback
,
152 CSD
* detach
, CSS
* status
, ClientStreamData data
)
155 /* test preconditions */
156 assert(list
!= NULL
);
158 clientStreamNode
*temp
= clientStreamNew(func
, callback
, detach
, status
, data
);
161 ("clientStreamInsertHead: Inserted node %p with data %p after head\n",
162 temp
, data
.getRaw());
164 if (list
->head
->next
)
165 temp
->readBuffer
= ((clientStreamNode
*)list
->head
->next
->data
)->readBuffer
;
167 dlinkAddAfter(temp
, &temp
->node
, list
->head
, list
);
169 cbdataReference(temp
);
173 * Callback the next node the in chain with it's requested data
176 clientStreamCallback(clientStreamNode
* thisObject
, clientHttpRequest
* http
,
177 HttpReply
* rep
, StoreIOBuffer replyBuffer
)
179 clientStreamNode
*next
;
180 assert(thisObject
&& http
&& thisObject
->node
.next
);
181 next
= thisObject
->next();
184 3) ("clientStreamCallback: Calling %p with cbdata %p from node %p\n",
185 next
->callback
, next
->data
.getRaw(), thisObject
);
186 next
->callback(next
, http
, rep
, replyBuffer
);
190 * Call the previous node in the chain to read some data
193 clientStreamRead(clientStreamNode
* thisObject
, clientHttpRequest
* http
,
194 StoreIOBuffer readBuffer
)
196 /* place the parameters on the 'stack' */
197 clientStreamNode
*prev
;
198 assert(thisObject
&& http
&& thisObject
->prev());
199 prev
= thisObject
->prev();
201 debug(87, 3) ("clientStreamRead: Calling %p with cbdata %p from node %p\n",
202 prev
->readfunc
, prev
->data
.getRaw(), thisObject
);
203 thisObject
->readBuffer
= readBuffer
;
204 prev
->readfunc(prev
, http
);
208 * Detach from the stream - only allowed for terminal members
211 clientStreamDetach(clientStreamNode
* thisObject
, clientHttpRequest
* http
)
213 clientStreamNode
*temp
= thisObject
;
215 assert(thisObject
->node
.next
== NULL
);
216 debug(87, 3) ("clientStreamDetach: Detaching node %p\n", thisObject
);
217 /* And clean up thisObject node */
218 /* ESI TODO: push refcount class through to head */
219 clientStreamNode
*prev
= NULL
;
221 if (thisObject
->prev())
222 prev
= cbdataReference(thisObject
->prev());
224 thisObject
->removeFromStream();
226 cbdataReferenceDone(temp
);
228 cbdataFree(thisObject
);
230 /* and tell the prev that the detach has occured */
232 * We do it in thisObject order so that the detaching node is always
233 * at the end of the list
237 debug(87, 3) ("clientStreamDetach: Calling %p with cbdata %p\n",
238 prev
->detach
, prev
->data
.getRaw());
240 if (cbdataReferenceValid(prev
))
241 prev
->detach(prev
, http
);
243 cbdataReferenceDone(prev
);
248 * Abort the stream - detach every node in the pipeline.
251 clientStreamAbort(clientStreamNode
* thisObject
, clientHttpRequest
* http
)
255 assert(thisObject
!= NULL
);
256 assert(http
!= NULL
);
257 list
= thisObject
->head
;
258 debug(87, 3) ("clientStreamAbort: Aborting stream with tail %p\n",
262 clientStreamDetach((clientStreamNode
*)list
->tail
->data
, http
);
267 * Call the upstream node to find it's status
269 clientStream_status_t
270 clientStreamStatus(clientStreamNode
* thisObject
, clientHttpRequest
* http
)
272 clientStreamNode
*prev
;
273 assert(thisObject
&& http
&& thisObject
->node
.prev
);
274 prev
= (clientStreamNode
*)thisObject
->node
.prev
->data
;
275 return prev
->status(prev
, http
);
278 /* Local function bodies */
280 clientStreamNode::removeFromStream()
283 dlinkDelete(&node
, head
);
289 clientStreamFree(void *foo
)
291 clientStreamNode
*thisObject
= (clientStreamNode
*)foo
;
293 debug(87, 3) ("Freeing clientStreamNode %p\n", thisObject
);
295 thisObject
->removeFromStream();
296 thisObject
->data
= NULL
;
300 clientStreamNode::prev() const
303 return (clientStreamNode
*)node
.prev
->data
;
309 clientStreamNode::next() const
312 return (clientStreamNode
*)node
.next
->data
;