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 0 : if (outEvent.EventType == TransferSession::OutputEventType::kMsgToSend)
67 : {
68 0 : CHIP_ERROR err = SendMessage(outEvent.msgTypeData, outEvent.MsgData);
69 :
70 : // If we failed to send the message across the exchange, just abort the transfer.
71 : // We have no way to notify our peer we are doing that (we can't send them a
72 : // message!) but eventually they will time out.
73 0 : if (err != CHIP_NO_ERROR)
74 : {
75 0 : DestroySelf();
76 0 : return;
77 : }
78 :
79 : // If we send out a status report across the exchange, that means there was an error.
80 : // We've sent our report about that error and can now abort the transfer. Our peer
81 : // will respond to the status report by tearing down their side.
82 0 : if (outEvent.msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport))
83 : {
84 0 : mDestroySelfAfterProcessingEvents = true;
85 0 : break;
86 : }
87 : }
88 : else
89 : {
90 0 : HandleTransferSessionOutput(outEvent);
91 : }
92 0 : mTransfer.GetNextAction(outEvent);
93 : }
94 :
95 0 : mProcessingOutputEvents = false;
96 :
97 : // If mDestroySelfAfterProcessingEvents is set (by our code above or by NotifyEventHandled), we need
98 : // to call DestroySelf() after processing all pending output events.
99 0 : if (mDestroySelfAfterProcessingEvents)
100 : {
101 0 : DestroySelf();
102 : }
103 0 : }
104 :
105 0 : CHIP_ERROR AsyncTransferFacilitator::SendMessage(const TransferSession::MessageTypeData msgTypeData,
106 : System::PacketBufferHandle & msgBuf)
107 : {
108 0 : VerifyOrReturnError(mExchange, CHIP_ERROR_INCORRECT_STATE);
109 :
110 0 : Messaging::SendFlags sendFlags;
111 :
112 : // All messages that are sent expect a response, except for a StatusReport which would indicate an error and
113 : // the end of the transfer.
114 0 : if (!msgTypeData.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport))
115 : {
116 0 : sendFlags.Set(Messaging::SendMessageFlags::kExpectResponse);
117 : }
118 :
119 0 : Messaging::ExchangeContext * ec = mExchange.Get();
120 :
121 : // Set the response timeout on the exchange before sending the message.
122 0 : ec->SetResponseTimeout(mTimeout);
123 0 : return ec->SendMessage(msgTypeData.ProtocolId, msgTypeData.MessageType, std::move(msgBuf), sendFlags);
124 : }
125 :
126 0 : CHIP_ERROR AsyncTransferFacilitator::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
127 : System::PacketBufferHandle && payload)
128 : {
129 0 : VerifyOrReturnError(mExchange, CHIP_ERROR_INCORRECT_STATE);
130 :
131 0 : VerifyOrReturnError(ec == mExchange.Get(), CHIP_ERROR_INCORRECT_STATE);
132 :
133 : CHIP_ERROR err =
134 0 : mTransfer.HandleMessageReceived(payloadHeader, std::move(payload), System::SystemClock().GetMonotonicTimestamp());
135 0 : if (err != CHIP_NO_ERROR)
136 : {
137 0 : ChipLogError(BDX, "OnMessageReceived: Failed to handle message: %" CHIP_ERROR_FORMAT, err.Format());
138 :
139 : // This should notify the transfer object to abort transfer so it can send a status report across the exchange
140 : // when we call ProcessOutputEvents below.
141 0 : mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(err));
142 : }
143 0 : else if (!payloadHeader.HasMessageType(MessageType::BlockAckEOF))
144 : {
145 : // Almost every BDX message expect BlockAckEOF will follow up with a response on the exchange.
146 0 : ec->WillSendMessage();
147 : }
148 :
149 0 : ProcessOutputEvents();
150 0 : return err;
151 : }
152 :
153 0 : void AsyncTransferFacilitator::OnResponseTimeout(Messaging::ExchangeContext * ec)
154 : {
155 0 : ChipLogDetail(BDX, "OnResponseTimeout, ec: " ChipLogFormatExchange, ChipLogValueExchange(ec));
156 0 : DestroySelf();
157 0 : }
158 :
159 0 : CHIP_ERROR AsyncResponder::Init(System::Layer * layer, Messaging::ExchangeContext * exchangeCtx, TransferRole role,
160 : BitFlags<TransferControlFlags> xferControlOpts, uint16_t maxBlockSize,
161 : System::Clock::Timeout timeout)
162 : {
163 0 : ReturnErrorOnFailure(AsyncTransferFacilitator::Init(layer, exchangeCtx, timeout));
164 0 : ReturnErrorOnFailure(mTransfer.WaitForTransfer(role, xferControlOpts, maxBlockSize, timeout));
165 0 : return CHIP_NO_ERROR;
166 : }
167 :
168 0 : void AsyncResponder::NotifyEventHandled(const TransferSession::OutputEventType eventType, CHIP_ERROR status)
169 : {
170 0 : ChipLogDetail(BDX, "NotifyEventHandled : Event %s Error %" CHIP_ERROR_FORMAT,
171 : TransferSession::OutputEvent::TypeToString(eventType), status.Format());
172 :
173 : // If this is the end of the transfer (whether a clean end, or some sort of error condition), ensure that
174 : // we destroy ourselves after processing any output events that might have been generated by
175 : // the transfer session to handle end-of-transfer (e.g. in some error conditions it might want to send
176 : // out a status report).
177 0 : if (eventType == TransferSession::OutputEventType::kAckEOFReceived ||
178 0 : eventType == TransferSession::OutputEventType::kInternalError ||
179 0 : eventType == TransferSession::OutputEventType::kTransferTimeout ||
180 : eventType == TransferSession::OutputEventType::kStatusReceived)
181 : {
182 0 : mDestroySelfAfterProcessingEvents = true;
183 : }
184 :
185 : // If there was an error handling the output event, this should notify the transfer object to abort transfer so it can send a
186 : // status report across the exchange when we call ProcessOutputEvents below.
187 0 : if (status != CHIP_NO_ERROR)
188 : {
189 0 : mTransfer.AbortTransfer(GetBdxStatusCodeFromChipError(status));
190 : }
191 :
192 0 : ProcessOutputEvents();
193 0 : }
194 :
195 : } // namespace bdx
196 : } // namespace chip
|