httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Ruediger Pluem <rpl...@apache.org>
Subject Re: svn commit: r645160 - in /httpd/httpd/trunk: CHANGES docs/manual/mod/mod_session_dbd.xml modules/session/config.m4 modules/session/mod_session_dbd.c
Date Sat, 05 Apr 2008 21:44:22 GMT


On 04/05/2008 08:59 PM, minfrin@apache.org wrote:
> Author: minfrin
> Date: Sat Apr  5 11:59:40 2008
> New Revision: 645160
> 
> URL: http://svn.apache.org/viewvc?rev=645160&view=rev
> Log:
> mod_session_dbd: Add a session implementation capable of storing
> session information in a SQL database via the dbd interface. Useful
> for sites where session privacy is important.
> 
> Added:
>     httpd/httpd/trunk/docs/manual/mod/mod_session_dbd.xml
>     httpd/httpd/trunk/modules/session/mod_session_dbd.c
> Modified:
>     httpd/httpd/trunk/CHANGES
>     httpd/httpd/trunk/modules/session/config.m4
> 
> Modified: httpd/httpd/trunk/CHANGES

> 
> Added: httpd/httpd/trunk/modules/session/mod_session_dbd.c
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/session/mod_session_dbd.c?rev=645160&view=auto
> ==============================================================================
> --- httpd/httpd/trunk/modules/session/mod_session_dbd.c (added)
> +++ httpd/httpd/trunk/modules/session/mod_session_dbd.c Sat Apr  5 11:59:40 2008

> +AP_DECLARE(int) ap_session_dbd_load(request_rec * r, session_rec ** z)
> +{
> +
> +    session_dbd_dir_conf *conf = ap_get_module_config(r->per_dir_config,
> +                                                      &session_dbd_module);
> +
> +    apr_status_t ret = APR_SUCCESS;
> +    session_rec *zz = NULL;
> +    const char *name = NULL;
> +    const char *note = NULL;
> +    const char *val = NULL;
> +    const char *key = NULL;
> +    request_rec *m = r->main ? r->main : r;
> +
> +    /* is our session in a cookie? */
> +    if (conf->name2_set) {
> +        name = conf->name2;
> +    }
> +    else if (conf->name_set) {
> +        name = conf->name;
> +    }
> +    else if (conf->peruser_set && r->user) {
> +        name = r->user;
> +    }
> +    else {
> +        return DECLINED;
> +    }
> +
> +    /* first look in the notes */
> +    note = apr_pstrcat(r->pool, MOD_SESSION_DBD, name, NULL);
> +    zz = (session_rec *)apr_table_get(m->notes, note);
> +    if (zz) {
> +        *z = zz;
> +        return OK;
> +    }
> +
> +    /* load anonymous sessions */
> +    if (conf->name_set || conf->name2_set) {
> +
> +        /* load RFC2109 compliant cookie */
> +        if (conf->name_set) {
> +            ap_cookie_read(r, conf->name, &key, conf->remove);
> +        }
> +
> +        /* load RFC2965 compliant cookie */
> +        if (!key && conf->name2_set) {
> +            ap_cookie_read(r, conf->name2, &key, conf->remove);
> +        }

Why this? name is already set to the correct value (either conf->name or conf->name2)
and ap_cookie_read looks for both cookie types?

> +
> +        if (key) {
> +            ret = dbd_load(r, key, &val);
> +            if (ret != APR_SUCCESS) {
> +                return ret;
> +            }
> +        }
> +
> +    }
> +
> +    /* load named session */
> +    else if (conf->peruser) {
> +        if (r->user) {
> +            ret = dbd_load(r, r->user, &val);
> +            if (ret != APR_SUCCESS) {
> +                return ret;
> +            }
> +        }
> +    }
> +
> +    /* otherwise not for us */
> +    else {
> +        return DECLINED;
> +    }
> +
> +    /* create a new session and return it */
> +    zz = (session_rec *) apr_pcalloc(r->pool, sizeof(session_rec));
> +    zz->pool = r->pool;
> +    zz->entries = apr_table_make(zz->pool, 10);
> +    zz->uuid = (apr_uuid_t *) apr_pcalloc(zz->pool, sizeof(apr_uuid_t));
> +    if (key) {
> +        apr_uuid_parse(zz->uuid, key);
> +    }
> +    else {
> +        apr_uuid_get(zz->uuid);
> +    }
> +    zz->encoded = val;
> +    *z = zz;
> +
> +    /* put the session in the notes so we don't have to parse it again */
> +    apr_table_setn(m->notes, note, (char *)zz);
> +
> +    return OK;
> +
> +}
> +
> +/**
> + * Save the session by the key specified.
> + */
> +static apr_status_t dbd_save(request_rec * r, const char *key, const char *val, apr_int64_t
expiry)
> +{
> +
> +    apr_status_t rv;
> +    ap_dbd_t *dbd = NULL;
> +    apr_dbd_prepared_t *statement;
> +    int rows = 0;
> +
> +    session_dbd_dir_conf *conf = ap_get_module_config(r->per_dir_config,
> +                                                      &session_dbd_module);
> +
> +    if (conf->updatelabel == NULL) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
> +                      "no SessionDBDupdatelabel has been specified");
> +        return APR_EGENERAL;
> +    }
> +
> +    rv = dbd_init(r, conf->updatelabel, &dbd, &statement);
> +    if (rv) {
> +        return rv;
> +    }
> +    rv = apr_dbd_pvbquery(dbd->driver, r->pool, dbd->handle, &rows, statement,
> +                          val, &expiry, key, NULL);
> +    if (rv) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
> +                      "query execution error updating session '%s' "
> +                      "using database query '%s': %s", key, conf->updatelabel,
> +                      apr_dbd_error(dbd->driver, dbd->handle, rv));
> +        return APR_EGENERAL;
> +    }
> +
> +    /*
> +     * if some rows were updated it means a session existed and was updated,
> +     * so we are done.
> +     */
> +    if (rows != 0) {
> +        return APR_SUCCESS;
> +    }
> +
> +    if (conf->insertlabel == NULL) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
> +                      "no SessionDBDinsertlabel has been specified");
> +        return APR_EGENERAL;
> +    }
> +
> +    rv = dbd_init(r, conf->insertlabel, &dbd, &statement);
> +    if (rv) {
> +        return rv;
> +    }
> +    rv = apr_dbd_pvbquery(dbd->driver, r->pool, dbd->handle, &rows, statement,
> +                          val, &expiry, key, NULL);
> +    if (rv) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, LOG_PREFIX
> +                      "query execution error inserting session '%s' "
> +                      "in database with '%s': %s", key, conf->insertlabel,
> +                      apr_dbd_error(dbd->driver, dbd->handle, rv));
> +        return APR_EGENERAL;
> +    }
> +
> +    /*
> +     * if some rows were inserted it means a session was inserted, so we are
> +     * done.
> +     */
> +    if (rows != 0) {
> +        return APR_SUCCESS;
> +    }
> +
> +    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
> +                  "the session insert query did not cause any rows to be added "
> +                  "to the database for session '%s', session not inserted", key);
> +
> +    return APR_EGENERAL;
> +
> +}
> +
> +/**
> + * Remove the session by the key specified.
> + */
> +static apr_status_t dbd_remove(request_rec * r, const char *key)
> +{
> +
> +    apr_status_t rv;
> +    apr_dbd_prepared_t *statement;
> +    int rows = 0;
> +
> +    session_dbd_dir_conf *conf = ap_get_module_config(r->per_dir_config,
> +                                                      &session_dbd_module);
> +    ap_dbd_t *dbd = session_dbd_acquire_fn(r);
> +    if (dbd == NULL) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
> +                      "failed to acquire database connection to remove "
> +                      "session with key '%s'", key);
> +        return APR_EGENERAL;
> +    }
> +
> +    if (conf->deletelabel == NULL) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
> +                      "no SessionDBDdeletelabel has been specified");
> +        return APR_EGENERAL;
> +    }
> +
> +    statement = apr_hash_get(dbd->prepared, conf->deletelabel, APR_HASH_KEY_STRING);
> +    if (statement == NULL) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
> +                      "prepared statement could not be found for "
> +                      "SessionDBDdeletelabel with the label '%s'", conf->deletelabel);
> +        return APR_EGENERAL;
> +    }
> +    rv = apr_dbd_pvbquery(dbd->driver, r->pool, dbd->handle, &rows, statement,
> +                          key, NULL);
> +    if (rv != APR_SUCCESS) {
> +        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, LOG_PREFIX
> +                      "query execution error removing session '%s' "
> +                      "from database", key);
> +        return rv;
> +    }
> +
> +    return APR_SUCCESS;
> +
> +}
> +
> +/**
> + * Clean out expired sessions.
> + * 
> + * TODO: We need to figure out a way to clean out expired sessions from the database.
> + * The monitor hook doesn't help us that much, as we have no handle into the
> + * server, and so we need to come up with a way to do this safely.
> + */
> +static apr_status_t dbd_clean(apr_pool_t *p)
> +{
> +
> +    return APR_ENOTIMPL;
> +
> +}
> +
> +/**
> + * Save the session by firing off a dbd query.
> + *
> + * If the session is anonymous, save the session and write a cookie
> + * containing the uuid.
> + *
> + * If the session is keyed to the username, save the session using
> + * the username as a key.
> + *
> + * On success, this method will return APR_SUCCESS.
> + *
> + * @param r The request pointer.
> + * @param z A pointer to where the session will be written.
> + */
> +AP_DECLARE(int) ap_session_dbd_save(request_rec * r, session_rec * z)
> +{
> +
> +    apr_status_t ret = APR_SUCCESS;
> +    session_dbd_dir_conf *conf = ap_get_module_config(r->per_dir_config,
> +                                                      &session_dbd_module);
> +
> +    /* support anonymous sessions */
> +    if (conf->name_set || conf->name2_set) {
> +
> +        /* don't cache pages with a session */
> +        apr_table_addn(r->headers_out, "Cache-Control", "no-cache");

Why don't we cache these pages? Because of the cookie? We can avoid saving
the Set-Cookie header in the the cache by CacheIgnoreHeaders. IMHO we should
give the user the chance to cache it.

> +
> +        /* must we create a uuid? */
> +        char *buffer = apr_pcalloc(r->pool, APR_UUID_FORMATTED_LENGTH + 1);
> +        apr_uuid_format(buffer, z->uuid);
> +
> +        /* save the session with the uuid as key */
> +        if (z->encoded && z->encoded[0]) {
> +            ret = dbd_save(r, buffer, z->encoded, z->expiry);
> +        }
> +        else {
> +            ret = dbd_remove(r, buffer);
> +        }
> +        if (ret != APR_SUCCESS) {
> +            return ret;
> +        }
> +
> +        /* create RFC2109 compliant cookie */
> +        if (conf->name_set) {
> +            ap_cookie_write(r, conf->name, buffer, conf->name_attrs, z->maxage);
> +        }
> +
> +        /* create RFC2965 compliant cookie */
> +        if (conf->name2_set) {
> +            ap_cookie_write2(r, conf->name2, buffer, conf->name2_attrs, z->maxage);
> +        }
> +
> +        return OK;
> +
> +    }
> +
> +    /* save named session */
> +    else if (conf->peruser) {
> +
> +        /* don't cache pages with a session */
> +        apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
> +
> +        if (r->user) {
> +            ret = dbd_save(r, r->user, z->encoded, z->expiry);
> +            if (ret != APR_SUCCESS) {
> +                return ret;
> +            }
> +            return OK;
> +        }
> +        else {
> +            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, LOG_PREFIX
> +               "peruser sessions can only be saved if a user is logged in, "
> +                          "session not saved: %s", r->uri);
> +        }
> +    }
> +
> +    return DECLINED;
> +
> +}
> +
> +/**
> + * This function performs housekeeping on the database, deleting expired
> + * sessions.
> + */
> +AP_DECLARE(int) ap_session_dbd_monitor(apr_pool_t *p)
> +{
> +    /* TODO handle housekeeping */
> +    dbd_clean(p);
> +    return OK;
> +}
> +
> +
> +static void *create_session_dbd_dir_config(apr_pool_t * p, char *dummy)
> +{
> +    session_dbd_dir_conf *new =
> +    (session_dbd_dir_conf *) apr_pcalloc(p, sizeof(session_dbd_dir_conf));
> +
> +    new->remove = 1;
> +    new->remove_set = 1;

So I will never inherit the base setting!

> +    
> +    new->selectlabel = "selectsession";
> +    new->insertlabel = "insertsession";
> +    new->updatelabel = "updatesession";
> +    new->deletelabel = "deletesession";
> +
> +    return (void *) new;
> +}
> +
> +static void *merge_session_dbd_dir_config(apr_pool_t * p, void *basev, void *addv)
> +{
> +    session_dbd_dir_conf *new = (session_dbd_dir_conf *) apr_pcalloc(p, sizeof(session_dbd_dir_conf));
> +    session_dbd_dir_conf *add = (session_dbd_dir_conf *) addv;
> +    session_dbd_dir_conf *base = (session_dbd_dir_conf *) basev;
> +
> +    new->name = (add->name_set == 0) ? base->name : add->name;
> +    new->name_attrs = (add->name_set == 0) ? base->name_attrs : add->name_attrs;
> +    new->name_set = add->name_set || base->name_set;
> +    new->name2 = (add->name2_set == 0) ? base->name2 : add->name2;
> +    new->name2_attrs = (add->name2_set == 0) ? base->name2_attrs : add->name2_attrs;
> +    new->name2_set = add->name2_set || base->name2_set;
> +    new->peruser = (add->peruser_set == 0) ? base->peruser : add->peruser;
> +    new->peruser_set = add->peruser_set || base->peruser_set;
> +    new->remove = (add->remove_set == 0) ? base->remove : add->remove;
> +    new->remove_set = add->remove_set || base->remove_set;
> +    new->selectlabel = (!add->selectlabel) ? base->selectlabel : add->selectlabel;
> +    new->updatelabel = (!add->updatelabel) ? base->updatelabel : add->updatelabel;
> +    new->insertlabel = (!add->insertlabel) ? base->insertlabel : add->insertlabel;
> +    new->deletelabel = (!add->deletelabel) ? base->deletelabel : add->deletelabel;
> +
> +    return new;
> +}
> +
> +/**
> + * Sanity check a given string that it exists, is not empty,
> + * and does not contain special characters.
> + */
> +static const char *check_string(cmd_parms * cmd, const char *string)
> +{
> +    if (APR_SUCCESS != ap_cookie_check_string(string)) {
> +        return apr_pstrcat(cmd->pool, cmd->directive->directive,
> +                           " cannot be empty, or contain '=', ';' or '&'.",
> +                           NULL);
> +    }
> +    return NULL;
> +}
> +
> +static const char *
> +     set_dbd_peruser(cmd_parms * parms, void *dconf, int flag)
> +{
> +    session_dbd_dir_conf *conf = dconf;
> +
> +    conf->peruser = flag;
> +    conf->peruser_set = 1;
> +
> +    return NULL;
> +}
> +
> +static const char *
> +     set_dbd_cookie_remove(cmd_parms * parms, void *dconf, int flag)
> +{
> +    session_dbd_dir_conf *conf = dconf;
> +
> +    conf->remove = flag;
> +    conf->remove_set = 1;
> +
> +    return NULL;
> +}
> +
> +static const char *set_cookie_name(cmd_parms * cmd, void *config, const char *args)
> +{
> +    char *last;
> +    char *line = apr_pstrdup(cmd->pool, args);
> +    session_dbd_dir_conf *conf = (session_dbd_dir_conf *) config;
> +    char *cookie = apr_strtok(line, " \t", &last);
> +    conf->name = cookie;
> +    conf->name_set = 1;
> +    while (apr_isspace(*last)) {
> +        last++;
> +    }
> +    conf->name_attrs = last;

So name_attrs may contain \t's. Is this intended?

> +    return check_string(cmd, cookie);
> +}
> +
> +static const char *set_cookie_name2(cmd_parms * cmd, void *config, const char *args)
> +{
> +    char *last;
> +    char *line = apr_pstrdup(cmd->pool, args);
> +    session_dbd_dir_conf *conf = (session_dbd_dir_conf *) config;
> +    char *cookie = apr_strtok(line, " \t", &last);
> +    conf->name2 = cookie;
> +    conf->name2_set = 1;
> +    while (apr_isspace(*last)) {
> +        last++;
> +    }
> +    conf->name2_attrs = last;

So name2_attrs may contain \t's. Is this intended?

> +    return check_string(cmd, cookie);
> +}
> +

Regards

RĂ¼diger

Mime
View raw message