merge with master
[framework/osp/net.git] / src / http / FNetHttp_HttpMultipleConnectionInfo.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /**
19  * @file                FNetHttp_HttpMultipleConnectionInfo.cpp
20  * @brief               This is the implementation file for _HttpMultipleConnectionInfo class.
21  */
22
23 #include <FBaseString.h>
24 #include <FBaseSysLog.h>
25 #include <FBaseCol.h>
26 #include <FBase_StringConverter.h>
27 #include <FBaseRt_EventDispatcher.h>
28 #include "FNetHttp_HttpCommon.h"
29 #include "FNetHttp_HttpMultipleConnectionInfo.h"
30 #include "FNetHttp_HttpTransactionImpl.h"
31 #include "FNetHttp_HttpTransactionUserData.h"
32 #include "FNetHttp_HttpCurl.h"
33 #include "FNetHttp_HttpSocketInfo.h"
34 #include "FNetHttp_HttpRequestImpl.h"
35 #include "FNetHttp_HttpResponseImpl.h"
36 #include "FNetHttp_HttpHeaderImpl.h"
37 #include "FNetHttp_HttpTransactionEventArg.h"
38 #include "FNetHttp_HttpTransactionEvent.h"
39
40 using namespace Tizen::Base;
41 using namespace Tizen::Base::Runtime;
42 using namespace Tizen::Base::Collection;
43
44 namespace Tizen { namespace Net { namespace Http
45 {
46
47 _HttpMultipleConnectionInfo::_HttpMultipleConnectionInfo()
48         : __refCount(0)
49         , __sessionId(-1)
50         , __pCurlM(null)
51         , __pGMainContext(null)
52         , __remainsConnection(-1)
53         , __isAlreadyClosedSession(false)
54         , __pTimerSource(null)
55         , __cookieFlag(NET_HTTP_COOKIE_FLAG_ALWAYS_MANUAL)
56 {
57 }
58
59 _HttpMultipleConnectionInfo::~_HttpMultipleConnectionInfo(void)
60 {
61         if (__pTimerSource != null)
62         {
63                 int timerId = g_source_get_id(__pTimerSource);
64                 g_source_set_callback(__pTimerSource, null, null, null);
65                 SysLog(NID_NET_HTTP, "Deleted g_source_destroy(%d)", timerId);
66                 g_source_destroy(__pTimerSource);
67                 g_source_unref(__pTimerSource);
68                 __pTimerSource = null;
69                 SysLog(NID_NET_HTTP, "Cancelled the TimerSource[%d] of HttpSession[%d].", timerId, __sessionId);
70         }
71
72         if (__pCurlM != null)
73         {
74                 SysLog(NID_NET_HTTP, "Deleted the __pCurlM(%x).", __pCurlM);
75                 curl_multi_cleanup(__pCurlM);
76                 __pCurlM = null;
77         }
78         __pGMainContext = null;
79
80         SysLog(NID_NET_HTTP, "Deleted the HttpMultipleConnectionInfo of HttpSession[%d]", __sessionId);
81 }
82
83 result
84 _HttpMultipleConnectionInfo::Construct(CURLM* pCurlM, int sessionId, NetHttpCookieFlag cookieFlag)
85 {
86         result r = E_SUCCESS;
87         SysTryReturnResult(NID_NET_HTTP, pCurlM != null,
88                                            E_INVALID_ARG, "pCurlM is null.");
89
90         _EventDispatcher* pEventDispatcher = _EventDispatcher::GetCurrentEventDispatcher();
91         SysTryReturnResult(NID_NET_HTTP, pEventDispatcher != null,
92                                            E_SYSTEM, "GetCurrentEventDispatcher() is null.");
93
94         __pCurlM = pCurlM;
95         SysLog(NID_NET_HTTP, "Created the __pCurlM(%x).", __pCurlM);
96         __pGMainContext = pEventDispatcher->GetGMainContext();
97         __sessionId = sessionId;
98         __cookieFlag = cookieFlag;
99
100         AddRef();
101
102         return r;
103 }
104
105 void
106 _HttpMultipleConnectionInfo::AddRef(void)
107 {
108         __refCount++;
109         SysLog(NID_NET_HTTP, "The reference count is %d. HttpSession[%d]", __refCount, __sessionId);
110 }
111
112 void
113 _HttpMultipleConnectionInfo::Release(void)
114 {
115         __refCount--;
116         SysLog(NID_NET_HTTP, "The reference count is %d. HttpSession[%d]", __refCount, __sessionId);
117
118         if (__refCount == 0)
119         {
120                 SysLog(NID_NET_HTTP, "The reference count is 0. HttpSession[%d]", __sessionId);
121                 delete this;
122         }
123 }
124
125 result
126 _HttpMultipleConnectionInfo::SetTimerEvent(int timeout_ms)
127 {
128         result r = E_SUCCESS;
129
130         int timerId = -1;
131
132         if (__pTimerSource != null)
133         {
134                 timerId = g_source_get_id(__pTimerSource);
135                 g_source_set_callback(__pTimerSource, null, null, null);
136                 SysLog(NID_NET_HTTP, "Deleted g_source_destroy(%d)", timerId);
137                 g_source_destroy(__pTimerSource);
138                 g_source_unref(__pTimerSource);
139                 __pTimerSource = null;
140                 SysLog(NID_NET_HTTP, "Cancelled the TimerSource[%d] of HttpSession[%d].", timerId, __sessionId);
141         }
142
143         if (timeout_ms >= 0)
144         {
145                 __pTimerSource = g_timeout_source_new(timeout_ms);
146                 g_source_set_callback(__pTimerSource, _HttpMultipleConnectionInfo::OnCurlTimerExpiredEvent, this, null);
147                 g_source_attach(__pTimerSource, __pGMainContext);
148
149                 timerId = g_source_get_id(__pTimerSource);
150                 SysLog(NID_NET_HTTP, "Created the TimerSource[%d] of HttpSession[%d], timeout[%d].", timerId, __sessionId, timeout_ms);
151         }
152         else
153         {
154                 SysLog(NID_NET_HTTP, "The value of timeout_ms is less than 0.");
155         }
156
157         return r;
158 }
159
160 int
161 _HttpMultipleConnectionInfo::OnCurlMultiSocketUpdated(CURL* pCurl, curl_socket_t socketFd, int curlAction, void* pUserData,
162                                                                                                           void* pCurlMAssigned)
163 {
164         static const char* pCurlSocketActionMessages[] = { "NONE", "IN", "OUT", "INOUT", "REMOVE" };
165         SysLog(NID_NET_HTTP, "Updated Socket(%d), Action(%s).", socketFd, pCurlSocketActionMessages[curlAction]);
166
167         _HttpMultipleConnectionInfo* pHttpMultipleConnectionInfo = static_cast< _HttpMultipleConnectionInfo* >(pUserData);
168         _HttpSocketInfo* pHttpSocketInfo = static_cast< _HttpSocketInfo* >(pCurlMAssigned);
169
170         if (curlAction == CURL_POLL_REMOVE)
171         {
172                 SysLog(NID_NET_HTTP, "Removed the Socket(%d).", socketFd);
173                 delete pHttpSocketInfo;
174         }
175         else
176         {
177                 if (pHttpSocketInfo == null)
178                 {
179                         SysLog(NID_NET_HTTP, "Adding the Socket(%d).", socketFd);
180                         pHttpSocketInfo = new (std::nothrow) _HttpSocketInfo();
181                         pHttpSocketInfo->SetSocketEvent(pHttpMultipleConnectionInfo, socketFd, curlAction, false);
182                 }
183                 else
184                 {
185                         SysLog(NID_NET_HTTP, "Changing the Socket(%d). Action %s => %s", socketFd,
186                                    pCurlSocketActionMessages[pHttpSocketInfo->GetSocketAction()], pCurlSocketActionMessages[curlAction]);
187                         pHttpSocketInfo->SetSocketEvent(pHttpMultipleConnectionInfo, socketFd, curlAction, true);
188                 }
189         }
190
191         return 0;
192 }
193
194 int
195 _HttpMultipleConnectionInfo::OnCurlMultiTimerChanged(CURLM* pCurlM, long timeout_ms, void* pUserData)
196 {
197         result r = E_SUCCESS;
198         SysLog(NID_NET_HTTP, "The timer(%d) of curlM was changed.", timeout_ms);
199
200         _HttpMultipleConnectionInfo* pHttpMultipleConnectionInfo = static_cast< _HttpMultipleConnectionInfo* >(pUserData);
201
202         r = pHttpMultipleConnectionInfo->SetTimerEvent(timeout_ms);
203         if (IsFailed(r))
204         {
205                 SysLog(NID_NET_HTTP, "Failed to Set the timer.");
206         }
207
208         return 0;
209 }
210
211 gboolean
212 _HttpMultipleConnectionInfo::OnCurlTimerExpiredEvent(gpointer pUserData)
213 {
214         CURLMcode rc;
215         int timerId = -1;
216
217         _HttpMultipleConnectionInfo* pHttpMultipleConnectionInfo = static_cast< _HttpMultipleConnectionInfo* >(pUserData);
218
219         if (pHttpMultipleConnectionInfo->__pTimerSource != null)
220         {
221                 timerId = g_source_get_id(pHttpMultipleConnectionInfo->__pTimerSource);
222                 g_source_set_callback(pHttpMultipleConnectionInfo->__pTimerSource, null, null, null);
223                 SysLog(NID_NET_HTTP, "Deleted g_source_destroy(%d).", timerId);
224                 g_source_destroy(pHttpMultipleConnectionInfo->__pTimerSource);
225                 g_source_unref(pHttpMultipleConnectionInfo->__pTimerSource);
226                 pHttpMultipleConnectionInfo->__pTimerSource = null;
227                 SysLog(NID_NET_HTTP, "Expired the TimerSource[%d] of HttpSession[%d].", timerId,
228                            pHttpMultipleConnectionInfo->__sessionId);
229         }
230
231         rc = curl_multi_socket_action(pHttpMultipleConnectionInfo->__pCurlM, CURL_SOCKET_TIMEOUT, 0,
232                                                                  &(pHttpMultipleConnectionInfo->__remainsConnection));
233         if (rc == CURLM_OK)
234         {
235                 SysLog(NID_NET_HTTP, "CURLM_OK - Called curl_multi_socket_action(), HttpSession[%d]",
236                            pHttpMultipleConnectionInfo->__sessionId);
237         }
238         else
239         {
240                 _HttpUtility::PrintCurlMultiErrorCode(rc);
241         }
242
243
244         pHttpMultipleConnectionInfo->CheckCurlMultiStatus();
245
246         return false;
247 }
248
249 void
250 _HttpMultipleConnectionInfo::CheckCurlMultiStatus(void)
251 {
252         result r = E_SUCCESS;
253         CURLMsg* pMessage = null;
254         int messageCount = 0;
255         CURL* pCurl = null;
256         char* pUrl = null;
257         CURLcode curlCode = CURLE_OK;
258         _HttpTransactionImpl* pHttpTransactionImpl = null;
259         _HttpTransactionUserData* pHttpTransactionUserData = null;
260
261         SysLog(NID_NET_HTTP, "The remains of connection is %d.", __remainsConnection);
262         pMessage = curl_multi_info_read(__pCurlM, &messageCount);
263         while (pMessage != null)
264         {
265                 SysLog(NID_NET_HTTP, "The count of message left is %d.", messageCount);
266                 if (pMessage->msg == CURLMSG_DONE)
267                 {
268                         pCurl = pMessage->easy_handle;
269                         curlCode = pMessage->data.result;
270                         curl_easy_getinfo(pCurl, CURLINFO_PRIVATE, &pHttpTransactionUserData);
271                         curl_easy_getinfo(pCurl, CURLINFO_EFFECTIVE_URL, &pUrl);
272
273                         if (pHttpTransactionUserData != null)
274                         {
275                                 _HttpCurl* pHttpCurl = null;
276                                 SysLog(NID_NET_HTTP, "Completed - HttpSession[%d], HttpTransaction[%d],  %s: result(%d) - %s", __sessionId, pHttpTransactionUserData->GetTransactionId(), pUrl, curlCode, pHttpTransactionUserData->GetErrorMessage());
277
278                                 if (pHttpTransactionUserData->IsClosedTransaction() == true)
279                                 {
280                                         SysLog(NID_NET_HTTP, "The HttpTransaction[%d] is already closed.",
281                                                    pHttpTransactionUserData->GetTransactionId());
282                                 }
283                                 else
284                                 {
285                                         pHttpTransactionImpl = pHttpTransactionUserData->GetHttpTransactionImpl();
286                                         SysAssertf(pHttpTransactionImpl != null, "The pHttpTransactionImpl must not be null.");
287
288                                         _HttpTransactionEvent* pHttpTransactionEvent = pHttpTransactionImpl->GetHttpTransactionEvent();
289                                         SysAssertf(pHttpTransactionEvent != null, "The pHttpTransactionEvent must not be null.");
290
291                                         if (__isAlreadyClosedSession == true)
292                                         {
293                                                 //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_ABORTED)
294                                                 pHttpTransactionEvent->FireTransactionAbortedEvent(E_NETWORK_UNAVAILABLE);
295                                                 goto FINISH;
296                                         }
297
298                                         //Check if the curlCode is CURLE_OK
299                                         if (curlCode == CURLE_OK)
300                                         {
301                                                 int readBodySize = 0;
302
303                                                 if (!pHttpTransactionImpl->IsHeaderEventFired())
304                                                 {
305                                                         _HttpResponseImpl* pHttpResponseImpl = pHttpTransactionImpl->GetHttpResponseImpl();
306                                                         pCurl = pHttpTransactionImpl->GetHttpCurl()->GetCurl();
307
308                                                         SysLog(NID_NET_HTTP, "The header event will be fired in CheckCurlMStatus()");
309
310                                                         r = pHttpResponseImpl->AddRawHeader(null, 0, true);
311                                                         if (IsFailed(r))
312                                                         {
313                                                                 r = E_SYSTEM;
314                                                                 SysLogException(NID_NET_HTTP, r, "[%s] Propagated.", GetErrorMessage(r));
315                                                         }
316
317                                                         long httpAuth = _CURL_HTTP_AUTH_NONE;
318                                                         long proxyAuth = _CURL_HTTP_AUTH_NONE;
319                                                         curl_easy_getinfo(pCurl, CURLINFO_HTTPAUTH_AVAIL, &httpAuth);
320                                                         curl_easy_getinfo(pCurl, CURLINFO_PROXYAUTH_AVAIL, &proxyAuth);
321
322                                                         //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_HEADER_COMPLETED)
323                                                         pHttpTransactionEvent->FireTransactionHeaderCompletedEvent(pHttpResponseImpl->GetHeaderLength(), proxyAuth, httpAuth);
324
325                                                         if (pHttpTransactionUserData->IsClosedTransaction() == true)
326                                                         {
327                                                                 SysLog(NID_NET_HTTP, "The HttpTransaction is already closed.");
328                                                                 goto FINISH;
329                                                         }
330                                                 }
331
332                                                 _HttpResponseImpl* pHttpResponseImpl = pHttpTransactionImpl->GetHttpResponseImpl();
333                                                 SysTryReturnVoidResult(NID_NET_HTTP, pHttpResponseImpl != null, E_SYSTEM,
334                                                                                            "[E_SYSTEM] An internal error has occurred.");
335
336                                                 r = pHttpResponseImpl->AddLastBody(readBodySize);
337
338                                                 if (readBodySize != 0)
339                                                 {
340                                                         pHttpResponseImpl->SetCurrentBodyLength(readBodySize);
341
342                                                         //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_READY_TO_READ)
343                                                         pHttpTransactionEvent->FireTransactionReadyToReadEvent(readBodySize);
344
345                                                         if (pHttpTransactionUserData->IsClosedTransaction() == true)
346                                                         {
347                                                                 SysLog(NID_NET_HTTP, "The HttpTransaction[%d] is already closed.", pHttpTransactionUserData->GetTransactionId());
348                                                                 goto FINISH;
349                                                         }
350
351                                                         if (pHttpTransactionImpl->GetHttpProgressEventListener() != null)
352                                                         {
353                                                                 //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_DOWNLOAD_PROGRESS)
354                                                                 pHttpTransactionEvent->FireHttpDownloadInProgressEvent(pHttpResponseImpl->GetCurrentBodyLength(), pHttpResponseImpl->GetTotalBodyLength());
355
356                                                                 if (pHttpTransactionUserData->IsClosedTransaction() == true)
357                                                                 {
358                                                                         SysLog(NID_NET_HTTP, "The HttpTransaction[%d] is already closed.", pHttpTransactionUserData->GetTransactionId());
359                                                                         goto FINISH;
360                                                                 }
361                                                         }
362                                                 }
363
364                                                 //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_COMPLETD)
365                                                 pHttpTransactionEvent->FireTransactionCompletedEvent();
366                                         }
367                                         else
368                                         {
369                                                 SysLog(NID_NET_HTTP, "Exceptional Case");
370
371                                                 //Convert the error code from CURLCode
372                                                 r = _HttpUtility::ConvertErrorCode(curlCode);
373                                                 if (r != E_OPERATION_CANCELED)
374                                                 {
375                                                         if (_HttpUtility::IsSslError(curlCode))
376                                                         {
377                                                                 _HttpSslInfo* pSSLInfo = _HttpUtility::GetSslCertInfo(pHttpTransactionUserData->GetSocketFd());
378
379                                                                 if (pSSLInfo == null)
380                                                                 {
381                                                                         if (curlCode == CURLE_SSL_CACERT || curlCode == CURLE_SSL_ISSUER_ERROR || curlCode == CURLE_SSL_CRL_BADFILE || curlCode == CURLE_SSL_CACERT_BADFILE)
382                                                                         {
383                                                                                 pHttpTransactionEvent->FireTransactionAbortedEvent(E_SYSTEM);
384                                                                         } else
385                                                                         {
386                                                                                 //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_ABORTED) - E_NO_CERTIFICATE
387                                                                                 pHttpTransactionEvent->FireTransactionAbortedEvent(E_NO_CERTIFICATE);
388                                                                         }
389                                                                 }
390                                                                 else
391                                                                 {
392                                                                         if (pSSLInfo->GetCertificateVerificationFlag() == HTTP_CV_FLAG_MANUAL)
393                                                                         {
394                                                                                 r = _HttpUtility::RemoveSslCertInfo(*pSSLInfo);
395                                                                                 if (IsFailed(r))
396                                                                                 {
397                                                                                         SysLogException(NID_NET_HTTP, E_SYSTEM, "[E_SYSTEM] Failed to remove the server cert.");
398                                                                                 }
399
400                                                                                 SysLog(NID_NET_HTTP, "The server certificate verification is failed by user.");
401
402                                                                                 //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_ABORTED)
403                                                                                 pHttpTransactionEvent->FireTransactionAbortedEvent(E_HTTP_USER);
404
405                                                                                 goto FINISH;
406                                                                         }
407
408                                                                         SysLog(NID_NET_HTTP, "The value of _HttpSslInfo is depth(%d), Subject(%ls).", pSSLInfo->GetDepth(), pSSLInfo->GetServerCert().GetPointer());
409
410                                                                         SysLog(NID_NET_HTTP, "SERVER CERT FAIL(Reason: %ls)", pSSLInfo->GetErrorMessage().GetPointer());
411                                                                         String* pServerCert = new (std::nothrow) String(pSSLInfo->GetServerCert());
412
413                                                                         r = _HttpUtility::RemoveSslCertInfo(*pSSLInfo);
414                                                                         if (IsFailed(r))
415                                                                         {
416                                                                                 SysLogException(NID_NET_HTTP, E_SYSTEM, "[E_SYSTEM] Failed to remove the server cert.");
417                                                                         }
418
419                                                                         //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_CERT_VERIFICATION_REQUIRED)
420                                                                         pHttpTransactionEvent->FireTransactionCertVerificationRequiredNEvent(pServerCert);
421
422                                                                         if (pHttpTransactionUserData->IsClosedTransaction() == false && pHttpTransactionImpl->IsAlreadyResumed() == false)
423                                                                         {
424                                                                                 SysLog(NID_NET_HTTP, "The HttpTransaction[%d] action(resume or pause) does not set.", pHttpTransactionUserData->GetTransactionId());
425
426                                                                                 //Start the timer. (30s)
427                                                                                 pHttpTransactionImpl->SetTimer();
428                                                                                 SysLog(NID_NET_HTTP, "The timer will be expired if the user does not call the API for resume or pause the transaction.");
429                                                                         }
430                                                                 }
431
432                                                         }
433                                                         else
434                                                         {
435                                                                 //Fire the event(_HTTP_TRANSACTION_EVENT_TYPE_ABORTED)
436                                                                 pHttpTransactionEvent->FireTransactionAbortedEvent(r);
437                                                         }
438                                                 }
439                                                 else
440                                                 {
441                                                         SysLog(NID_NET_HTTP, "Ignored to fire the event(_HTTP_TRANSACTION_EVENT_TYPE_ABORTED), the result is E_OPERATION_CANCELED.");
442                                                 }
443                                         }
444                                 }
445
446 FINISH:
447                                 curl_multi_remove_handle(__pCurlM, pCurl);
448
449                                 if (__cookieFlag == NET_HTTP_COOKIE_FLAG_ALWAYS_AUTOMATIC)
450                                 {
451                                         curl_easy_setopt(pCurl, CURLOPT_COOKIELIST, "FLUSH");
452                                         SysLog(NID_NET_HTTP, "Flush the cookie data to the file.");
453                                 }
454
455                                 pHttpCurl = pHttpTransactionUserData->GetHttpCurl();
456                                 pHttpCurl->Release();
457
458                                 pHttpTransactionUserData->Release();
459
460                                 if (__refCount == 1)
461                                 {
462                                         this->Release();
463                                         SysLog(NID_NET_HTTP, "The _HttpMultipleConnectionInfo instance was deleted.");
464                                         return;
465                                 }
466                                 else
467                                 {
468                                         this->Release();
469                                 }
470                         }
471                         else
472                         {
473                                 SysAssertf(false, "The pHttpTransactionUserData must not be null.");
474                         }
475                 }
476                 //Next while-loop
477                 pMessage = curl_multi_info_read(__pCurlM, &messageCount);
478         }
479 }
480
481 void
482 _HttpMultipleConnectionInfo::SetRemainsConnection(int remainsConnection)
483 {
484         __remainsConnection = remainsConnection;
485 }
486
487 int
488 _HttpMultipleConnectionInfo::GetRemainsConnection(void) const
489 {
490         return __remainsConnection;
491 }
492
493 void
494 _HttpMultipleConnectionInfo::Close(void)
495 {
496         __isAlreadyClosedSession = true;
497 }
498
499 CURLM*
500 _HttpMultipleConnectionInfo::GetCurlM(void) const
501 {
502         return __pCurlM;
503 }
504
505 GMainContext*
506 _HttpMultipleConnectionInfo::GetGMainContext(void) const
507 {
508         return __pGMainContext;
509 }
510
511 } } } // Tizen::Net::Http