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 #include "clientStream.h"
38 #include "HttpReply.h"
39 #include "HttpRequest.h"
40 #include "client_side_request.h"
43 \defgroup ClientStreamInternal Client Streams Internals
44 \ingroup ClientStreamAPI
46 * A client Stream is a uni directional pipe, with the usual non-blocking
47 * asynchronous approach present elsewhere in squid.
50 * Each pipe node has a data push function, and a data request function.
51 * This limits flexability - the data flow is no longer assembled at each
55 * An alternative approach is to pass each node in the pipe the call-
56 * back to use on each IO call. This allows the callbacks to be changed
57 * very easily by a participating node, but requires more maintenance
58 * in each node (store the callback to the most recent IO request in
59 * the nodes context.) Such an approach also prevents dynamically
60 * changing the pipeline from outside without an additional interface
61 * method to extract the callback and context from the next node.
64 * One important characteristic of the stream is that the readfunc
65 * on the terminating node, and the callback on the first node
66 * will be NULL, and never used.
68 \section QuickNotes Quick Notes
70 * Each node including the HEAD of the clientStream has a cbdataReference
71 * held by the stream. Freeing the stream then removes that reference
72 * and cbdataFree()'s every node.
73 * Any node with other References, and all nodes downstream will only
74 * free when those references are released.
75 * Stream nodes MAY hold references to the data member of the node.
78 * Specifically - on creation no reference is made.
79 * If you pass a data variable to a node, give it an initial reference.
80 * If the data member is non-null on FREE, cbdataFree WILL be called.
81 * This you must never call cbdataFree on your own context without
82 * explicitly setting the stream node data member to NULL and
83 * cbdataReferenceDone'ing it.
86 * No data member may hold a reference to it's stream node.
87 * The stream guarantees that DETACH will be called before
88 * 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 \todo rather than each node undeleting the next, have a clientStreamDelete that walks the list.
108 /// \ingroup ClientStreamInternal
109 CBDATA_TYPE(clientStreamNode
);
111 /* Local functions */
112 static FREE clientStreamFree
;
114 /// \ingroup ClientStreamInternal
116 clientStreamNew(CSR
* readfunc
, CSCB
* callback
, CSD
* detach
, CSS
* status
,
117 ClientStreamData data
)
119 clientStreamNode
*temp
;
120 CBDATA_INIT_TYPE_FREECB(clientStreamNode
, clientStreamFree
);
121 temp
= cbdataAlloc(clientStreamNode
);
122 temp
->readfunc
= readfunc
;
123 temp
->callback
= callback
;
124 temp
->detach
= detach
;
125 temp
->status
= status
;
131 \ingroup ClientStreamInternal
132 * Initialise a client Stream.
134 * func is the read function for the head
135 * callback is the callback for the tail
136 * tailbuf and taillen are the initial buffer and length for the tail.
139 clientStreamInit(dlink_list
* list
, CSR
* func
, CSD
* rdetach
, CSS
* readstatus
,
140 ClientStreamData readdata
, CSCB
* callback
, CSD
* cdetach
, ClientStreamData callbackdata
,
141 StoreIOBuffer tailBuffer
)
143 clientStreamNode
*temp
= clientStreamNew(func
, NULL
, rdetach
, readstatus
,
145 dlinkAdd(cbdataReference(temp
), &temp
->node
, list
);
147 clientStreamInsertHead(list
, NULL
, callback
, cdetach
, NULL
, callbackdata
);
148 temp
= (clientStreamNode
*)list
->tail
->data
;
149 temp
->readBuffer
= tailBuffer
;
153 \ingroup ClientStreamInternal
154 * Doesn't actually insert at head. Instead it inserts one *after*
155 * head. This is because HEAD is a special node, as is tail
156 * This function is not suitable for inserting the real HEAD.
159 clientStreamInsertHead(dlink_list
* list
, CSR
* func
, CSCB
* callback
,
160 CSD
* detach
, CSS
* status
, ClientStreamData data
)
163 /* test preconditions */
164 assert(list
!= NULL
);
166 clientStreamNode
*temp
= clientStreamNew(func
, callback
, detach
, status
, data
);
168 debugs(87, 3, "clientStreamInsertHead: Inserted node " << temp
<<
169 " with data " << data
.getRaw() << " after head");
171 if (list
->head
->next
)
172 temp
->readBuffer
= ((clientStreamNode
*)list
->head
->next
->data
)->readBuffer
;
174 dlinkAddAfter(cbdataReference(temp
), &temp
->node
, list
->head
, list
);
179 clientStreamCallback(clientStreamNode
* thisObject
, ClientHttpRequest
* http
,
180 HttpReply
* rep
, StoreIOBuffer replyBuffer
)
182 clientStreamNode
*next
;
183 assert(thisObject
&& http
&& thisObject
->node
.next
);
184 next
= thisObject
->next();
186 debugs(87, 3, "clientStreamCallback: Calling " << next
->callback
<< " with cbdata " <<
187 next
->data
.getRaw() << " from node " << thisObject
);
188 next
->callback(next
, http
, rep
, replyBuffer
);
192 \ingroup ClientStreamInternal
193 * Call the previous node in the chain to read some data
200 clientStreamRead(clientStreamNode
* thisObject
, ClientHttpRequest
* http
,
201 StoreIOBuffer readBuffer
)
203 /* place the parameters on the 'stack' */
204 clientStreamNode
*prev
;
205 assert(thisObject
&& http
&& thisObject
->prev());
206 prev
= thisObject
->prev();
208 debugs(87, 3, "clientStreamRead: Calling " << prev
->readfunc
<<
209 " with cbdata " << prev
->data
.getRaw() << " from node " << thisObject
);
210 thisObject
->readBuffer
= readBuffer
;
211 prev
->readfunc(prev
, http
);
215 \ingroup ClientStreamInternal
216 * Detach from the stream - only allowed for terminal members
222 clientStreamDetach(clientStreamNode
* thisObject
, ClientHttpRequest
* http
)
224 clientStreamNode
*temp
= thisObject
;
226 assert(thisObject
->node
.next
== NULL
);
227 debugs(87, 3, "clientStreamDetach: Detaching node " << thisObject
);
228 /* And clean up thisObject node */
229 /* ESI TODO: push refcount class through to head */
230 clientStreamNode
*prev
= NULL
;
232 if (thisObject
->prev())
233 prev
= cbdataReference(thisObject
->prev());
235 thisObject
->removeFromStream();
237 cbdataReferenceDone(temp
);
239 cbdataFree(thisObject
);
241 /* and tell the prev that the detach has occured */
243 * We do it in thisObject order so that the detaching node is always
244 * at the end of the list
248 debugs(87, 3, "clientStreamDetach: Calling " << prev
->detach
<< " with cbdata " << prev
->data
.getRaw());
250 if (cbdataReferenceValid(prev
))
251 prev
->detach(prev
, http
);
253 cbdataReferenceDone(prev
);
258 \ingroup ClientStreamInternal
259 * Abort the stream - detach every node in the pipeline.
265 clientStreamAbort(clientStreamNode
* thisObject
, ClientHttpRequest
* http
)
269 assert(thisObject
!= NULL
);
270 assert(http
!= NULL
);
271 list
= thisObject
->head
;
272 debugs(87, 3, "clientStreamAbort: Aborting stream with tail " << list
->tail
);
275 clientStreamDetach((clientStreamNode
*)list
->tail
->data
, http
);
280 \ingroup ClientStreamInternal
281 * Call the upstream node to find it's status
286 clientStream_status_t
287 clientStreamStatus(clientStreamNode
* thisObject
, ClientHttpRequest
* http
)
289 clientStreamNode
*prev
;
290 assert(thisObject
&& http
&& thisObject
->node
.prev
);
291 prev
= (clientStreamNode
*)thisObject
->node
.prev
->data
;
292 return prev
->status(prev
, http
);
295 /* Local function bodies */
298 clientStreamNode::removeFromStream()
301 dlinkDelete(&node
, head
);
306 /// \ingroup ClientStreamInternal
308 clientStreamFree(void *foo
)
310 clientStreamNode
*thisObject
= (clientStreamNode
*)foo
;
312 debugs(87, 3, "Freeing clientStreamNode " << thisObject
);
314 thisObject
->removeFromStream();
315 thisObject
->data
= NULL
;
319 clientStreamNode::prev() const
322 return (clientStreamNode
*)node
.prev
->data
;
328 clientStreamNode::next() const
331 return (clientStreamNode
*)node
.next
->data
;