]> git.ipfire.org Git - thirdparty/squid.git/blob - src/whois.cc
Stop processing a response if the Store entry is gone (#806)
[thirdparty/squid.git] / src / whois.cc
1 /*
2 * Copyright (C) 1996-2020 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 75 WHOIS protocol */
10
11 #include "squid.h"
12 #include "comm.h"
13 #include "comm/Read.h"
14 #include "comm/Write.h"
15 #include "errorpage.h"
16 #include "FwdState.h"
17 #include "HttpReply.h"
18 #include "HttpRequest.h"
19 #include "SquidConfig.h"
20 #include "StatCounters.h"
21 #include "Store.h"
22 #include "tools.h"
23
24 #include <cerrno>
25
26 class WhoisState
27 {
28 CBDATA_CLASS(WhoisState);
29
30 public:
31 void readReply(const Comm::ConnectionPointer &, char *aBuffer, size_t aBufferLength, Comm::Flag flag, int xerrno);
32 void setReplyToOK(StoreEntry *sentry);
33 StoreEntry *entry;
34 HttpRequest::Pointer request;
35 FwdState::Pointer fwd;
36 char buf[BUFSIZ+1]; /* readReply adds terminating NULL */
37 bool dataWritten;
38 };
39
40 CBDATA_CLASS_INIT(WhoisState);
41
42 static CLCB whoisClose;
43 static CTCB whoisTimeout;
44 static IOCB whoisReadReply;
45
46 /* PUBLIC */
47
48 static void
49 whoisWriteComplete(const Comm::ConnectionPointer &, char *buf, size_t, Comm::Flag, int, void *)
50 {
51 xfree(buf);
52 }
53
54 void
55 whoisStart(FwdState * fwd)
56 {
57 WhoisState *p = new WhoisState;
58 p->request = fwd->request;
59 p->entry = fwd->entry;
60 p->fwd = fwd;
61 p->dataWritten = false;
62
63 p->entry->lock("whoisStart");
64 comm_add_close_handler(fwd->serverConnection()->fd, whoisClose, p);
65
66 size_t l = p->request->url.path().length() + 3;
67 char *buf = (char *)xmalloc(l);
68
69 const SBuf str_print = p->request->url.path().substr(1);
70 snprintf(buf, l, SQUIDSBUFPH "\r\n", SQUIDSBUFPRINT(str_print));
71
72 AsyncCall::Pointer writeCall = commCbCall(5,5, "whoisWriteComplete",
73 CommIoCbPtrFun(whoisWriteComplete, p));
74 Comm::Write(fwd->serverConnection(), buf, strlen(buf), writeCall, NULL);
75 AsyncCall::Pointer readCall = commCbCall(5,4, "whoisReadReply",
76 CommIoCbPtrFun(whoisReadReply, p));
77 comm_read(fwd->serverConnection(), p->buf, BUFSIZ, readCall);
78 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "whoisTimeout",
79 CommTimeoutCbPtrFun(whoisTimeout, p));
80 commSetConnTimeout(fwd->serverConnection(), Config.Timeout.read, timeoutCall);
81 }
82
83 /* PRIVATE */
84
85 static void
86 whoisTimeout(const CommTimeoutCbParams &io)
87 {
88 WhoisState *p = static_cast<WhoisState *>(io.data);
89 debugs(75, 3, HERE << io.conn << ", URL " << p->entry->url());
90 io.conn->close();
91 }
92
93 static void
94 whoisReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
95 {
96 WhoisState *p = (WhoisState *)data;
97 p->readReply(conn, buf, len, flag, xerrno);
98 }
99
100 void
101 WhoisState::setReplyToOK(StoreEntry *sentry)
102 {
103 HttpReply *reply = new HttpReply;
104 sentry->buffer();
105 reply->setHeaders(Http::scOkay, "Gatewaying", "text/plain", -1, -1, -2);
106 reply->sources |= Http::Message::srcWhois;
107 sentry->replaceHttpReply(reply);
108 }
109
110 void
111 WhoisState::readReply(const Comm::ConnectionPointer &conn, char *aBuffer, size_t aBufferLength, Comm::Flag flag, int xerrno)
112 {
113 /* Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us */
114 if (flag == Comm::ERR_CLOSING)
115 return;
116
117 aBuffer[aBufferLength] = '\0';
118 debugs(75, 3, HERE << conn << " read " << aBufferLength << " bytes");
119 debugs(75, 5, "{" << aBuffer << "}");
120
121 // TODO: Honor delay pools.
122
123 // XXX: Update statCounter before bailing
124 if (!entry->isAccepting()) {
125 debugs(52, 3, "terminating due to bad " << *entry);
126 // TODO: Do not abuse connection for triggering cleanup.
127 conn->close();
128 return;
129 }
130
131 if (flag != Comm::OK) {
132 debugs(50, 2, conn << ": read failure: " << xstrerr(xerrno));
133
134 if (ignoreErrno(xerrno)) {
135 AsyncCall::Pointer call = commCbCall(5,4, "whoisReadReply",
136 CommIoCbPtrFun(whoisReadReply, this));
137 comm_read(conn, aBuffer, BUFSIZ, call);
138 } else {
139 const auto err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, fwd->request, fwd->al);
140 err->xerrno = xerrno;
141 fwd->fail(err);
142 conn->close();
143 }
144 return;
145 }
146
147 if (aBufferLength > 0) {
148 if (!dataWritten)
149 setReplyToOK(entry);
150
151 statCounter.server.all.kbytes_in += aBufferLength;
152 statCounter.server.http.kbytes_in += aBufferLength;
153
154 /* No range support, we always grab it all */
155 dataWritten = true;
156 entry->append(aBuffer, aBufferLength);
157 entry->flush();
158
159 AsyncCall::Pointer call = commCbCall(5,4, "whoisReadReply",
160 CommIoCbPtrFun(whoisReadReply, this));
161 comm_read(conn, aBuffer, BUFSIZ, call);
162 return;
163 }
164
165 /* no bytes read. stop reading */
166 entry->timestampsSet();
167 entry->flush();
168
169 if (!entry->makePublic())
170 entry->makePrivate(true);
171
172 fwd->complete();
173 debugs(75, 3, "whoisReadReply: Done: " << entry->url());
174 conn->close();
175 }
176
177 static void
178 whoisClose(const CommCloseCbParams &params)
179 {
180 WhoisState *p = (WhoisState *)params.data;
181 debugs(75, 3, "whoisClose: FD " << params.fd);
182 p->entry->unlock("whoisClose");
183 delete p;
184 }
185