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

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

E-mail: