]> git.ipfire.org Git - thirdparty/squid.git/blame_incremental - src/clientStream.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / clientStream.cc
... / ...
CommitLineData
1/*
2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 87 Client-side Stream routines. */
10
11#include "squid.h"
12#include "client_side_request.h"
13#include "clientStream.h"
14#include "HttpReply.h"
15#include "HttpRequest.h"
16
17/**
18 \defgroup ClientStreamInternal Client Streams Internals
19 \ingroup ClientStreamAPI
20 \par
21 * A client Stream is a uni directional pipe, with the usual non-blocking
22 * asynchronous approach present elsewhere in squid.
23 *
24 \par
25 * Each pipe node has a data push function, and a data request function.
26 * This limits flexability - the data flow is no longer assembled at each
27 * step.
28 *
29 \par
30 * An alternative approach is to pass each node in the pipe the call-
31 * back to use on each IO call. This allows the callbacks to be changed
32 * very easily by a participating node, but requires more maintenance
33 * in each node (store the callback to the most recent IO request in
34 * the nodes context.) Such an approach also prevents dynamically
35 * changing the pipeline from outside without an additional interface
36 * method to extract the callback and context from the next node.
37 *
38 \par
39 * One important characteristic of the stream is that the readfunc
40 * on the terminating node, and the callback on the first node
41 * will be NULL, and never used.
42 *
43 \section QuickNotes Quick Notes
44 \par
45 * Each node including the HEAD of the clientStream has a cbdataReference
46 * held by the stream. Freeing the stream then removes that reference
47 * and cbdataFree()'s every node.
48 * Any node with other References, and all nodes downstream will only
49 * free when those references are released.
50 * Stream nodes MAY hold references to the data member of the node.
51 *
52 \par
53 * Specifically - on creation no reference is made.
54 * If you pass a data variable to a node, give it an initial reference.
55 * If the data member is non-null on FREE, cbdataFree WILL be called.
56 * This you must never call cbdataFree on your own context without
57 * explicitly setting the stream node data member to NULL and
58 * cbdataReferenceDone'ing it.
59 *
60 \par
61 * No data member may hold a reference to it's stream node.
62 * The stream guarantees that DETACH will be called before
63 * freeing the node, alowing data members to cleanup.
64 *
65 \par
66 * If a node's data holds a reference to something that needs to
67 * free the stream a circular reference list will occur.
68 * This results no data being freed until that reference is removed.
69 * One way to accomplish thisObject is to explicitly remove the
70 * data from your own node before freeing the stream.
71 *
72 \code
73 mycontext = thisObject->data;
74 thisObject->data = NULL;
75 clientStreamFree (thisObject->head);
76 mycontext = NULL;
77 return;
78 \endcode
79 *
80 \todo rather than each node undeleting the next, have a clientStreamDelete that walks the list.
81 */
82
83/// \ingroup ClientStreamInternal
84CBDATA_TYPE(clientStreamNode);
85
86/* Local functions */
87static FREE clientStreamFree;
88
89/// \ingroup ClientStreamInternal
90clientStreamNode *
91clientStreamNew(CSR * readfunc, CSCB * callback, CSD * detach, CSS * status,
92 ClientStreamData data)
93{
94 clientStreamNode *temp;
95 CBDATA_INIT_TYPE_FREECB(clientStreamNode, clientStreamFree);
96 temp = cbdataAlloc(clientStreamNode);
97 temp->readfunc = readfunc;
98 temp->callback = callback;
99 temp->detach = detach;
100 temp->status = status;
101 temp->data = data;
102 return temp;
103}
104
105/**
106 \ingroup ClientStreamInternal
107 * Initialise a client Stream.
108 * list is the stream
109 * func is the read function for the head
110 * callback is the callback for the tail
111 * tailbuf and taillen are the initial buffer and length for the tail.
112 */
113void
114clientStreamInit(dlink_list * list, CSR * func, CSD * rdetach, CSS * readstatus,
115 ClientStreamData readdata, CSCB * callback, CSD * cdetach, ClientStreamData callbackdata,
116 StoreIOBuffer tailBuffer)
117{
118 clientStreamNode *temp = clientStreamNew(func, NULL, rdetach, readstatus,
119 readdata);
120 dlinkAdd(cbdataReference(temp), &temp->node, list);
121 temp->head = list;
122 clientStreamInsertHead(list, NULL, callback, cdetach, NULL, callbackdata);
123 temp = (clientStreamNode *)list->tail->data;
124 temp->readBuffer = tailBuffer;
125}
126
127/**
128 \ingroup ClientStreamInternal
129 * Doesn't actually insert at head. Instead it inserts one *after*
130 * head. This is because HEAD is a special node, as is tail
131 * This function is not suitable for inserting the real HEAD.
132 */
133void
134clientStreamInsertHead(dlink_list * list, CSR * func, CSCB * callback,
135 CSD * detach, CSS * status, ClientStreamData data)
136{
137
138 /* test preconditions */
139 assert(list != NULL);
140 assert(list->head);
141 clientStreamNode *temp = clientStreamNew(func, callback, detach, status, data);
142 temp->head = list;
143 debugs(87, 3, "clientStreamInsertHead: Inserted node " << temp <<
144 " with data " << data.getRaw() << " after head");
145
146 if (list->head->next)
147 temp->readBuffer = ((clientStreamNode *)list->head->next->data)->readBuffer;
148
149 dlinkAddAfter(cbdataReference(temp), &temp->node, list->head, list);
150}
151
152// API
153void
154clientStreamCallback(clientStreamNode * thisObject, ClientHttpRequest * http,
155 HttpReply * rep, StoreIOBuffer replyBuffer)
156{
157 clientStreamNode *next;
158 assert(thisObject && http && thisObject->node.next);
159 next = thisObject->next();
160
161 debugs(87, 3, "clientStreamCallback: Calling " << next->callback << " with cbdata " <<
162 next->data.getRaw() << " from node " << thisObject);
163 next->callback(next, http, rep, replyBuffer);
164}
165
166/**
167 \ingroup ClientStreamInternal
168 * Call the previous node in the chain to read some data
169 *
170 \param thisObject ??
171 \param http ??
172 \param readBuffer ??
173 */
174void
175clientStreamRead(clientStreamNode * thisObject, ClientHttpRequest * http,
176 StoreIOBuffer readBuffer)
177{
178 /* place the parameters on the 'stack' */
179 clientStreamNode *prev;
180 assert(thisObject && http && thisObject->prev());
181 prev = thisObject->prev();
182
183 debugs(87, 3, "clientStreamRead: Calling " << prev->readfunc <<
184 " with cbdata " << prev->data.getRaw() << " from node " << thisObject);
185 thisObject->readBuffer = readBuffer;
186 prev->readfunc(prev, http);
187}
188
189/**
190 \ingroup ClientStreamInternal
191 * Detach from the stream - only allowed for terminal members
192 *
193 \param thisObject ??
194 \param http ??
195 */
196void
197clientStreamDetach(clientStreamNode * thisObject, ClientHttpRequest * http)
198{
199 clientStreamNode *temp = thisObject;
200
201 assert(thisObject->node.next == NULL);
202 debugs(87, 3, "clientStreamDetach: Detaching node " << thisObject);
203 /* And clean up thisObject node */
204 /* ESI TODO: push refcount class through to head */
205 clientStreamNode *prev = NULL;
206
207 if (thisObject->prev())
208 prev = cbdataReference(thisObject->prev());
209
210 thisObject->removeFromStream();
211
212 cbdataReferenceDone(temp);
213
214 cbdataFree(thisObject);
215
216 /* and tell the prev that the detach has occured */
217 /*
218 * We do it in thisObject order so that the detaching node is always
219 * at the end of the list
220 */
221
222 if (prev) {
223 debugs(87, 3, "clientStreamDetach: Calling " << prev->detach << " with cbdata " << prev->data.getRaw());
224
225 if (cbdataReferenceValid(prev))
226 prev->detach(prev, http);
227
228 cbdataReferenceDone(prev);
229 }
230}
231
232/**
233 \ingroup ClientStreamInternal
234 * Abort the stream - detach every node in the pipeline.
235 *
236 \param thisObject ??
237 \param http ??
238 */
239void
240clientStreamAbort(clientStreamNode * thisObject, ClientHttpRequest * http)
241{
242 dlink_list *list;
243
244 assert(thisObject != NULL);
245 assert(http != NULL);
246 list = thisObject->head;
247 debugs(87, 3, "clientStreamAbort: Aborting stream with tail " << list->tail);
248
249 if (list->tail) {
250 clientStreamDetach((clientStreamNode *)list->tail->data, http);
251 }
252}
253
254/**
255 \ingroup ClientStreamInternal
256 * Call the upstream node to find it's status
257 *
258 \param thisObject ??
259 \param http ??
260 */
261clientStream_status_t
262clientStreamStatus(clientStreamNode * thisObject, ClientHttpRequest * http)
263{
264 clientStreamNode *prev;
265 assert(thisObject && http && thisObject->node.prev);
266 prev = (clientStreamNode *)thisObject->node.prev->data;
267 return prev->status(prev, http);
268}
269
270/* Local function bodies */
271
272void
273clientStreamNode::removeFromStream()
274{
275 if (head)
276 dlinkDelete(&node, head);
277
278 head = NULL;
279}
280
281/// \ingroup ClientStreamInternal
282void
283clientStreamFree(void *foo)
284{
285 clientStreamNode *thisObject = (clientStreamNode *)foo;
286
287 debugs(87, 3, "Freeing clientStreamNode " << thisObject);
288
289 thisObject->removeFromStream();
290 thisObject->data = NULL;
291}
292
293clientStreamNode *
294clientStreamNode::prev() const
295{
296 if (node.prev)
297 return (clientStreamNode *)node.prev->data;
298 else
299 return NULL;
300}
301
302clientStreamNode *
303clientStreamNode::next() const
304{
305 if (node.next)
306 return (clientStreamNode *)node.next->data;
307 else
308 return NULL;
309}
310