]> git.ipfire.org Git - thirdparty/squid.git/blame - src/clientStream.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / clientStream.cc
CommitLineData
edce4d98 1
2/*
262a0e14 3 * $Id$
edce4d98 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.
26ac0430 24 *
edce4d98 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.
26ac0430 29 *
edce4d98 30 * You should have received a copy of the GNU General Public License
e6ccf245 31 * along with thisObject program; if not, write to the Free Software
edce4d98 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
582c2af2 36#include "squid.h"
63be0a78 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
edce4d98 46 * A client Stream is a uni directional pipe, with the usual non-blocking
47 * asynchronous approach present elsewhere in squid.
48 *
63be0a78 49 \par
edce4d98 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
26ac0430 52 * step.
edce4d98 53 *
63be0a78 54 \par
edce4d98 55 * An alternative approach is to pass each node in the pipe the call-
26ac0430
AJ
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
edce4d98 60 * changing the pipeline from outside without an additional interface
61 * method to extract the callback and context from the next node.
62 *
63be0a78 63 \par
edce4d98 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.
edce4d98 67 *
63be0a78 68 \section QuickNotes Quick Notes
69 \par
edce4d98 70 * Each node including the HEAD of the clientStream has a cbdataReference
71 * held by the stream. Freeing the stream then removes that reference
63be0a78 72 * and cbdataFree()'s every node.
26ac0430 73 * Any node with other References, and all nodes downstream will only
edce4d98 74 * free when those references are released.
26ac0430 75 * Stream nodes MAY hold references to the data member of the node.
edce4d98 76 *
63be0a78 77 \par
26ac0430 78 * Specifically - on creation no reference is made.
edce4d98 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 *
63be0a78 85 \par
edce4d98 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.
26ac0430 89 *
63be0a78 90 \par
edce4d98 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.
e6ccf245 94 * One way to accomplish thisObject is to explicitly remove the
edce4d98 95 * data from your own node before freeing the stream.
96 *
63be0a78 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.
edce4d98 106 */
107
63be0a78 108/// \ingroup ClientStreamInternal
109CBDATA_TYPE(clientStreamNode);
110
edce4d98 111/* Local functions */
112static FREE clientStreamFree;
113
63be0a78 114/// \ingroup ClientStreamInternal
edce4d98 115clientStreamNode *
116clientStreamNew(CSR * readfunc, CSCB * callback, CSD * detach, CSS * status,
0655fa4d 117 ClientStreamData data)
edce4d98 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
63be0a78 130/**
131 \ingroup ClientStreamInternal
edce4d98 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 */
138void
139clientStreamInit(dlink_list * list, CSR * func, CSD * rdetach, CSS * readstatus,
0655fa4d 140 ClientStreamData readdata, CSCB * callback, CSD * cdetach, ClientStreamData callbackdata,
62e76326 141 StoreIOBuffer tailBuffer)
edce4d98 142{
143 clientStreamNode *temp = clientStreamNew(func, NULL, rdetach, readstatus,
62e76326 144 readdata);
7e6b941f 145 dlinkAdd(cbdataReference(temp), &temp->node, list);
edce4d98 146 temp->head = list;
147 clientStreamInsertHead(list, NULL, callback, cdetach, NULL, callbackdata);
e6ccf245 148 temp = (clientStreamNode *)list->tail->data;
c8be6d7b 149 temp->readBuffer = tailBuffer;
edce4d98 150}
151
63be0a78 152/**
153 \ingroup ClientStreamInternal
edce4d98 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.
edce4d98 157 */
158void
159clientStreamInsertHead(dlink_list * list, CSR * func, CSCB * callback,
0655fa4d 160 CSD * detach, CSS * status, ClientStreamData data)
edce4d98 161{
edce4d98 162
163 /* test preconditions */
164 assert(list != NULL);
165 assert(list->head);
0655fa4d 166 clientStreamNode *temp = clientStreamNew(func, callback, detach, status, data);
edce4d98 167 temp->head = list;
bf8fe701 168 debugs(87, 3, "clientStreamInsertHead: Inserted node " << temp <<
169 " with data " << data.getRaw() << " after head");
43ae1d95 170
171 if (list->head->next)
172 temp->readBuffer = ((clientStreamNode *)list->head->next->data)->readBuffer;
173
7e6b941f 174 dlinkAddAfter(cbdataReference(temp), &temp->node, list->head, list);
edce4d98 175}
176
63be0a78 177// API
edce4d98 178void
59a1efb2 179clientStreamCallback(clientStreamNode * thisObject, ClientHttpRequest * http,
62e76326 180 HttpReply * rep, StoreIOBuffer replyBuffer)
edce4d98 181{
182 clientStreamNode *next;
e6ccf245 183 assert(thisObject && http && thisObject->node.next);
184 next = thisObject->next();
edce4d98 185
26ac0430 186 debugs(87, 3, "clientStreamCallback: Calling " << next->callback << " with cbdata " <<
bf8fe701 187 next->data.getRaw() << " from node " << thisObject);
c8be6d7b 188 next->callback(next, http, rep, replyBuffer);
edce4d98 189}
190
63be0a78 191/**
192 \ingroup ClientStreamInternal
edce4d98 193 * Call the previous node in the chain to read some data
63be0a78 194 *
195 \param thisObject ??
196 \param http ??
197 \param readBuffer ??
edce4d98 198 */
199void
59a1efb2 200clientStreamRead(clientStreamNode * thisObject, ClientHttpRequest * http,
62e76326 201 StoreIOBuffer readBuffer)
edce4d98 202{
203 /* place the parameters on the 'stack' */
204 clientStreamNode *prev;
e6ccf245 205 assert(thisObject && http && thisObject->prev());
206 prev = thisObject->prev();
edce4d98 207
bf8fe701 208 debugs(87, 3, "clientStreamRead: Calling " << prev->readfunc <<
209 " with cbdata " << prev->data.getRaw() << " from node " << thisObject);
e6ccf245 210 thisObject->readBuffer = readBuffer;
edce4d98 211 prev->readfunc(prev, http);
212}
213
63be0a78 214/**
215 \ingroup ClientStreamInternal
edce4d98 216 * Detach from the stream - only allowed for terminal members
63be0a78 217 *
218 \param thisObject ??
219 \param http ??
edce4d98 220 */
221void
59a1efb2 222clientStreamDetach(clientStreamNode * thisObject, ClientHttpRequest * http)
edce4d98 223{
e6ccf245 224 clientStreamNode *temp = thisObject;
edce4d98 225
e6ccf245 226 assert(thisObject->node.next == NULL);
bf8fe701 227 debugs(87, 3, "clientStreamDetach: Detaching node " << thisObject);
e6ccf245 228 /* And clean up thisObject node */
edce4d98 229 /* ESI TODO: push refcount class through to head */
0655fa4d 230 clientStreamNode *prev = NULL;
231
232 if (thisObject->prev())
233 prev = cbdataReference(thisObject->prev());
234
235 thisObject->removeFromStream();
236
edce4d98 237 cbdataReferenceDone(temp);
0655fa4d 238
e6ccf245 239 cbdataFree(thisObject);
0655fa4d 240
edce4d98 241 /* and tell the prev that the detach has occured */
242 /*
e6ccf245 243 * We do it in thisObject order so that the detaching node is always
edce4d98 244 * at the end of the list
245 */
62e76326 246
edce4d98 247 if (prev) {
bf8fe701 248 debugs(87, 3, "clientStreamDetach: Calling " << prev->detach << " with cbdata " << prev->data.getRaw());
0655fa4d 249
250 if (cbdataReferenceValid(prev))
251 prev->detach(prev, http);
252
253 cbdataReferenceDone(prev);
edce4d98 254 }
255}
256
63be0a78 257/**
258 \ingroup ClientStreamInternal
edce4d98 259 * Abort the stream - detach every node in the pipeline.
63be0a78 260 *
261 \param thisObject ??
262 \param http ??
edce4d98 263 */
264void
59a1efb2 265clientStreamAbort(clientStreamNode * thisObject, ClientHttpRequest * http)
edce4d98 266{
267 dlink_list *list;
268
e6ccf245 269 assert(thisObject != NULL);
edce4d98 270 assert(http != NULL);
e6ccf245 271 list = thisObject->head;
bf8fe701 272 debugs(87, 3, "clientStreamAbort: Aborting stream with tail " << list->tail);
62e76326 273
edce4d98 274 if (list->tail) {
62e76326 275 clientStreamDetach((clientStreamNode *)list->tail->data, http);
edce4d98 276 }
277}
278
63be0a78 279/**
280 \ingroup ClientStreamInternal
281 * Call the upstream node to find it's status
282 *
283 \param thisObject ??
284 \param http ??
edce4d98 285 */
286clientStream_status_t
59a1efb2 287clientStreamStatus(clientStreamNode * thisObject, ClientHttpRequest * http)
edce4d98 288{
289 clientStreamNode *prev;
e6ccf245 290 assert(thisObject && http && thisObject->node.prev);
291 prev = (clientStreamNode *)thisObject->node.prev->data;
edce4d98 292 return prev->status(prev, http);
293}
294
295/* Local function bodies */
63be0a78 296
0655fa4d 297void
298clientStreamNode::removeFromStream()
299{
300 if (head)
301 dlinkDelete(&node, head);
302
303 head = NULL;
304}
305
63be0a78 306/// \ingroup ClientStreamInternal
edce4d98 307void
308clientStreamFree(void *foo)
309{
e6ccf245 310 clientStreamNode *thisObject = (clientStreamNode *)foo;
edce4d98 311
bf8fe701 312 debugs(87, 3, "Freeing clientStreamNode " << thisObject);
62e76326 313
0655fa4d 314 thisObject->removeFromStream();
315 thisObject->data = NULL;
edce4d98 316}
e6ccf245 317
43ae1d95 318clientStreamNode *
319clientStreamNode::prev() const
e6ccf245 320{
321 if (node.prev)
43ae1d95 322 return (clientStreamNode *)node.prev->data;
e6ccf245 323 else
62e76326 324 return NULL;
e6ccf245 325}
326
43ae1d95 327clientStreamNode *
328clientStreamNode::next() const
e6ccf245 329{
330 if (node.next)
43ae1d95 331 return (clientStreamNode *)node.next->data;
e6ccf245 332 else
62e76326 333 return NULL;
e6ccf245 334}