(request->flags.interceptTproxy || request->flags.intercepted));
}
+ /// Sends "502 Bad Gateway" error response to the client,
+ /// if it is waiting for Squid CONNECT response, closing connections.
+ void informUserOfPeerError(const char *errMsg);
+
class Connection
{
void error(int const xerrno);
int debugLevelForError(int const xerrno) const;
- /// handles a non-I/O error associated with this Connection
- void logicError(const char *errMsg);
void closeIfOpen();
void dataSent (size_t amount);
+ /// writes 'b' buffer, setting the 'writer' member to 'callback'.
+ void write(const char *b, int size, AsyncCall::Pointer &callback, FREE * free_func);
int len;
char *buf;
+ AsyncCall::Pointer writer; ///< pending Comm::Write callback
int64_t *size_ptr; /* pointer to size in an ConnStateData for logging */
Comm::ConnectionPointer conn; ///< The currently connected connection.
TunnelStateData *tunnelState = (TunnelStateData *)params.data;
debugs(26, 3, HERE << tunnelState->server.conn);
tunnelState->server.conn = NULL;
+ tunnelState->server.writer = NULL;
if (tunnelState->request != NULL)
tunnelState->request->hier.stopPeerClock(false);
return;
}
- if (!tunnelState->server.len) {
+ if (!tunnelState->client.writer) {
tunnelState->client.conn->close();
return;
}
TunnelStateData *tunnelState = (TunnelStateData *)params.data;
debugs(26, 3, HERE << tunnelState->client.conn);
tunnelState->client.conn = NULL;
+ tunnelState->client.writer = NULL;
if (tunnelState->noConnections()) {
delete tunnelState;
return;
}
- if (!tunnelState->client.len) {
+ if (!tunnelState->server.writer) {
tunnelState->server.conn->close();
return;
}
handleConnectResponse(len);
}
+void
+TunnelStateData::informUserOfPeerError(const char *errMsg)
+{
+ server.len = 0;
+ if (!clientExpectsConnectResponse()) {
+ // closing the connection is the best we can do here
+ debugs(50, 3, server.conn << " closing on error: " << errMsg);
+ server.conn->close();
+ return;
+ }
+ ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw());
+ err->callback = tunnelErrorComplete;
+ err->callback_data = this;
+ *status_ptr = Http::scBadGateway;
+ errorSend(http->getConn()->clientConnection, err);
+}
+
/* Read from client side and queue it for writing to the server */
void
TunnelStateData::ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data)
const bool parsed = rep.parse(connectRespBuf->content(), connectRespBuf->contentSize(), eof, &parseErr);
if (!parsed) {
if (parseErr > 0) { // unrecoverable parsing error
- server.logicError("malformed CONNECT response from peer");
+ informUserOfPeerError("malformed CONNECT response from peer");
return;
}
assert(!parseErr);
if (!connectRespBuf->hasSpace()) {
- server.logicError("huge CONNECT response from peer");
+ informUserOfPeerError("huge CONNECT response from peer");
return;
}
// bail if we did not get an HTTP 200 (Connection Established) response
if (rep.sline.status() != Http::scOkay) {
- server.logicError("unsupported CONNECT response status code");
+ // if we ever decide to reuse the peer connection, we must extract the error response first
+ informUserOfPeerError("unsupported CONNECT response status code");
return;
}
connectExchangeCheckpoint();
}
-void
-TunnelStateData::Connection::logicError(const char *errMsg)
-{
- debugs(50, 3, conn << " closing on error: " << errMsg);
- conn->close();
-}
-
void
TunnelStateData::Connection::error(int const xerrno)
{
debugs(26, 3, HERE << "Schedule Write");
AsyncCall::Pointer call = commCbCall(5,5, "TunnelBlindCopyWriteHandler",
CommIoCbPtrFun(completion, this));
- Comm::Write(to.conn, from.buf, len, call, NULL);
+ to.write(from.buf, len, call, NULL);
}
/* Writes data from the client buffer to the server side */
{
TunnelStateData *tunnelState = (TunnelStateData *)data;
assert (cbdataReferenceValid (tunnelState));
+ tunnelState->server.writer = NULL;
tunnelState->writeServerDone(buf, len, flag, xerrno);
}
{
TunnelStateData *tunnelState = (TunnelStateData *)data;
assert (cbdataReferenceValid (tunnelState));
+ tunnelState->client.writer = NULL;
tunnelState->writeClientDone(buf, len, flag, xerrno);
}
*size_ptr += amount;
}
+void
+TunnelStateData::Connection::write(const char *b, int size, AsyncCall::Pointer &callback, FREE * free_func)
+{
+ writer = callback;
+ Comm::Write(conn, b, size, callback, free_func);
+}
+
void
TunnelStateData::writeClientDone(char *, size_t len, Comm::Flag flag, int xerrno)
{
{
TunnelStateData *tunnelState = (TunnelStateData *)data;
debugs(26, 3, HERE << conn << ", flag=" << flag);
+ tunnelState->client.writer = NULL;
if (flag != Comm::OK) {
*tunnelState->status_ptr = Http::scInternalServerError;
{
TunnelStateData *tunnelState = (TunnelStateData *)data;
debugs(26, 3, conn << ", flag=" << flag);
+ tunnelState->server.writer = NULL;
assert(tunnelState->waitingForConnectRequest());
if (flag != Comm::OK) {
else {
AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
- Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL);
+ tunnelState->client.write(conn_established, strlen(conn_established), call, NULL);
}
}
debugs(11, 2, "Tunnel Server REQUEST: " << tunnelState->server.conn << ":\n----------\n" <<
Raw("tunnelRelayConnectRequest", mb.content(), mb.contentSize()) << "\n----------");
- if (tunnelState->clientExpectsConnectResponse()) {
- // hack: blindly tunnel peer response (to our CONNECT request) to the client as ours.
- AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectedWriteDone",
- CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
- Comm::Write(srv, &mb, writeCall);
- } else {
- // we have to eat the connect response from the peer (so that the client
- // does not see it) and only then start shoveling data to the client
- AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone",
- CommIoCbPtrFun(tunnelConnectReqWriteDone,
- tunnelState));
- Comm::Write(srv, &mb, writeCall);
- tunnelState->connectReqWriting = true;
-
- tunnelState->connectRespBuf = new MemBuf;
- // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer
- // can hold since any CONNECT response leftovers have to fit into server.buf.
- // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space.
- tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF);
- tunnelState->readConnectResponse();
-
- assert(tunnelState->waitingForConnectExchange());
- }
+ AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone",
+ CommIoCbPtrFun(tunnelConnectReqWriteDone,
+ tunnelState));
+
+ tunnelState->server.write(mb.buf, mb.size, writeCall, mb.freeFunc());
+ tunnelState->connectReqWriting = true;
+
+ tunnelState->connectRespBuf = new MemBuf;
+ // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer
+ // can hold since any CONNECT response leftovers have to fit into server.buf.
+ // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space.
+ tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF);
+ tunnelState->readConnectResponse();
+
+ assert(tunnelState->waitingForConnectExchange());
AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
- Comm::Write(tunnelState->client.conn, buf.content(), buf.contentSize(), call, NULL);
+ tunnelState->client.write(buf.content(), buf.contentSize(), call, NULL);
}
#endif //USE_OPENSSL