summaryrefslogtreecommitdiffstats
path: root/tinyHTTP/src/thttp_session.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyHTTP/src/thttp_session.c')
-rw-r--r--tinyHTTP/src/thttp_session.c522
1 files changed, 522 insertions, 0 deletions
diff --git a/tinyHTTP/src/thttp_session.c b/tinyHTTP/src/thttp_session.c
new file mode 100644
index 0000000..0066715
--- /dev/null
+++ b/tinyHTTP/src/thttp_session.c
@@ -0,0 +1,522 @@
+/*
+* Copyright (C) 2009-2010 Mamadou Diop.
+*
+* Contact: Mamadou Diop <diopmamadou(at)doubango.org>
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*
+*/
+
+/**@file thttp_session.c
+ * @brief HTTP/HTTPS session.
+ *
+ * @author Mamadou Diop <diopmamadou(at)doubango.org>
+ *
+ * @date Created: Sat Nov 8 16:54:58 2009 mdiop
+ */
+#include "tinyhttp/thttp_session.h"
+
+#include "thttp.h"
+#include "tinyhttp/thttp_action.h"
+
+#include "tinyhttp/headers/thttp_header_Dummy.h"
+#include "tinyhttp/headers/thttp_header_WWW_Authenticate.h"
+
+#include "tsk_debug.h"
+
+/**@defgroup thttp_session_group HTTP Session
+*/
+
+int thttp_session_signal(thttp_session_t *self, thttp_action_type_t atype);
+
+/**Sets parameters.
+*/
+int __thttp_session_set(thttp_session_t *self, va_list* app)
+{
+ thttp_session_param_type_t curr;
+
+ if(!self){
+ return -1;
+ }
+
+ while((curr=va_arg(*app, thttp_session_param_type_t)) != httpp_null){
+ switch(curr){
+ case httpp_option:
+ { /* (thttp_session_option_t)ID_ENUM, (const char*)VALUE_STR */
+ thttp_session_option_t id = va_arg(*app, thttp_session_option_t);
+ const char* value = va_arg(*app, const char *);
+ tsk_options_add_option(&self->options, id, value);
+ break;
+ }
+
+ case httpp_header:
+ { /* (const char*)NAME_STR, (const char*)VALUE_STR */
+ const char* name = va_arg(*app, const char *);
+ const char* value = va_arg(*app, const char *);
+ if(value == ((const char*)-1)){ /* UNSET */
+ tsk_params_remove_param(self->headers, name);
+ }
+ else{ /* SET */
+ tsk_params_add_param(&self->headers, name, value);
+ }
+ break;
+ }
+
+ case httpp_cred:
+ { /* (const char*)USERNAME_STR, (const char*)PASSWORD_STR */
+ tsk_strupdate(&self->cred.usename, va_arg(*app, const char *));
+ tsk_strupdate(&self->cred.password, va_arg(*app, const char *));
+ break;
+ }
+
+ case httpp_userdata:
+ { /* (const void*)USERDATA_PTR */
+ self->userdata = va_arg(*app, const void *);
+ break;
+ }
+
+ default:
+ { /* va_list will be unsafe => exit */
+ TSK_DEBUG_ERROR("NOT SUPPORTED.");
+ goto bail;
+ }
+ } /* sxitch */
+ } /* while */
+ return 0;
+
+bail:
+ return -2;
+}
+
+/**@ingroup thttp_session_group
+* Creates new session.
+* @param stack The HTTP/HTTPS @a stack to use. The @a stack shall be created using @ref thttp_stack_create.
+* @param ... Any @b THTTP_SESSION_SET_*() macros. MUST ends with @ref THTTP_SESSION_SET_NULL().
+* @retval A pointer to the newly created session.
+* A session is a well-defined object.
+*
+* @code
+thttp_session_handle_t * session = thttp_session_create(stack,
+ // session-level parameters
+ THTTP_SESSION_SET_PARAM("timeout", "6000"),
+
+ // session-level headers
+ THTTP_SESSION_SET_HEADER("Pragma", "No-Cache"),
+ THTTP_SESSION_SET_HEADER("Connection", "Keep-Alive"),
+ THTTP_SESSION_SET_HEADER("User-Agent", "doubango 1.0"),
+
+ THTTP_SESSION_SET_NULL());
+* @endcode
+*
+* @sa @ref thttp_session_set
+*/
+thttp_session_handle_t* thttp_session_create(const thttp_stack_handle_t* stack, ...)
+{
+ thttp_session_handle_t* ret = tsk_null;
+
+ if((ret = tsk_object_new(thttp_session_def_t, stack))){
+ va_list ap;
+ va_start(ap, stack);
+ if(__thttp_session_set(ret, &ap)){
+ TSK_OBJECT_SAFE_FREE(ret);
+ }
+ va_end(ap);
+ }
+ else{
+ TSK_DEBUG_ERROR("failed to create new HTTP/HTTPS session.");
+ }
+ return ret;
+}
+
+/**@ingroup thttp_session_group
+* Updates the session parameters.
+* @param self The session to update. The session shall be created using @ref thttp_session_create().
+* @param ... Any @b THTTP_SESSION_SET_*() macros. MUST ends with @ref THTTP_SESSION_SET_NULL().
+* @retval Zero if succeed and non zero error code otherwise.
+*
+* @code
+int ret = thttp_session_set(session,
+ // session-level parameters
+ THTTP_SESSION_SET_OPTION(THTTP_SESSION_OPTION_TIMEOUT, "6000"),
+
+ // session-level headers
+ THTTP_SESSION_SET_HEADER("Pragma", "No-Cache"),
+ THTTP_SESSION_SET_HEADER("Connection", "Keep-Alive"),
+ THTTP_SESSION_SET_HEADER("User-Agent", "doubango 1.0"),
+
+ THTTP_SESSION_SET_NULL());
+* @endcode
+*
+* @sa @ref thttp_session_create
+*/
+int thttp_session_set(thttp_session_handle_t *self, ...)
+{
+ if(self){
+ int ret;
+ va_list ap;
+
+ thttp_session_t *session = self;
+
+ if(session->id == THTTP_SESSION_INVALID_ID){
+ TSK_DEBUG_ERROR("Using invalid session.");
+ return -2;
+ }
+
+ va_start(ap, self);
+ ret = __thttp_session_set(session, &ap);
+ va_end(ap);
+ return ret;
+ }
+
+ return -1;
+}
+
+/**@ingroup thttp_session_group
+* Gets the session id.
+* @param self The session for which to get the id.
+* @retval The id of the session.
+*/
+thttp_session_id_t thttp_session_get_id(const thttp_session_handle_t *self)
+{
+ const thttp_session_t *session = self;
+ if(session){
+ return session->id;
+ }
+ return THTTP_SESSION_INVALID_ID;
+}
+
+/**@ingroup thttp_session_group
+* Gets the user context (user/application data).
+* @param self A pointer to the session from which to get the context.
+* @retval A pointer to the context. Previously defined by using @ref THTTP_SESSION_SET_USERDATA() macro.
+* @sa @ref THTTP_SESSION_SET_USERDATA()
+*/
+const void* thttp_session_get_userdata(const thttp_session_handle_t *self)
+{
+ if(self){
+ return ((const thttp_session_t*)self)->userdata;
+ }
+ return tsk_null;
+}
+
+int thttp_session_closefd(thttp_session_handle_t *_self)
+{
+ int ret = 0;
+ thttp_session_t* self = _self;
+
+ if(self->fd != TNET_INVALID_FD){
+ if((ret = tnet_transport_remove_socket(self->stack->transport, &self->fd))){
+ ret = tnet_sockfd_close(&self->fd);
+ }
+ }
+
+ return ret;
+}
+
+/** Updates authentications headers.
+*/
+int thttp_session_update_challenges(thttp_session_t *self, const thttp_response_t* response, tsk_bool_t answered)
+{
+ int ret = 0;
+ tsk_size_t i;
+
+ tsk_list_item_t *item;
+
+ thttp_challenge_t *challenge;
+
+ const thttp_header_WWW_Authenticate_t *WWW_Authenticate;
+ const thttp_header_Proxy_Authenticate_t *Proxy_Authenticate;
+
+ tsk_safeobj_lock(self);
+
+ /* RFC 2617 - Digest Operation
+
+ * (A) The client response to a WWW-Authenticate challenge for a protection
+ space starts an authentication session with that protection space.
+ The authentication session lasts until the client receives another
+ WWW-Authenticate challenge from any server in the protection space.
+
+ (B) The server may return a 401 response with a new nonce value, causing the client
+ to retry the request; by specifying stale=TRUE with this response,
+ the server tells the client to retry with the new nonce, but without
+ prompting for a new username and password.
+ */
+ /* RFC 2617 - 1.2 Access Authentication Framework
+ The realm directive (case-insensitive) is required for all authentication schemes that issue a challenge.
+ */
+
+ /* FIXME: As we perform the same task ==> Use only one loop.
+ */
+
+ for(i =0; (WWW_Authenticate = (const thttp_header_WWW_Authenticate_t*)thttp_message_get_headerAt(response, thttp_htype_WWW_Authenticate, i)); i++)
+ {
+ tsk_bool_t isnew = tsk_true;
+
+ tsk_list_foreach(item, self->challenges)
+ {
+ challenge = item->data;
+ if(challenge->isproxy){
+ continue;
+ }
+
+ if(tsk_striequals(challenge->realm, WWW_Authenticate->realm) && (WWW_Authenticate->stale || !answered)){
+ /*== (B) ==*/
+ if((ret = thttp_challenge_update(challenge,
+ WWW_Authenticate->scheme,
+ WWW_Authenticate->realm,
+ WWW_Authenticate->nonce,
+ WWW_Authenticate->opaque,
+ WWW_Authenticate->algorithm,
+ WWW_Authenticate->qop))){
+ return ret;
+ }
+ else{
+ isnew = tsk_false;
+ continue;
+ }
+ }
+ else{
+ ret = -1;
+ goto bail;
+ }
+ }
+
+ if(isnew){
+ if((challenge = thttp_challenge_create(tsk_false, /* Not proxy */
+ WWW_Authenticate->scheme,
+ WWW_Authenticate->realm,
+ WWW_Authenticate->nonce,
+ WWW_Authenticate->opaque,
+ WWW_Authenticate->algorithm,
+ WWW_Authenticate->qop))){
+ tsk_list_push_back_data(self->challenges, (void**)&challenge);
+ }
+ else{
+ ret = -1;
+ goto bail;
+ }
+ }
+ }
+
+ for(i=0; (Proxy_Authenticate = (const thttp_header_Proxy_Authenticate_t*)thttp_message_get_headerAt(response, thttp_htype_Proxy_Authenticate, i)); i++)
+ {
+ tsk_bool_t isnew = tsk_true;
+
+ tsk_list_foreach(item, self->challenges){
+ challenge = item->data;
+ if(!challenge->isproxy){
+ continue;
+ }
+
+ if(tsk_striequals(challenge->realm, Proxy_Authenticate->realm) && (Proxy_Authenticate->stale || !answered)){
+ /*== (B) ==*/
+ if((ret = thttp_challenge_update(challenge,
+ Proxy_Authenticate->scheme,
+ Proxy_Authenticate->realm,
+ Proxy_Authenticate->nonce,
+ Proxy_Authenticate->opaque,
+ Proxy_Authenticate->algorithm,
+ Proxy_Authenticate->qop)))
+ {
+ goto bail;
+ }
+ else{
+ isnew = tsk_false;
+ continue;
+ }
+ }
+ else{
+ ret = -1;
+ goto bail;
+ }
+ }
+
+ if(isnew){
+ if((challenge = thttp_challenge_create(tsk_true, /* Proxy */
+ Proxy_Authenticate->scheme,
+ Proxy_Authenticate->realm,
+ Proxy_Authenticate->nonce,
+ Proxy_Authenticate->opaque,
+ Proxy_Authenticate->algorithm,
+ Proxy_Authenticate->qop)))
+ {
+ tsk_list_push_back_data(self->challenges, (void**)&challenge);
+ }
+ else{
+ ret = -1;
+ goto bail;
+ }
+ }
+ }
+
+bail:
+ tsk_safeobj_unlock(self);
+ return ret;
+
+}
+
+/* internal function */
+int thttp_session_signal(thttp_session_t *self, thttp_action_type_t atype)
+{
+ tsk_list_item_t *item;
+
+ if(!self){
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_safeobj_lock(self);
+again:
+ tsk_list_foreach(item, self->dialogs){
+ item = tsk_object_ref(item);
+ thttp_dialog_fsm_act((thttp_dialog_t*)item->data, atype, tsk_null, tsk_null);
+ /* As the above action could terminate the dialog (which means change the content of self->dialogs)
+ * => list becomes unsafe */
+ if(!(item = tsk_object_unref(item))){
+ goto again;
+ }
+ }
+
+ switch(atype){
+ case thttp_thttp_atype_closed:
+ self->fd = TNET_INVALID_FD;
+ break;
+ default:
+ break;
+ }
+
+ tsk_safeobj_unlock(self);
+
+ return 0;
+}
+
+/** Signals to all dialogs that the connection have been closed. */
+int thttp_session_signal_closed(thttp_session_t *self)
+{
+ return thttp_session_signal(self, thttp_thttp_atype_closed);
+}
+
+/** Signals to all dialogss that we got an error */
+int thttp_session_signal_error(thttp_session_t *self)
+{
+ return thttp_session_signal(self, thttp_atype_error);
+}
+
+
+/** Retrieves a session by fd */
+thttp_session_t* thttp_session_get_by_fd(thttp_sessions_L_t* sessions, tnet_fd_t fd)
+{
+ thttp_session_t* ret = tsk_null;
+ const tsk_list_item_t *item;
+
+ if(!sessions){
+ goto bail;
+ }
+
+ tsk_list_foreach(item, sessions){
+ if(((thttp_session_t*)item->data)->fd == fd){
+ ret = tsk_object_ref(item->data);
+ goto bail;
+ }
+ }
+
+bail:
+ return ret;
+}
+
+
+
+
+//========================================================
+// HTTP SESSION object definition
+//
+static tsk_object_t* _thttp_session_create(tsk_object_t * self, va_list * app)
+{
+ thttp_session_t *session = self;
+ static thttp_session_id_t unique_id = THTTP_SESSION_INVALID_ID;
+ if(session){
+ tsk_safeobj_init(session);
+
+ session->stack = va_arg(*app, const thttp_stack_handle_t*);
+ session->options = tsk_list_create();
+ session->headers = tsk_list_create();
+ session->challenges = tsk_list_create();
+ session->dialogs = tsk_list_create();
+ session->fd = TNET_INVALID_FD;
+
+ session->id = THTTP_SESSION_INVALID_ID;
+
+ /* add the session to the stack */
+ if(session->stack){
+ session->id = ++unique_id;
+ tsk_list_push_back_data(session->stack->sessions, (void**)&session);
+ }
+ }
+
+ return self;
+}
+
+static tsk_object_t* thttp_session_destroy(tsk_object_t * self)
+{
+ thttp_session_t *session = self;
+ if(session){
+ TSK_DEBUG_INFO("*** HTTP/HTTPS Session destroyed ***");
+
+ /* remove from the stack */
+ if(session->stack){
+ tsk_list_remove_item_by_data(session->stack->sessions, session);
+ }
+
+ TSK_OBJECT_SAFE_FREE(session->options);
+ TSK_OBJECT_SAFE_FREE(session->headers);
+ TSK_OBJECT_SAFE_FREE(session->challenges);
+ TSK_OBJECT_SAFE_FREE(session->dialogs);
+
+ // cred
+ TSK_FREE(session->cred.usename);
+ TSK_FREE(session->cred.password);
+
+ // fd
+ if(session->fd != TNET_INVALID_FD){
+ if(tnet_transport_remove_socket(session->stack->transport, &session->fd)){
+ tnet_sockfd_close(&session->fd);
+ }
+ }
+
+ tsk_safeobj_deinit(session);
+ }
+ return self;
+}
+
+static int thttp_session_cmp(const tsk_object_t *_session1, const tsk_object_t *_session2)
+{
+ const thttp_session_t *session1 = _session1;
+ const thttp_session_t *session2 = _session2;
+
+ if(session1 && session2){
+ return (int)(session1->id-session2->id);
+ }
+ return -1;
+}
+
+static const tsk_object_def_t thttp_session_def_s =
+{
+ sizeof(thttp_session_t),
+ _thttp_session_create,
+ thttp_session_destroy,
+ thttp_session_cmp,
+};
+const tsk_object_def_t *thttp_session_def_t = &thttp_session_def_s;
OpenPOWER on IntegriCloud