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: