void addReply(client *c, const char *fmt, ...) { va_list ap; va_start(ap,fmt); sds s = sdscatvprintf(sdsempty(),fmt,ap); va_end(ap); addReplyString(c,s,sdslen(s)); addReplyString(c,"\r\n",2); sdsfree(s); }
/* Add a long long as integer reply or bulk len / multi bulk count. * Basically this is used to output <prefix><long long><crlf>. */ void addReplyLongLongWithPrefix(client *c, long long ll, char prefix) { char buf[128]; int len; buf[0] = prefix; len = ll2string(buf+1,sizeof(buf)-1,ll); buf[len+1] = '\r'; buf[len+2] = '\n'; addReplyString(c,buf,len+3); }
void mgetCommand(client *c) { counter *cntr; int j; addReplyMultiBulkLen(c,c->argc-1); for (j = 1; j < c->argc; j++) { cntr = counterLookup(c->argv[j]->ptr); if (cntr == NULL) { addReplyString(c,OBJ_SHARED_0STR,sizeof(OBJ_SHARED_0STR)-1); } else { /* Do we need to recalculate the cached response? */ if (cntr->rlen == 0) { counterCacheResponse(cntr); } addReplyString(c,cntr->rbuf,cntr->rlen); } } }
/* Add a double as a bulk reply */ void addReplyDouble(client *c, double d) { char dbuf[128], sbuf[128]; int dlen, slen; if (isinf(d)) { /* Libc in odd systems (Hi Solaris!) will format infinite in a * different way, so better to handle it in an explicit way. */ addReplyBulkCString(c, d > 0 ? "inf" : "-inf"); } else { dlen = snprintf(dbuf,sizeof(dbuf),"%.17g",d); slen = snprintf(sbuf,sizeof(sbuf),"$%d\r\n%s\r\n",dlen,dbuf); addReplyString(c,sbuf,slen); } }
void genericIncrCommand(client *c, long double increment) { counter *cntr; cntr = counterLookup(c->argv[1]->ptr); if (cntr == NULL) { cntr = counterCreate(c->argv[1]->ptr); } if (cntr->myshard == NULL) { counterAddShard(cntr, myself, myself->name); } cntr->myshard->value += increment; cntr->value += increment; server.dirty++; counterCacheResponse(cntr); addReplyString(c,cntr->rbuf,cntr->rlen); }
void getCommand(client *c) { counter *cntr; cntr = counterLookup(c->argv[1]->ptr); if (cntr == NULL) { if (c->argc == 2) { addReplyString(c,OBJ_SHARED_0STR,sizeof(OBJ_SHARED_0STR)-1); } else if (!strcasecmp(c->argv[2]->ptr,"state")) { addReplyMultiBulkLen(c,2); addReplyString(c,OBJ_SHARED_0STR,sizeof(OBJ_SHARED_0STR)-1); if (server.cluster->failing_nodes_count > 0) { addReplyString(c, OBJ_SHARED_INCONSISTENT, sizeof(OBJ_SHARED_INCONSISTENT)-1); } else { addReplyString(c, OBJ_SHARED_CONSISTENT, sizeof(OBJ_SHARED_CONSISTENT)-1); } } return; } /* Do we need to recalculate the cached response? */ if (cntr->rlen == 0) { counterCacheResponse(cntr); } if (c->argc == 2) { addReplyString(c,cntr->rbuf,cntr->rlen); } else if (!strcasecmp(c->argv[2]->ptr,"state")) { addReplyMultiBulkLen(c,2); addReplyString(c,cntr->rbuf,cntr->rlen); if (server.cluster->failing_nodes_count > 0) { addReplyString(c, OBJ_SHARED_INCONSISTENT, sizeof(OBJ_SHARED_INCONSISTENT)-1); } else { addReplyString(c, OBJ_SHARED_CONSISTENT, sizeof(OBJ_SHARED_CONSISTENT)-1); } } else { addReplyErrorFormat(c, "Unknown GET option '%s'", (char*)c->argv[2]->ptr); } }
void setCommand(client *c) { counter *cntr; long double value; unsigned int i; if (getLongDoubleFromObjectOrReply(c,c->argv[2],&value,NULL) != C_OK) return; cntr = counterLookup(c->argv[1]->ptr); if (cntr == NULL) { cntr = counterCreate(c->argv[1]->ptr); } if (cntr->myshard == NULL) { counterAddShard(cntr,myself,myself->name); } /* myshard->value = 4 * cntr->value = 10 * value = 2 * value in other shards = 6 * new myshard->value = 2 - (10 - 4) = -4 */ cntr->myshard->value = value - (cntr->value - cntr->myshard->value); /* Force a new prediction to be send. */ cntr->myshard->predict_time = 0; /* Make sure the prediction is 0 so it doesn't change every second. */ for (i = 0; i < server.history_size; i++) { cntr->history[i] = cntr->myshard->value; } cntr->value = value; server.dirty++; counterCacheResponse(cntr); addReplyString(c,cntr->rbuf,cntr->rlen); }
/* Add a C buffer as bulk reply */ void addReplyBulkCBuffer(client *c, const void *p, size_t len) { addReplyLongLongWithPrefix(c,len,'$'); addReplyString(c,p,len); addReplyString(c,"\r\n",2); }
void addReplyStatusLength(client *c, const char *s, size_t len) { addReplyString(c,"+",1); addReplyString(c,s,len); addReplyString(c,"\r\n",2); }
void addReplyErrorLength(client *c, const char *s, size_t len) { addReplyString(c,"-ERR ",5); addReplyString(c,s,len); addReplyString(c,"\r\n",2); }
static bool sendRestAPIReply(cli *c, sds file) { //printf("sendRestAPIReply\n"); int argc; bool ret = 0; sds pb = c->http.post_body; //TODO cat [file,pb] is too much copying sds url = pb ? sdscatprintf(sdsempty(), "%s%s", file, pb) : sdsdup(file); //FREE 156 sds *argv = sdssplitlen(url, sdslen(url), "/", 1, &argc); //FREE 157 rcommand *cmd = lookupCommand(argv[0]); if (!cmd) goto send_rest_end; ret = 1; //printf("sendRestAPIReply: found cmd: %s\n", cmd->name); if ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity)) { addReplyErrorFormat(c,"wrong number of arguments for '%s' command", cmd->name); goto send_rest_end; } //NOTE: rest is copy of redis.c processCommand() if (server.maxmemory) freeMemoryIfNeeded(); if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) && zmalloc_used_memory() > server.maxmemory) { addReplyError(c, "command not allowed when used memory > 'maxmemory'"); goto send_rest_end; } if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0) && cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand && cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) { addReplyError(c, "only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context"); goto send_rest_end; } if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED && server.repl_serve_stale_data == 0 && cmd->proc != infoCommand && cmd->proc != slaveofCommand) { addReplyError(c, "link with MASTER is down and slave-serve-stale-data is set to no"); goto send_rest_end; } if (server.loading && cmd->proc != infoCommand) { addReply(c, shared.loadingerr); goto send_rest_end; } if (c->flags & REDIS_MULTI && cmd->proc != execCommand && cmd->proc != discardCommand && cmd->proc != multiCommand && cmd->proc != watchCommand) { queueMultiCommand(c, cmd); addReply(c, shared.queued); goto send_rest_end; } listNode *ln; listIter *li; cli *restc = server.alc.RestClient; // 1.) call() cmd in RestClient { // REST CLIENT call robj **rargv = zmalloc(sizeof(robj *) * argc); for (int i = 0; i < argc; i++) { rargv[i] = createStringObject(argv[i], sdslen(argv[i])); } restc->argc = argc; restc->argv = rargv; call(restc, cmd); for (int i = 0; i < argc; i++) decrRefCount(rargv[i]); zfree(rargv); } // 2.) calculate Content-Length from RestClient's response ulong brlen = restc->bufpos; ulong trlen = brlen; if (restc->reply->len) { li = listGetIterator(restc->reply, AL_START_HEAD); while((ln = listNext(li))) { robj *r = ln->value; trlen += sdslen(r->ptr); } listReleaseIterator(li); } bool err = brlen && (*restc->buf == '-'); //TODO check for "+OK" and return 201 w/ no body // 3.) create header w/ Content-Length sds s = err ? send_http404_reponse_header(c, trlen) : send_http200_reponse_header(c, trlen); robj *ho = createObject(REDIS_STRING, s); addReply(c, ho); decrRefCount(ho); // 4.) tack on RestClient's response as HTTP Body if (brlen) { addReplyString(c, restc->buf, brlen); } if (restc->reply->len) { li = listGetIterator(restc->reply, AL_START_HEAD); while((ln = listNext(li))) { robj *r = ln->value; addReply(c, r); } listReleaseIterator(li); } // 5.) reset RestClient restc->bufpos = 0; while (restc->reply->len) { listDelNode(restc->reply, listFirst(restc->reply)); } send_rest_end: sdsfree(url); // FREE 156 for (int i = 0; i < argc; i++) sdsfree(argv[i]); zfree(argv); // FREE 157 return ret; }