@@ -507,9 +507,253 @@ bool EspHttpBinding::basicAuth(IEspContext* ctx)
507507 ctx->addTraceSummaryTimeStamp (LogMin, " basicAuth" );
508508 return authorized;
509509}
510-
510+
511+ #include < libmemcached/memcached.hpp>
512+ #include < libmemcached/util.h>
513+
514+ #define MEMCACHED_VERSION " memcached plugin 1.0.0"
515+
511516void EspHttpBinding::handleHttpPost (CHttpRequest *request, CHttpResponse *response)
512517{
518+ class ESPMemCached : public CInterface
519+ {
520+ memcached_st * connection = nullptr ;
521+ memcached_pool_st * pool = nullptr ;
522+ StringAttr options;
523+
524+ public :
525+ ESPMemCached (const char * _options)
526+ {
527+ #if (LIBMEMCACHED_VERSION_HEX < 0x01000010)
528+ VStringBuffer msg (" Memcached Plugin: libmemcached version '%s' incompatible with min version>=1.0.10" , LIBMEMCACHED_VERSION_STRING);
529+ ESPLOG (LogNormal, " %s" , msg.str ());
530+ #endif
531+
532+ options.set (_options);
533+ pool = memcached_pool (_options, strlen (_options));
534+ assertPool ();
535+
536+ setPoolSettings ();
537+ connect ();
538+ if (connection)
539+ {
540+ DBGLOG (" ESPMemCached connection<%d>" , memcached_server_count (connection));
541+ /* const char * msg = "'Set' request failed - ";
542+ DBGLOG("20");
543+ char key[1024];
544+ int length= snprintf(key, sizeof(key), "%s%u", "n", 1);
545+ memcached_set(connection, key, length,
546+ NULL, 0, // Zero length values
547+ time_t(0), uint32_t(0));
548+ DBGLOG("21");*/
549+ checkServersUp ();
550+ }
551+ }
552+
553+ ~ESPMemCached ()
554+ {
555+ if (pool)
556+ {
557+ memcached_pool_release (pool, connection);
558+ connection = nullptr ;// For safety (from changing this destructor) as not implicit in either the above or below.
559+ memcached_st *memc = memcached_pool_destroy (pool);
560+ if (memc)
561+ memcached_free (memc);
562+ }
563+ else if (connection)// This should never be needed but just in case.
564+ {
565+ memcached_free (connection);
566+ }
567+ };
568+
569+ void setPoolSettings ()
570+ {
571+ assertPool ();
572+ const char * msg = " memcached_pool_behavior_set failed - " ;
573+ assertOnError (memcached_pool_behavior_set (pool, MEMCACHED_BEHAVIOR_KETAMA, 1 ), msg);// NOTE: alias of MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA amongst others.
574+ memcached_pool_behavior_set (pool, MEMCACHED_BEHAVIOR_USE_UDP, 0 ); // Note that this fails on early versions of libmemcached, so ignore result
575+ assertOnError (memcached_pool_behavior_set (pool, MEMCACHED_BEHAVIOR_NO_BLOCK, 0 ), msg);
576+ assertOnError (memcached_pool_behavior_set (pool, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, 1000 ), msg);// units of ms.
577+ assertOnError (memcached_pool_behavior_set (pool, MEMCACHED_BEHAVIOR_SND_TIMEOUT, 1000000 ), msg);// units of mu-s.
578+ assertOnError (memcached_pool_behavior_set (pool, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, 1000000 ), msg);// units of mu-s.
579+ assertOnError (memcached_pool_behavior_set (pool, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0 ), msg);
580+ assertOnError (memcached_pool_behavior_set (pool, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1 ), " memcached_pool_behavior_set failed - " );
581+ }
582+
583+ void connect ()
584+ {
585+ assertPool ();
586+ if (connection)
587+ #if (LIBMEMCACHED_VERSION_HEX<0x53000)
588+ memcached_pool_push (pool, connection);
589+ #else
590+ memcached_pool_release (pool, connection);
591+ #endif
592+ memcached_return_t rc;
593+ #if (LIBMEMCACHED_VERSION_HEX<0x53000)
594+ connection = memcached_pool_pop (pool, (struct timespec *)0 , &rc);
595+ #else
596+ connection = memcached_pool_fetch (pool, (struct timespec *)0 , &rc);
597+ #endif
598+ assertOnError (rc, " memcached_pool_pop failed - " );
599+ }
600+
601+ // This call does not work here.
602+ void checkServersUp ()
603+ {
604+ memcached_return_t rc;
605+ char * args = NULL ;
606+ OwnedMalloc<memcached_stat_st> stats;
607+ DBGLOG (" Before memcached_stat" );
608+ stats.setown (memcached_stat (connection, args, &rc));
609+ DBGLOG (" After memcached_stat" );
610+
611+ unsigned int numberOfServers = memcached_server_count (connection);
612+ unsigned int numberOfServersDown = 0 ;
613+ for (unsigned i = 0 ; i < numberOfServers; ++i)
614+ {
615+ if (stats[i].pid == -1 )// perhaps not the best test?
616+ {
617+ numberOfServersDown++;
618+ VStringBuffer msg (" Memcached Plugin: Failed connecting to entry %u\n within the server list: %s" , i+1 , options.str ());
619+ DBGLOG (" %s" , msg.str ());
620+ }
621+ }
622+ if (numberOfServersDown == numberOfServers)
623+ ESPLOG (LogNormal," Memcached Plugin: Failed connecting to ALL servers. Check memcached on all servers and \" memcached -B ascii\" not used." );
624+
625+ // check memcached version homogeneity
626+ for (unsigned i = 0 ; i < numberOfServers-1 ; ++i)
627+ {
628+ if (!streq (stats[i].version , stats[i+1 ].version ))
629+ DBGLOG (" Memcached Plugin: Inhomogeneous versions of memcached across servers." );
630+ }
631+ };
632+
633+ void clear (unsigned when)
634+ {
635+ // NOTE: memcached_flush is the actual cache flush/clear/delete and not an io buffer flush.
636+ assertOnError (memcached_flush (connection, (time_t )(when)), " 'Clear' request failed - " );
637+ };
638+
639+ bool exists (const char * key, const char * partitionKey)
640+ {
641+ #if (LIBMEMCACHED_VERSION_HEX<0x53000)
642+ throw makeStringException (0 , " memcached_exist not supported in this version of libmemcached" );
643+ #else
644+ memcached_return_t rc;
645+ size_t partitionKeyLength = strlen (partitionKey);
646+ if (partitionKeyLength)
647+ rc = memcached_exist_by_key (connection, partitionKey, partitionKeyLength, key, strlen (key));
648+ else
649+ rc = memcached_exist (connection, key, strlen (key));
650+
651+ if (rc == MEMCACHED_NOTFOUND)
652+ return false ;
653+ else
654+ {
655+ assertOnError (rc, " 'Exists' request failed - " );
656+ return true ;
657+ }
658+ #endif
659+ };
660+
661+ char * get (const char * partitionKey, const char * key, size_t & returnLength)
662+ {
663+ uint32_t flag = 0 ;
664+ memcached_return_t rc;
665+
666+ OwnedMalloc<char > value;
667+ size_t partitionKeyLength = strlen (partitionKey);
668+ if (partitionKeyLength)
669+ value.setown (memcached_get_by_key (connection, partitionKey, partitionKeyLength, key, strlen (key), &returnLength, &flag, &rc));
670+ else
671+ value.setown (memcached_get (connection, key, strlen (key), &returnLength, &flag, &rc));
672+
673+ StringBuffer keyMsg = " 'Get<type>' request failed - " ;
674+ assertOnError (rc, appendIfKeyNotFoundMsg (rc, key, keyMsg));
675+
676+ // returnValue = reinterpret_cast<type*>(cpy(value, returnLength));
677+ return value;
678+ };
679+
680+ /* void * cpy(const char * src, size_t length)
681+ {
682+ void * value = rtlMalloc(length);
683+ return memcpy(value, src, length);
684+ };*/
685+
686+ void set (const char * partitionKey, const char * key, const char * value, unsigned __int64 expireSec)
687+ {
688+ size_t partitionKeyLength = strlen (partitionKey);
689+ const char * msg = " 'Set' request failed - " ;
690+ DBGLOG (" 1" );
691+ if (connection)
692+ DBGLOG (" 2" );
693+ if (partitionKeyLength)
694+ assertOnError (memcached_set_by_key (connection, partitionKey, partitionKeyLength, key, strlen (key), value, strlen (value), (time_t )expireSec, 0 ), msg);
695+ else
696+ assertOnError (memcached_set (connection, key, strlen (key), value, strlen (value), (time_t )expireSec, 0 ), msg);
697+ DBGLOG (" 3" );
698+ };
699+
700+ void deleteKey (const char * key, const char * partitionKey)
701+ {
702+ memcached_return_t rc;
703+ size_t partitionKeyLength = strlen (partitionKey);
704+ if (partitionKeyLength)
705+ rc = memcached_delete_by_key (connection, partitionKey, partitionKeyLength, key, strlen (key), (time_t )0 );
706+ else
707+ rc = memcached_delete (connection, key, strlen (key), (time_t )0 );
708+ assertOnError (rc, " 'Delete' request failed - " );
709+ };
710+
711+ void assertOnError (memcached_return_t rc, const char * _msg)
712+ {
713+ DBGLOG (" assertOnError:1" );
714+ if (rc != MEMCACHED_SUCCESS)
715+ {
716+ VStringBuffer msg (" Memcached Plugin: %s%s" , _msg, memcached_strerror (connection, rc));
717+ ESPLOG (LogNormal, " %s" , msg.str ());
718+ }
719+ DBGLOG (" assertOnError:2" );
720+ };
721+
722+ const char * appendIfKeyNotFoundMsg (memcached_return_t rc, const char * key, StringBuffer & target) const
723+ {
724+ if (rc == MEMCACHED_NOTFOUND)
725+ target.append (" (key: '" ).append (key).append (" ') " );
726+ return target.str ();
727+ };
728+
729+ void assertPool ()
730+ {
731+ if (!pool)
732+ {
733+ StringBuffer msg = " Memcached Plugin: Failed to instantiate server pool with:" ;
734+ msg.newline ().append (options);
735+ ESPLOG (LogNormal, " %s" , msg.str ());
736+ }
737+ }
738+ };
739+ StringBuffer testOpt;
740+ // testOpt.set("--SERVER=10.176.152.33 --POOL-MIN=1 --POOL-MAX=32");
741+ // testOpt.set("--SERVER=host10.example.com");
742+ testOpt.set (" --SERVER=10.176.152.33" );
743+ // testOpt.set("-POOL-MIN=10");
744+ ESPMemCached testCache (testOpt.str ());
745+ DBGLOG (" Before set" );
746+ testCache.set (" ESPResponse" , " AKey" , " AValue" , 300 );
747+ size_t returnLength1, returnLength2;
748+ char * retV1 = testCache.get (" ESPResponse" , " AKey" , returnLength1);
749+ if (retV1 && *retV1 && (returnLength1 > 0 ))
750+ DBGLOG (" retV1<%s>" , retV1);
751+ testCache.clear (0 );
752+ char * retV2 = testCache.get (" ESPResponse" , " AKey" , returnLength2);
753+ if (retV2 && *retV2 && (returnLength2 > 0 ))
754+ DBGLOG (" retV2<%s>" , retV2);
755+ DBGLOG (" Done" );
756+
513757 if (request->isSoapMessage ())
514758 {
515759 request->queryParameters ()->setProp (" __wsdl_address" , m_wsdlAddress.str ());
0 commit comments