Annotation of parser3/src/types/pa_vredis.h, revision 1.2

1.1       moko        1: /** @file
                      2:        Parser: @b redis class decls.
                      3: 
                      4:        Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com)
                      5:        Authors: Konstantin Morshnev <moko@design.ru>
                      6: */
                      7: 
                      8: #ifndef PA_VREDIS_H
                      9: #define PA_VREDIS_H
                     10: 
1.2     ! moko       11: #define IDENT_PA_VREDIS_H "$Id: pa_vredis.h,v 1.1 2026/10/06 23:34:31 moko Exp $"
1.1       moko       12: 
                     13: #include "classes.h"
                     14: #include "pa_vstateless_object.h"
                     15: #include "pa_common.h"
                     16: #include "pa_os.h"
                     17: 
                     18: #ifdef WITH_REDIS
                     19: #include <hiredis/hiredis.h>
1.2     ! moko       20: #ifdef WITH_REDIS_SSL
        !            21: #include <hiredis/hiredis_ssl.h>
        !            22: #endif
1.1       moko       23: #endif
                     24: 
                     25: // defines
                     26: #define VREDIS_TYPE "redis"
                     27: #define MAX_REDIS_COMMAND_PARAMS 10000
                     28: 
                     29: // externs
                     30: extern Methoded *redis_class;
                     31: 
                     32: #ifdef WITH_REDIS
                     33: /// classes/redis.C: the native code of ^r.command_name[arguments...]
                     34: void redis_command_method(Request& r, MethodParams& params);
                     35: 
                     36: class Temp_redis_reply {
                     37:        redisReply* freply;
                     38: public:
                     39:        Temp_redis_reply(redisReply* areply): freply(areply) {}
                     40:        ~Temp_redis_reply() { if(freply) freeReplyObject(freply); }
                     41: };
                     42: #endif
                     43: 
                     44: class VRedis: public VStateless_object {
                     45: public:
                     46:        // value
                     47:        override const char* type() const { return VREDIS_TYPE; }
                     48:        override VStateless_class *get_class() { return redis_class; }
                     49: 
                     50: #ifdef WITH_REDIS
                     51: #ifdef FEATURE_GET_ELEMENT4CALL
                     52:        override Value* get_element4call(const String& aname) {
                     53: #else
                     54:        override Value* get_element(const String& aname) {
                     55: #endif
                     56:                if(Value* result=VStateless_object::get_element(aname))
                     57:                        return result;
                     58: 
                     59:                Method* method=new Method(
                     60:                        Method::CT_DYNAMIC,
                     61:                        0, MAX_REDIS_COMMAND_PARAMS,
                     62:                        0/*params_names*/, 0/*locals_names*/,
                     63:                        0/*parser_code*/, redis_command_method, false/*all_vars_local*/
                     64: #ifdef OPTIMIZE_RESULT
                     65:                        , Method::RO_USE_WCONTEXT
                     66: #endif
                     67: #ifdef OPTIMIZE_CALL
                     68:                        , Method::CO_WITHOUT_WCONTEXT
                     69: #endif
                     70:                        );
                     71:                method->name=&aname;
                     72:                return method->get_vjunction(*this);
                     73: }
                     74: 
                     75: public: // usage
                     76: 
1.2     ! moko       77:        VRedis(): fcontext(0), fssl_context(0), fuser(0), fpassword(0), fdb(0), fprotocol(3), fauto_reconnect(0) {}
1.1       moko       78: 
                     79:        redisContext* fcontext;
1.2     ! moko       80:        struct redisSSLContext* fssl_context; // with $.tls only
1.1       moko       81:        const char* fuser;
                     82:        const char* fpassword;
                     83:        int fdb;
                     84:        int fprotocol;
                     85:        int fauto_reconnect; // 0 = disabled, N>0 = sleeps N sec before reconnecting
                     86: 
1.2     ! moko       87:        /// TLS, HELLO or AUTH, then SELECT: on open and on reconnect
1.1       moko       88:        void handshake() {
1.2     ! moko       89: #ifdef WITH_REDIS_SSL
        !            90:                if(fssl_context && redisInitiateSSLWithContext(fcontext, fssl_context)!=REDIS_OK)
        !            91:                        throw Exception("redis", 0, "%s", fcontext->errstr);
        !            92: #endif
1.1       moko       93:                if(fprotocol==3) {
                     94:                        const char* argv[5]={"HELLO", "3"};
                     95:                        int argc=2;
                     96:                        if(fpassword) {
                     97:                                argv[argc++]="AUTH";
                     98:                                argv[argc++]=fuser ? fuser : "default";
                     99:                                argv[argc++]=fpassword;
                    100:                        }
                    101:                        void_command(argc, argv);
                    102:                } else if(fpassword) {
                    103:                        const char* argv[3]={"AUTH"};
                    104:                        int argc=1;
                    105:                        if(fuser)
                    106:                                argv[argc++]=fuser;
                    107:                        argv[argc++]=fpassword;
                    108:                        void_command(argc, argv);
                    109:                }
                    110:                if(fdb) {
                    111:                        const char* argv[2]={"SELECT", pa_itoa(fdb)};
                    112:                        void_command(2, argv);
                    113:                }
                    114:        }
                    115: 
1.2     ! moko      116:        /// frees the connection and its TLS context
        !           117:        void release() {
        !           118:                if(fcontext) {
        !           119:                        redisFree(fcontext);
        !           120:                        fcontext=0;
        !           121:                }
        !           122: #ifdef WITH_REDIS_SSL
        !           123:                if(fssl_context) {
        !           124:                        redisFreeSSLContext(fssl_context);
        !           125:                        fssl_context=0;
        !           126:                }
        !           127: #endif
        !           128:        }
        !           129: 
1.1       moko      130:        /// sends the command and returns the reply, which the caller frees
                    131:        redisReply* command(int argc, const char** argv, const size_t* lengths=0) {
                    132:                redisReply* reply=(redisReply*)redisCommandArgv(context(), argc, argv, lengths);
                    133:                if(!reply)
                    134:                        throw Exception("redis", 0, "%s failed: %s", argv[0], fcontext->errstr);
                    135:                return reply;
                    136:        }
                    137: 
                    138:        /// a command whose reply is only checked for an error
                    139:        void void_command(int argc, const char** argv) {
                    140:                redisReply* reply=command(argc, argv);
                    141:                Temp_redis_reply temp_reply(reply);
                    142:                if(reply->type==REDIS_REPLY_ERROR)
                    143:                        throw Exception("redis", 0, "%s failed: %s", argv[0], reply->str);
                    144:        }
                    145: 
                    146: private:
                    147: 
                    148:        /// reconnects first if the connection is broken and auto_reconnect is enabled
                    149:        redisContext* context() {
                    150:                if(!fcontext)
                    151:                        throw Exception("redis", 0, "using unopened or released redis object");
                    152:                if(fcontext->err) {
                    153:                        if(fauto_reconnect <= 0)
                    154:                                throw Exception("redis", 0, "connection is broken (auto_reconnect is not enabled): %s", fcontext->errstr);
                    155:                        pa_sleep(fauto_reconnect, 0);
                    156:                        if(redisReconnect(fcontext) != REDIS_OK)
                    157:                                throw Exception("redis", 0, "reconnect failed: %s", fcontext->errstr);
                    158:                        handshake();
                    159:                }
                    160:                return fcontext;
                    161:        }
                    162: 
                    163: #endif // WITH_REDIS
                    164: };
                    165: 
                    166: #endif

E-mail: