Line data Source code
1 : /*
2 : * Copyright (c) 2024 Project CHIP Authors
3 : *
4 : * Licensed under the Apache License, Version 2.0 (the "License");
5 : * you may not use this file except in compliance with the License.
6 : * You may obtain a copy of the License at
7 : *
8 : * http://www.apache.org/licenses/LICENSE-2.0
9 : *
10 : * Unless required by applicable law or agreed to in writing, software
11 : * distributed under the License is distributed on an "AS IS" BASIS,
12 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : * See the License for the specific language governing permissions and
14 : * limitations under the License.
15 : */
16 :
17 : #include "AsyncTransferFacilitator.h"
18 :
19 : #include <protocols/bdx/StatusCode.h>
20 : #include <system/SystemClock.h>
21 :
22 : namespace chip {
23 : namespace bdx {
24 :
25 0 : AsyncTransferFacilitator::~AsyncTransferFacilitator() {}
26 :
27 0 : CHIP_ERROR AsyncTransferFacilitator::Init(System::Layer * layer, Messaging::ExchangeContext * exchangeCtx,
28 : System::Clock::Timeout timeout)
29 : {
30 0 : VerifyOrReturnError(layer != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
31 0 : VerifyOrReturnError(exchangeCtx != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
32 0 : VerifyOrReturnError(!mExchange, CHIP_ERROR_INCORRECT_STATE);
33 :
34 0 : mSystemLayer = layer;
35 0 : mExchange.Grab(exchangeCtx);
36 0 : mTimeout = timeout;
37 0 : mProcessingOutputEvents = false;
38 0 : mDestroySelfAfterProcessingEvents = false;
39 0 : return CHIP_NO_ERROR;
40 : }
41 :
42 : /**
43 : * Get events one by one from the TransferSession and process them,
44 : * until there are no more events to process.
45 : */
46 0 : void AsyncTransferFacilitator::ProcessOutputEvents()
47 : {
48 0 : if (mProcessingOutputEvents)
49 : {
50 0 : ChipLogDetail(BDX,
51 : "ProcessOutputEvents: we are already in the middle of processing events, so nothing to do here; when we "
52 : "unwind to the processing loop the events will get processed.");
53 0 : return;
54 : }
55 :
56 0 : mProcessingOutputEvents = true;
57 :
58 : // Get the next output event and handle it based on the type of event.
59 : // If its of type kMsgToSend send it over the exchange, otherwise call the HandleTransferSessionOutput
60 : // virtual method that must be implemeted by the subclass of this class to handle the BDX message.
61 0 : TransferSession::OutputEvent outEvent;
62 :
63 0 : mTransfer.GetNextAction(outEvent);
64 0 : while (outEvent.EventType != TransferSession::OutputEventType::kNone)
65 : {
66 :
67 : // If the transfer session state machine generates an event of type TransferSession::OutputEventType::kInternalError,
68 : // indicating that the session is in a bad state, it will keep doing that thereafter.
69 : //
70 : // So stop trying to process events, and go ahead and destroy ourselves to clean up the transfer.
71 0 : if (outEvent.EventType == TransferSession::OutputEventType::kInternalError)
72 : {
73 0 : mDestroySelfAfterProcessingEvents = true;
74 0 : break;
75 : }
76 :
77 0 : if (outEvent.EventType == TransferSession::OutputEventType::kMsgToSend)
78 : {
79 0 : CHIP_ERROR err = SendMessage(outEvent.msgTypeData, outEvent.MsgData);
80 :
81 : // If we failed to send the message across the exchange, just abort the transfer.
82 : // We have no way to notify our peer we are doing that (we can't send them a
83 : // message!) but eventually they will time out.
84 0 : if (err != CHIP_NO_ERROR)
85 : {
86 0 : DestroySelf();
87 0 : return;
88 : }
89 :
90 : // If we send out a status report across the exchange, that means there was an error.
91 : // We've sent our report about that error and can now abort the transfer. Our peer
92 : // will respond to the status report by tearing down their side.
93 0 : if (outEvent.msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport))
94 : {
95 0 : mDestroySelfAfterProcessingEvents = true;
96 0 : break;
97 : }
98 : }
99 : else
100 : {
101 0 : HandleTransferSessionOutput(outEvent);
102 : }
103 0 : mTransfer.GetNextAction(outEvent);
104 : }
105 :
106 0 : mProcessingOutputEvents = false;
107 :
108 : // If mDestroySelfAfterProcessingEvents is set (by our code above or by NotifyEventHandled), we need
109 : // to call DestroySelf() after processing all pending output events.
110 0 : if (mDestroySelfAfterProcessingEvents)
111 : {
112 0 : DestroySelf();
113 : }
114 0 : }
115 :
116 0 : CHIP_ERROR AsyncTransferFacilitator::SendMessage(const TransferSession::MessageTypeData msgTypeData,
117 : System::PacketBufferHandle & msgBuf)
118 : {
119 0 : VerifyOrReturnError(mExchange, CHIP_ERROR_INCORRECT_STATE);
120 :
121 0 : Messaging::SendFlags sendFlags;
122 :
123 : // All messages that are sent expect a response, except for a StatusReport which would indicate an error and
124 : // the end of the transfer.
125 0 : if (!msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport))
126 : {
127 0 : sendFlags.Set(Messaging::SendMessageFlags::kExpectResponse);
128 : }
129 :
130 0 : Messaging::ExchangeContext * ec = mExchange.Get();
131 :
132 : // Set the response timeout on the exchange before sending the message.
133 0 : ec->SetResponseTimeout(mTimeout);
134 0 : return ec->SendMessage(msgTypeData.ProtocolId, msgTypeData.MessageType, std::move(msgBuf), sendFlags);
135 : }
136 :
137 0 : CHIP_ERROR AsyncTransferFacilitator::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
138 : System::PacketBufferHandle && payload)
139 : {
140 0 : VerifyOrReturnError(mExchange, CHIP_ERROR_INCORRECT_STATE);
141 :
142 0 : VerifyOrReturnError(ec == mExchange.Get(), CHIP_ERROR_INCORRECT_STATE);
143 :
144 : CHIP_ERROR err =
145 0 : mTransfer.HandleMessageReceived(payloadHeader, std::move(payload), System::SystemClock().GetMonotonicTimestamp());
146 0 : if (err != CHIP_NO_ERROR)
147 : {
148 0 : ChipLogError(BDX, "OnMessageReceived: Failed to handle message: %" CHIP_ERROR_FORMAT, err.Format());
149 :
150 : // This should notify the transfer object to abort transfer so it can send a status report across the exchange
151 : // when we call ProcessOutputEvents below.
152 0 : mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(err));
153 : }
154 0 : else if (!payloadHeader.HasMessageType(MessageType::BlockAckEOF))
155 : {
156 : // Almost every BDX message expect BlockAckEOF will follow up with a response on the exchange.
157 0 : ec->WillSendMessage();
158 : }
159 :
160 0 : ProcessOutputEvents();
161 0 : return err;
162 : }
163 :
164 0 : void AsyncTransferFacilitator::OnResponseTimeout(Messaging::ExchangeContext * ec)
165 : {
166 0 : ChipLogDetail(BDX, "OnResponseTimeout, ec: " ChipLogFormatExchange, ChipLogValueExchange(ec));
167 0 : DestroySelf();
168 0 : }
169 :
170 0 : CHIP_ERROR AsyncResponder::Init(System::Layer * layer, Messaging::ExchangeContext * exchangeCtx, TransferRole role,
171 : BitFlags<TransferControlFlags> xferControlOpts, uint16_t maxBlockSize,
172 : System::Clock::Timeout timeout)
173 : {
174 0 : ReturnErrorOnFailure(AsyncTransferFacilitator::Init(layer, exchangeCtx, timeout));
175 0 : ReturnErrorOnFailure(mTransfer.WaitForTransfer(role, xferControlOpts, maxBlockSize, timeout));
176 0 : return CHIP_NO_ERROR;
177 : }
178 :
179 0 : void AsyncResponder::NotifyEventHandled(const TransferSession::OutputEventType eventType, CHIP_ERROR status)
180 : {
181 : // If this is the end of the transfer (whether a clean end, or some sort of error condition), ensure
182 : // that we destroy ourselves after unwinding the processing loop in the ProcessOutputEvents API.
183 : // We can ignore the status for these output events because none of them are supposed to result in
184 : // us sending a StatusReport, and that's all we use the status for.
185 : //
186 : // In particular, for kTransferTimeout, kAckEOFReceived, and kStatusReceived per spec we
187 : // are not supposed to reply with a StatusReport. And for kInternalError the state machine
188 : // is in an unrecoverable state of some sort, and we should stop trying to make use of it.
189 0 : if (eventType == TransferSession::OutputEventType::kAckEOFReceived ||
190 0 : eventType == TransferSession::OutputEventType::kInternalError ||
191 0 : eventType == TransferSession::OutputEventType::kTransferTimeout ||
192 : eventType == TransferSession::OutputEventType::kStatusReceived)
193 : {
194 0 : ChipLogProgress(BDX, "NotifyEventHandled : Event %s Error %" CHIP_ERROR_FORMAT,
195 : TransferSession::OutputEvent::TypeToString(eventType), status.Format());
196 0 : mDestroySelfAfterProcessingEvents = true;
197 : }
198 0 : else if (status != CHIP_NO_ERROR)
199 : {
200 : // If there was an error handling the output event, this should notify the transfer object to abort transfer
201 : // so it can send a status report across the exchange when we call ProcessOutputEvents below.
202 0 : mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(status));
203 : }
204 :
205 0 : ProcessOutputEvents();
206 0 : }
207 :
208 : } // namespace bdx
209 : } // namespace chip
|