1 ------------------------------------------------------------
3 revision-id: squid3@treenet.co.nz-20161209015833-xm965d5l6u03qhew
4 parent: squid3@treenet.co.nz-20161130233304-lk3q0bx8gn5l3l85
5 fixes bug: http://bugs.squid-cache.org/show_bug.cgi?id=4174
6 author: Christos Tsantilas <chtsanti@users.sourceforge.net>
7 committer: Amos Jeffries <squid3@treenet.co.nz>
9 timestamp: Fri 2016-12-09 14:58:33 +1300
11 Bug 4174 partial: fix Write.cc:41 "!ccb->active()" assertion.
13 The following sequence of events triggers this assertion:
14 - The server sends an 1xx control message.
15 - http.cc schedules ConnStateData::sendControlMsg call.
16 - Before sendControlMsg is fired, http.cc detects an error (e.g., I/O
17 error or timeout) and starts writing the reply to the user.
18 - The ConnStateData::sendControlMsg is fired, starts writing 1xx, and
19 hits the "no concurrent writes" assertion.
21 We could only reproduce this sequence in the lab after changing Squid
22 code to trigger a timeout at the right moment, but the sequence looks
23 plausible. Other event sequences might result in the same outcome.
25 To avoid concurrent writes, Squid now drops the control message if
26 Http::One::Server detects that a reply is already being written. Also,
27 ConnStateData delays reply writing until a pending control message write
30 This is a Measurement Factory project.
31 ------------------------------------------------------------
32 # Bazaar merge directive format 2 (Bazaar 0.90)
33 # revision_id: squid3@treenet.co.nz-20161209015833-xm965d5l6u03qhew
34 # target_branch: http://bzr.squid-cache.org/bzr/squid3/3.5
35 # testament_sha1: 103c6fc1fa45d78ba7f9e85ab3d89fff898ee762
36 # timestamp: 2016-12-09 02:51:06 +0000
37 # source_branch: http://bzr.squid-cache.org/bzr/squid3/3.5
38 # base_revision_id: squid3@treenet.co.nz-20161130233304-\
42 === modified file 'src/client_side.cc'
43 --- src/client_side.cc 2016-09-23 20:49:24 +0000
44 +++ src/client_side.cc 2016-12-09 01:58:33 +0000
46 AsyncCall::Pointer call = commCbCall(33, 5, "ClientSocketContext::wroteControlMsg",
47 CommIoCbPtrFun(&WroteControlMsg, this));
49 - getConn()->writeControlMsgAndCall(this, rep.getRaw(), call);
50 + if (!getConn()->writeControlMsgAndCall(this, rep.getRaw(), call)) {
51 + // but still inform the caller (so it may resume its operation)
52 + doneWithControlMsg();
57 +ClientSocketContext::doneWithControlMsg()
59 + ScheduleCallHere(cbControlMsgSent);
60 + cbControlMsgSent = NULL;
62 + debugs(33, 3, clientConnection << ": calling PushDeferredIfNeeded after control msg wrote");
63 + ClientSocketContextPushDeferredIfNeeded(this, getConn());
67 /// called when we wrote the 1xx response
71 if (errflag == Comm::OK) {
72 - ScheduleCallHere(cbControlMsgSent);
73 + doneWithControlMsg();
79 if (context != http->getConn()->getCurrentContext())
80 context->deferRecipientForLater(node, rep, receivedData);
81 + else if (context->controlMsgIsPending())
82 + context->deferRecipientForLater(node, rep, receivedData);
84 http->getConn()->handleReply(rep, receivedData);
87 === modified file 'src/client_side.h'
88 --- src/client_side.h 2016-06-18 13:36:07 +0000
89 +++ src/client_side.h 2016-12-09 01:58:33 +0000
91 /// starts writing 1xx control message to the client
92 void writeControlMsg(HttpControlMsg &msg);
94 + /// true if 1xx to the user is pending
95 + bool controlMsgIsPending() {return cbControlMsgSent != NULL;}
98 static IOCB WroteControlMsg;
99 void wroteControlMsg(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag, int xerrno);
100 + void doneWithControlMsg();
103 void prepareReply(HttpReply * rep);
105 void connectionTag(const char *aTag) { connectionTag_ = aTag; }
107 /// handle a control message received by context from a peer and call back
108 - virtual void writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call) = 0;
109 + virtual bool writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call) = 0;
111 /// ClientStream calls this to supply response header (once) and data
112 /// for the current ClientSocketContext.
114 === modified file 'src/servers/FtpServer.cc'
115 --- src/servers/FtpServer.cc 2016-06-30 21:09:12 +0000
116 +++ src/servers/FtpServer.cc 2016-12-09 01:58:33 +0000
117 @@ -1152,12 +1152,13 @@
118 writeErrorReply(reply, 451);
123 Ftp::Server::writeControlMsgAndCall(ClientSocketContext *context, HttpReply *reply, AsyncCall::Pointer &call)
125 // the caller guarantees that we are dealing with the current context only
126 // the caller should also make sure reply->header.has(HDR_FTP_STATUS)
127 writeForwardedReplyAndCall(reply, call);
133 === modified file 'src/servers/FtpServer.h'
134 --- src/servers/FtpServer.h 2016-03-15 18:14:15 +0000
135 +++ src/servers/FtpServer.h 2016-12-09 01:58:33 +0000
137 virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io);
138 virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData);
139 virtual int pipelinePrefetchMax() const;
140 - virtual void writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call);
141 + virtual bool writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call);
142 virtual time_t idleTimeout() const;
146 === modified file 'src/servers/HttpServer.cc'
147 --- src/servers/HttpServer.cc 2016-01-01 00:14:27 +0000
148 +++ src/servers/HttpServer.cc 2016-12-09 01:58:33 +0000
150 virtual ClientSocketContext *parseOneRequest(Http::ProtocolVersion &ver);
151 virtual void processParsedRequest(ClientSocketContext *context, const Http::ProtocolVersion &ver);
152 virtual void handleReply(HttpReply *rep, StoreIOBuffer receivedData);
153 - virtual void writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call);
154 + virtual bool writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call);
155 virtual time_t idleTimeout() const;
159 context->sendStartOfMessage(rep, receivedData);
164 Http::Server::writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call)
166 + // Ignore this late control message if we have started sending a
167 + // reply to the user already (e.g., after an error).
168 + if (context->reply) {
169 + debugs(11, 2, "drop 1xx made late by " << context->reply);
173 // apply selected clientReplyContext::buildReplyHeader() mods
174 // it is not clear what headers are required for control messages
175 rep->header.removeHopByHopEntries();
177 Comm::Write(context->clientConnection, mb, call);