unique_ptr<IOBuf> HTTPSession::getNextToSend(bool* cork, bool* eom) {
  // limit ourselves to one outstanding write at a time (onWriteSuccess calls
  // scheduleWrite)
  if (numActiveWrites_ > 0 || writesShutdown()) {
    VLOG(4) << "skipping write during this loop, numActiveWrites_=" <<
      numActiveWrites_ << " writesShutdown()=" << writesShutdown();
    return nullptr;
  }

  // We always tack on at least one body packet to the current write buf
  // This ensures that a short HTTPS response will go out in a single SSL record
  while (!txnEgressQueue_.empty()) {
    uint32_t toSend = kWriteReadyMax;
    if (connFlowControl_) {
      if (connFlowControl_->getAvailableSend() == 0) {
        VLOG(4) << "Session-level send window is full, skipping remaining "
                << "body writes this loop";
        break;
      }
      toSend = std::min(toSend, connFlowControl_->getAvailableSend());
    }
    txnEgressQueue_.nextEgress(nextEgressResults_,
                               isSpdyCodecProtocol(codec_->getProtocol()));
    CHECK(!nextEgressResults_.empty()); // Queue was non empty, so this must be
    // The maximum we will send for any transaction in this loop
    uint32_t txnMaxToSend = toSend * nextEgressResults_.front().second;
    if (txnMaxToSend == 0) {
      // toSend is smaller than the number of transactions.  Give all egress
      // to the first transaction
      nextEgressResults_.erase(++nextEgressResults_.begin(),
                               nextEgressResults_.end());
      txnMaxToSend = std::min(toSend, egressBodySizeLimit_);
      nextEgressResults_.front().second = 1;
    }
    if (nextEgressResults_.size() > 1 && txnMaxToSend > egressBodySizeLimit_) {
      // Cap the max to egressBodySizeLimit_, and recompute toSend accordingly
      txnMaxToSend = egressBodySizeLimit_;
      toSend = txnMaxToSend / nextEgressResults_.front().second;
    }
    // split allowed by relative weight, with some minimum
    for (auto txnPair: nextEgressResults_) {
      uint32_t txnAllowed = txnPair.second * toSend;
      if (nextEgressResults_.size() > 1) {
        CHECK_LE(txnAllowed, egressBodySizeLimit_);
      }
      if (connFlowControl_) {
        CHECK_LE(txnAllowed, connFlowControl_->getAvailableSend());
      }
      if (txnAllowed == 0) {
        // The ratio * toSend was so small this txn gets nothing.
        VLOG(4) << *this << " breaking egress loop on 0 txnAllowed";
        break;
      }

      VLOG(4) << *this << " egressing txnID=" << txnPair.first->getID() <<
        " allowed=" << txnAllowed;
      txnPair.first->onWriteReady(txnAllowed, txnPair.second);
    }
    nextEgressResults_.clear();
    // it can be empty because of HTTPTransaction rate limiting.  We should
    // change rate limiting to clearPendingEgress while waiting.
    if (!writeBuf_.empty()) {
      break;
    }
  }
  *eom = false;
  if (byteEventTracker_) {
    uint64_t needed = byteEventTracker_->preSend(cork, eom, bytesWritten_);
    if (needed > 0) {
      VLOG(5) << *this << " writeBuf_.chainLength(): "
              << writeBuf_.chainLength() << " txnEgressQueue_.empty(): "
              << txnEgressQueue_.empty();

      if (needed < writeBuf_.chainLength()) {
        // split the next EOM chunk
        VLOG(5) << *this << " splitting " << needed << " bytes out of a "
                << writeBuf_.chainLength() << " bytes IOBuf";
        *cork = true;
        if (sessionStats_) {
          sessionStats_->recordTTLBAIOBSplitByEom();
        }
        return writeBuf_.split(needed);
      } else {
        CHECK_EQ(needed, writeBuf_.chainLength());
      }
    }
  }

  // cork if there are txns with pending egress and room to send them
  *cork = !txnEgressQueue_.empty() && !isConnWindowFull();
  return writeBuf_.move();
}