]> git.ipfire.org Git - thirdparty/squid.git/blob - src/clientStream.cc
merge from trunk r12441
[thirdparty/squid.git] / src / clientStream.cc
1
2 /*
3 * DEBUG: section 87 Client-side Stream routines.
4 * AUTHOR: Robert Collins
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
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.
17 *
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.
22 *
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.
27 *
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.
31 *
32 */
33
34 #include "squid.h"
35 #include "clientStream.h"
36 #include "HttpReply.h"
37 #include "HttpRequest.h"
38 #include "client_side_request.h"
39
40 /**
41 \defgroup ClientStreamInternal Client Streams Internals
42 \ingroup ClientStreamAPI
43 \par
44 * A client Stream is a uni directional pipe, with the usual non-blocking
45 * asynchronous approach present elsewhere in squid.
46 *
47 \par
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
50 * step.
51 *
52 \par
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.
60 *
61 \par
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.
65 *
66 \section QuickNotes Quick Notes
67 \par
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.
74 *
75 \par
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.
82 *
83 \par
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.
87 *
88 \par
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.
94 *
95 \code
96 mycontext = thisObject->data;
97 thisObject->data = NULL;
98 clientStreamFree (thisObject->head);
99 mycontext = NULL;
100 return;
101 \endcode
102 *
103 \todo rather than each node undeleting the next, have a clientStreamDelete that walks the list.
104 */
105
106 /// \ingroup ClientStreamInternal
107 CBDATA_TYPE(clientStreamNode);
108
109 /* Local functions */
110 static FREE clientStreamFree;
111
112 /// \ingroup ClientStreamInternal
113 clientStreamNode *
114 clientStreamNew(CSR * readfunc, CSCB * callback, CSD * detach, CSS * status,
115 ClientStreamData data)
116 {
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;
124 temp->data = data;
125 return temp;
126 }
127
128 /**
129 \ingroup ClientStreamInternal
130 * Initialise a client Stream.
131 * list is the 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.
135 */
136 void
137 clientStreamInit(dlink_list * list, CSR * func, CSD * rdetach, CSS * readstatus,
138 ClientStreamData readdata, CSCB * callback, CSD * cdetach, ClientStreamData callbackdata,
139 StoreIOBuffer tailBuffer)
140 {
141 clientStreamNode *temp = clientStreamNew(func, NULL, rdetach, readstatus,
142 readdata);
143 dlinkAdd(cbdataReference(temp), &temp->node, list);
144 temp->head = list;
145 clientStreamInsertHead(list, NULL, callback, cdetach, NULL, callbackdata);
146 temp = (clientStreamNode *)list->tail->data;
147 temp->readBuffer = tailBuffer;
148 }
149
150 /**
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.
155 */
156 void
157 clientStreamInsertHead(dlink_list * list, CSR * func, CSCB * callback,
158 CSD * detach, CSS * status, ClientStreamData data)
159 {
160
161 /* test preconditions */
162 assert(list != NULL);
163 assert(list->head);
164 clientStreamNode *temp = clientStreamNew(func, callback, detach, status, data);
165 temp->head = list;
166 debugs(87, 3, "clientStreamInsertHead: Inserted node " << temp <<
167 " with data " << data.getRaw() << " after head");
168
169 if (list->head->next)
170 temp->readBuffer = ((clientStreamNode *)list->head->next->data)->readBuffer;
171
172 dlinkAddAfter(cbdataReference(temp), &temp->node, list->head, list);
173 }
174
175 // API
176 void
177 clientStreamCallback(clientStreamNode * thisObject, ClientHttpRequest * http,
178 HttpReply * rep, StoreIOBuffer replyBuffer)
179 {
180 clientStreamNode *next;
181 assert(thisObject && http && thisObject->node.next);
182 next = thisObject->next();
183
184 debugs(87, 3, "clientStreamCallback: Calling " << next->callback << " with cbdata " <<
185 next->data.getRaw() << " from node " << thisObject);
186 next->callback(next, http, rep, replyBuffer);
187 }
188
189 /**
190 \ingroup ClientStreamInternal
191 * Call the previous node in the chain to read some data
192 *
193 \param thisObject ??
194 \param http ??
195 \param readBuffer ??
196 */
197 void
198 clientStreamRead(clientStreamNode * thisObject, ClientHttpRequest * http,
199 StoreIOBuffer readBuffer)
200 {
201 /* place the parameters on the 'stack' */
202 clientStreamNode *prev;
203 assert(thisObject && http && thisObject->prev());
204 prev = thisObject->prev();
205
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);
210 }
211
212 /**
213 \ingroup ClientStreamInternal
214 * Detach from the stream - only allowed for terminal members
215 *
216 \param thisObject ??
217 \param http ??
218 */
219 void
220 clientStreamDetach(clientStreamNode * thisObject, ClientHttpRequest * http)
221 {
222 clientStreamNode *temp = thisObject;
223
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;
229
230 if (thisObject->prev())
231 prev = cbdataReference(thisObject->prev());
232
233 thisObject->removeFromStream();
234
235 cbdataReferenceDone(temp);
236
237 cbdataFree(thisObject);
238
239 /* and tell the prev that the detach has occured */
240 /*
241 * We do it in thisObject order so that the detaching node is always
242 * at the end of the list
243 */
244
245 if (prev) {
246 debugs(87, 3, "clientStreamDetach: Calling " << prev->detach << " with cbdata " << prev->data.getRaw());
247
248 if (cbdataReferenceValid(prev))
249 prev->detach(prev, http);
250
251 cbdataReferenceDone(prev);
252 }
253 }
254
255 /**
256 \ingroup ClientStreamInternal
257 * Abort the stream - detach every node in the pipeline.
258 *
259 \param thisObject ??
260 \param http ??
261 */
262 void
263 clientStreamAbort(clientStreamNode * thisObject, ClientHttpRequest * http)
264 {
265 dlink_list *list;
266
267 assert(thisObject != NULL);
268 assert(http != NULL);
269 list = thisObject->head;
270 debugs(87, 3, "clientStreamAbort: Aborting stream with tail " << list->tail);
271
272 if (list->tail) {
273 clientStreamDetach((clientStreamNode *)list->tail->data, http);
274 }
275 }
276
277 /**
278 \ingroup ClientStreamInternal
279 * Call the upstream node to find it's status
280 *
281 \param thisObject ??
282 \param http ??
283 */
284 clientStream_status_t
285 clientStreamStatus(clientStreamNode * thisObject, ClientHttpRequest * http)
286 {
287 clientStreamNode *prev;
288 assert(thisObject && http && thisObject->node.prev);
289 prev = (clientStreamNode *)thisObject->node.prev->data;
290 return prev->status(prev, http);
291 }
292
293 /* Local function bodies */
294
295 void
296 clientStreamNode::removeFromStream()
297 {
298 if (head)
299 dlinkDelete(&node, head);
300
301 head = NULL;
302 }
303
304 /// \ingroup ClientStreamInternal
305 void
306 clientStreamFree(void *foo)
307 {
308 clientStreamNode *thisObject = (clientStreamNode *)foo;
309
310 debugs(87, 3, "Freeing clientStreamNode " << thisObject);
311
312 thisObject->removeFromStream();
313 thisObject->data = NULL;
314 }
315
316 clientStreamNode *
317 clientStreamNode::prev() const
318 {
319 if (node.prev)
320 return (clientStreamNode *)node.prev->data;
321 else
322 return NULL;
323 }
324
325 clientStreamNode *
326 clientStreamNode::next() const
327 {
328 if (node.next)
329 return (clientStreamNode *)node.next->data;
330 else
331 return NULL;
332 }