From 0b3eec66f790c0e8c8e343e8d6e537a3c786c42f Mon Sep 17 00:00:00 2001 From: zhangchengwei Date: Tue, 23 Oct 2018 20:26:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81Head=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=85=83=E4=BF=A1=E6=81=AF=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E4=BB=8Eredis=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=85=83=E4=BF=A1=E6=81=AF=EF=BC=9B=E8=B0=83=E6=95=B4=E5=86=85?= =?UTF-8?q?=E9=83=A8=E8=B6=85=E6=97=B6=E6=A3=80=E6=9F=A5=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cache/include/cache_evbase_client.h | 1 + cache/include/tango_cache_client.h | 5 +- cache/src/README.txt | 2 + cache/src/cJSON/cJSON.c | 596 ++++++++++++++++++++++++++++ cache/src/cJSON/cJSON.h | 143 +++++++ cache/src/cache_evbase_client.cpp | 46 ++- cache/src/tango_cache_client.cpp | 58 ++- cache/src/tango_cache_client_in.h | 13 +- cache/src/tango_cache_redis.cpp | 243 ++++++++++++ cache/src/tango_cache_redis.h | 13 + cache/src/tango_cache_transfer.cpp | 85 ++-- cache/src/tango_cache_transfer.h | 3 + cache/test/cache_evbase_test.cpp | 49 ++- cache/test/tango_cache_test.c | 49 ++- vendor/hiredis-master.zip | Bin 0 -> 93224 bytes 15 files changed, 1244 insertions(+), 62 deletions(-) create mode 100644 cache/src/README.txt create mode 100644 cache/src/cJSON/cJSON.c create mode 100644 cache/src/cJSON/cJSON.h create mode 100644 cache/src/tango_cache_redis.cpp create mode 100644 cache/src/tango_cache_redis.h create mode 100644 vendor/hiredis-master.zip diff --git a/cache/include/cache_evbase_client.h b/cache/include/cache_evbase_client.h index 15bf3c3..529a805 100644 --- a/cache/include/cache_evbase_client.h +++ b/cache/include/cache_evbase_client.h @@ -34,6 +34,7 @@ struct cache_evbase_instance *cache_evbase_instance_new(const char* profile_path //GET接口,成功返回0,失败返回-1,future回调函数会在另外的线程中执行,下同 int cache_evbase_fetch_object(struct cache_evbase_instance *instance, struct future* f, struct tango_cache_meta_get *meta); +int cache_evbase_head_object(struct cache_evbase_instance *instance, struct future* f, struct tango_cache_meta_get *meta); struct tango_cache_result *cache_evbase_read_result(void *promise_result); //DELETE接口 diff --git a/cache/include/tango_cache_client.h b/cache/include/tango_cache_client.h index 109c258..1e45187 100644 --- a/cache/include/tango_cache_client.h +++ b/cache/include/tango_cache_client.h @@ -19,6 +19,8 @@ enum CACHE_ERR_CODE CACHE_ERR_WIREDLB, CACHE_ERR_SOCKPAIR, CACHE_ERR_INTERNAL, + CACHE_ERR_REDIS_JSON, + CACHE_ERR_REDIS_CONNECT, }; enum PUT_MEMORY_COPY_WAY @@ -79,7 +81,7 @@ enum CACHE_HTTP_HDR_TYPE struct tango_cache_meta_get { - const char* url; //缓存:URL;非结构化日志:文件名;CACHE_OBJECT_KEY_HASH_SWITCH=0时最大长度256字节,=1时无限制 + const char* url; //缓存:URI;非结构化日志:文件路径名。不要以'/'开头;CACHE_OBJECT_KEY_HASH_SWITCH=0时最大长度256字节,=1时无限制 struct request_freshness get; }; @@ -112,6 +114,7 @@ struct tango_cache_instance *tango_cache_instance_new(struct event_base* evbase, //失败时回调promise_failed(仅一次),使用get_last_error获取错误码; //future不可以为NULL int tango_cache_fetch_object(struct tango_cache_instance *instance, struct future* f, struct tango_cache_meta_get *meta); +int tango_cache_head_object(struct tango_cache_instance *instance, struct future* f, struct tango_cache_meta_get *meta); //从promise_success的result参数提取结果 struct tango_cache_result *tango_cache_read_result(future_result_t *promise_result); diff --git a/cache/src/README.txt b/cache/src/README.txt new file mode 100644 index 0000000..cdd34c9 --- /dev/null +++ b/cache/src/README.txt @@ -0,0 +1,2 @@ +HEAD操作支持从minio和redis两种渠道获取,默认情况下从minio获取。 +若想从redis获取,编译时加宏定义-DHEAD_OBJECT_FROM_REDIS \ No newline at end of file diff --git a/cache/src/cJSON/cJSON.c b/cache/src/cJSON/cJSON.c new file mode 100644 index 0000000..35452cb --- /dev/null +++ b/cache/src/cJSON/cJSON.c @@ -0,0 +1,596 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static const char *ep; + +const char *cJSON_GetErrorPtr(void) {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +static unsigned parse_hex4(const char *str) +{ + unsigned h=0; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + return h; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + + if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ + uc2=parse_hex4(ptr+3);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ + uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0;unsigned char token; + + if (!str) return cJSON_strdup(""); + ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + + out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +{ + const char *end=0; + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + end=parse_value(c,skip(value)); + if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} + if (return_parse_end) *return_parse_end=end; + return c; +} +/* Default options for cJSON_Parse */ +cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + out=(char*)cJSON_malloc(3); + if (out) strcpy(out,"[]"); + return out; + } + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + out=(char*)cJSON_malloc(fmt?depth+4:3); + if (!out) return 0; + ptr=out;*ptr++='{'; + if (fmt) {*ptr++='\n';for (i=0;ichild;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} + +/* Duplication */ +cJSON *cJSON_Duplicate(cJSON *item,int recurse) +{ + cJSON *newitem,*cptr,*nptr=0,*newchild; + /* Bail on bad ptr */ + if (!item) return 0; + /* Create new item */ + newitem=cJSON_New_Item(); + if (!newitem) return 0; + /* Copy over all vars */ + newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; + if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} + if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} + /* If non-recursive, then we're done! */ + if (!recurse) return newitem; + /* Walk the ->next chain for the child. */ + cptr=item->child; + while (cptr) + { + newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) {cJSON_Delete(newitem);return 0;} + if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ + cptr=cptr->next; + } + return newitem; +} + +void cJSON_Minify(char *json) +{ + char *into=json; + while (*json) + { + if (*json==' ') json++; + else if (*json=='\t') json++; // Whitespace characters. + else if (*json=='\r') json++; + else if (*json=='\n') json++; + else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line. + else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments. + else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive. + else *into++=*json++; // All other characters. + } + *into=0; // and null-terminate. +} diff --git a/cache/src/cJSON/cJSON.h b/cache/src/cJSON/cJSON.h new file mode 100644 index 0000000..867b7c3 --- /dev/null +++ b/cache/src/cJSON/cJSON.h @@ -0,0 +1,143 @@ +/* + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(void); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); + +extern void cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cache/src/cache_evbase_client.cpp b/cache/src/cache_evbase_client.cpp index 0f9059b..6cd4ea6 100644 --- a/cache/src/cache_evbase_client.cpp +++ b/cache/src/cache_evbase_client.cpp @@ -17,6 +17,9 @@ #include "cache_evbase_client.h" #include "tango_cache_transfer.h" #include "tango_cache_tools.h" +#ifdef HEAD_OBJECT_FROM_REDIS +#include "tango_cache_redis.h" +#endif enum CACHE_ASYN_CMD { @@ -28,6 +31,7 @@ enum CACHE_ASYN_CMD CACHE_ASYN_UPLOAD_FRAG_EVBUF, CACHE_ASYN_UPLOAD_END, CACHE_ASYN_DELETE, + CACHE_ASYN_HEAD, }; struct databuffer @@ -158,7 +162,15 @@ static void cache_asyn_ioevent_dispatch(struct databuffer *buffer) tango_cache_fetch_start(ctx_asyn->ctx); cache_asyn_ctx_destroy(ctx_asyn); break; - + case CACHE_ASYN_HEAD: +#ifdef HEAD_OBJECT_FROM_REDIS + tango_cache_head_redis(ctx_asyn->ctx); +#else + tango_cache_fetch_start(ctx_asyn->ctx); +#endif + cache_asyn_ctx_destroy(ctx_asyn); + break; + case CACHE_ASYN_DELETE: cache_delete_minio_object(ctx_asyn->ctx); cache_asyn_ctx_destroy(ctx_asyn); @@ -450,7 +462,7 @@ int cache_evbase_fetch_object(struct cache_evbase_instance *instance, struct fut ctx_asyn = (struct cache_evbase_ctx *)calloc(1, sizeof(struct cache_evbase_ctx)); ctx_asyn->instance_asyn = instance; - ctx_asyn->ctx = tango_cache_fetch_prepare(instance->instance, f, meta); + ctx_asyn->ctx = tango_cache_fetch_prepare(instance->instance, CACHE_REQUEST_GET, f, meta); if(ctx_asyn->ctx == NULL) { free(ctx_asyn); @@ -473,6 +485,36 @@ int cache_evbase_fetch_object(struct cache_evbase_instance *instance, struct fut return 0; } +int cache_evbase_head_object(struct cache_evbase_instance *instance, struct future* f, struct tango_cache_meta_get *meta) +{ + struct cache_evbase_ctx *ctx_asyn; + struct databuffer *buffer; + + ctx_asyn = (struct cache_evbase_ctx *)calloc(1, sizeof(struct cache_evbase_ctx)); + ctx_asyn->instance_asyn = instance; + ctx_asyn->ctx = tango_cache_fetch_prepare(instance->instance, CACHE_REQUEST_HEAD, f, meta); + if(ctx_asyn->ctx == NULL) + { + free(ctx_asyn); + return -1; + } + + buffer = (struct databuffer *)malloc(sizeof(struct databuffer)); + buffer->ctx_asyn = ctx_asyn; + buffer->cmd_type = CACHE_ASYN_HEAD; + + if(iothread_notify_event(instance->notify_sendfd, &buffer, sizeof(void *), 2) != sizeof(void *)) + { + tango_cache_set_fail_state(ctx_asyn->ctx, CACHE_ERR_SOCKPAIR); + promise_failed(future_to_promise(f), FUTURE_ERROR_CANCEL, tango_cache_get_errstring(ctx_asyn->ctx)); + tango_cache_ctx_destroy(ctx_asyn->ctx); + cache_asyn_ctx_destroy(ctx_asyn); + free(buffer); + return -2; + } + return 0; +} + int cache_evbase_delete_object(struct cache_evbase_instance *instance, struct future* f, const char *objkey) { struct cache_evbase_ctx *ctx_asyn; diff --git a/cache/src/tango_cache_client.cpp b/cache/src/tango_cache_client.cpp index baf58dc..1575437 100644 --- a/cache/src/tango_cache_client.cpp +++ b/cache/src/tango_cache_client.cpp @@ -17,6 +17,9 @@ #include "tango_cache_transfer.h" #include "tango_cache_tools.h" #include "tango_cache_xml.h" +#ifdef HEAD_OBJECT_FROM_REDIS +#include "tango_cache_redis.h" +#endif int TANGO_CACHE_VERSION_20181009=0; @@ -91,6 +94,8 @@ const char *tango_cache_get_errstring(const struct tango_cache_ctx *ctx) case CACHE_ERR_WIREDLB: return "wiredlb error"; case CACHE_ERR_SOCKPAIR:return "socketpair error"; case CACHE_ERR_INTERNAL:return "internal error"; + case CACHE_ERR_REDIS_JSON:return "parse redis json error"; + case CACHE_ERR_REDIS_CONNECT:return "redis is not connected"; default: return ctx->error; } } @@ -137,6 +142,7 @@ static void update_statistics(struct tango_cache_ctx *ctx, struct cache_statisti } break; case CACHE_REQUEST_GET: + case CACHE_REQUEST_HEAD: if(ctx->fail_state) { if(ctx->error_code == CACHE_CACHE_MISS || ctx->error_code == CACHE_TIMEOUT) @@ -204,6 +210,7 @@ void tango_cache_ctx_destroy(struct tango_cache_ctx *ctx) switch(ctx->method) { case CACHE_REQUEST_GET: + case CACHE_REQUEST_HEAD: easy_string_destroy(&ctx->get.response_tag); break; @@ -431,7 +438,7 @@ int tango_cache_upload_once_evbuf(struct tango_cache_instance *instance, struct return tango_cache_upload_once_start_evbuf(ctx, way, evbuf); } -struct tango_cache_ctx *tango_cache_fetch_prepare(struct tango_cache_instance *instance, struct future* f, struct tango_cache_meta_get *meta) +struct tango_cache_ctx *tango_cache_fetch_prepare(struct tango_cache_instance *instance, enum CACHE_REQUEST_METHOD method, struct future* f, struct tango_cache_meta_get *meta) { struct tango_cache_ctx *ctx; char sha256[72]; @@ -439,7 +446,7 @@ struct tango_cache_ctx *tango_cache_fetch_prepare(struct tango_cache_instance *i ctx = (struct tango_cache_ctx *)calloc(1, sizeof(struct tango_cache_ctx)); ctx->instance = instance; ctx->future = f; - ctx->method = CACHE_REQUEST_GET; + ctx->method = method; ctx->get.state = GET_STATE_START; ctx->get.max_age = meta->get.max_age; ctx->get.min_fresh = meta->get.min_fresh; @@ -467,7 +474,7 @@ int tango_cache_fetch_object(struct tango_cache_instance *instance, struct futur { struct tango_cache_ctx *ctx; - ctx = tango_cache_fetch_prepare(instance, f, meta); + ctx = tango_cache_fetch_prepare(instance, CACHE_REQUEST_GET, f, meta); if(ctx == NULL) { return -1; @@ -475,6 +482,22 @@ int tango_cache_fetch_object(struct tango_cache_instance *instance, struct futur return tango_cache_fetch_start(ctx); } +int tango_cache_head_object(struct tango_cache_instance *instance, struct future* f, struct tango_cache_meta_get *meta) +{ + struct tango_cache_ctx *ctx; + + ctx = tango_cache_fetch_prepare(instance, CACHE_REQUEST_HEAD, f, meta); + if(ctx == NULL) + { + return -1; + } +#ifdef HEAD_OBJECT_FROM_REDIS + return tango_cache_head_redis(ctx); +#else + return tango_cache_fetch_start(ctx); +#endif +} + struct tango_cache_ctx *tango_cache_delete_prepare(struct tango_cache_instance *instance, struct future* f, const char *objkey) { struct tango_cache_ctx *ctx; @@ -585,6 +608,7 @@ static void check_multi_info(CURLM *multi) switch(ctx->method) { case CACHE_REQUEST_GET: + case CACHE_REQUEST_HEAD: tango_cache_curl_get_done(ctx, res, res_code); break; case CACHE_REQUEST_PUT: @@ -732,7 +756,20 @@ static int load_local_configure(struct tango_cache_instance *instance, const cha MESA_load_profile_uint_def(profile_path, section, "MAX_USED_MEMORY_SIZE_MB", &intval, 5120); longval = intval; instance->cache_limit_size = longval * 1024 * 1024; - MESA_load_profile_string_def(profile_path, section, "CACHE_BUCKET_NAME", instance->bucketname, 256, "openbucket"); + if(MESA_load_profile_string_nodef(profile_path, section, "CACHE_BUCKET_NAME", instance->bucketname, 256) < 0) + { + MESA_HANDLE_RUNTIME_LOGV2(instance->runtime_log, RLOG_LV_FATAL, "Load config %s [%s] CACHE_BUCKET_NAME not found.\n", profile_path, section); + return -1; + } +#ifdef HEAD_OBJECT_FROM_REDIS + MESA_load_profile_string_def(profile_path, section, "CACHE_REDIS_KEY", instance->redis_key, 256, instance->bucketname); + if(MESA_load_profile_string_nodef(profile_path, section, "CACHE_REDIS_IP", instance->redis_ip, 256) < 0) + { + MESA_HANDLE_RUNTIME_LOGV2(instance->runtime_log, RLOG_LV_FATAL, "Load config %s [%s] CACHE_REDIS_IP not found.\n", profile_path, section); + return -1; + } + MESA_load_profile_int_def(profile_path, section, "CACHE_REDIS_PORT", &instance->redis_port, 6379); +#endif MESA_load_profile_uint_def(profile_path, section, "CACHE_OBJECT_KEY_HASH_SWITCH", &instance->hash_object_key, 1); MESA_load_profile_uint_def(profile_path, section, "MINIO_LISTEN_PORT", &instance->minio_port, 9000); if(MESA_load_profile_string_nodef(profile_path, section, "MINIO_IP_LIST", instance->minio_iplist, 4096) < 0) @@ -792,8 +829,19 @@ struct tango_cache_instance *tango_cache_instance_new(struct event_base* evbase, curl_multi_setopt(instance->multi_hd, CURLMOPT_TIMERFUNCTION, curl_timer_function_cb); curl_multi_setopt(instance->multi_hd, CURLMOPT_TIMERDATA, instance); +#ifdef HEAD_OBJECT_FROM_REDIS + if(redis_asyn_connect_init(instance, instance->redis_ip, instance->redis_port)) + { + MESA_HANDLE_RUNTIME_LOGV2(instance->runtime_log, RLOG_LV_FATAL, "redis_asyn_connect_init %s:%u failed.", instance->redis_ip, instance->redis_port); + free(instance); + return NULL; + } + else + { + MESA_HANDLE_RUNTIME_LOGV2(instance->runtime_log, RLOG_LV_FATAL, "redis_asyn_connect_init %s:%u success.", instance->redis_ip, instance->redis_port); + } +#endif evtimer_assign(&instance->timer_event, evbase, libevent_timer_event_cb, instance); - return instance; } diff --git a/cache/src/tango_cache_client_in.h b/cache/src/tango_cache_client_in.h index 9dfb97e..aa67ec8 100644 --- a/cache/src/tango_cache_client_in.h +++ b/cache/src/tango_cache_client_in.h @@ -6,6 +6,9 @@ #include #include +#ifdef HEAD_OBJECT_FROM_REDIS +#include +#endif #include #include "tango_cache_client.h" @@ -20,6 +23,7 @@ enum CACHE_REQUEST_METHOD CACHE_REQUEST_PUT, CACHE_REQUEST_DELETE, CACHE_REQUEST_DELETE_MUL, + CACHE_REQUEST_HEAD, }; enum GET_OBJECT_STATE @@ -67,6 +71,13 @@ struct tango_cache_instance long max_cnn_host; u_int32_t upload_block_size; //minio分段上传块的最小长度 enum CACHE_ERR_CODE error_code; +#ifdef HEAD_OBJECT_FROM_REDIS + redisAsyncContext *redis_ac; + char redis_key[256]; + char redis_ip[128]; + int redis_port; + int redis_connecting; +#endif }; struct multipart_etag_list @@ -146,7 +157,7 @@ void tango_cache_set_fail_state(struct tango_cache_ctx *ctx, enum CACHE_ERR_CODE const char *tango_cache_get_errstring(const struct tango_cache_ctx *ctx); struct tango_cache_ctx *tango_cache_update_prepare(struct tango_cache_instance *instance, struct future* f, struct tango_cache_meta_put *meta); -struct tango_cache_ctx *tango_cache_fetch_prepare(struct tango_cache_instance *instance, struct future* f, struct tango_cache_meta_get *meta); +struct tango_cache_ctx *tango_cache_fetch_prepare(struct tango_cache_instance *instance, enum CACHE_REQUEST_METHOD method, struct future* f, struct tango_cache_meta_get *meta); struct tango_cache_ctx *tango_cache_delete_prepare(struct tango_cache_instance *instance, struct future* f, const char *objkey); #endif diff --git a/cache/src/tango_cache_redis.cpp b/cache/src/tango_cache_redis.cpp new file mode 100644 index 0000000..dfc5e3b --- /dev/null +++ b/cache/src/tango_cache_redis.cpp @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tango_cache_transfer.h" +#include "tango_cache_tools.h" +#include "tango_cache_redis.h" +#include "cJSON.h" + +#define PARSE_JSON_RET_ERROR -1 +#define PARSE_JSON_RET_TIMEOUT 0 +#define PARSE_JSON_RET_SUCC 1 + +#define CACHE_REDIS_CONNECT_IDLE 0 +#define CACHE_REDIS_CONNECTING 1 +#define CACHE_REDIS_CONNECTED 2 +#define CACHE_REDIS_DISCONNECTED 3 + +struct http_hdr_name +{ + const char *json_name; + const char *http_name; +}; +struct http_hdr_name g_http_hdr_name[HDR_CONTENT_NUM]= +{ + {"content-type", "Content-Type: "}, + {"content-encoding", "Content-Encoding: "}, + {"content-disposition", "Content-Disposition: "}, + {"content-md5", "Content-MD5: "} +}; + +void redis_asyn_disconnect_cb(const struct redisAsyncContext *ac, int status) +{ + struct tango_cache_instance *instance = (struct tango_cache_instance *)redisAsyncGetConnectionData(ac); + + if(status == REDIS_OK) + { + MESA_HANDLE_RUNTIME_LOGV2(instance->runtime_log, RLOG_LV_INFO, "Redis disconnect %s:%u success.", instance->redis_ip, instance->redis_port); + } + else + { + MESA_HANDLE_RUNTIME_LOGV2(instance->runtime_log, RLOG_LV_INFO, "Redis disconnect %s:%u failed: %s.", instance->redis_ip, instance->redis_port, ac->errstr); + } + instance->redis_connecting = CACHE_REDIS_DISCONNECTED; +} + +void redis_asyn_connect_cb(const struct redisAsyncContext *ac, int status) +{ + struct tango_cache_instance *instance = (struct tango_cache_instance *)redisAsyncGetConnectionData(ac); + + if(status == REDIS_OK) + { + MESA_HANDLE_RUNTIME_LOGV2(instance->runtime_log, RLOG_LV_INFO, "Redis connect %s:%u success.", instance->redis_ip, instance->redis_port); + instance->redis_connecting = CACHE_REDIS_CONNECTED; + } + else + { + MESA_HANDLE_RUNTIME_LOGV2(instance->runtime_log, RLOG_LV_INFO, "Redis connect %s:%u failed.", instance->redis_ip, instance->redis_port, ac->errstr); + instance->redis_connecting = CACHE_REDIS_CONNECT_IDLE; + } +} + +int redis_asyn_connect_init(struct tango_cache_instance *instance, const char *redisip, int redis_port) +{ + instance->redis_ac = redisAsyncConnect(redisip, redis_port); + if(instance->redis_ac == NULL) + { + return -1; + } + instance->redis_connecting = CACHE_REDIS_CONNECTING; + redisLibeventAttach(instance->redis_ac, instance->evbase); + redisAsyncSetConnectionData(instance->redis_ac, instance); + redisAsyncSetConnectCallback(instance->redis_ac, redis_asyn_connect_cb); + redisAsyncSetDisconnectCallback(instance->redis_ac, redis_asyn_disconnect_cb); + return 0; +} + +int parse_minio_events_json(struct tango_cache_ctx *ctx, const char *jcontent) +{ + cJSON *root, *pobject = NULL, *ptarget, *plastMod, *pexpires; + int ret = PARSE_JSON_RET_ERROR; + char usertag[2048]; + size_t datalen; + + //Records[0]->s3->object->key...userMetaData->metas... + if(NULL == (root=cJSON_Parse(jcontent))) + { + goto out_json; + } + if(NULL==(pobject=cJSON_GetObjectItem(root, "Records")) || pobject->type!=cJSON_Array) + { + goto out_json; + } + if(NULL == (pobject=cJSON_GetArrayItem(pobject, 0))) //第一个数组元素,一般只有一个 + { + goto out_json; + } + if(NULL == (pobject=cJSON_GetObjectItem(pobject, "s3")) || pobject->type!=cJSON_Object) + { + goto out_json; + } + if(NULL == (pobject=cJSON_GetObjectItem(pobject, "object")) || pobject->type!=cJSON_Object) + { + goto out_json; + } + //获取结果 + if(NULL == (ptarget=cJSON_GetObjectItem(pobject, "size")) || ptarget->type!=cJSON_Number) + { + goto out_json; + } + ctx->get.result.tlength = ptarget->valueint; //TODO: 若超过4GB怎么办? + if(NULL == (ptarget=cJSON_GetObjectItem(pobject, "userMetadata")) || ptarget->type!=cJSON_Object) + { + goto out_json; + } + if(NULL==(plastMod=cJSON_GetObjectItem(ptarget, "X-Amz-Meta-Lm")) || NULL==(pexpires=cJSON_GetObjectItem(ptarget, "expires"))) + { + goto out_json; + } + ctx->get.need_hdrs = RESPONSE_HDR_ALL; + ctx->get.last_modify = atol(plastMod->valuestring); + ctx->get.expires = expires_hdr2timestamp(pexpires->valuestring, strlen(pexpires->valuestring)); + if(!check_expires_fresh_header(ctx)) + { + ret = PARSE_JSON_RET_TIMEOUT; + goto out_json; + } + + if(NULL!=(plastMod=cJSON_GetObjectItem(ptarget, "X-Amz-Meta-User"))) + { + if((datalen = Base64_DecodeBlock((unsigned char*)plastMod->valuestring, strlen(plastMod->valuestring), (unsigned char*)usertag, 2048))>0) + { + easy_string_savedata(&ctx->get.response_tag, usertag, datalen); + } + } + for(int i=0; iresponse, g_http_hdr_name[i].http_name, strlen(g_http_hdr_name[i].http_name)); + easy_string_savedata(&ctx->response, plastMod->valuestring, strlen(plastMod->valuestring)); + easy_string_savedata(&ctx->response, "\r\n", strlen("\r\n")); + } + } + return PARSE_JSON_RET_SUCC; + +out_json: + cJSON_Delete(root); + return ret; +} + +void redis_hget_command_cb(struct redisAsyncContext *ac, void *vreply, void *privdata) +{ + redisReply *reply = (redisReply *)vreply; + struct tango_cache_ctx *ctx = (struct tango_cache_ctx *)privdata; + int ret; + + if(reply == NULL || reply->type!=REDIS_REPLY_STRING) + { + if(reply->type == REDIS_REPLY_NIL) + { + tango_cache_set_fail_state(ctx, CACHE_CACHE_MISS); + ctx->get.result.type = RESULT_TYPE_MISS; + promise_success(future_to_promise(ctx->future), &ctx->get.result); + + } + else + { + if(ac->err) MESA_HANDLE_RUNTIME_LOGV2(ctx->instance->runtime_log, RLOG_LV_FATAL, "redis_hget_command_cb error: %s.", ac->errstr); + tango_cache_set_fail_state(ctx, CACHE_ERR_REDIS_JSON); + promise_failed(future_to_promise(ctx->future), FUTURE_ERROR_CANCEL, tango_cache_get_errstring(ctx)); + } + tango_cache_ctx_destroy(ctx); + return; + } + + ret = parse_minio_events_json(ctx, reply->str); + switch(ret) + { + case PARSE_JSON_RET_ERROR: + tango_cache_set_fail_state(ctx, CACHE_ERR_REDIS_JSON); + promise_failed(future_to_promise(ctx->future), FUTURE_ERROR_CANCEL, tango_cache_get_errstring(ctx)); + tango_cache_ctx_destroy(ctx); + break; + case PARSE_JSON_RET_TIMEOUT: + if(ctx->get.state == GET_STATE_DELETE) + { + ctx->get.state = GET_STATE_END; + cache_delete_minio_object(ctx); + } + break; + case PARSE_JSON_RET_SUCC: + fetch_header_over_biz(ctx); + ctx->get.result.type = RESULT_TYPE_END; + promise_success(future_to_promise(ctx->future), &ctx->get.result); + tango_cache_ctx_destroy(ctx); + break; + default: assert(0);break; + } +} + +int tango_cache_head_redis(struct tango_cache_ctx *ctx) +{ + int ret = -1; + + ctx->instance->statistic.get_recv_num += 1; + switch(ctx->instance->redis_connecting) + { + case CACHE_REDIS_CONNECTED: + ret = redisAsyncCommand(ctx->instance->redis_ac, redis_hget_command_cb, ctx, "HGET %s %s/%s", + ctx->instance->redis_key, ctx->instance->bucketname, ctx->object_key); + if(ret < 0) + { + redisAsyncDisconnect(ctx->instance->redis_ac); + redis_asyn_connect_init(ctx->instance, ctx->instance->redis_ip, ctx->instance->redis_port); + tango_cache_set_fail_state(ctx, CACHE_ERR_REDIS_CONNECT); + promise_failed(future_to_promise(ctx->future), FUTURE_ERROR_CANCEL, tango_cache_get_errstring(ctx)); + tango_cache_ctx_destroy(ctx); + } + break; + case CACHE_REDIS_DISCONNECTED: + case CACHE_REDIS_CONNECT_IDLE: + redis_asyn_connect_init(ctx->instance, ctx->instance->redis_ip, ctx->instance->redis_port); + case CACHE_REDIS_CONNECTING: + tango_cache_set_fail_state(ctx, CACHE_ERR_REDIS_CONNECT); + promise_failed(future_to_promise(ctx->future), FUTURE_ERROR_CANCEL, tango_cache_get_errstring(ctx)); + tango_cache_ctx_destroy(ctx); + break; + default: assert(0);break; + } + return ret; +} + diff --git a/cache/src/tango_cache_redis.h b/cache/src/tango_cache_redis.h new file mode 100644 index 0000000..de0acae --- /dev/null +++ b/cache/src/tango_cache_redis.h @@ -0,0 +1,13 @@ +#ifndef __TANGO_CACHE_REDIS_H__ +#define __TANGO_CACHE_REDIS_H__ + +#include +#include + +#include "tango_cache_client_in.h" + +int tango_cache_head_redis(struct tango_cache_ctx *ctx); +int redis_asyn_connect_init(struct tango_cache_instance *instance, const char *redisip, int redis_port); + +#endif + diff --git a/cache/src/tango_cache_transfer.cpp b/cache/src/tango_cache_transfer.cpp index 1889341..87d870e 100644 --- a/cache/src/tango_cache_transfer.cpp +++ b/cache/src/tango_cache_transfer.cpp @@ -174,7 +174,6 @@ int curl_get_minio_uploadID(struct tango_cache_ctx *ctx) if(NULL == (ctx->curl=curl_easy_init())) { - free(ctx); return -1; } @@ -209,6 +208,7 @@ int cache_delete_minio_object(struct tango_cache_ctx *ctx) ctx->instance->statistic.del_recv_num += 1; if(NULL == (ctx->curl=curl_easy_init())) { + tango_cache_ctx_destroy(ctx); //终结者 return 0; } @@ -368,7 +368,7 @@ int cache_kick_upload_minio_end(struct tango_cache_ctx *ctx) switch(ctx->put.state) { case PUT_STATE_START: - http_put_complete_part_evbuf(ctx); + ret = http_put_complete_part_evbuf(ctx); break; case PUT_STATE_PART: @@ -652,24 +652,16 @@ int tango_cache_multi_delete_start(struct tango_cache_ctx *ctx) return 0; } - -static size_t curl_get_response_body_cb(void *ptr, size_t size, size_t count, void *userp) +bool fetch_header_over_biz(struct tango_cache_ctx *ctx) { - struct tango_cache_ctx *ctx = (struct tango_cache_ctx *)userp; - - if(ctx->fail_state || ctx->get.state==GET_STATE_DELETE) - { - return size*count; - } - if(ctx->get.need_hdrs!=RESPONSE_HDR_ALL) //无Expires时 { tango_cache_set_fail_state(ctx, CACHE_ERR_INTERNAL); ctx->get.state = GET_STATE_DELETE; promise_failed(future_to_promise(ctx->future), FUTURE_ERROR_CANCEL, tango_cache_get_errstring(ctx)); - return size*count; + return false; } - + if(ctx->get.response_tag.len > 0) { ctx->get.result.data_frag = ctx->get.response_tag.buff; @@ -686,6 +678,23 @@ static size_t curl_get_response_body_cb(void *ptr, size_t size, size_t count, vo promise_success(future_to_promise(ctx->future), &ctx->get.result); easy_string_destroy(&ctx->response); } + return true; +} + +static size_t curl_get_response_body_cb(void *ptr, size_t size, size_t count, void *userp) +{ + struct tango_cache_ctx *ctx = (struct tango_cache_ctx *)userp; + + if(ctx->fail_state || ctx->get.state==GET_STATE_DELETE) + { + return size*count; + } + + if(!fetch_header_over_biz(ctx)) + { + return size*count; + } + ctx->get.result.data_frag = (const char *)ptr; ctx->get.result.size = size * count; ctx->get.result.type = RESULT_TYPE_BODY; @@ -693,14 +702,16 @@ static size_t curl_get_response_body_cb(void *ptr, size_t size, size_t count, vo return size*count; } -static bool check_expires_header(struct tango_cache_ctx *ctx, const char *expires_val, size_t len) +bool check_expires_fresh_header(struct tango_cache_ctx *ctx) { - time_t time_gmt; + time_t now_gmt; - ctx->get.expires = expires_hdr2timestamp(expires_val, len); - time_gmt = get_gmtime_timestamp(time(NULL)); + if(ctx->get.need_hdrs != RESPONSE_HDR_ALL) + return true; + + now_gmt = get_gmtime_timestamp(time(NULL)); - if(time_gmt > ctx->get.expires) + if(now_gmt > ctx->get.expires) { tango_cache_set_fail_state(ctx, CACHE_TIMEOUT); ctx->get.state = GET_STATE_DELETE; //缓存失效时在下载完毕时触发删除动作 @@ -709,17 +720,7 @@ static bool check_expires_header(struct tango_cache_ctx *ctx, const char *expire easy_string_destroy(&ctx->response); return false; } - return true; -} - -static bool check_fresh_header(struct tango_cache_ctx *ctx) -{ - time_t now_gmt; - - if(ctx->get.need_hdrs != RESPONSE_HDR_ALL) - return true; - - now_gmt = get_gmtime_timestamp(time(NULL)); + if(ctx->get.last_modify+ctx->get.max_age > now_gmt || now_gmt+ctx->get.min_fresh>ctx->get.expires) { tango_cache_set_fail_state(ctx, CACHE_TIMEOUT); @@ -791,7 +792,8 @@ static size_t curl_get_response_header_cb(void *ptr, size_t size, size_t count, if(strcmp_one_word_mesa_equal_len("expires", "EXPIRES", start, 7)) { ctx->get.need_hdrs |= RESPONSE_HDR_EXPIRES; - if(!check_expires_header(ctx, pos_colon + 1, raw_len - datalen - 1) || !check_fresh_header(ctx)) + ctx->get.expires = expires_hdr2timestamp(pos_colon + 1, raw_len - datalen - 1); + if(!check_expires_fresh_header(ctx)) { return raw_len; } @@ -802,7 +804,7 @@ static size_t curl_get_response_header_cb(void *ptr, size_t size, size_t count, { ctx->get.need_hdrs |= RESPONSE_HDR_LAST_MOD; sscanf(pos_colon+1, "%lu", &ctx->get.last_modify); - if(!check_fresh_header(ctx)) + if(!check_expires_fresh_header(ctx)) { return raw_len; } @@ -839,21 +841,18 @@ void tango_cache_curl_get_done(struct tango_cache_ctx *ctx, CURLcode res, long r case GET_STATE_START: if(!ctx->fail_state && check_get_result_code(ctx, res, res_code)) { - ctx->get.result.type = RESULT_TYPE_END; - promise_success(future_to_promise(ctx->future), &ctx->get.result); + if(ctx->method!=CACHE_REQUEST_HEAD || fetch_header_over_biz(ctx)) //HEAD发现的字段不全先不删,正常情况下无; + { + ctx->get.result.type = RESULT_TYPE_END; + promise_success(future_to_promise(ctx->future), &ctx->get.result); + } } tango_cache_ctx_destroy(ctx); break; case GET_STATE_DELETE: - if(cache_delete_minio_object(ctx)) - { - ctx->get.state = GET_STATE_END; - } - else - { - tango_cache_ctx_destroy(ctx); - } + ctx->get.state = GET_STATE_END; + cache_delete_minio_object(ctx); break; case GET_STATE_END: @@ -877,6 +876,10 @@ int tango_cache_fetch_start(struct tango_cache_ctx *ctx) snprintf(minio_url, 256, "http://%s/%s/%s", ctx->hostaddr, ctx->instance->bucketname, ctx->object_key); curl_easy_setopt(ctx->curl, CURLOPT_URL, minio_url); + if(ctx->method == CACHE_REQUEST_HEAD) + { + curl_easy_setopt(ctx->curl, CURLOPT_NOBODY, 1L); + } curl_easy_setopt(ctx->curl, CURLOPT_USERAGENT, "aws-sdk-cpp/1.5.24 Linux/3.10.0-327.el7.x86_64 x86_64 pangu_cache"); curl_easy_setopt(ctx->curl, CURLOPT_NOSIGNAL,1L); curl_easy_setopt(ctx->curl, CURLOPT_WRITEFUNCTION, curl_get_response_body_cb); diff --git a/cache/src/tango_cache_transfer.h b/cache/src/tango_cache_transfer.h index f976fd1..9c85eb5 100644 --- a/cache/src/tango_cache_transfer.h +++ b/cache/src/tango_cache_transfer.h @@ -6,6 +6,9 @@ #include "tango_cache_client_in.h" +bool check_expires_fresh_header(struct tango_cache_ctx *ctx); +bool fetch_header_over_biz(struct tango_cache_ctx *ctx); + void tango_cache_curl_put_done(struct tango_cache_ctx *ctx, CURLcode res, long res_code); void tango_cache_curl_get_done(struct tango_cache_ctx *ctx, CURLcode res, long res_code); void tango_cache_curl_del_done(struct tango_cache_ctx *ctx, CURLcode res, long res_code); diff --git a/cache/test/cache_evbase_test.cpp b/cache/test/cache_evbase_test.cpp index 9670cbc..9077f19 100644 --- a/cache/test/cache_evbase_test.cpp +++ b/cache/test/cache_evbase_test.cpp @@ -68,6 +68,43 @@ void get_future_failed(enum e_future_error err, const char * what, void * user) runing_over = 2; } +void head_future_success(future_result_t* result, void * user) +{ + struct tango_cache_result *res = cache_evbase_read_result(result); + struct future_pdata *pdata = (struct future_pdata *)user; + char buffer[1024]; + + switch(res->type) + { + case RESULT_TYPE_USERTAG: + case RESULT_TYPE_HEADER: + memcpy(buffer, res->data_frag, res->size>=1024?1023:res->size); + buffer[res->size] = '\0'; + printf("%s", buffer); + break; + case RESULT_TYPE_BODY: + assert(0); + break; + case RESULT_TYPE_MISS: + printf("cache not hit/fresh\n"); + case RESULT_TYPE_END: + if(res->type != RESULT_TYPE_MISS) + printf("HEAD cache over, total length: %ld\n", res->tlength); + future_destroy(pdata->future); + free(pdata); + runing_over = 1; + break; + default:break; + } +} + +void head_future_failed(enum e_future_error err, const char * what, void * user) +{ + printf("HEAD fail: %s\n", what); + runing_over = 2; +} + + void put_future_success(future_result_t* result, void * user) { struct future_pdata *pdata = (struct future_pdata *)user; @@ -201,15 +238,18 @@ int main(int argc, char **argv) { sprintf(filename_out, "file_index_%u.bin", index); pdata->future = future_create(get_future_success, get_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); pdata->fp = fopen(filename_out, "w"); cache_evbase_fetch_object(instance_asyn, pdata->future, &getmeta); } + else if(!strcasecmp(p, "HEAD")) + { + pdata->future = future_create(head_future_success, head_future_failed, pdata); + cache_evbase_head_object(instance_asyn, pdata->future, &getmeta); + } else if(!strcasecmp(p, "DEL")) { pdata->future = future_create(del_future_success, del_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); sprintf(pdata->filename, "%s", filename_in); cache_evbase_delete_object(instance_asyn, pdata->future, filename_in); } @@ -218,7 +258,6 @@ int main(int argc, char **argv) size_t filelen; p = get_file_content(filename_in, &filelen); pdata->future = future_create(put_future_success, put_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); cache_evbase_upload_once_data(instance_asyn, pdata->future, PUT_MEM_FREE, p, filelen, &putmeta, pdata->filename, 256); } @@ -226,7 +265,6 @@ int main(int argc, char **argv) { size_t readlen; pdata->future = future_create(put_future_success, put_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); struct evbuffer *evbuf = evbuffer_new(); char buffer[1024]; @@ -246,7 +284,6 @@ int main(int argc, char **argv) else { pdata->future = future_create(put_future_success, put_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); ctx = cache_evbase_update_start(instance_asyn, pdata->future, &putmeta); cache_evbase_get_object_path(ctx, pdata->filename, 256); @@ -265,7 +302,7 @@ int main(int argc, char **argv) } printf("Waiting to finish.......\n"); - static int num=0; + //static int num=0; while(!runing_over) { /*if(++num==10) diff --git a/cache/test/tango_cache_test.c b/cache/test/tango_cache_test.c index 36a2bc3..c24ac41 100644 --- a/cache/test/tango_cache_test.c +++ b/cache/test/tango_cache_test.c @@ -79,6 +79,40 @@ void get_future_failed(enum e_future_error err, const char * what, void * user) printf("GET fail: %s\n", what); } +void head_future_success(future_result_t* result, void * user) +{ + struct tango_cache_result *res = tango_cache_read_result(result); + struct future_pdata *pdata = (struct future_pdata *)user; + char buffer[1024]; + + switch(res->type) + { + case RESULT_TYPE_USERTAG: + case RESULT_TYPE_HEADER: + memcpy(buffer, res->data_frag, res->size>=1024?1023:res->size); + buffer[res->size] = '\0'; + printf("%s", buffer); + break; + case RESULT_TYPE_BODY: + assert(0); + break; + case RESULT_TYPE_MISS: + printf("cache not hit/fresh\n"); + case RESULT_TYPE_END: + if(res->type != RESULT_TYPE_MISS) + printf("HEAD cache over, total length: %ld\n", res->tlength); + future_destroy(pdata->future); + free(pdata); + break; + default:break; + } +} + +void head_future_failed(enum e_future_error err, const char * what, void * user) +{ + printf("HEAD fail: %s\n", what); +} + void put_future_success(future_result_t* result, void * user) { struct future_pdata *pdata = (struct future_pdata *)user; @@ -182,6 +216,7 @@ static void dummy_accept_callback(evutil_socket_t fd, short events, void *arg) p = method; memset(&putmeta, 0, sizeof(struct tango_cache_meta_put)); + memset(&getmeta, 0, sizeof(struct tango_cache_meta_get)); putmeta.url = s; putmeta.std_hdr[HDR_CONTENT_TYPE] = "Content-Type: maintype/subtype"; putmeta.std_hdr[HDR_CONTENT_ENCODING] = "Content-Encoding: gzip"; @@ -197,16 +232,22 @@ static void dummy_accept_callback(evutil_socket_t fd, short events, void *arg) sprintf(filename, "file_index_%u.bin", index++); pdata->fp = fopen(filename, "w"); pdata->future = future_create(get_future_success, get_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); tango_cache_fetch_object(tango_instance, pdata->future, &getmeta); } + else if(!strcasecmp(p, "HEAD")) + { + sprintf(filename, "file_index_%u.bin", index++); + pdata->fp = fopen(filename, "w"); + pdata->future = future_create(head_future_success, head_future_failed, pdata); + + tango_cache_head_object(tango_instance, pdata->future, &getmeta); + } else if(!strcasecmp(p, "PUTONCE")) { size_t filelen; p = get_file_content(s, &filelen); pdata->future = future_create(put_future_success, put_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); tango_cache_upload_once_data(tango_instance, pdata->future, PUT_MEM_FREE, p, filelen, &putmeta, pdata->filename, 256); } @@ -214,7 +255,6 @@ static void dummy_accept_callback(evutil_socket_t fd, short events, void *arg) { size_t readlen; pdata->future = future_create(put_future_success, put_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); struct evbuffer *evbuf = evbuffer_new(); char buffer[1024]; @@ -234,14 +274,12 @@ static void dummy_accept_callback(evutil_socket_t fd, short events, void *arg) else if(!strcasecmp(p, "DEL")) { pdata->future = future_create(del_future_success, del_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); sprintf(pdata->filename, "%s", s); tango_cache_delete_object(tango_instance, pdata->future, s); } else if(!strcasecmp(p, "DELMUL")) //TODO { pdata->future = future_create(del_future_success, del_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); sprintf(pdata->filename, "%s", s); for(pstart = strtok_r(s, ";", &save_ptr); pstart != NULL; pstart = strtok_r(NULL, ";", &save_ptr)) @@ -253,7 +291,6 @@ static void dummy_accept_callback(evutil_socket_t fd, short events, void *arg) else { pdata->future = future_create(put_future_success, put_future_failed, pdata); - promise_set_ctx(future_to_promise(pdata->future), NULL, NULL); ctx = tango_cache_update_start(tango_instance, pdata->future, &putmeta); tango_cache_get_object_path(ctx, pdata->filename, 256); diff --git a/vendor/hiredis-master.zip b/vendor/hiredis-master.zip new file mode 100644 index 0000000000000000000000000000000000000000..c5229101e7257426659d05f170f44e79ea3bed33 GIT binary patch literal 93224 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

${BuXG5SosLPGNuO9|g@X7PBHS?!1GKe| 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 Kxu)T+>-ZmkrcL1h literal 0 HcmV?d00001