| 252 | /// Application-level failures should be signalled through the response type. |
| 253 | template <class F, class Request, class Response> |
| 254 | Status DoRpc(const F& f, const Request& request, Response* response) { |
| 255 | DCHECK(response != nullptr); |
| 256 | client_is_unrecoverable_ = true; |
| 257 | bool send_done = false; |
| 258 | try { |
| 259 | (client_->*f)(*response, request, &send_done); |
| 260 | } catch (const apache::thrift::transport::TTransportException& e) { |
| 261 | if (send_done && IsReadTimeoutTException(e)) { |
| 262 | return RecvTimeoutStatus(typeid(*response).name()); |
| 263 | } |
| 264 | |
| 265 | // Client may have unexpectedly been closed, so re-open and retry once if we didn't |
| 266 | // successfully send the payload yet or if the exception indicates the connection |
| 267 | // was closed on the other end. Note that TCP can have a half-open connection so |
| 268 | // send() may still succeed even after the other end already closed the socket. |
| 269 | // The payload can just be buffered in the kernel. |
| 270 | if (!send_done || IsConnResetTException(e)) { |
| 271 | return RetryRpc(f, request, response); |
| 272 | } |
| 273 | |
| 274 | // Payload was sent and failure wasn't a timeout waiting for response. Fail the RPC. |
| 275 | return GeneralErrorStatus(e, typeid(*response).name(), send_done); |
| 276 | } catch (const apache::thrift::TException& e) { |
| 277 | return GeneralErrorStatus(e, typeid(*response).name(), send_done); |
| 278 | } |
| 279 | client_is_unrecoverable_ = false; |
| 280 | return Status::OK(); |
| 281 | } |
| 282 | |
| 283 | /// Return struct for DoRpcWithRetry() that allows callers to distinguish between |
| 284 | /// failures in getting a client and failures sending the RPC. |