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: