xref: /aosp_15_r20/external/ot-br-posix/src/rest/connection.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
1 /*
2  *  Copyright (c) 2020, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "rest/connection.hpp"
30 
31 #include <cerrno>
32 
33 #include <assert.h>
34 
35 #include <sys/socket.h>
36 #include <sys/time.h>
37 
38 using std::chrono::duration_cast;
39 using std::chrono::microseconds;
40 using std::chrono::seconds;
41 using std::chrono::steady_clock;
42 
43 namespace otbr {
44 namespace rest {
45 
46 // The timeout (in microseconds) since a connection is in wait callback state
47 static const uint32_t kCallbackTimeout = 10000000;
48 
49 // The time interval (in microseconds) for checking again if there is a connection need callback.
50 static const uint32_t kCallbackCheckInterval = 500000;
51 
52 // The timeout (in microseconds) since a connection is in wait write state
53 static const uint32_t kWriteTimeout = 10000000;
54 
55 // The timeout (in microseconds) since a connection is in wait read state
56 static const uint32_t kReadTimeout = 1000000;
57 
Connection(steady_clock::time_point aStartTime,Resource * aResource,int aFd)58 Connection::Connection(steady_clock::time_point aStartTime, Resource *aResource, int aFd)
59     : mTimeStamp(aStartTime)
60     , mFd(aFd)
61     , mState(ConnectionState::kInit)
62     , mParser(&mRequest)
63     , mResource(aResource)
64 {
65 }
66 
~Connection(void)67 Connection::~Connection(void)
68 {
69     Disconnect();
70 }
71 
Init(void)72 void Connection::Init(void)
73 {
74     mParser.Init();
75 }
76 
UpdateReadFdSet(fd_set & aReadFdSet,int & aMaxFd) const77 void Connection::UpdateReadFdSet(fd_set &aReadFdSet, int &aMaxFd) const
78 {
79     if (mState == ConnectionState::kReadWait || mState == ConnectionState::kInit)
80     {
81         FD_SET(mFd, &aReadFdSet);
82         aMaxFd = aMaxFd < mFd ? mFd : aMaxFd;
83     }
84 }
85 
UpdateWriteFdSet(fd_set & aWriteFdSet,int & aMaxFd) const86 void Connection::UpdateWriteFdSet(fd_set &aWriteFdSet, int &aMaxFd) const
87 {
88     if (mState == ConnectionState::kWriteWait)
89     {
90         FD_SET(mFd, &aWriteFdSet);
91         aMaxFd = aMaxFd < mFd ? mFd : aMaxFd;
92     }
93 }
94 
UpdateTimeout(timeval & aTimeout) const95 void Connection::UpdateTimeout(timeval &aTimeout) const
96 {
97     struct timeval timeout;
98     uint32_t       timeoutLen = kReadTimeout;
99     auto           duration   = duration_cast<microseconds>(steady_clock::now() - mTimeStamp).count();
100 
101     switch (mState)
102     {
103     case ConnectionState::kReadWait:
104         timeoutLen = kReadTimeout;
105         break;
106     case ConnectionState::kCallbackWait:
107         timeoutLen = kCallbackCheckInterval;
108         break;
109     case ConnectionState::kWriteWait:
110         timeoutLen = kWriteTimeout;
111         break;
112     case ConnectionState::kComplete:
113         timeoutLen = 0;
114         break;
115     default:
116         break;
117     }
118 
119     if (duration <= timeoutLen)
120     {
121         timeout.tv_sec  = 0;
122         timeout.tv_usec = timeoutLen - duration;
123     }
124     else
125     {
126         timeout.tv_sec  = 0;
127         timeout.tv_usec = 0;
128     }
129 
130     if (timercmp(&timeout, &aTimeout, <))
131     {
132         aTimeout = timeout;
133     }
134 }
135 
Update(MainloopContext & aMainloop)136 void Connection::Update(MainloopContext &aMainloop)
137 {
138     UpdateTimeout(aMainloop.mTimeout);
139     UpdateReadFdSet(aMainloop.mReadFdSet, aMainloop.mMaxFd);
140     UpdateWriteFdSet(aMainloop.mWriteFdSet, aMainloop.mMaxFd);
141 }
142 
Disconnect(void)143 void Connection::Disconnect(void)
144 {
145     mState = ConnectionState::kComplete;
146 
147     if (mFd != -1)
148     {
149         close(mFd);
150         mFd = -1;
151     }
152 }
153 
Process(const MainloopContext & aMainloop)154 void Connection::Process(const MainloopContext &aMainloop)
155 {
156     otbrError error = OTBR_ERROR_NONE;
157 
158     switch (mState)
159     {
160     // Initial state, directly read for the first time.
161     case ConnectionState::kInit:
162     case ConnectionState::kReadWait:
163         ProcessWaitRead(aMainloop.mReadFdSet);
164         break;
165     case ConnectionState::kCallbackWait:
166         //  Wait for Callback process.
167         ProcessWaitCallback();
168         break;
169     case ConnectionState::kWriteWait:
170         ProcessWaitWrite(aMainloop.mWriteFdSet);
171         break;
172     default:
173         assert(false);
174     }
175 
176     if (error != OTBR_ERROR_NONE)
177     {
178         Disconnect();
179     }
180 }
181 
ProcessWaitRead(const fd_set & aReadFdSet)182 void Connection::ProcessWaitRead(const fd_set &aReadFdSet)
183 {
184     otbrError error    = OTBR_ERROR_NONE;
185     int32_t   received = 0, err;
186     char      buf[2048];
187     auto      duration = duration_cast<microseconds>(steady_clock::now() - mTimeStamp).count();
188 
189     // Reach a read timeout, will send response about this timeout later.
190     VerifyOrExit(duration <= kReadTimeout, error = OTBR_ERROR_REST);
191 
192     // It will succeed either fd is set or it is in kInit state.
193     VerifyOrExit(FD_ISSET(mFd, &aReadFdSet) || mState == ConnectionState::kInit);
194 
195     do
196     {
197         mState   = ConnectionState::kReadWait;
198         received = read(mFd, buf, sizeof(buf));
199         err      = errno;
200         if (received > 0)
201         {
202             mParser.Process(buf, received);
203         }
204     } while ((received > 0 && !mRequest.IsComplete()) || err == EINTR);
205 
206     if (mRequest.IsComplete())
207     {
208         Handle();
209     }
210 
211     // Check first failure situation: received == 0 (indicate another side at least has closes its write side )
212     // and at the same time, the request has not been parsed completely.
213     VerifyOrExit(received != 0 || mRequest.IsComplete(), error = OTBR_ERROR_REST);
214 
215     // Check second  failure situation : received = -1 error(indicates that our system call read raise an error )
216     // then try to send back a response that there is an internal error.
217     VerifyOrExit(received > 0 || (received == -1 && (err == EAGAIN || err == EWOULDBLOCK)), error = OTBR_ERROR_REST);
218 
219 exit:
220     if (error != OTBR_ERROR_NONE)
221     {
222         if (received < 0)
223         {
224             mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError);
225             Write();
226         }
227         else
228         {
229             mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusRequestTimeout);
230             Write();
231         }
232     }
233 }
234 
Handle(void)235 void Connection::Handle(void)
236 {
237     otbrError error = OTBR_ERROR_NONE;
238 
239     // Try to close server read side here, because we have started to handle the request and no longler read from
240     // socket.
241     VerifyOrExit((shutdown(mFd, SHUT_RD) == 0), error = OTBR_ERROR_REST);
242 
243     mResource->Handle(mRequest, mResponse);
244 
245     if (mResponse.NeedCallback())
246     {
247         mState     = ConnectionState::kCallbackWait;
248         mTimeStamp = steady_clock::now();
249     }
250     else
251     {
252         // Normal Write back process.
253         Write();
254     }
255 
256 exit:
257 
258     if (error != OTBR_ERROR_NONE)
259     {
260         mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError);
261         Write();
262     }
263 }
264 
ProcessWaitCallback(void)265 void Connection::ProcessWaitCallback(void)
266 {
267     auto duration = duration_cast<microseconds>(steady_clock::now() - mTimeStamp).count();
268 
269     mResource->HandleCallback(mRequest, mResponse);
270 
271     if (mResponse.IsComplete())
272     {
273         Write();
274     }
275     else
276     {
277         if (duration >= kCallbackTimeout)
278         {
279             mResource->ErrorHandler(mResponse, HttpStatusCode::kStatusInternalServerError);
280             Write();
281         }
282     }
283 }
284 
ProcessWaitWrite(const fd_set & aWriteFdSet)285 void Connection::ProcessWaitWrite(const fd_set &aWriteFdSet)
286 {
287     auto duration = duration_cast<microseconds>(steady_clock::now() - mTimeStamp).count();
288 
289     if (duration <= kWriteTimeout)
290     {
291         if (FD_ISSET(mFd, &aWriteFdSet))
292         {
293             Write();
294         }
295     }
296     else
297     {
298         Disconnect();
299     }
300 }
301 
Write(void)302 void Connection::Write(void)
303 {
304     otbrError   error = OTBR_ERROR_NONE;
305     std::string errorCode;
306     int32_t     sendLength;
307     int32_t     err;
308 
309     if (mState != ConnectionState::kWriteWait)
310     {
311         // Change its state when try write for the first time.
312         mState        = ConnectionState::kWriteWait;
313         mTimeStamp    = steady_clock::now();
314         mWriteContent = mResponse.Serialize();
315     }
316 
317     // Check we do have something to write.
318     VerifyOrExit(mWriteContent.size() > 0, error = OTBR_ERROR_REST);
319 
320     sendLength = write(mFd, mWriteContent.c_str(), mWriteContent.size());
321     err        = errno;
322 
323     // Write successfully
324     if (sendLength == static_cast<int32_t>(mWriteContent.size()))
325     {
326         // Normal Exit
327         Disconnect();
328     }
329     else if (sendLength > 0)
330     {
331         // Partly write
332         mWriteContent = mWriteContent.substr(sendLength);
333     }
334     else
335     {
336         if (errno == EINTR)
337         {
338             // Try again
339             Write();
340         }
341         else
342         {
343             // There is an error when we write, if this, we directly disconnect this connection.
344             VerifyOrExit(err == EAGAIN || err == EWOULDBLOCK, error = OTBR_ERROR_REST);
345         }
346     }
347 
348 exit:
349     if (error != OTBR_ERROR_NONE)
350     {
351         Disconnect();
352     }
353 }
354 
IsComplete() const355 bool Connection::IsComplete() const
356 {
357     return mState == ConnectionState::kComplete;
358 }
359 
360 } // namespace rest
361 } // namespace otbr
362