]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_client.cc
added HDR_WAIT to storeEntryFlags() description
[thirdparty/squid.git] / src / store_client.cc
CommitLineData
9cef6668 1
2/*
db1cd23c 3 * $Id: store_client.cc,v 1.49 1998/12/05 00:54:44 wessels Exp $
9cef6668 4 *
5 * DEBUG: section 20 Storage Manager Client-Side Interface
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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 this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
f09f5b26 36#include "squid.h"
37
e3ef2b09 38/*
39 * NOTE: 'Header' refers to the swapfile metadata header.
40 * 'Body' refers to the swapfile body, which is the full
41 * HTTP reply (including HTTP headers and body).
42 */
43static DRCB storeClientReadBody;
44static DRCB storeClientReadHeader;
45static SIH storeClientFileOpened;
f09f5b26 46static void storeClientCopy2(StoreEntry * e, store_client * sc);
e3ef2b09 47static void storeClientFileRead(store_client * sc);
d6f51e3c 48static EVH storeClientCopyEvent;
7405a782 49static store_client_t storeClientType(StoreEntry *);
f09f5b26 50
51/* check if there is any client waiting for this object at all */
52/* return 1 if there is at least one client */
53int
54storeClientWaiting(const StoreEntry * e)
55{
56 MemObject *mem = e->mem_obj;
57 store_client *sc;
58 for (sc = mem->clients; sc; sc = sc->next) {
59 if (sc->callback_data != NULL)
60 return 1;
61 }
62 return 0;
63}
64
65store_client *
66storeClientListSearch(const MemObject * mem, void *data)
67{
68 store_client *sc;
69 for (sc = mem->clients; sc; sc = sc->next) {
70 if (sc->callback_data == data)
71 break;
72 }
73 return sc;
74}
75
7405a782 76static store_client_t
135171fe 77storeClientType(StoreEntry * e)
7405a782 78{
79 MemObject *mem = e->mem_obj;
7405a782 80 if (mem->inmem_lo)
81 return STORE_DISK_CLIENT;
6a888d16 82 if (e->store_status == STORE_ABORTED) {
83 /* I don't think we should be adding clients to aborted entries */
135171fe 84 debug(20, 1) ("storeClientType: adding to STORE_ABORTED entry\n");
7405a782 85 return STORE_MEM_CLIENT;
6a888d16 86 }
87 if (e->store_status == STORE_OK) {
88 if (mem->inmem_lo == 0 && mem->inmem_hi > 0)
89 return STORE_MEM_CLIENT;
90 else
91 return STORE_DISK_CLIENT;
92 }
93 /* here and past, entry is STORE_PENDING */
7405a782 94 /*
95 * If this is the first client, let it be the mem client
96 */
6a888d16 97 else if (mem->nclients == 1)
98 return STORE_MEM_CLIENT;
7405a782 99 /*
100 * otherwise, make subsequent clients read from disk so they
101 * can not delay the first, and vice-versa.
102 */
103 else
6a888d16 104 return STORE_DISK_CLIENT;
7405a782 105}
106
f09f5b26 107/* add client with fd to client list */
108void
109storeClientListAdd(StoreEntry * e, void *data)
110{
111 MemObject *mem = e->mem_obj;
112 store_client **T;
113 store_client *sc;
114 assert(mem);
115 if (storeClientListSearch(mem, data) != NULL)
116 return;
117 mem->nclients++;
7021844c 118 sc = memAllocate(MEM_STORE_CLIENT);
db1cd23c 119 cbdataAdd(sc, memFree, MEM_STORE_CLIENT); /* sc is callback_data for file_read */
f09f5b26 120 sc->callback_data = data;
121 sc->seen_offset = 0;
122 sc->copy_offset = 0;
123 sc->swapin_fd = -1;
f115fadd 124 sc->flags.disk_io_pending = 0;
07304bf9 125 sc->entry = e;
7405a782 126 sc->type = storeClientType(e);
127 if (sc->type == STORE_DISK_CLIENT)
698a99c2 128 /* assert we'll be able to get the data we want */
129 /* maybe we should open swapin_fd here */
5f25e839 130 assert(e->swap_file_number > -1 || storeSwapOutAble(e));
f09f5b26 131 for (T = &mem->clients; *T; T = &(*T)->next);
132 *T = sc;
133}
134
f115fadd 135static void
136storeClientCopyEvent(void *data)
137{
138 store_client *sc = data;
fe4a33ac 139 debug(20, 3) ("storeClientCopyEvent: Running\n");
67fd69de 140 sc->flags.copy_event_pending = 0;
a2899918 141 if (!sc->callback)
142 return;
f115fadd 143 storeClientCopy2(sc->entry, sc);
f115fadd 144}
145
f09f5b26 146/* copy bytes requested by the client */
147void
148storeClientCopy(StoreEntry * e,
149 off_t seen_offset,
150 off_t copy_offset,
151 size_t size,
152 char *buf,
153 STCB * callback,
154 void *data)
155{
156 store_client *sc;
f09f5b26 157 assert(e->store_status != STORE_ABORTED);
f09f5b26 158 debug(20, 3) ("storeClientCopy: %s, seen %d, want %d, size %d, cb %p, cbdata %p\n",
159 storeKeyText(e->key),
160 (int) seen_offset,
161 (int) copy_offset,
162 (int) size,
163 callback,
164 data);
165 sc = storeClientListSearch(e->mem_obj, data);
166 assert(sc != NULL);
167 assert(sc->callback == NULL);
168 sc->copy_offset = copy_offset;
169 sc->seen_offset = seen_offset;
170 sc->callback = callback;
171 sc->copy_buf = buf;
172 sc->copy_size = size;
173 sc->copy_offset = copy_offset;
67fd69de 174 storeClientCopy2(e, sc);
f09f5b26 175}
176
07304bf9 177/*
178 * This function is used below to decide if we have any more data to
0bb129ee 179 * send to the client. If the store_status is STORE_PENDING, then we
180 * do have more data to send. If its STORE_OK or STORE_ABORTED, then
181 * we continue checking. If the object length is negative, then we
182 * don't know the real length and must open the swap file to find out.
183 * If the length is >= 0, then we compare it to the requested copy
184 * offset.
07304bf9 185 */
186static int
187storeClientNoMoreToSend(StoreEntry * e, store_client * sc)
188{
189 ssize_t len;
0bb129ee 190 if (e->store_status == STORE_PENDING)
07304bf9 191 return 0;
192 if ((len = objectLen(e)) < 0)
193 return 0;
194 if (sc->copy_offset < len)
195 return 0;
196 return 1;
197}
198
f09f5b26 199static void
200storeClientCopy2(StoreEntry * e, store_client * sc)
201{
202 STCB *callback = sc->callback;
203 MemObject *mem = e->mem_obj;
204 size_t sz;
67fd69de 205 if (sc->flags.copy_event_pending)
206 return;
db1cd23c 207 if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
208 debug(20, 5) ("storeClientCopy2: returning because ENTRY_FWD_HDR_WAIT set\n");
7e3e1d01 209 return;
db1cd23c 210 }
67fd69de 211 if (sc->flags.store_copying) {
212 sc->flags.copy_event_pending = 1;
3d36b8ab 213 debug(20, 3) ("storeClientCopy2: Queueing storeClientCopyEvent()\n");
c43f5247 214 eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0);
67fd69de 215 return;
216 }
a455c097 217 cbdataLock(sc); /* ick, prevent sc from getting freed */
67fd69de 218 sc->flags.store_copying = 1;
f09f5b26 219 debug(20, 3) ("storeClientCopy2: %s\n", storeKeyText(e->key));
220 assert(callback != NULL);
0bb129ee 221 /*
222 * We used to check for STORE_ABORTED here. But there were some
223 * problems. For example, we might have a slow client (or two) and
224 * the server-side is reading far ahead and swapping to disk. Even
225 * if the server-side aborts, we want to give the client(s)
226 * everything we got before the abort condition occurred.
227 */
228 if (storeClientNoMoreToSend(e, sc)) {
f09f5b26 229 /* There is no more to send! */
230#if USE_ASYNC_IO
27002b34 231 if (sc->flags.disk_io_pending) {
f09f5b26 232 if (sc->swapin_fd >= 0)
233 aioCancel(sc->swapin_fd, NULL);
234 else
235 aioCancel(-1, sc);
236 }
237#endif
f115fadd 238 sc->flags.disk_io_pending = 0;
f09f5b26 239 sc->callback = NULL;
240 callback(sc->callback_data, sc->copy_buf, 0);
2a503f87 241 } else if (e->store_status == STORE_PENDING && sc->seen_offset >= mem->inmem_hi) {
f09f5b26 242 /* client has already seen this, wait for more */
243 debug(20, 3) ("storeClientCopy2: Waiting for more\n");
3450c822 244 } else if (sc->copy_offset >= mem->inmem_lo && sc->copy_offset < mem->inmem_hi) {
f09f5b26 245 /* What the client wants is in memory */
246 debug(20, 3) ("storeClientCopy2: Copying from memory\n");
18fe65d0 247 sz = stmemCopy(&mem->data_hdr, sc->copy_offset, sc->copy_buf, sc->copy_size);
f09f5b26 248#if USE_ASYNC_IO
27002b34 249 if (sc->flags.disk_io_pending) {
f09f5b26 250 if (sc->swapin_fd >= 0)
251 aioCancel(sc->swapin_fd, NULL);
252 else
253 aioCancel(-1, sc);
254 }
255#endif
f115fadd 256 sc->flags.disk_io_pending = 0;
f09f5b26 257 sc->callback = NULL;
258 callback(sc->callback_data, sc->copy_buf, sz);
259 } else if (sc->swapin_fd < 0) {
260 debug(20, 3) ("storeClientCopy2: Need to open swap in file\n");
261 assert(sc->type == STORE_DISK_CLIENT);
262 /* gotta open the swapin file */
263 /* assert(sc->copy_offset == 0); */
27002b34 264 if (!sc->flags.disk_io_pending) {
f115fadd 265 sc->flags.disk_io_pending = 1;
e3ef2b09 266 storeSwapInStart(e, storeClientFileOpened, sc);
f09f5b26 267 } else {
268 debug(20, 2) ("storeClientCopy2: Averted multiple fd operation\n");
269 }
270 } else {
271 debug(20, 3) ("storeClientCopy: reading from disk FD %d\n",
272 sc->swapin_fd);
273 assert(sc->type == STORE_DISK_CLIENT);
27002b34 274 if (!sc->flags.disk_io_pending) {
e3ef2b09 275 storeClientFileRead(sc);
f09f5b26 276 } else {
277 debug(20, 2) ("storeClientCopy2: Averted multiple fd operation\n");
278 }
279 }
67fd69de 280 sc->flags.store_copying = 0;
a455c097 281 cbdataUnlock(sc); /* ick, allow sc to be freed */
f09f5b26 282}
283
284static void
e3ef2b09 285storeClientFileOpened(int fd, void *data)
f09f5b26 286{
287 store_client *sc = data;
288 STCB *callback = sc->callback;
289 if (fd < 0) {
e3ef2b09 290 debug(20, 3) ("storeClientFileOpened: failed\n");
f115fadd 291 sc->flags.disk_io_pending = 0;
f09f5b26 292 sc->callback = NULL;
293 callback(sc->callback_data, sc->copy_buf, -1);
294 return;
295 }
296 sc->swapin_fd = fd;
e3ef2b09 297 storeClientFileRead(sc);
f09f5b26 298}
299
300static void
e3ef2b09 301storeClientFileRead(store_client * sc)
f09f5b26 302{
07304bf9 303 MemObject *mem = sc->entry->mem_obj;
f09f5b26 304 assert(sc->callback != NULL);
dee51794 305#ifdef OPTIMISTIC_IO
9d66d521 306 sc->flags.disk_io_pending = 1;
9d66d521 307#endif
dee51794 308 if (mem->swap_hdr_sz == 0) {
e3ef2b09 309 file_read(sc->swapin_fd,
d8b9a541 310 sc->copy_buf,
311 sc->copy_size,
e3ef2b09 312 0,
313 storeClientReadHeader,
314 sc);
9d66d521 315 } else {
3157c72f 316 if (sc->entry->swap_status == SWAPOUT_WRITING)
0cdcddb9 317 assert(mem->swapout.done_offset > sc->copy_offset + mem->swap_hdr_sz);
e3ef2b09 318 file_read(sc->swapin_fd,
319 sc->copy_buf,
320 sc->copy_size,
321 sc->copy_offset + mem->swap_hdr_sz,
322 storeClientReadBody,
323 sc);
3157c72f 324 }
9d66d521 325#ifndef OPTIMISTIC_IO
f115fadd 326 sc->flags.disk_io_pending = 1;
9d66d521 327#endif
f09f5b26 328}
329
330static void
e3ef2b09 331storeClientReadBody(int fd, const char *buf, int len, int flagnotused, void *data)
f09f5b26 332{
333 store_client *sc = data;
07304bf9 334 MemObject *mem = sc->entry->mem_obj;
f09f5b26 335 STCB *callback = sc->callback;
27002b34 336 assert(sc->flags.disk_io_pending);
f115fadd 337 sc->flags.disk_io_pending = 0;
f09f5b26 338 assert(sc->callback != NULL);
e3ef2b09 339 debug(20, 3) ("storeClientReadBody: FD %d, len %d\n", fd, len);
cb69b4c7 340 if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0)
341 httpReplyParse(mem->reply, sc->copy_buf);
f09f5b26 342 sc->callback = NULL;
343 callback(sc->callback_data, sc->copy_buf, len);
344}
345
e3ef2b09 346static void
347storeClientReadHeader(int fd, const char *buf, int len, int flagnotused, void *data)
348{
e3ef2b09 349 store_client *sc = data;
07304bf9 350 StoreEntry *e = sc->entry;
351 MemObject *mem = e->mem_obj;
e3ef2b09 352 STCB *callback = sc->callback;
353 int swap_hdr_sz = 0;
354 size_t body_sz;
355 size_t copy_sz;
356 tlv *tlv_list;
27002b34 357 assert(sc->flags.disk_io_pending);
f115fadd 358 sc->flags.disk_io_pending = 0;
e3ef2b09 359 assert(sc->callback != NULL);
360 debug(20, 3) ("storeClientReadHeader: FD %d, len %d\n", fd, len);
361 if (len < 0) {
25535cbe 362 debug(20, 3) ("storeClientReadHeader: FD %d: %s\n", fd, xstrerror());
25535cbe 363 sc->callback = NULL;
364 callback(sc->callback_data, sc->copy_buf, len);
e3ef2b09 365 return;
366 }
367 tlv_list = storeSwapMetaUnpack(buf, &swap_hdr_sz);
368 if (tlv_list == NULL) {
25535cbe 369 debug(20, 1) ("storeClientReadHeader: failed to unpack meta data\n");
25535cbe 370 sc->callback = NULL;
371 callback(sc->callback_data, sc->copy_buf, -1);
e3ef2b09 372 return;
373 }
374 /*
375 * XXX Here we should check the meta data and make sure we got
376 * the right object.
377 */
07304bf9 378 storeSwapTLVFree(tlv_list);
e3ef2b09 379 mem->swap_hdr_sz = swap_hdr_sz;
07304bf9 380 mem->object_sz = e->swap_file_sz - swap_hdr_sz;
e3ef2b09 381 /*
382 * If our last read got some data the client wants, then give
383 * it to them, otherwise schedule another read.
384 */
385 body_sz = len - swap_hdr_sz;
386 if (sc->copy_offset < body_sz) {
387 /*
25535cbe 388 * we have (part of) what they want
e3ef2b09 389 */
390 copy_sz = XMIN(sc->copy_size, body_sz);
25535cbe 391 debug(20, 3) ("storeClientReadHeader: copying %d bytes of body\n",
392 copy_sz);
d8b9a541 393 xmemmove(sc->copy_buf, sc->copy_buf + swap_hdr_sz, copy_sz);
cb69b4c7 394 if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0)
395 httpReplyParse(mem->reply, sc->copy_buf);
e3ef2b09 396 sc->callback = NULL;
25535cbe 397 callback(sc->callback_data, sc->copy_buf, copy_sz);
e3ef2b09 398 return;
399 }
400 /*
401 * we don't have what the client wants, but at least we now
402 * know the swap header size.
403 */
e3ef2b09 404 storeClientFileRead(sc);
405}
406
f09f5b26 407int
408storeClientCopyPending(StoreEntry * e, void *data)
409{
410 /* return 1 if there is a callback registered for this client */
411 store_client *sc = storeClientListSearch(e->mem_obj, data);
412 if (sc == NULL)
413 return 0;
414 if (sc->callback == NULL)
415 return 0;
416 return 1;
417}
418
419int
420storeUnregister(StoreEntry * e, void *data)
421{
422 MemObject *mem = e->mem_obj;
423 store_client *sc;
424 store_client **S;
425 STCB *callback;
426 if (mem == NULL)
427 return 0;
428 debug(20, 3) ("storeUnregister: called for '%s'\n", storeKeyText(e->key));
429 for (S = &mem->clients; (sc = *S) != NULL; S = &(*S)->next) {
430 if (sc->callback_data == data)
431 break;
432 }
433 if (sc == NULL)
434 return 0;
4c454c5c 435 if (sc == mem->clients) {
436 /*
437 * If we are unregistering the _first_ client for this
438 * entry, then we have to reset the client FD to -1.
439 */
440 mem->fd = -1;
441 }
f09f5b26 442 *S = sc->next;
443 mem->nclients--;
f115fadd 444 sc->flags.disk_io_pending = 0;
f09f5b26 445 if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE)
446 storeCheckSwapOut(e);
447 if (sc->swapin_fd > -1) {
448 commSetSelect(sc->swapin_fd, COMM_SELECT_READ, NULL, NULL, 0);
449 file_close(sc->swapin_fd);
fd8fe6ea 450 /* XXX this probably leaks file_read handler structures */
f09f5b26 451 }
452#if USE_ASYNC_IO
453 else
454 aioCancel(-1, sc);
455#endif
456 if ((callback = sc->callback) != NULL) {
457 /* callback with ssize = -1 to indicate unexpected termination */
458 debug(20, 3) ("storeUnregister: store_client for %s has a callback\n",
459 mem->url);
460 sc->callback = NULL;
461 callback(sc->callback_data, sc->copy_buf, -1);
462 }
463 cbdataFree(sc);
464 return 1;
465}
466
467off_t
468storeLowestMemReaderOffset(const StoreEntry * entry)
469{
470 const MemObject *mem = entry->mem_obj;
471 off_t lowest = mem->inmem_hi;
472 store_client *sc;
473 store_client *nx = NULL;
474 for (sc = mem->clients; sc; sc = nx) {
475 nx = sc->next;
476 if (sc->callback_data == NULL) /* open slot */
477 continue;
478 if (sc->type != STORE_MEM_CLIENT)
479 continue;
480 if (sc->copy_offset < lowest)
481 lowest = sc->copy_offset;
482 }
483 return lowest;
484}
485
486/* Call handlers waiting for data to be appended to E. */
487void
488InvokeHandlers(StoreEntry * e)
489{
490 int i = 0;
491 MemObject *mem = e->mem_obj;
492 store_client *sc;
493 store_client *nx = NULL;
494 assert(mem->clients != NULL || mem->nclients == 0);
495 debug(20, 3) ("InvokeHandlers: %s\n", storeKeyText(e->key));
496 /* walk the entire list looking for valid callbacks */
497 for (sc = mem->clients; sc; sc = nx) {
498 nx = sc->next;
499 debug(20, 3) ("InvokeHandlers: checking client #%d\n", i++);
500 if (sc->callback_data == NULL)
501 continue;
502 if (sc->callback == NULL)
503 continue;
504 storeClientCopy2(e, sc);
505 }
506}
507
508int
509storePendingNClients(const StoreEntry * e)
510{
f09f5b26 511 MemObject *mem = e->mem_obj;
36547bcf 512 int npend = NULL == mem ? 0 : mem->nclients;
1afe05c5 513 debug(20, 3) ("storePendingNClients: returning %d\n", npend);
f09f5b26 514 return npend;
515}