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