Beispiel #1
0
/**
 * @short Creates a redis backend for sessions
 * @ingroup sessions
 */
onion_sessions* onion_sessions_redis_new(const char* server_ip, int port)
{
    onion_random_init();

    onion_sessions *ret = onion_low_malloc(sizeof(onion_sessions));
    ret->data = onion_low_malloc(sizeof(onion_session_redis));
    ret->free=onion_sessions_redis_free;
    ret->get=onion_sessions_redis_get;
    ret->save=onion_sessions_redis_save;

    onion_session_redis *p = ret->data;
    p->context = redisConnect(server_ip, port);

    if(p->context != NULL && p->context->err)
    {
        ONION_ERROR("Can't connect to redis. Error (%s)", p->context->errstr);
        redisFree(p->context);
        return NULL;
    }

#ifdef HAVE_PTHREADS
    pthread_mutex_init(&p->mutex, NULL);
#endif

    return ret;
}
Beispiel #2
0
/**
 * @short Creates a sqlite backend for sessions
 * @ingroup sessions
 *
 * @see onion_set_session_backend
 */
onion_sessions* onion_sessions_sqlite3_new(const char *database_filename)
{
	onion_random_init();

	int rc;
	sqlite3 *db;
	sqlite3_stmt *save;
	sqlite3_stmt *get;
	rc = sqlite3_open(database_filename, &db);
	if( rc ){
		ONION_ERROR("Can't open database: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		return NULL;
	}
	// I blindly try to create tables. If they exist, error, if not, create
	sqlite3_exec(db, "CREATE TABLE sessions (id TEXT PRIMARY KEY, data TEXT)", 0,0,0);

	const char *GET_SQL="SELECT data FROM sessions WHERE id=?";
	rc=sqlite3_prepare_v2(db, GET_SQL, strlen(GET_SQL), &get, NULL);
	if( rc != SQLITE_OK ){
		ONION_ERROR("Cant prepare statement to get (%d)", rc);
		sqlite3_close(db);
		return NULL;
	}

	const char *SAVE_SQL="INSERT OR REPLACE INTO sessions (id, data) VALUES (?, ?);";
	rc=sqlite3_prepare_v2(db, SAVE_SQL, strlen(SAVE_SQL), &save, NULL);
	if( rc != SQLITE_OK ){
		ONION_ERROR("Cant prepare statement to save (%d)", rc);
		sqlite3_close(db);
		return NULL;
	}

	onion_sessions *ret=onion_low_malloc(sizeof(onion_sessions));

	ret->data=onion_low_malloc(sizeof(onion_session_sqlite3));
	ret->free=onion_sessions_sqlite3_free;
	ret->get=onion_sessions_sqlite3_get;
	ret->save=onion_sessions_sqlite3_save;

	onion_session_sqlite3 *p=ret->data;
	p->db=db;
	p->save=save;
	p->get=get;
#ifdef HAVE_PTHREADS
	pthread_mutex_init(&p->mutex, NULL);
#endif

	return ret;
}
Beispiel #3
0
/**
 * @short Sets the port to listen to.
 * @memberof onion_t
 * 
 * Default listen point is HTTP at localhost:8080.
 * 
 * @param server The onion server to act on.
 * @param port The number of port to listen to, or service name, as string always.
 */
int onion_add_listen_point(onion* server, const char* hostname, const char* port, onion_listen_point* protocol){
	if (protocol==NULL){
		ONION_ERROR("Trying to add an invalid entry point. Ignoring.");
		return -1;
	}
	
	protocol->server=server;
	if (hostname)
		protocol->hostname=onion_low_strdup(hostname);
	if (port)
		protocol->port=onion_low_strdup(port);
	
	if (server->listen_points){
		onion_listen_point **p=server->listen_points;
		int protcount=0;
		while (*p++) protcount++;
		server->listen_points=
		  (onion_listen_point**)onion_low_realloc(server->listen_points,
							 (protcount+2)*sizeof(onion_listen_point));
		server->listen_points[protcount]=protocol;
		server->listen_points[protcount+1]=NULL;
	}
	else{
		server->listen_points=onion_low_malloc(sizeof(onion_listen_point*)*2);
		server->listen_points[0]=protocol;
		server->listen_points[1]=NULL;
	}
	
	ONION_DEBUG("add %p listen_point (%p, %p, %p)", protocol, server->listen_points, *server->listen_points, *(server->listen_points+1));
	return 0;
}
Beispiel #4
0
/**
 * @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;
}
Beispiel #5
0
/// Allocates a new node data, and sets the data itself.
static onion_dict_node *onion_dict_node_new(const char *key, const void *value, int flags){
	onion_dict_node *node=onion_low_malloc(sizeof(onion_dict_node));

	onion_dict_set_node_data(&node->data, key, value, flags);

	node->left=NULL;
	node->right=NULL;
	node->level=1;
	return node;
}
Beispiel #6
0
/**
 * @short Creates a static handler that just writes some static data.
 *
 * Path is a regex for the url, as arrived here.
 */
onion_handler *onion_handler_static(const char *text, int code){
	onion_handler_static_data *priv_data=onion_low_malloc(sizeof(onion_handler_static_data));
	if (!priv_data)
		return NULL;

	priv_data->code=code;
	priv_data->data=onion_low_strdup(text);

	onion_handler *ret=onion_handler_new((onion_handler_handler)onion_handler_static_handler,
																			 priv_data,(onion_handler_private_data_free) onion_handler_static_delete);
	return ret;
}
Beispiel #7
0
onion_sessions *onion_sessions_mem_new(){
	onion_random_init();
	
	onion_sessions *ret=onion_low_malloc(sizeof(onion_sessions));
	ret->data=onion_dict_new();
	
	ret->get=onion_sessions_mem_get;
	ret->save=onion_sessions_mem_save;
	ret->free=onion_sessions_mem_free;
	
	return ret;
}
Beispiel #8
0
onion_connection_status handler(void *request_list_v, onion_request * req,
                                onion_response * res) {
  // Some hello message
  onion_response_printf(res, "Starting poll\n");
  onion_response_flush(res);

  // Add it to the request_list lists.
  request_list_t *request_list = request_list_v;
  reqres_t *reqres = onion_low_malloc(sizeof(reqres_t));
  reqres->req = req;
  reqres->res = res;
  pthread_mutex_lock(&request_list->lock);
  request_list->reqres = onion_ptr_list_add(request_list->reqres, reqres);
  pthread_mutex_unlock(&request_list->lock);

  // Yield thread to the poller, ensures request and response are not freed.
  return OCS_YIELD;
}
Beispiel #9
0
/**
 * @short Prepares the POST
 */
static onion_connection_status prepare_POST(onion_request *req){
	// ok post
	onion_token *token=req->parser_data;
	const char *content_type=onion_dict_get(req->headers, "Content-Type");
	const char *content_size=onion_dict_get(req->headers, "Content-Length");

	if (!content_size){
		ONION_ERROR("I need the content size header to support POST data");
		return OCS_INTERNAL_ERROR;
	}
	size_t cl=atol(content_size);
	if (cl==0)
		return OCS_REQUEST_READY;

	//ONION_DEBUG("Content type %s",content_type);
	if (!content_type || (strstr(content_type, "application/x-www-form-urlencoded"))){
		if (cl>req->connection.listen_point->server->max_post_size){
			ONION_ERROR("Asked to send much POST data. Limit %d. Failing.",req->connection.listen_point->server->max_post_size);
			return OCS_INTERNAL_ERROR;
		}
		assert(token->extra==NULL);
		token->extra=onion_low_scalar_malloc(cl+1); // Cl + \0
		token->extra_size=cl;
		req->free_list=onion_ptr_list_add(req->free_list, token->extra); // Free when the request is freed.

		req->parser=parse_POST_urlencode;
		return OCS_NEED_MORE_DATA;
	}

	// multipart.

	const char *mp_token=strstr(content_type, "boundary=");
	if (!mp_token){
		ONION_ERROR("No boundary set at content-type");
		return OCS_INTERNAL_ERROR;
	}
	mp_token+=9;
	if (cl>req->connection.listen_point->server->max_post_size) // I hope the missing part is files, else error later.
		cl=req->connection.listen_point->server->max_post_size;

	int mp_token_size=strlen(mp_token);
	token->extra_size=cl; // Max size of the multipart->data
	onion_multipart_buffer *multipart=onion_low_malloc(token->extra_size+sizeof(onion_multipart_buffer)+mp_token_size+2);
	assert(token->extra==NULL);
	token->extra=(char*)multipart;

	multipart->boundary=(char*)multipart+sizeof(onion_multipart_buffer)+1;
	multipart->size=mp_token_size+4;
	multipart->pos=2; // First boundary already have [\r]\n readen
	multipart->post_total_size=cl;
	multipart->file_total_size=0;
	multipart->boundary[0]='\r';
	multipart->boundary[1]='\n';
	multipart->boundary[2]='-';
	multipart->boundary[3]='-';
	strcpy(&multipart->boundary[4],mp_token);
	multipart->data=(char*)multipart+sizeof(onion_multipart_buffer)+multipart->size+1;

	//ONION_DEBUG("Multipart POST boundary '%s'",multipart->boundary);

	req->parser=parse_POST_multipart_start;

	return OCS_NEED_MORE_DATA;
}
Beispiel #10
0
/**
 * @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;
}
Beispiel #11
0
/**
 * @short Performs the listening with the given mode
 * @memberof onion_t
 *
 * This is the main loop for the onion server.
 * 
 * It initiates the listening on all the selected ports and addresses.
 *
 * @returns !=0 if there is any error. It returns actualy errno from the network operations. See socket for more information.
 */
int onion_listen(onion *o){
#ifdef HAVE_PTHREADS
	if (!(o->flags&O_DETACHED) && (o->flags&O_DETACH_LISTEN)){ // Must detach and return
		o->flags|=O_DETACHED;
		int errcode=onion_low_pthread_create(&o->listen_thread,NULL, onion_listen_start, o);
		if (errcode!=0)
			return errcode;
		return 0;
	}
#endif
	
	if (!o->listen_points){
		onion_add_listen_point(o,NULL,NULL,onion_http_new());
		ONION_DEBUG("Created default HTTP listen port");
	}

	/// Start listening
	size_t successful_listened_points=0;
	onion_listen_point **lp=o->listen_points;
	while (*lp){
		int listen_result=onion_listen_point_listen(*lp);
		if (!listen_result) {
			successful_listened_points++;
		}
		lp++;
	}
	if (!successful_listened_points){
		ONION_ERROR("There are no available listen points");
		return 1;
	}
	
	o->flags|=O_LISTENING;

	
	if (o->flags&O_ONE){
		onion_listen_point **listen_points=o->listen_points;
		if (listen_points[1]!=NULL){
			ONION_WARNING("Trying to use non-poll and non-thread mode with several listen points. Only the first will be listened");
		}
		onion_listen_point *op=listen_points[0];
		do{
			onion_request *req=onion_request_new(op);
			if (!req)
				continue;
			ONION_DEBUG("Accepted request %p", req);
			onion_request_set_no_keep_alive(req);
			int ret;
			do{
				ret=req->connection.listen_point->read_ready(req);
			}while(ret>=0);
			ONION_DEBUG("End of request %p", req);
			onion_request_free(req);
			//req->connection.listen_point->close(req);
		}while(((o->flags&O_ONE_LOOP) == O_ONE_LOOP) && op->listenfd>0);
	}
	else{
		onion_listen_point **listen_points=o->listen_points;
		while (*listen_points){
			onion_listen_point *p=*listen_points;
			ONION_DEBUG("Adding listen point fd %d to poller", p->listenfd);
			onion_poller_slot *slot=onion_poller_slot_new(p->listenfd, (void*)onion_listen_point_accept, p);
			onion_poller_slot_set_type(slot, O_POLL_ALL);
			onion_poller_add(o->poller, slot);
			listen_points++;
		}

#ifdef HAVE_PTHREADS
		ONION_DEBUG("Start polling / listening %p, %p, %p", o->listen_points, *o->listen_points, *(o->listen_points+1));
		if (o->flags&O_THREADED){
			o->threads=onion_low_malloc(sizeof(pthread_t)*(o->nthreads-1));
			int i;
			for (i=0;i<o->nthreads-1;i++){
				onion_low_pthread_create(&o->threads[i],NULL,onion_poller_poll_start, o->poller);
			}
			
			// Here is where it waits.. but eventually it will exit at onion_listen_stop
			onion_poller_poll(o->poller);
			ONION_DEBUG("Closing onion_listen");
			
			for (i=0;i<o->nthreads-1;i++){
				onion_low_pthread_join(o->threads[i],NULL);
			}
		}
		else
#endif
			onion_poller_poll(o->poller);

		listen_points=o->listen_points;
		while (*listen_points){
			onion_listen_point *p=*listen_points;
			if (p->listenfd>0){
				ONION_DEBUG("Removing %d from poller", p->listenfd);
				onion_poller_remove(o->poller, p->listenfd);
			}
			listen_points++;
		}
	}
	
	o->flags=o->flags & ~O_LISTENING;
	
	return 0;
}