]> git.ipfire.org Git - thirdparty/squid.git/blob - src/comm/Read.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / comm / Read.cc
1 /*
2 * Copyright (C) 1996-2021 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 05 Socket Functions */
10
11 #include "squid.h"
12 #include "comm.h"
13 #include "comm/IoCallback.h"
14 #include "comm/Loops.h"
15 #include "comm/Read.h"
16 #include "comm_internal.h"
17 #include "CommCalls.h"
18 #include "Debug.h"
19 #include "fd.h"
20 #include "fde.h"
21 #include "sbuf/SBuf.h"
22 #include "SquidConfig.h"
23 #include "StatCounters.h"
24
25 // Does comm check this fd for read readiness?
26 // Note that when comm is not monitoring, there can be a pending callback
27 // call, which may resume comm monitoring once fired.
28 bool
29 Comm::MonitorsRead(int fd)
30 {
31 assert(isOpen(fd) && COMMIO_FD_READCB(fd) != NULL);
32 // Being active is usually the same as monitoring because we always
33 // start monitoring the FD when we configure Comm::IoCallback for I/O
34 // and we usually configure Comm::IoCallback for I/O when we starting
35 // monitoring a FD for reading.
36 return COMMIO_FD_READCB(fd)->active();
37 }
38
39 void
40 Comm::Read(const Comm::ConnectionPointer &conn, AsyncCall::Pointer &callback)
41 {
42 // TODO: move comm_read_base() internals into here
43 // when comm_read() char* API is no longer needed
44 comm_read_base(conn, NULL, 0, callback);
45 }
46
47 /**
48 * Queue a read.
49 * If a buffer is given the callback is scheduled when the read
50 * completes, on error, or on file descriptor close.
51 *
52 * If no buffer (NULL) is given the callback is scheduled when
53 * the socket FD is ready for a read(2)/recv(2).
54 */
55 void
56 comm_read_base(const Comm::ConnectionPointer &conn, char *buf, int size, AsyncCall::Pointer &callback)
57 {
58 debugs(5, 5, "comm_read, queueing read for " << conn << "; asynCall " << callback);
59
60 /* Make sure we are open and not closing */
61 assert(Comm::IsConnOpen(conn));
62 assert(!fd_table[conn->fd].closing());
63 Comm::IoCallback *ccb = COMMIO_FD_READCB(conn->fd);
64
65 // Make sure we are either not reading or just passively monitoring.
66 // Active/passive conflicts are OK and simply cancel passive monitoring.
67 if (ccb->active()) {
68 // if the assertion below fails, we have an active comm_read conflict
69 assert(fd_table[conn->fd].halfClosedReader != NULL);
70 commStopHalfClosedMonitor(conn->fd);
71 assert(!ccb->active());
72 }
73 ccb->conn = conn;
74
75 /* Queue the read */
76 ccb->setCallback(Comm::IOCB_READ, callback, (char *)buf, NULL, size);
77 Comm::SetSelect(conn->fd, COMM_SELECT_READ, Comm::HandleRead, ccb, 0);
78 }
79
80 Comm::Flag
81 Comm::ReadNow(CommIoCbParams &params, SBuf &buf)
82 {
83 /* Attempt a read */
84 ++ statCounter.syscalls.sock.reads;
85 SBuf::size_type sz = buf.spaceSize();
86 if (params.size > 0 && params.size < sz)
87 sz = params.size;
88 char *inbuf = buf.rawAppendStart(sz);
89 errno = 0;
90 const int retval = FD_READ_METHOD(params.conn->fd, inbuf, sz);
91 params.xerrno = errno;
92
93 debugs(5, 3, params.conn << ", size " << sz << ", retval " << retval << ", errno " << params.xerrno);
94
95 if (retval > 0) { // data read most common case
96 buf.rawAppendFinish(inbuf, retval);
97 fd_bytes(params.conn->fd, retval, FD_READ);
98 params.flag = Comm::OK;
99 params.size = retval;
100
101 } else if (retval == 0) { // remote closure (somewhat less) common
102 // Note - read 0 == socket EOF, which is a valid read.
103 params.flag = Comm::ENDFILE;
104 params.size = 0;
105
106 } else if (retval < 0) { // connection errors are worst-case
107 debugs(5, 3, params.conn << " Comm::COMM_ERROR: " << xstrerr(params.xerrno));
108 if (ignoreErrno(params.xerrno))
109 params.flag = Comm::INPROGRESS;
110 else
111 params.flag = Comm::COMM_ERROR;
112 params.size = 0;
113 }
114
115 return params.flag;
116 }
117
118 /**
119 * Handle an FD which is ready for read(2).
120 *
121 * If there is no provided buffer to fill call the callback.
122 *
123 * Otherwise attempt a read into the provided buffer.
124 * If the read attempt succeeds or fails, call the callback.
125 * Else, wait for another IO notification.
126 */
127 void
128 Comm::HandleRead(int fd, void *data)
129 {
130 Comm::IoCallback *ccb = (Comm::IoCallback *) data;
131
132 assert(data == COMMIO_FD_READCB(fd));
133 assert(ccb->active());
134
135 // Without a buffer, just call back.
136 // The callee may ReadMore() to get the data.
137 if (!ccb->buf) {
138 ccb->finish(Comm::OK, 0);
139 return;
140 }
141
142 /* For legacy callers : Attempt a read */
143 // Keep in sync with Comm::ReadNow()!
144 ++ statCounter.syscalls.sock.reads;
145 int xerrno = errno = 0;
146 int retval = FD_READ_METHOD(fd, ccb->buf, ccb->size);
147 xerrno = errno;
148 debugs(5, 3, "FD " << fd << ", size " << ccb->size << ", retval " << retval << ", errno " << xerrno);
149
150 /* See if we read anything */
151 /* Note - read 0 == socket EOF, which is a valid read */
152 if (retval >= 0) {
153 fd_bytes(fd, retval, FD_READ);
154 ccb->offset = retval;
155 ccb->finish(Comm::OK, 0);
156 return;
157 } else if (retval < 0 && !ignoreErrno(xerrno)) {
158 debugs(5, 3, "comm_read_try: scheduling Comm::COMM_ERROR");
159 ccb->offset = 0;
160 ccb->finish(Comm::COMM_ERROR, xerrno);
161 return;
162 };
163
164 /* Nope, register for some more IO */
165 Comm::SetSelect(fd, COMM_SELECT_READ, Comm::HandleRead, data, 0);
166 }
167
168 /**
169 * Cancel a pending read. Assert that we have the right parameters,
170 * and that there are no pending read events!
171 *
172 * XXX: We do not assert that there are no pending read events and
173 * with async calls it becomes even more difficult.
174 * The whole interface should be reworked to do callback->cancel()
175 * instead of searching for places where the callback may be stored and
176 * updating the state of those places.
177 *
178 * AHC Don't call the comm handlers?
179 */
180 void
181 comm_read_cancel(int fd, IOCB *callback, void *data)
182 {
183 if (!isOpen(fd)) {
184 debugs(5, 4, "fails: FD " << fd << " closed");
185 return;
186 }
187
188 Comm::IoCallback *cb = COMMIO_FD_READCB(fd);
189 // TODO: is "active" == "monitors FD"?
190 if (!cb->active()) {
191 debugs(5, 4, "fails: FD " << fd << " inactive");
192 return;
193 }
194
195 typedef CommCbFunPtrCallT<CommIoCbPtrFun> Call;
196 Call *call = dynamic_cast<Call*>(cb->callback.getRaw());
197 if (!call) {
198 debugs(5, 4, "fails: FD " << fd << " lacks callback");
199 return;
200 }
201
202 call->cancel("old comm_read_cancel");
203
204 typedef CommIoCbParams Params;
205 const Params &params = GetCommParams<Params>(cb->callback);
206
207 /* Ok, we can be reasonably sure we won't lose any data here! */
208 assert(call->dialer.handler == callback);
209 assert(params.data == data);
210
211 /* Delete the callback */
212 cb->cancel("old comm_read_cancel");
213
214 /* And the IO event */
215 Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
216 }
217
218 void
219 Comm::ReadCancel(int fd, AsyncCall::Pointer &callback)
220 {
221 callback->cancel("comm_read_cancel");
222
223 if (!isOpen(fd)) {
224 debugs(5, 4, "fails: FD " << fd << " closed");
225 return;
226 }
227
228 Comm::IoCallback *cb = COMMIO_FD_READCB(fd);
229
230 if (!cb->active()) {
231 debugs(5, 4, "fails: FD " << fd << " inactive");
232 return;
233 }
234
235 AsyncCall::Pointer call = cb->callback;
236
237 /* Ok, we can be reasonably sure we won't lose any data here! */
238 assert(call == callback);
239
240 /* Delete the callback */
241 cb->cancel("comm_read_cancel");
242
243 /* And the IO event */
244 Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
245 }
246
247 time_t
248 Comm::MortalReadTimeout(const time_t startTime, const time_t lifetimeLimit)
249 {
250 if (lifetimeLimit > 0) {
251 const time_t timeUsed = (squid_curtime > startTime) ? (squid_curtime - startTime) : 0;
252 const time_t timeLeft = (lifetimeLimit > timeUsed) ? (lifetimeLimit - timeUsed) : 0;
253 return min(::Config.Timeout.read, timeLeft);
254 } else
255 return ::Config.Timeout.read;
256 }
257