void NetworkTransaction::SetConnectionLost() { cs = NULL; FreePbuf(); for (NetworkTransaction *rs = nextWrite; rs != NULL; rs = rs->nextWrite) { rs->cs = NULL; } }
void RequestState::SetConnectionLost() { hs = NULL; FreePbuf(); for (RequestState *rs = nextWrite; rs != NULL; rs = rs->nextWrite) { rs->hs = NULL; } }
// This method should be called if we don't want to send data to the client and if we // don't want to interfere with the connection state. May also be called from ISR! void NetworkTransaction::Discard() { // Can we do anything? if (status == released) { // No - don't free up released items multiple times return; } // Free up some resources FreePbuf(); if (fileBeingSent != nullptr) { fileBeingSent->Close(); fileBeingSent = nullptr; } OutputBuffer::ReleaseAll(sendBuffer); sendStack->ReleaseAll(); // Unlink this transactions from the list of ready transactions and free it. It is then appended to the list of // free transactions because we don't want to risk reusing it when the ethernet ISR processes incoming data NetworkTransaction *previous = nullptr; for(NetworkTransaction *item = reprap.GetNetwork().readyTransactions; item != nullptr; item = item->next) { if (item == this) { if (previous == nullptr) { reprap.GetNetwork().readyTransactions = next; } else { previous->next = next; } break; } previous = item; } reprap.GetNetwork().AppendTransaction(&reprap.GetNetwork().freeTransactions, this); bool callDisconnectHandler = (cs != nullptr && status == disconnected); status = released; // Call disconnect event if this transaction indicates a graceful disconnect and if the connection // still persists (may not be the case if a RST packet was received before) if (callDisconnectHandler) { if (reprap.Debug(moduleNetwork)) { reprap.GetPlatform().Message(UsbMessage, "Network: Discard() is handling a graceful disconnect\n"); } reprap.GetNetwork().ConnectionClosed(cs, false); } }
// Call this to perform some networking tasks while processing deferred requests, // and to move this transaction and all transactions that are associated with its // connection to the end of readyTransactions. There are three ways to do this: // // 1) DeferOnly: Do not modify any of the processed data and don't send an ACK. // This will ensure that zero-window packets are sent back to the client // 2) ResetData: Reset the read pointers and acknowledge that the data has been processed // 3) DiscardData: Free the processed data, acknowledge it and append this transaction as // an empty item again without payload (i.e. without pbufs) // void NetworkTransaction::Defer(DeferralMode mode) { if (mode == DeferralMode::ResetData) { // Reset the reading pointers and send an ACK inputPointer = 0; readingPb = pb; if (IsConnected() && pb != nullptr && !dataAcknowledged) { tcp_recved(cs->pcb, pb->tot_len); dataAcknowledged = true; } } else if (mode == DeferralMode::DiscardData) { // Discard the incoming data, because we don't need to process it any more FreePbuf(); } status = deferred; // Unlink this transaction from the list of ready transactions and append it again Network& network = reprap.GetNetwork(); NetworkTransaction *item, *previous = nullptr; for(item = network.readyTransactions; item != nullptr; item = item->next) { if (item == this) { if (previous == nullptr) { network.readyTransactions = next; } else { previous->next = next; } break; } previous = item; } network.AppendTransaction(&network.readyTransactions, this); // Append all other transactions that are associated to this connection, so that the // Webserver gets a chance to deal with all connected clients even while multiple // deferred requests are present in the list. item = network.readyTransactions; previous = nullptr; while (item != this) { if (item->cs == cs) { NetworkTransaction *nextItem = item->next; if (previous == nullptr) { network.readyTransactions = item->next; network.AppendTransaction(&network.readyTransactions, item); } else { previous->next = item->next; network.AppendTransaction(&network.readyTransactions, item); } item = nextItem; } else { previous = item; item = item->next; } } }
// This is called by the Webserver to send output data to a client. If keepConnectionAlive is set to false, // the current connection will be terminated once everything has been sent. void NetworkTransaction::Commit(bool keepConnectionAlive) { // If the connection has been terminated (e.g. RST received while writing upload data), discard this transaction if (!IsConnected() || status == released) { Discard(); return; } // Free buffer holding the incoming data and prepare some values for the sending process FreePbuf(); cs->persistConnection = keepConnectionAlive; if (sendBuffer == nullptr) { sendBuffer = sendStack->Pop(); } status = sending; // Unlink the item(s) from the list of ready transactions if (keepConnectionAlive) { // Our connection is still of interest, remove only this transaction from the list NetworkTransaction *previous = nullptr; for(NetworkTransaction *item = reprap.GetNetwork().readyTransactions; item != nullptr; item = item->next) { if (item == this) { if (previous == nullptr) { reprap.GetNetwork().readyTransactions = next; } else { previous->next = next; } break; } previous = item; } } else { // We will close this connection soon, stop receiving data from this PCB tcp_recv(cs->pcb, nullptr); // Also remove all ready transactions pointing to our ConnectionState NetworkTransaction *previous = nullptr, *item = reprap.GetNetwork().readyTransactions; while (item != nullptr) { if (item->cs == cs) { if (item == this) { // Only unlink this item if (previous == nullptr) { reprap.GetNetwork().readyTransactions = next; } else { previous->next = next; } item = next; } else { // Remove all others item->Discard(); item = (previous == nullptr) ? reprap.GetNetwork().readyTransactions : previous->next; } } else { previous = item; item = item->next; } } } // Enqueue this transaction, so it's sent in the right order NetworkTransaction *mySendingTransaction = cs->sendingTransaction; if (mySendingTransaction == nullptr) { cs->sendingTransaction = this; reprap.GetNetwork().AppendTransaction(&reprap.GetNetwork().writingTransactions, this); } else { while (mySendingTransaction->nextWrite != nullptr) { mySendingTransaction = mySendingTransaction->nextWrite; } mySendingTransaction->nextWrite = this; } }