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

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.3     ! moko       11: #define IDENT_PA_VREDIS_H "$Id: pa_vredis.h,v 1.2 2026/10/09 00:30:40 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
                     57:                if(Value* result=VStateless_object::get_element(aname))
                     58:                        return result;
                     59: 
                     60:                Method* method=new Method(
                     61:                        Method::CT_DYNAMIC,
                     62:                        0, MAX_REDIS_COMMAND_PARAMS,
                     63:                        0/*params_names*/, 0/*locals_names*/,
                     64:                        0/*parser_code*/, redis_command_method, false/*all_vars_local*/
                     65: #ifdef OPTIMIZE_RESULT
                     66:                        , Method::RO_USE_WCONTEXT
                     67: #endif
                     68: #ifdef OPTIMIZE_CALL
                     69:                        , Method::CO_WITHOUT_WCONTEXT
                     70: #endif
                     71:                        );
                     72:                method->name=&aname;
                     73:                return method->get_vjunction(*this);
                     74: }
                     75: 
                     76: public: // usage
                     77: 
1.3     ! moko       78:        VRedis(Pool& apool): Pooled(apool), fcontext(0), fssl_context(0), fuser(0), fpassword(0), fdb(0), fprotocol(3), fauto_reconnect(0) {}
        !            79: 
        !            80:        /// called by the request pool at the end of the request
        !            81:        override ~VRedis() { release(); }
1.1       moko       82: 
                     83:        redisContext* fcontext;
1.2       moko       84:        struct redisSSLContext* fssl_context; // with $.tls only
1.1       moko       85:        const char* fuser;
                     86:        const char* fpassword;
                     87:        int fdb;
                     88:        int fprotocol;
                     89:        int fauto_reconnect; // 0 = disabled, N>0 = sleeps N sec before reconnecting
                     90: 
1.2       moko       91:        /// TLS, HELLO or AUTH, then SELECT: on open and on reconnect
1.1       moko       92:        void handshake() {
1.2       moko       93: #ifdef WITH_REDIS_SSL
                     94:                if(fssl_context && redisInitiateSSLWithContext(fcontext, fssl_context)!=REDIS_OK)
                     95:                        throw Exception("redis", 0, "%s", fcontext->errstr);
                     96: #endif
1.1       moko       97:                if(fprotocol==3) {
                     98:                        const char* argv[5]={"HELLO", "3"};
                     99:                        int argc=2;
                    100:                        if(fpassword) {
                    101:                                argv[argc++]="AUTH";
                    102:                                argv[argc++]=fuser ? fuser : "default";
                    103:                                argv[argc++]=fpassword;
                    104:                        }
                    105:                        void_command(argc, argv);
                    106:                } else if(fpassword) {
                    107:                        const char* argv[3]={"AUTH"};
                    108:                        int argc=1;
                    109:                        if(fuser)
                    110:                                argv[argc++]=fuser;
                    111:                        argv[argc++]=fpassword;
                    112:                        void_command(argc, argv);
                    113:                }
                    114:                if(fdb) {
                    115:                        const char* argv[2]={"SELECT", pa_itoa(fdb)};
                    116:                        void_command(2, argv);
                    117:                }
                    118:        }
                    119: 
1.2       moko      120:        /// frees the connection and its TLS context
                    121:        void release() {
                    122:                if(fcontext) {
                    123:                        redisFree(fcontext);
                    124:                        fcontext=0;
                    125:                }
                    126: #ifdef WITH_REDIS_SSL
                    127:                if(fssl_context) {
                    128:                        redisFreeSSLContext(fssl_context);
                    129:                        fssl_context=0;
                    130:                }
                    131: #endif
                    132:        }
                    133: 
1.1       moko      134:        /// sends the command and returns the reply, which the caller frees
                    135:        redisReply* command(int argc, const char** argv, const size_t* lengths=0) {
                    136:                redisReply* reply=(redisReply*)redisCommandArgv(context(), argc, argv, lengths);
                    137:                if(!reply)
                    138:                        throw Exception("redis", 0, "%s failed: %s", argv[0], fcontext->errstr);
                    139:                return reply;
                    140:        }
                    141: 
                    142:        /// a command whose reply is only checked for an error
                    143:        void void_command(int argc, const char** argv) {
                    144:                redisReply* reply=command(argc, argv);
                    145:                Temp_redis_reply temp_reply(reply);
                    146:                if(reply->type==REDIS_REPLY_ERROR)
                    147:                        throw Exception("redis", 0, "%s failed: %s", argv[0], reply->str);
                    148:        }
                    149: 
                    150: private:
                    151: 
                    152:        /// reconnects first if the connection is broken and auto_reconnect is enabled
                    153:        redisContext* context() {
                    154:                if(!fcontext)
                    155:                        throw Exception("redis", 0, "using unopened or released redis object");
                    156:                if(fcontext->err) {
                    157:                        if(fauto_reconnect <= 0)
                    158:                                throw Exception("redis", 0, "connection is broken (auto_reconnect is not enabled): %s", fcontext->errstr);
                    159:                        pa_sleep(fauto_reconnect, 0);
                    160:                        if(redisReconnect(fcontext) != REDIS_OK)
                    161:                                throw Exception("redis", 0, "reconnect failed: %s", fcontext->errstr);
                    162:                        handshake();
                    163:                }
                    164:                return fcontext;
                    165:        }
                    166: 
1.3     ! moko      167: #else
        !           168:        VRedis(Pool& apool): Pooled(apool) {}
1.1       moko      169: #endif // WITH_REDIS
                    170: };
                    171: 
                    172: #endif

E-mail: