void RequestHandlerAdaptor::onError(const HTTPException& error) noexcept { if (err_ != kErrorNone) { // we have already handled an error and upstream would have been deleted return; } if (error.getProxygenError() == kErrorTimeout) { setError(kErrorTimeout); if (responseStarted_) { sendAbort(); } else { ResponseBuilder(this) .status(408, "Request Timeout") .closeConnection() .sendWithEOM(); } } else if (error.getDirection() == HTTPException::Direction::INGRESS) { setError(kErrorRead); if (responseStarted_) { sendAbort(); } else { ResponseBuilder(this) .status(400, "Bad Request") .closeConnection() .sendWithEOM(); } } else { setError(kErrorWrite); } // Wait for detachTransaction to clean up }
void HTTPTransaction::onIngressBody(unique_ptr<IOBuf> chain, uint16_t padding) { DestructorGuard g(this); if (isIngressEOMSeen()) { sendAbort(ErrorCode::STREAM_CLOSED); return; } auto len = chain->computeChainDataLength(); if (len == 0) { return; } if (!validateIngressStateTransition( HTTPTransactionIngressSM::Event::onBody)) { return; } if (expectedContentLengthRemaining_.hasValue()) { if (expectedContentLengthRemaining_.value() >= len) { expectedContentLengthRemaining_ = expectedContentLengthRemaining_.value() - len; } else { auto errorMsg = folly::to<std::string>( "Content-Length/body mismatch: received=", len, " expecting no more than ", expectedContentLengthRemaining_.value()); LOG(ERROR) << *this << " " << errorMsg; if (handler_) { HTTPException ex(HTTPException::Direction::INGRESS, errorMsg); ex.setProxygenError(kErrorParseBody); onError(ex); } return; } } if (transportCallback_) { transportCallback_->bodyBytesReceived(len); } if (mustQueueIngress()) { // register the bytes in the receive window if (!recvWindow_.reserve(len + padding, useFlowControl_)) { LOG(ERROR) << *this << " recvWindow_.reserve failed with len=" << len << " padding=" << padding << " capacity=" << recvWindow_.getCapacity() << " outstanding=" << recvWindow_.getOutstanding(); sendAbort(ErrorCode::FLOW_CONTROL_ERROR); } else { CHECK(recvWindow_.free(padding)); recvToAck_ += padding; checkCreateDeferredIngress(); deferredIngress_->emplace(id_, HTTPEvent::Type::BODY, std::move(chain)); VLOG(4) << *this << " Queued ingress event of type " << HTTPEvent::Type::BODY << " size=" << len; } } else { processIngressBody(std::move(chain), len); } }
void BehaviorNotification::abort() { //set confirmation to be parented to this so it is deleted with rest of notification ui->confirmation_widget_->setParent(this); Q_EMIT sendAbort(ui->action_label_->text(), goal_id_); }
TEST_F(ProxygenTransportTest, push_abort_incomplete) { // Push a resource Array headers; uint8_t pri = 1; headers.add(String("hello"), String("world")); // dict serializtion path pushResource(headers, pri); // Creates a new transaction and sends headers, but not body MockHTTPTransaction pushTxn(TransportDirection::DOWNSTREAM, HTTPCodec::StreamID(1), 1, m_egressQueue, WheelTimerInstance(m_timeouts.get())); HTTPPushTransactionHandler* pushHandler = nullptr; expectPushPromiseAndHeaders(pushTxn, pri, &pushHandler); m_server.deliverMessages(); sendResponse("12345"); EXPECT_CALL(pushTxn, sendAbort()) .WillOnce(Invoke([pushHandler] { pushHandler->detachTransaction(); })); // Simulate termination of the VM thread while there is an incomplete push // This aborts the incomplete push TearDown(); }
void TpReceiveTransaction::getConsecutiveFrame(ConsecutiveFrame frame) { if (state == PROGRESS) { timer.stop(); std::vector<byte> v = frame.getData(); if ( (++consecutiveFrameCounter & 0x0F) == frame.getIndex() ) { buffer.insert(pointer, v.begin(), (buffLength - std::distance(buffer.begin(), pointer)) > 7 ? v.end() : v.begin() + buffLength - std::distance(buffer.begin(), pointer)); pointer = buffer.end(); consIndex++; if ((buffer.size() < buffLength) && (blockSize == consIndex)) readyFlowControl(); else timer.start(5000); if ((buffer.size() == buffLength)) { timer.stop(); state = INIT; emit transactionReaceived(buffer); } } else { state = BROKEN; sendAbort(); //LOG_WRITER.write(QString(tr("IsoTp последовательность нарушена. Ожидался фрейм с индексом %1, а получен - с индексом %2")). // arg(consecutiveFrameCounter & 0x0F). // arg(frame.getIndex()), QColor(255, 0, 255), 1); } } else if (state == INIT) { //LOG_WRITER.write(tr("Неожиданный ConsecutiveFrame"), QColor(255, 0, 0), 1); state = BROKEN; sendAbort(); } else if (state == BROKEN) { // silent } }
void DebuggerMagager::abort() //abort execution { if (ConfigVariable::getPauseLevel() != 0) { //inform debuggers sendAbort(); throw ast::InternalAbort(); } }
void HTTPTransaction::onIngressSetSendWindow(const uint32_t newWindowSize) { if (!useFlowControl_) { return; } updateReadTimeout(); if (sendWindow_.setCapacity(newWindowSize)) { notifyTransportPendingEgress(); } else { LOG(ERROR) << *this << "sendWindow_.setCapacity failed with newWindowSize=" << newWindowSize << " capacity=" << sendWindow_.getCapacity() << " outstanding=" << sendWindow_.getOutstanding(); sendAbort(ErrorCode::FLOW_CONTROL_ERROR); } }
void HTTPTransaction::onIngressWindowUpdate(const uint32_t amount) { if (!useFlowControl_) { return; } DestructorGuard g(this); VLOG(4) << *this << " Remote side ack'd " << amount << " bytes"; updateReadTimeout(); if (sendWindow_.free(amount)) { notifyTransportPendingEgress(); } else { LOG(ERROR) << *this << "sendWindow_.free failed with amount=" << amount << " capacity=" << sendWindow_.getCapacity() << " outstanding=" << sendWindow_.getOutstanding(); sendAbort(ErrorCode::FLOW_CONTROL_ERROR); } }
void HTTPTransaction::onIngressEOM() { if (isIngressEOMSeen()) { // This can happen when HTTPSession calls onIngressEOF() sendAbort(ErrorCode::STREAM_CLOSED); return; } if (expectedContentLengthRemaining_.hasValue() && expectedContentLengthRemaining_.value() > 0) { auto errorMsg = folly::to<std::string>( "Content-Length/body mismatch: expecting another ", expectedContentLengthRemaining_.value()); LOG(ERROR) << *this << " " << errorMsg; if (handler_) { HTTPException ex(HTTPException::Direction::INGRESS, errorMsg); ex.setProxygenError(kErrorParseBody); onError(ex); } return; } // TODO: change the codec to not give an EOM callback after a 100 response? // We could then delete the below 'if' if (isUpstream() && extraResponseExpected()) { VLOG(4) << "Ignoring EOM on initial 100 response on " << *this; return; } if (!validateIngressStateTransition( HTTPTransactionIngressSM::Event::onEOM)) { return; } // We need to update the read timeout here. We're not likely to be // expecting any more ingress, and the timer should be cancelled // immediately. If we are expecting more, this will reset the timer. updateReadTimeout(); if (mustQueueIngress()) { checkCreateDeferredIngress(); deferredIngress_->emplace(id_, HTTPEvent::Type::MESSAGE_COMPLETE); VLOG(4) << *this << " Queued ingress event of type " << HTTPEvent::Type::MESSAGE_COMPLETE; } else { processIngressEOM(); } }
void TpReceiveTransaction::getFirstFrame(FirstFrame frame) { if (state == INIT || state == BROKEN) { timer.stop(); state = PROGRESS; buffer.clear(); buffLength = frame.getPacketSize(); consecutiveFrameCounter = 0; std::vector<byte> v = frame.getData(); pointer = buffer.begin(); buffer.insert(pointer, v.begin(), v.end()); pointer = buffer.end(); readyFlowControl(); } else if (state == PROGRESS) { //LOG_WRITER.write(tr("Неожиданный FirstFrame"), QColor(255, 0, 255), 1); state = BROKEN; sendAbort(); } }
void ProxygenTransport::messageAvailable(ResponseMessage&& message) { if (!m_clientTxn) { return; } HTTPMessage *msg = nullptr; bool newPushOk = (message.m_type == ResponseMessage::Type::HEADERS); HTTPTransaction *txn = getTransaction(message.m_pushId, &msg, newPushOk); if (!txn) { // client is gone, just eat the msg return; } switch (message.m_type) { case ResponseMessage::Type::HEADERS: CHECK(msg); txn->sendHeaders(*msg); if (!message.m_chunk && !message.m_eom) { break; } // else fall through case ResponseMessage::Type::BODY: if (message.m_chunk && m_method != Transport::Method::HEAD) { // TODO: experiment with disabling this chunked flag and letting // proxygen framework do the chunking if (message.m_chunked) { txn->sendChunkHeader(message.m_chunk->length()); txn->sendBody(std::move(message.m_chunk)); txn->sendChunkTerminator(); } else { txn->sendBody(std::move(message.m_chunk)); } } if (!message.m_eom) { break; } // else fall through case ResponseMessage::Type::EOM: cancelTimeout(); txn->sendEOM(); break; case ResponseMessage::Type::FINISH: // need to make sure the last reference is deleted in this thread // The VM thread is done, any outstanding push txns need aborts. // It's possible that we could drive pushes from somewhere other // than the VM thread, in which case we'll need a different saftey // mechanism { Lock lock(this); for (auto& it: m_pushHandlers) { auto pushTxn = it.second->getTransaction(); if (pushTxn && !pushTxn->isEgressEOMSeen()) { LOG(ERROR) << "Aborting unfinished push txn=" << *pushTxn; pushTxn->sendAbort(); } } } break; case ResponseMessage::Type::RESUME_INGRESS: txn->resumeIngress(); break; } }
void HTTPTransaction::sendAbort() { sendAbort(isUpstream() ? ErrorCode::CANCEL : ErrorCode::INTERNAL_ERROR); }
void HTTPTransaction::onError(const HTTPException& error) { DestructorGuard g(this); const bool wasAborted = aborted_; // see comment below const bool wasEgressComplete = isEgressComplete(); const bool wasIngressComplete = isIngressComplete(); bool notify = (handler_); HTTPException::Direction direction = error.getDirection(); if (direction == HTTPException::Direction::INGRESS && isIngressEOMSeen() && isExpectingWindowUpdate()) { // we got an ingress error, we've seen the entire message, but we're // expecting more (window updates). These aren't coming, convert to // INGRESS_AND_EGRESS VLOG(4) << *this << " Converting ingress error to ingress+egress due to" " flow control, and aborting"; direction = HTTPException::Direction::INGRESS_AND_EGRESS; sendAbort(ErrorCode::FLOW_CONTROL_ERROR); } if (error.getProxygenError() == kErrorStreamAbort) { DCHECK(error.getDirection() == HTTPException::Direction::INGRESS_AND_EGRESS); aborted_ = true; } else if (error.hasCodecStatusCode()) { DCHECK(error.getDirection() == HTTPException::Direction::INGRESS_AND_EGRESS); sendAbort(error.getCodecStatusCode()); } switch (direction) { case HTTPException::Direction::INGRESS_AND_EGRESS: markEgressComplete(); markIngressComplete(); if (wasEgressComplete && wasIngressComplete && // We mark egress complete before we get acknowledgement of the // write segment finishing successfully. // TODO: instead of using DestructorGuard hacks to keep txn around, // use an explicit callback function and set egress complete after // last byte flushes (or egress error occurs), see #3912823 (error.getProxygenError() != kErrorWriteTimeout || wasAborted)) { notify = false; } break; case HTTPException::Direction::EGRESS: markEgressComplete(); if (!wasEgressComplete && isIngressEOMSeen() && ingressErrorSeen_) { // we've already seen an ingress error but we ignored it, hoping the // handler would resume and read our queued EOM. Now both sides are // dead and we need to kill this transaction. markIngressComplete(); } if (wasEgressComplete) { notify = false; } break; case HTTPException::Direction::INGRESS: if (isIngressEOMSeen()) { // Not an error, for now ingressErrorSeen_ = true; return; } markIngressComplete(); if (wasIngressComplete) { notify = false; } break; } if (notify && handler_) { // mark egress complete may result in handler detaching handler_->onError(error); } }
void TpReceiveTransaction::timeout() { timer.stop(); sendAbort(); emit watingTimeOut(); }