3 * DEBUG: section 87 Client-side Stream routines.
4 * AUTHOR: Robert Collins
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with thisObject program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
35 #include "client_side_request.h"
36 #include "clientStream.h"
37 #include "HttpReply.h"
38 #include "HttpRequest.h"
41 \defgroup ClientStreamInternal Client Streams Internals
42 \ingroup ClientStreamAPI
44 * A client Stream is a uni directional pipe, with the usual non-blocking
45 * asynchronous approach present elsewhere in squid.
48 * Each pipe node has a data push function, and a data request function.
49 * This limits flexability - the data flow is no longer assembled at each
53 * An alternative approach is to pass each node in the pipe the call-
54 * back to use on each IO call. This allows the callbacks to be changed
55 * very easily by a participating node, but requires more maintenance
56 * in each node (store the callback to the most recent IO request in
57 * the nodes context.) Such an approach also prevents dynamically
58 * changing the pipeline from outside without an additional interface
59 * method to extract the callback and context from the next node.
62 * One important characteristic of the stream is that the readfunc
63 * on the terminating node, and the callback on the first node
64 * will be NULL, and never used.
66 \section QuickNotes Quick Notes
68 * Each node including the HEAD of the clientStream has a cbdataReference
69 * held by the stream. Freeing the stream then removes that reference
70 * and cbdataFree()'s every node.
71 * Any node with other References, and all nodes downstream will only
72 * free when those references are released.
73 * Stream nodes MAY hold references to the data member of the node.
76 * Specifically - on creation no reference is made.
77 * If you pass a data variable to a node, give it an initial reference.
78 * If the data member is non-null on FREE, cbdataFree WILL be called.
79 * This you must never call cbdataFree on your own context without
80 * explicitly setting the stream node data member to NULL and
81 * cbdataReferenceDone'ing it.
84 * No data member may hold a reference to it's stream node.
85 * The stream guarantees that DETACH will be called before
86 * freeing the node, alowing data members to cleanup.
89 * If a node's data holds a reference to something that needs to
90 * free the stream a circular reference list will occur.
91 * This results no data being freed until that reference is removed.
92 * One way to accomplish thisObject is to explicitly remove the
93 * data from your own node before freeing the stream.
96 mycontext = thisObject->data;
97 thisObject->data = NULL;
98 clientStreamFree (thisObject->head);
103 \todo rather than each node undeleting the next, have a clientStreamDelete that walks the list.
106 /// \ingroup ClientStreamInternal
107 CBDATA_TYPE(clientStreamNode
);
109 /* Local functions */
110 static FREE clientStreamFree
;
112 /// \ingroup ClientStreamInternal
114 clientStreamNew(CSR
* readfunc
, CSCB
* callback
, CSD
* detach
, CSS
* status
,
115 ClientStreamData data
)
117 clientStreamNode
*temp
;
118 CBDATA_INIT_TYPE_FREECB(clientStreamNode
, clientStreamFree
);
119 temp
= cbdataAlloc(clientStreamNode
);
120 temp
->readfunc
= readfunc
;
121 temp
->callback
= callback
;
122 temp
->detach
= detach
;
123 temp
->status
= status
;
129 \ingroup ClientStreamInternal
130 * Initialise a client Stream.
132 * func is the read function for the head
133 * callback is the callback for the tail
134 * tailbuf and taillen are the initial buffer and length for the tail.
137 clientStreamInit(dlink_list
* list
, CSR
* func
, CSD
* rdetach
, CSS
* readstatus
,
138 ClientStreamData readdata
, CSCB
* callback
, CSD
* cdetach
, ClientStreamData callbackdata
,
139 StoreIOBuffer tailBuffer
)
141 clientStreamNode
*temp
= clientStreamNew(func
, NULL
, rdetach
, readstatus
,
143 dlinkAdd(cbdataReference(temp
), &temp
->node
, list
);
145 clientStreamInsertHead(list
, NULL
, callback
, cdetach
, NULL
, callbackdata
);
146 temp
= (clientStreamNode
*)list
->tail
->data
;
147 temp
->readBuffer
= tailBuffer
;
151 \ingroup ClientStreamInternal
152 * Doesn't actually insert at head. Instead it inserts one *after*
153 * head. This is because HEAD is a special node, as is tail
154 * This function is not suitable for inserting the real HEAD.
157 clientStreamInsertHead(dlink_list
* list
, CSR
* func
, CSCB
* callback
,
158 CSD
* detach
, CSS
* status
, ClientStreamData data
)
161 /* test preconditions */
162 assert(list
!= NULL
);
164 clientStreamNode
*temp
= clientStreamNew(func
, callback
, detach
, status
, data
);
166 debugs(87, 3, "clientStreamInsertHead: Inserted node " << temp
<<
167 " with data " << data
.getRaw() << " after head");
169 if (list
->head
->next
)
170 temp
->readBuffer
= ((clientStreamNode
*)list
->head
->next
->data
)->readBuffer
;
172 dlinkAddAfter(cbdataReference(temp
), &temp
->node
, list
->head
, list
);
177 clientStreamCallback(clientStreamNode
* thisObject
, ClientHttpRequest
* http
,
178 HttpReply
* rep
, StoreIOBuffer replyBuffer
)
180 clientStreamNode
*next
;
181 assert(thisObject
&& http
&& thisObject
->node
.next
);
182 next
= thisObject
->next();
184 debugs(87, 3, "clientStreamCallback: Calling " << next
->callback
<< " with cbdata " <<
185 next
->data
.getRaw() << " from node " << thisObject
);
186 next
->callback(next
, http
, rep
, replyBuffer
);
190 \ingroup ClientStreamInternal
191 * Call the previous node in the chain to read some data
198 clientStreamRead(clientStreamNode
* thisObject
, ClientHttpRequest
* http
,
199 StoreIOBuffer readBuffer
)
201 /* place the parameters on the 'stack' */
202 clientStreamNode
*prev
;
203 assert(thisObject
&& http
&& thisObject
->prev());
204 prev
= thisObject
->prev();
206 debugs(87, 3, "clientStreamRead: Calling " << prev
->readfunc
<<
207 " with cbdata " << prev
->data
.getRaw() << " from node " << thisObject
);
208 thisObject
->readBuffer
= readBuffer
;
209 prev
->readfunc(prev
, http
);
213 \ingroup ClientStreamInternal
214 * Detach from the stream - only allowed for terminal members
220 clientStreamDetach(clientStreamNode
* thisObject
, ClientHttpRequest
* http
)
222 clientStreamNode
*temp
= thisObject
;
224 assert(thisObject
->node
.next
== NULL
);
225 debugs(87, 3, "clientStreamDetach: Detaching node " << thisObject
);
226 /* And clean up thisObject node */
227 /* ESI TODO: push refcount class through to head */
228 clientStreamNode
*prev
= NULL
;
230 if (thisObject
->prev())
231 prev
= cbdataReference(thisObject
->prev());
233 thisObject
->removeFromStream();
235 cbdataReferenceDone(temp
);
237 cbdataFree(thisObject
);
239 /* and tell the prev that the detach has occured */
241 * We do it in thisObject order so that the detaching node is always
242 * at the end of the list
246 debugs(87, 3, "clientStreamDetach: Calling " << prev
->detach
<< " with cbdata " << prev
->data
.getRaw());
248 if (cbdataReferenceValid(prev
))
249 prev
->detach(prev
, http
);
251 cbdataReferenceDone(prev
);
256 \ingroup ClientStreamInternal
257 * Abort the stream - detach every node in the pipeline.
263 clientStreamAbort(clientStreamNode
* thisObject
, ClientHttpRequest
* http
)
267 assert(thisObject
!= NULL
);
268 assert(http
!= NULL
);
269 list
= thisObject
->head
;
270 debugs(87, 3, "clientStreamAbort: Aborting stream with tail " << list
->tail
);
273 clientStreamDetach((clientStreamNode
*)list
->tail
->data
, http
);
278 \ingroup ClientStreamInternal
279 * Call the upstream node to find it's status
284 clientStream_status_t
285 clientStreamStatus(clientStreamNode
* thisObject
, ClientHttpRequest
* http
)
287 clientStreamNode
*prev
;
288 assert(thisObject
&& http
&& thisObject
->node
.prev
);
289 prev
= (clientStreamNode
*)thisObject
->node
.prev
->data
;
290 return prev
->status(prev
, http
);
293 /* Local function bodies */
296 clientStreamNode::removeFromStream()
299 dlinkDelete(&node
, head
);
304 /// \ingroup ClientStreamInternal
306 clientStreamFree(void *foo
)
308 clientStreamNode
*thisObject
= (clientStreamNode
*)foo
;
310 debugs(87, 3, "Freeing clientStreamNode " << thisObject
);
312 thisObject
->removeFromStream();
313 thisObject
->data
= NULL
;
317 clientStreamNode::prev() const
320 return (clientStreamNode
*)node
.prev
->data
;
326 clientStreamNode::next() const
329 return (clientStreamNode
*)node
.next
->data
;