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: