[capi-http] Fixed crashes in http_transaction_destroy & http_session_destroy
[platform/core/api/http.git] / src / http_session.c
1 /*
2  * Copyright (c) 2012, 2013 Samsung Electronics Co., Ltd.
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 "http.h"
18 #include "http_private.h"
19
20 void _check_curl_multi_status(gpointer user_data)
21 {
22         __http_transaction_h *transaction = NULL;
23         __http_session_h *session = (__http_session_h *)user_data;
24
25         CURLMsg* message = NULL;
26         int count = 0;
27         CURL* curl_easy = NULL;
28         char* url = NULL;
29         CURLcode curl_code = CURLE_OK;
30
31         message = curl_multi_info_read(session->multi_handle, &count);
32
33         while (message != NULL) {
34                 if (message->msg == CURLMSG_DONE) {
35                         curl_easy = message->easy_handle;
36                         curl_code = message->data.result;
37                         curl_easy_getinfo(curl_easy, CURLINFO_PRIVATE, &transaction);
38                         curl_easy_getinfo(curl_easy, CURLINFO_EFFECTIVE_URL, &url);
39
40                         DBG("Completed -%s: result(%d)\n", url, curl_code);
41
42                         switch (curl_code) {
43                         case CURLE_OK:
44                                 if (transaction->completed_cb)
45                                         transaction->completed_cb(transaction, transaction->completed_user_data);
46                                 break;
47                         case CURLE_COULDNT_RESOLVE_HOST:
48                                 if (transaction->aborted_cb)
49                                         transaction->aborted_cb(transaction, HTTP_ERROR_COULDNT_RESOLVE_HOST, transaction->aborted_user_data);
50                                 break;
51                         case CURLE_COULDNT_CONNECT:
52                                 if (transaction->aborted_cb)
53                                         transaction->aborted_cb(transaction, HTTP_ERROR_COULDNT_CONNECT, transaction->aborted_user_data);
54                                 break;
55                         case CURLE_SSL_CONNECT_ERROR:
56                                 if (transaction->aborted_cb)
57                                         transaction->aborted_cb(transaction, HTTP_ERROR_SSL_CONNECT_ERROR, transaction->aborted_user_data);
58                                 break;
59                         case CURLE_OPERATION_TIMEDOUT:
60                                 if (transaction->aborted_cb)
61                                         transaction->aborted_cb(transaction, HTTP_ERROR_OPERATION_TIMEDOUT, transaction->aborted_user_data);
62                                 break;
63                         default:
64                                 break;
65                         }
66
67                         if (session->multi_handle != NULL && curl_easy != NULL) {
68                                 curl_multi_remove_handle(session->multi_handle, curl_easy);
69                         }
70                 }
71                 message = curl_multi_info_read(session->multi_handle, &count);
72         }
73 }
74
75 int _generate_session_id(void)
76 {
77         int session_id = 0;
78
79         return session_id;
80 }
81
82 gboolean timer_expired_callback(gpointer user_data)
83 {
84         __http_session_h* session = (__http_session_h *)user_data;
85
86         CURLMcode ret;
87
88         ret = curl_multi_socket_action(session->multi_handle, CURL_SOCKET_TIMEOUT, 0, &(session->still_running));
89         if (ret == CURLM_OK)
90                 DBG("CURLM_OK - Called curl_multi_socket_action()\n");
91         else
92                 print_curl_multi_errorCode(ret);
93
94         _check_curl_multi_status(session);
95
96         return FALSE;
97 }
98
99 gboolean _handle_event(int fd, int action, gpointer user_data)
100 {
101         __http_session_h *session = (__http_session_h *)user_data;
102
103         int running_handles = -1;
104
105         CURLMcode ret = CURLM_OK;
106
107         ret = curl_multi_socket_action(session->multi_handle, fd, action, &running_handles);
108         if (ret == CURLM_OK)
109                 DBG("CURLM_OK: Called curl_multi_socket_action(%d)\n", action);
110         else
111                 print_curl_multi_errorCode(ret);
112
113         _check_curl_multi_status(session);
114
115         if (running_handles > 0) {
116                 return TRUE;
117         } else {
118                 DBG("last transfer done, kill timeout\n");
119                 if (session->timer_event) {
120                         g_source_remove(session->timer_event);
121                         session->timer_event = 0;
122                 }
123                 return FALSE;
124         }
125 }
126
127 gboolean __handle_socket_received_event_cb(GIOChannel *channel, GIOCondition condition, gpointer user_data)
128 {
129         int fd, action, ret;
130
131         if (condition & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
132                 return FALSE;
133
134         fd = g_io_channel_unix_get_fd(channel);
135
136         /* CURL_CSELECT_IN : 1, CURL_CSELECT_OUT: 2 */
137         action = (condition & G_IO_IN ? CURL_CSELECT_IN : 0) | (condition & G_IO_OUT ? CURL_CSELECT_OUT : 0);
138
139         ret = _handle_event(fd, action, user_data);
140         if (ret)
141                 return TRUE;
142
143         return FALSE;
144 }
145
146 /* Clean up the __http_socket_info_h structure */
147 static void _remove_socket_info(__http_socket_info_h *sock_info)
148 {
149         if (!sock_info)
150                 return;
151
152         if (sock_info->event) {
153                 g_source_remove(sock_info->event);
154                 sock_info->event = 0;
155         }
156         if (sock_info->channel) {
157                 g_io_channel_unref(sock_info->channel);
158                 sock_info->channel = NULL;
159         }
160         g_free(sock_info);
161         sock_info = NULL;
162 }
163
164 /* Assign socket information to a __http_socket_info_h structure */
165 static void _set_socket_info(__http_socket_info_h *sock_info, curl_socket_t fd, CURL *curl_easy, int action, void *user_data)
166 {
167         __http_session_h *session = (__http_session_h *)user_data;
168         GIOCondition condition = (action & CURL_POLL_IN ? G_IO_IN : 0) | (action & CURL_POLL_OUT ? G_IO_OUT : 0);
169
170         sock_info->sockfd = fd;
171         sock_info->action = action;
172         sock_info->easy_handle = curl_easy;
173         if (sock_info->event) {
174                 g_source_remove(sock_info->event);
175                 sock_info->event = 0;
176         }
177         sock_info->event = g_io_add_watch(sock_info->channel, condition, __handle_socket_received_event_cb, session);
178 }
179
180 /* Initialize a new Socket Info structure */
181 static void _add_socket_info(curl_socket_t fd, CURL *curl_easy, int action, void *user_data)
182 {
183         __http_session_h *session = (__http_session_h *)user_data;
184         __http_socket_info_h *sock_info = (__http_socket_info_h *)malloc(sizeof(__http_socket_info_h));
185
186         sock_info->session = session;
187         sock_info->channel = g_io_channel_unix_new(fd);
188         sock_info->event = 0;
189         _set_socket_info(sock_info, fd, curl_easy, action, session);
190         curl_multi_assign(session->multi_handle, fd, sock_info);
191 }
192
193 int __handle_socket_cb(CURL *curl_easy, curl_socket_t fd, int action, void *user_data, void *socketp)
194 {
195         __http_session_h *session = (__http_session_h *)user_data;
196         __http_socket_info_h *sock_info = (__http_socket_info_h*) socketp;
197
198         static const char *actionstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE"};
199
200         DBG("__handle_socket_cb: fd=%d easy_handle=%p action=%s ", fd, curl_easy, actionstr[action]);
201         if (action == CURL_POLL_REMOVE) {
202                 DBG("CURL_POLL_REMOVE\n");
203                 _remove_socket_info(sock_info);
204         } else {
205                 if (!sock_info) {
206                         DBG("Adding data: %s%s\n", action & CURL_POLL_IN ? "READ" : "", action & CURL_POLL_OUT ? "WRITE" : "");
207                         _add_socket_info(fd, curl_easy, action, session);
208                 } else {
209                         DBG("Changing action from %d to %d\n", sock_info->action, action);
210                         _set_socket_info(sock_info, fd, curl_easy, action, session);
211                 }
212         }
213
214         return 0;
215 }
216
217 int __handle_timer_cb(CURLM *curl_multi, long timeout_ms, void *user_data)
218 {
219         __http_session_h* session = (__http_session_h *)user_data;
220
221         session->timer_event = g_timeout_add(timeout_ms , timer_expired_callback , session);
222
223         return 0;
224 }
225
226 API int http_session_create(http_session_mode_e mode, http_session_h *http_session)
227 {
228         _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION,
229                         "http isn't initialized");
230         _retvm_if(http_session == NULL, HTTP_ERROR_INVALID_PARAMETER,
231                         "parameter(http_session) is NULL\n");
232
233         __http_session_h *session = NULL;
234
235         session = (__http_session_h *)malloc(sizeof(__http_session_h));
236         if (session == NULL) {
237                 ERR("Fail to allocate session memory!!");
238                 return HTTP_ERROR_OUT_OF_MEMORY;
239         }
240
241         session->multi_handle = curl_multi_init();
242         session->session_id = _generate_session_id();
243         session->active_transaction_count = 0;
244         session->session_mode = mode;
245         session->auto_redirect = FALSE;
246
247         curl_multi_setopt(session->multi_handle, CURLMOPT_SOCKETFUNCTION, __handle_socket_cb);
248         curl_multi_setopt(session->multi_handle, CURLMOPT_SOCKETDATA, session);
249         curl_multi_setopt(session->multi_handle, CURLMOPT_TIMERFUNCTION, __handle_timer_cb);
250         curl_multi_setopt(session->multi_handle, CURLMOPT_TIMERDATA, session);
251
252         if (mode == HTTP_SESSION_MODE_PIPELINING)
253                 curl_multi_setopt(session->multi_handle, CURLMOPT_PIPELINING, 1L);
254
255         *http_session = (http_session_h)session;
256
257         return HTTP_ERROR_NONE;
258 }
259
260 API int http_session_destroy(http_session_h http_session)
261 {
262         _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION,
263                         "http isn't initialized");
264         _retvm_if(http_session == NULL, HTTP_ERROR_INVALID_PARAMETER,
265                         "parameter(http_session) is NULL\n");
266
267         __http_session_h *session = (__http_session_h *)http_session;
268
269         if (session) {
270                 if (session->multi_handle) {
271                         curl_multi_cleanup(session->multi_handle);
272                         session->multi_handle = NULL;
273                 }
274
275                 session->active_transaction_count = 0;
276                 session->still_running = 0;
277                 session->auto_redirect = FALSE;
278
279                 if (session->timer_event) {
280                         g_source_remove(session->timer_event);
281                         session->timer_event = 0;
282                 }
283
284                 free(session);
285                 session = NULL;
286         }
287
288         return HTTP_ERROR_NONE;
289 }
290
291 API int http_session_set_auto_redirection(http_session_h http_session, bool auto_redirection)
292 {
293         _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION,
294                         "http isn't initialized");
295         _retvm_if(http_session == NULL, HTTP_ERROR_INVALID_PARAMETER,
296                         "parameter(http_session) is NULL\n");
297
298         __http_session_h *session = (__http_session_h *)http_session;
299
300         session->auto_redirect = auto_redirection;
301
302         return HTTP_ERROR_NONE;
303 }
304
305 API int http_session_get_auto_redirection(http_session_h http_session, bool *auto_redirect)
306 {
307         _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION, "http isn't initialized");
308         _retvm_if(http_session == NULL, HTTP_ERROR_INVALID_PARAMETER, "parameter(http_session) is NULL\n");
309         _retvm_if(auto_redirect == NULL, HTTP_ERROR_INVALID_PARAMETER, "parameter(auto_redirect) is NULL\n");
310
311         __http_session_h *session = (__http_session_h *)http_session;
312
313         *auto_redirect =  session->auto_redirect;
314
315         return HTTP_ERROR_NONE;
316 }
317
318 API int http_session_get_active_transaction_count(http_session_h http_session, int *active_transaction_count)
319 {
320         _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION,
321                         "http isn't initialized");
322         _retvm_if(http_session == NULL, HTTP_ERROR_INVALID_PARAMETER,
323                         "parameter(http_session) is NULL\n");
324         _retvm_if(active_transaction_count == NULL, HTTP_ERROR_INVALID_PARAMETER,
325                         "parameter(active_transaction_count) is NULL\n");
326
327         __http_session_h *session = (__http_session_h *)http_session;
328
329         *active_transaction_count = session->active_transaction_count;
330
331         return HTTP_ERROR_NONE;
332 }
333
334 API int http_session_get_max_transaction_count(http_session_h http_session, int *transaction_count)
335 {
336         _retvm_if(_http_is_init() == false, HTTP_ERROR_INVALID_OPERATION,
337                         "http isn't initialized");
338         _retvm_if(http_session == NULL, HTTP_ERROR_INVALID_PARAMETER,
339                         "parameter(http_session) is NULL\n");
340         _retvm_if(transaction_count == NULL, HTTP_ERROR_INVALID_PARAMETER,
341                         "parameter(transaction_count) is NULL\n");
342
343         __http_session_h *session = (__http_session_h *)http_session;
344
345         if (session->session_mode == HTTP_SESSION_MODE_NORMAL)
346                 *transaction_count =  _MAX_HTTP_TRANSACTIONS_PER_SESSION_NORMAL;
347         else if (session->session_mode == HTTP_SESSION_MODE_PIPELINING)
348                 *transaction_count =  _MAX_HTTP_TRANSACTIONS_PER_SESSION_PIPE;
349         else
350                 *transaction_count =  -1;
351
352         return HTTP_ERROR_NONE;
353 }