/** * @short Removes the allocated data * @memberof onion_t */ void onion_free(onion *onion){ ONION_DEBUG("Onion free"); if (onion->flags&O_LISTENING) onion_listen_stop(onion); if (onion->poller) onion_poller_free(onion->poller); if (onion->username) onion_low_free(onion->username); if (onion->listen_points){ onion_listen_point **p=onion->listen_points; while(*p!=NULL){ ONION_DEBUG("Free %p listen_point", *p); onion_listen_point_free(*p++); } onion_low_free(onion->listen_points); } if (onion->root_handler) onion_handler_free(onion->root_handler); if (onion->internal_error_handler) onion_handler_free(onion->internal_error_handler); onion_mime_set(NULL); if (onion->sessions) onion_sessions_free(onion->sessions); #ifdef HAVE_PTHREADS if (onion->threads) onion_low_free(onion->threads); #endif onion_low_free(onion); }
/** * @short Helps to prepare each pair. */ static void onion_dict_json_preorder(onion_block *block, const char *key, const void *value, int flags){ if (!onion_block_size(block)) // Error somewhere. return; char *s; s=onion_c_quote_new(key); if (s==NULL){ onion_block_clear(block); return; } onion_block_add_str(block, s); onion_low_free(s); onion_block_add_char(block, ':'); if (flags&OD_DICT){ onion_block *tmp; tmp=onion_dict_to_json((onion_dict*)value); if (!tmp){ onion_block_clear(block); return; } onion_block_add_block(block, tmp); onion_block_free(tmp); } else{ s=onion_c_quote_new(value); if (s==NULL){ onion_block_clear(block); return; } onion_block_add_str(block, s); onion_low_free(s); } onion_block_add_data(block, ", ",2); }
/** * @short Frees the parser data. */ void onion_request_parser_data_free(void *t){ ONION_DEBUG0("Free parser data"); onion_token *token=t; if (token->extra){ onion_low_free(token->extra); token->extra=NULL; } onion_low_free(token); }
/** * @short Cleans a request object to reuse it. * @memberof onion_request_t */ void onion_request_clean(onion_request* req) { ONION_DEBUG0("Clean request %p", req); onion_dict_free(req->headers); req->headers=onion_dict_new(); onion_dict_set_flags(req->headers, OD_ICASE); req->flags&=OR_NO_KEEP_ALIVE; // I keep keep alive. if (req->parser_data) { onion_request_parser_data_free(req->parser_data); req->parser_data=NULL; } if (req->fullpath) { onion_low_free(req->fullpath); req->path=req->fullpath=NULL; } if (req->GET) { onion_dict_free(req->GET); req->GET=NULL; } if (req->POST) { onion_dict_free(req->POST); req->POST=NULL; } if (req->FILES) { onion_dict_preorder(req->FILES, unlink_files, NULL); onion_dict_free(req->FILES); req->FILES=NULL; } if (req->session_id) { if (onion_dict_count(req->session)==0) { onion_request_session_free(req); } else { onion_sessions_save(req->connection.listen_point->server->sessions, req->session_id, req->session); onion_dict_free(req->session); // Not really remove, just dereference req->session=NULL; onion_low_free(req->session_id); req->session_id=NULL; } } if (req->data) { onion_block_free(req->data); req->data=NULL; } if (req->connection.cli_info) { onion_low_free(req->connection.cli_info); req->connection.cli_info=NULL; } if (req->cookies) { onion_dict_free(req->cookies); req->cookies=NULL; } if (req->free_list) { onion_ptr_list_foreach(req->free_list, onion_low_free); onion_ptr_list_free(req->free_list); req->free_list=NULL; } }
/** * @short Free and closes the listen point * @memberof onion_listen_point_t * * Calls the custom listen_stop mathod, and frees all common structures. * * @param op the listen point */ void onion_listen_point_free(onion_listen_point *op){ ONION_DEBUG("Free listen point %d", op->listenfd); onion_listen_point_listen_stop(op); if (op->free_user_data) op->free_user_data(op); if (op->hostname) onion_low_free(op->hostname); if (op->port) onion_low_free(op->port); onion_low_free(op); }
/** * @short Removes the allocated data * @memberof onion_t */ void onion_free(onion *onion){ ONION_DEBUG("Onion free"); if (onion->flags&O_LISTENING) onion_listen_stop(onion); if (onion->poller) onion_poller_free(onion->poller); if (onion->username) onion_low_free(onion->username); if (onion->listen_points){ onion_listen_point **p=onion->listen_points; while(*p!=NULL){ ONION_DEBUG("Free %p listen_point", *p); onion_listen_point_free(*p++); } onion_low_free(onion->listen_points); } if (onion->root_handler) onion_handler_free(onion->root_handler); if (onion->internal_error_handler) onion_handler_free(onion->internal_error_handler); onion_mime_set(NULL); if (onion->sessions) onion_sessions_free(onion->sessions); { #ifdef HAVE_PTHREADS pthread_mutex_lock (&onion->mutex); #endif void* data = onion->client_data; onion->client_data = NULL; if (data && onion->client_data_free) onion->client_data_free (data); onion->client_data_free = NULL; #ifdef HAVE_PTHREADS pthread_mutex_unlock (&onion->mutex); pthread_mutex_destroy (&onion->mutex); #endif }; #ifdef HAVE_PTHREADS if (onion->threads) onion_low_free(onion->threads); #endif if (!(onion->flags&O_NO_SIGTERM)){ signal(SIGINT,SIG_DFL); signal(SIGTERM,SIG_DFL); } last_onion=NULL; onion_low_free(onion); }
/// Frees the memory, if necesary of key and value static void onion_dict_node_data_free(onion_dict_node_data *data){ if (data->flags&OD_FREE_KEY){ onion_low_free((char*)data->key); } if (data->flags&OD_FREE_VALUE){ if (data->flags&OD_DICT){ onion_dict_free((onion_dict*)data->value); } else onion_low_free((char*)data->value); } }
static void onion_sessions_redis_free(onion_sessions *sessions) { ONION_DEBUG0("Free onion sessions redis"); onion_session_redis *p = sessions->data; #ifdef HAVE_PTHREADS pthread_mutex_destroy(&p->mutex); #endif redisFree(p->context); onion_low_free(sessions->data); onion_low_free(sessions); }
static void onion_sessions_sqlite3_free(onion_sessions *sessions) { ONION_DEBUG0("Free onion sessions sqlite3"); onion_session_sqlite3 *p=sessions->data; #ifdef HAVE_PTHREADS pthread_mutex_destroy(&p->mutex); #endif sqlite3_finalize(p->save); sqlite3_finalize(p->get); sqlite3_close(p->db); onion_low_free(sessions->data); onion_low_free(sessions); }
/** * @short Returns a poller object that helps polling on sockets and files * @memberof onion_poller_t * * This poller is implemented through epoll, but other implementations are possible * */ onion_poller *onion_poller_new(int n){ onion_poller *p=onion_low_malloc(sizeof(onion_poller)); p->fd=epoll_create1(EPOLL_CLOEXEC); if (p->fd < 0){ ONION_ERROR("Error creating the poller. %s", strerror(errno)); onion_low_free(p); return NULL; } p->eventfd=eventfd(0,EFD_CLOEXEC | EFD_NONBLOCK); #if EFD_CLOEXEC == 0 fcntl(p->eventfd,F_SETFD,FD_CLOEXEC); #endif p->head=NULL; p->n=0; p->stop=0; #ifdef HAVE_PTHREADS ONION_DEBUG("Init thread stuff for poll. Eventfd at %d", p->eventfd); p->npollers=0; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&p->mutex, &attr); pthread_mutexattr_destroy(&attr); #endif onion_poller_slot *ev=onion_poller_slot_new(p->eventfd,onion_poller_stop_helper,p); onion_poller_add(p,ev); return p; }
/** * @short Reads from the data to fulfill content-length data. * * All data is writen a temporal file, which will be removed later. */ static onion_connection_status parse_PUT(onion_request *req, onion_buffer *data){ onion_token *token=req->parser_data; int length=data->size-data->pos; int exit=0; if (length>=token->extra_size-token->pos){ exit=1; length=token->extra_size-token->pos; } //ONION_DEBUG0("Writing %d. %d / %d bytes", length, token->pos+length, token->extra_size); int *fd=(int*)token->extra; ssize_t w=write(*fd, &data->data[data->pos], length); if (w<0){ ONION_ERROR("Could not write all data to temporal file."); return OCS_INTERNAL_ERROR; } data->pos+=length; token->pos+=length; #if __DEBUG__ const char *filename=onion_block_data(req->data); ONION_DEBUG0("Done with PUT. Created %s (%d bytes)", filename, token->pos); #endif if (exit){ close (*fd); onion_low_free(token->extra); token->extra=NULL; return onion_request_process(req); } return OCS_NEED_MORE_DATA; }
/// Deinits static data at onion_poller. static void onion_poller_static_deinit(){ int16_t nextcount = __sync_sub_and_fetch(&onion_poller_static.refcount, 1); if (nextcount!=0) return; onion_low_free(onion_poller_static.slots); }
/** * @short Tryes to handle the petition with that handler. * @memberof onion_handler_t * * It needs the handler to handle, the request and the response. * * It checks this parser, and siblings. * * @returns If can not, returns OCS_NOT_PROCESSED (0), else the onion_connection_status. (normally OCS_PROCESSED) */ onion_connection_status onion_handler_handle(onion_handler *handler, onion_request *request, onion_response *response){ onion_connection_status res; while (handler){ if (handler->handler){ #ifdef __DEBUG0__ char **bs=backtrace_symbols((void * const *)&handler->handler, 1); ONION_DEBUG0("Calling handler: %s",bs[0]); /* backtrace_symbols is explicitly documented to malloc. We need to call the system free routine, not our onion_low_free ! */ onion_low_free(bs); /* Can't be onion_low_free.... */ #endif res=handler->handler(handler->priv_data, request, response); ONION_DEBUG0("Result: %d",res); if (res){ // write pending data. if (!(response->flags&OR_HEADER_SENT) && response->buffer_pos<sizeof(response->buffer)) onion_response_set_length(response, response->buffer_pos); onion_response_flush(response); if (res==OCS_WEBSOCKET){ if (request->websocket) return onion_websocket_call(request->websocket); else{ ONION_ERROR("Handler did set the OCS_WEBSOCKET, but did not initialize the websocket on this request."); return OCS_INTERNAL_ERROR; } } return res; } } handler=handler->next; } return OCS_NOT_PROCESSED; }
/** * @short Creates the onion structure to fill with the server data, and later do the onion_listen() * @memberof onion_t * * Creates an onion structure that can be used to set the server, port, SSL and similar parameters. It works over * the onion structure, which is the main structure to control the listening of new connections throught TCP/IP. * * A normal usage would be like this: * * @code * * onion *o=onion_new(O_THREADED); * onion_set_root_handler(o, onion_handler_directory(".")); * onion_listen(o); * * @endcode * * @param flags Or'ed flags to use at the listening daemon. Normally one of O_ONE, O_ONE_LOOP or O_THREADED. * * @returns The onion structure. * * @see onion_mode_e onion_t */ onion *onion_new(int flags){ ONION_DEBUG0("Some internal sizes: onion size: %d, request size %d, response size %d",sizeof(onion),sizeof(onion_request),sizeof(onion_response)); if(SOCK_CLOEXEC == 0){ ONION_WARNING("There is no support for SOCK_CLOEXEC compiled in libonion. This may be a SECURITY PROBLEM as connections may leak into executed programs."); } if (!(flags&O_NO_SIGPIPE)){ ONION_DEBUG("Ignoring SIGPIPE"); signal(SIGPIPE, SIG_IGN); } onion *o=onion_low_calloc(1,sizeof(onion)); if (!o){ return NULL; } o->flags=(flags&0x0FF)|O_SSL_AVAILABLE; o->timeout=5000; // 5 seconds of timeout, default. o->poller=onion_poller_new(15); if (!o->poller){ onion_low_free(o); return NULL; } o->sessions=onion_sessions_new(); o->internal_error_handler=onion_handler_new((onion_handler_handler)onion_default_error, NULL, NULL); o->max_post_size=1024*1024; // 1MB o->max_file_size=1024*1024*1024; // 1GB #ifdef HAVE_PTHREADS o->flags|=O_THREADS_AVALIABLE; o->nthreads=8; if (o->flags&O_THREADED) o->flags|=O_THREADS_ENABLED; #endif return o; }
/** * @short Writes some data to the response. Using sprintf format strings. va_list args version * * @param args va_list of arguments * @memberof onion_response_t */ ssize_t onion_response_vprintf(onion_response *res, const char *fmt, va_list args) { char temp[512]; int l; l=vsnprintf(temp, sizeof(temp), fmt, args); if (l<0) { ONION_ERROR("Invalid vprintf fmt"); return -1; } else if (l<sizeof(temp)) { return onion_response_write(res, temp, l); } else { ssize_t s; char*buf = onion_low_scalar_malloc(l+1); if (!buf){ // this cannot happen, since onion_low_scalar_malloc // handles that error... ONION_ERROR("Could not reserve %d bytes", l+1); return -1; } vsnprintf(buf, l, fmt, args); s = onion_response_write (res, buf, l); onion_low_free (buf); return s; } }
/// @memberof onion_poller_t void onion_poller_free(onion_poller *p){ ONION_DEBUG("Free onion poller: %d waiting", p->n); p->stop=1; close(p->fd); // Wait until all pollers exit. if (pthread_mutex_trylock(&p->mutex)>0){ ONION_WARNING("When cleaning the poller object, some poller is still active; not freeing memory"); } else{ onion_poller_slot *next=p->head; while (next){ onion_poller_slot *tnext=next->next; if (next->shutdown) next->shutdown(next->shutdown_data); next=tnext; } pthread_mutex_unlock(&p->mutex); if (p->eventfd>=0) close(p->eventfd); if (p->timerfd>=0) close(p->timerfd); onion_poller_static_deinit(); onion_low_free(p); } ONION_DEBUG0("Done"); }
/** * @short AA tree insert * * Returns the root node of the subtree */ static onion_dict_node *onion_dict_node_add(onion_dict *d, onion_dict_node *node, onion_dict_node *nnode){ if (node==NULL){ //ONION_DEBUG("Add here %p",nnode); return nnode; } signed int cmp=d->cmp(nnode->data.key, node->data.key); //ONION_DEBUG0("cmp %d, %X, %X %X",cmp, nnode->data.flags,nnode->data.flags&OD_REPLACE, OD_REPLACE); if ((cmp==0) && (nnode->data.flags&OD_REPLACE)){ //ONION_DEBUG("Replace %s with %s", node->data.key, nnode->data.key); onion_dict_node_data_free(&node->data); memcpy(&node->data, &nnode->data, sizeof(onion_dict_node_data)); onion_low_free(nnode); return node; } else if (cmp<0){ node->left=onion_dict_node_add(d, node->left, nnode); //ONION_DEBUG("%p[%s]->left=%p[%s]",node, node->data.key, node->left, node->left->data.key); } else{ // >= node->right=onion_dict_node_add(d, node->right, nnode); //ONION_DEBUG("%p[%s]->right=%p[%s]",node, node->data.key, node->right, node->right->data.key); } node=skew(node); node=split(node); return node; }
/// Removes a node and its data static void onion_dict_node_free(onion_dict_node *node){ if (node->left) onion_dict_node_free(node->left); if (node->right) onion_dict_node_free(node->right); onion_dict_node_data_free(&node->data); onion_low_free(node); }
/// Sets the hostname on which to listen void onion_set_hostname(onion *server, const char *hostname){ if (server->listen_points){ onion_low_free(server->listen_points[0]->hostname); server->listen_points[0]->hostname=onion_low_strdup(hostname); } else{ onion_add_listen_point(server, hostname, NULL, onion_http_new()); } }
/// Sets the port to listen void onion_set_port(onion *server, const char *port){ if (server->listen_points){ onion_low_free(server->listen_points[0]->port); server->listen_points[0]->port=onion_low_strdup(port); } else{ onion_add_listen_point(server, NULL, port, onion_http_new()); } }
/** * @short Writes the given string to the res, but encodes the data using html entities * @ingroup response * * The encoding mens that <code><html> whould become <html></code> */ ssize_t onion_response_write_html_safe(onion_response * res, const char *data) { char *tmp = onion_html_quote(data); if (tmp) { int r = onion_response_write0(res, tmp); onion_low_free(tmp); return r; } else return onion_response_write0(res, data); }
/** * @short Deletes a request and all its data * @memberof onion_request_t */ void onion_request_free(onion_request *req) { ONION_DEBUG0("Free request %p", req); onion_dict_free(req->headers); if (req->connection.listen_point!=NULL && req->connection.listen_point->close) req->connection.listen_point->close(req); if (req->fullpath) onion_low_free(req->fullpath); if (req->GET) onion_dict_free(req->GET); if (req->POST) onion_dict_free(req->POST); if (req->FILES) { onion_dict_preorder(req->FILES, unlink_files, NULL); onion_dict_free(req->FILES); } if (req->session) { if (onion_dict_count(req->session)==0) onion_request_session_free(req); else { onion_sessions_save(req->connection.listen_point->server->sessions, req->session_id, req->session); onion_dict_free(req->session); // Not really remove, just dereference onion_low_free(req->session_id); } } if (req->data) onion_block_free(req->data); if (req->connection.cli_info) onion_low_free(req->connection.cli_info); if (req->websocket) onion_websocket_free(req->websocket); if (req->parser_data) { onion_low_free(req->parser_data); } if (req->cookies) onion_dict_free(req->cookies); if (req->free_list) { onion_ptr_list_foreach(req->free_list, onion_low_free); onion_ptr_list_free(req->free_list); } onion_low_free(req); }
/** * @short Frees the user data * @memberof onion_https_t * * @param op */ static void onion_https_free_user_data(onion_listen_point *op){ ONION_DEBUG("Free HTTPS %s:%s", op->hostname, op->port); onion_https *https=(onion_https*)op->user_data; gnutls_certificate_free_credentials (https->x509_cred); gnutls_dh_params_deinit(https->dh_params); gnutls_priority_deinit (https->priority_cache); //if (op->server->flags&O_SSL_NO_DEINIT) gnutls_global_deinit(); // This may cause problems if several characters use the gnutls on the same binary. onion_low_free(https); }
/** * @short Frees the session dictionary. * @memberof onion_request_t * * It removes the session from the sessions dictionary, so this session does not exist anymore. * * If data is under onion_dict scope (just dicts into dicts and strings), all data is freed. * If the user has set some custom data, THAT MEMORY IS LEAKED. */ void onion_request_session_free(onion_request *req) { if (!req->session_id) onion_request_guess_session_id(req); if (req->session_id) { ONION_DEBUG("Removing from session storage session id: %s",req->session_id); onion_sessions_remove(req->connection.listen_point->server->sessions, req->session_id); onion_dict_free(req->session); req->session=NULL; onion_low_free(req->session_id); req->session_id=NULL; } }
/** * @short Frees the memory used by this handler. * @memberof onion_handler_t * * It calls the private data handler free if available, and free the 'next' handler too. * * It should be called when this handler is not going to be used anymore. Most of the cases you * call it over the root handler, and from there it removes all the handlers. * * Returns the number of handlers freed on this level. */ int onion_handler_free(onion_handler *handler){ int n=0; onion_handler *next=handler; while (next){ handler=next; if (handler->priv_data_free && handler->priv_data){ handler->priv_data_free(handler->priv_data); } next=handler->next; onion_low_free(handler); n++; } return n; }
/// AA tree remove the node static onion_dict_node *onion_dict_node_remove(const onion_dict *d, onion_dict_node *node, const char *key){ if (!node) return NULL; int cmp=d->cmp(key, node->data.key); if (cmp<0){ node->left=onion_dict_node_remove(d, node->left, key); //ONION_DEBUG("%p[%s]->left=%p[%s]",node, node->data.key, node->left, node->left ? node->left->data.key : "NULL"); } else if (cmp>0){ node->right=onion_dict_node_remove(d, node->right, key); //ONION_DEBUG("%p[%s]->right=%p[%s]",node, node->data.key, node->right, node->right ? node->right->data.key : "NULL"); } else{ // Real remove //ONION_DEBUG("Remove here %p", node); onion_dict_node_data_free(&node->data); if (node->left==NULL && node->right==NULL){ onion_low_free(node); return NULL; } if (node->left==NULL){ onion_dict_node *t=node->right; // Get next key node while (t->left) t=t->left; //ONION_DEBUG("Set data from %p[%s] to %p[already deleted %s]",t,t->data.key, node, key); memcpy(&node->data, &t->data, sizeof(onion_dict_node_data)); t->data.flags=0; // No double free later, please node->right=onion_dict_node_remove(d, node->right, t->data.key); //ONION_DEBUG("%p[%s]->right=%p[%s]",node, node->data.key, node->right, node->right ? node->right->data.key : "NULL"); } else{ onion_dict_node *t=node->left; // Get prev key node while (t->right) t=t->right; memcpy(&node->data, &t->data, sizeof(onion_dict_node_data)); t->data.flags=0; // No double free later, please node->left=onion_dict_node_remove(d, node->left, t->data.key); //ONION_DEBUG("%p[%s]->left=%p[%s]",node, node->data.key, node->left, node->left ? node->left->data.key : "NULL"); } } decrease_level(node); node=skew(node); if (node->right){ node->right=skew(node->right); if (node->right->right) node->right->right=skew(node->right->right); } node=split(node); if (node->right) node->right=split(node->right); return node; }
/** * @short Frees the memory consumed by this object * @memberof onion_response_t * @ingroup response * * This function returns the close status: OR_KEEP_ALIVE or OR_CLOSE_CONNECTION as needed. * * @returns Whether the connection should be closed or not, or an error status to be handled by server. * @see onion_connection_status */ onion_connection_status onion_response_free(onion_response * res) { // write pending data. if (!(res->flags & OR_HEADER_SENT) && res->buffer_pos < sizeof(res->buffer)) onion_response_set_length(res, res->buffer_pos); if (!(res->flags & OR_HEADER_SENT)) onion_response_write_headers(res); onion_response_flush(res); onion_request *req = res->request; if (res->flags & OR_CHUNKED) { // Set the chunked data end. req->connection.listen_point->write(req, "0\r\n\r\n", 5); } int r = OCS_CLOSE_CONNECTION; // it is a rare ocasion that there is no request, but although unlikely, it may happen if (req) { // keep alive only on HTTP/1.1. ONION_DEBUG0 ("keep alive [req wants] %d && ([skip] %d || [lenght ok] %d==%d || [chunked] %d)", onion_request_keep_alive(req), res->flags & OR_SKIP_CONTENT, res->length, res->sent_bytes, res->flags & OR_CHUNKED); if (onion_request_keep_alive(req) && (res->flags & OR_SKIP_CONTENT || res->length == res->sent_bytes || res->flags & OR_CHUNKED) ) r = OCS_KEEP_ALIVE; if ((onion_log_flags & OF_NOINFO) != OF_NOINFO) // FIXME! This is no proper logging at all. Maybe use a handler. ONION_INFO("[%s] \"%s %s\" %d %d (%s)", onion_request_get_client_description(res->request), onion_request_methods[res->request->flags & OR_METHODS], res->request->fullpath, res->code, res->sent_bytes, (r == OCS_KEEP_ALIVE) ? "Keep-Alive" : "Close connection"); } onion_dict_free(res->headers); onion_low_free(res); return r; }
/** * @short Gets the sessionid cookie, if any, and sets it to req->session_id. * @memberof onion_request_t */ void onion_request_guess_session_id(onion_request *req) { if (req->session_id) // already known. return; const char *ov=onion_dict_get(req->headers, "Cookie"); const char *v=ov; ONION_DEBUG("Session ID, maybe from %s",v); char *r=NULL; onion_dict *session=NULL; do { // Check all possible sessions if (r) { onion_low_free(r); r=NULL; } if (!v) return; v=strstr(v,"sessionid="); if (!v) // exit point, no session found. return; if (v>ov && isalnum(v[-1])) { ONION_DEBUG("At -1: %c %d (%p %p)",v[-1],isalnum(v[-1]),v,ov); v=strstr(v,";"); } else { v+=10; r=onion_low_strdup(v); // Maybe allocated more memory, not much anyway. char *p=r; while (*p!='\0' && *p!=';') p++; *p='\0'; ONION_DEBUG0("Checking if %s exists in sessions", r); session=onion_sessions_get(req->connection.listen_point->server->sessions, r); } } while(!session); req->session_id=r; req->session=session; ONION_DEBUG("Session ID, from cookie, is %s",req->session_id); }
/** * @short Removes the full dict struct from mem. * @memberof onion_dict_t */ void onion_dict_free(onion_dict *dict){ if (!dict) // No free NULL return; ONION_DEBUG0("Free %p", dict); #ifdef HAVE_PTHREADS pthread_mutex_lock(&dict->refmutex); #endif dict->refcount--; ONION_DEBUG0("Free %p refcount %d", dict, dict->refcount); int remove=(dict->refcount==0); #ifdef HAVE_PTHREADS pthread_mutex_unlock(&dict->refmutex); #endif if(remove){ #ifdef HAVE_PTHREADS pthread_rwlock_destroy(&dict->lock); pthread_mutex_destroy(&dict->refmutex); #endif if (dict->root) onion_dict_node_free(dict->root); onion_low_free(dict); } }
/** * @short Generates a new response object * @memberof onion_response_t * * This response is generated from a request, and gets from there the writer and writer data. * * Also fills some important data, as server Id, License type, and the default content type. * * Default content type is HTML, as normally this is what is needed. This is nontheless just * the default, and can be changed to any other with a call to: * * onion_response_set_header(res, "Content-Type", my_type); * * The response object must be freed with onion_response_free, which also returns the keep alive * status. * * onion_response objects are passed by onion internally to process the request, and should not be * created by user normally. Nontheless the option exist. * * @returns An onion_response object for that request. */ onion_response *onion_response_new(onion_request *req){ onion_response *res=onion_low_malloc(sizeof(onion_response)); res->request=req; res->headers=onion_dict_new(); res->code=200; // The most normal code, so no need to overwrite it in other codes. res->flags=0; res->sent_bytes_total=res->length=res->sent_bytes=0; res->buffer_pos=0; #ifndef DONT_USE_DATE_HEADER { time_t t; struct tm *tmp; t = time(NULL); // onion_response_last_date_header is set to t later. It should be more or less atomic. // If not no big deal, as we will just use slightly more CPU on those "ephemeral" moments. if (t!=onion_response_last_time){ ONION_DEBUG("Recalculating date header"); char current_datetime[200]; tmp = localtime(&t); if (tmp == NULL) { perror("localtime"); exit(EXIT_FAILURE); } if (strftime(current_datetime, sizeof(current_datetime), "%a, %d %b %Y %H:%M:%S %Z", tmp) == 0) { fprintf(stderr, "strftime returned 0"); exit(EXIT_FAILURE); } // Risky, not using mutex... #ifdef HAVE_PTHREAD pthread_rwlock_wrlock(&onion_response_date_lock); #endif onion_response_last_time=t; if (onion_response_last_date_header) onion_low_free(onion_response_last_date_header); onion_response_last_date_header=onion_low_strdup(current_datetime); #ifdef HAVE_PTHREAD pthread_rwlock_unlock(&onion_response_date_lock); #endif } } #ifdef HAVE_PTHREAD pthread_rwlock_rdlock(&onion_response_date_lock); #endif assert(onion_response_last_date_header); onion_dict_add(res->headers, "Date", onion_response_last_date_header, OD_DUP_VALUE); #ifdef HAVE_PTHREAD pthread_rwlock_unlock(&onion_response_date_lock); #endif #endif // USE_DATE_HEADER // Sorry for the advertisment. onion_dict_add(res->headers, "Server", "libonion v" ONION_VERSION " - coralbits.com", 0); onion_dict_add(res->headers, "Content-Type", "text/html", 0); // Maybe not the best guess, but really useful. //time_t t=time(NULL); //onion_dict_add(res->headers, "Date", asctime(localtime(&t)), OD_DUP_VALUE); return res; }