diff --git a/README.md b/README.md index e0f2932f1..f60011465 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ It include as application example a powerful search engine with relative [web in The current version offers the following features : - * HTTP/1.0 and 1.1 protocols supported and experimental implementations of HTTP/2. + * HTTP/1.0 and 1.1 protocols supported and experimental implementations of HTTP/2 ([h2spec compliant](https://github.com/summerwind/h2spec)). * Persistent connections for HTTP/1.1 and Keep-Alive support for HTTP/1.0. * Browser cache management (headers: If-Modified-Since/Last-modified). * Chunk-encoding transfers support. diff --git a/include/ulib/container/hash_map.h b/include/ulib/container/hash_map.h index 339a6d240..5e5c1e04e 100644 --- a/include/ulib/container/hash_map.h +++ b/include/ulib/container/hash_map.h @@ -849,6 +849,8 @@ template <> class U_EXPORT UHashMap : public UHashMap { UHashMap::insertAfterFind(_key, str.rep); } + uint32_t getSpaceToDump() const; + // OPERATOR bool operator==(const UHashMap& v) __pure; diff --git a/include/ulib/container/vector.h b/include/ulib/container/vector.h index 63679a6a2..fe7b348ed 100644 --- a/include/ulib/container/vector.h +++ b/include/ulib/container/vector.h @@ -1242,6 +1242,21 @@ template <> class U_EXPORT UVector : public UVector { bool contains(UVector& vec, bool ignore_case = false); bool isContained(const UString& str, bool ignore_case = false) { return (contains(str, ignore_case) != U_NOT_FOUND); } + uint32_t getSpaceToDump() const + { + U_TRACE_NO_PARAM(0, "UVector::getSpaceToDump()") + + U_CHECK_MEMORY + + U_INTERNAL_DUMP("_length = %u", _length) + + uint32_t space = U_CONSTANT_SIZE("( )"); + + for (uint32_t i = 0; i < _length; ++i) space += at(i).getSpaceToDump() + 1; + + U_RETURN(space); + } + // OPERATOR bool operator==(const UVector& v) const __pure; diff --git a/include/ulib/debug/macro.h b/include/ulib/debug/macro.h index d983b09e5..5930994ed 100644 --- a/include/ulib/debug/macro.h +++ b/include/ulib/debug/macro.h @@ -16,7 +16,7 @@ // Design by contract - if (expr == false) then stop -#ifdef DEBUG // NB: we need to save the status on the stack because of thread (ex. time resolution optimation) +#ifdef DEBUG // NB: we need to save the status on the stack because of thread (ex. time resolution optimizatio) # define U_ASSERT(expr) { int _s = u_trace_suspend; u_trace_suspend = 1; U_INTERNAL_ASSERT(expr); u_trace_suspend = _s; } # define U_ASSERT_MINOR(a,b) { int _s = u_trace_suspend; u_trace_suspend = 1; U_INTERNAL_ASSERT_MINOR(a,b); u_trace_suspend = _s; } # define U_ASSERT_MAJOR(a,b) { int _s = u_trace_suspend; u_trace_suspend = 1; U_INTERNAL_ASSERT_MAJOR(a,b); u_trace_suspend = _s; } @@ -186,8 +186,8 @@ if (envp) \ U_NEW(CLASS,obj,args), \ UObjectDB::flag_ulib_object = false) -# define U_DUMP_OBJECT(obj) { u_trace_dump(U_CONSTANT_TO_PARAM(#obj" = %S"), (obj).dump(true)); } -# define U_DUMP_CONTAINER(obj) { if (utr.active[0]) u_trace_dump(U_CONSTANT_TO_PARAM(#obj" = %O"), U_OBJECT_TO_TRACE((obj))); } +# define U_DUMP_OBJECT(obj) { if (utr.active[0]) u_trace_dump(U_CONSTANT_TO_PARAM(#obj" = %S"), U_OBJECT_TO_TRACE((obj))); } +# define U_DUMP_CONTAINER(obj) { if (utr.active[0] && (obj).getSpaceToDump() < U_MAX_SIZE_PREALLOCATE) u_trace_dump(U_CONSTANT_TO_PARAM(#obj" = %O"), U_OBJECT_TO_TRACE((obj))); } # define U_DUMP_OBJECT_TO_TMP(obj,fname) \ { char _buffer[2 * 1024 * 1024]; \ diff --git a/include/ulib/internal/chttp.h b/include/ulib/internal/chttp.h index 8cd5f67d2..648f288ed 100644 --- a/include/ulib/internal/chttp.h +++ b/include/ulib/internal/chttp.h @@ -302,13 +302,10 @@ enum HttpRequestType { #define U_HTTP_METHOD_NUM_TO_PARAM(num) u_clientimage_info.http_method_list[num].name, u_clientimage_info.http_method_list[num].len #define U_HTTP_METHOD_NUM_TO_TRACE(num) u_clientimage_info.http_method_list[num].len, u_clientimage_info.http_method_list[num].name -#define U_HTTP_HOST_STREQ(str) (U_http_host_len ? U_STREQ(u_clientimage_info.http_info.host, U_http_host_len, str) : false) - +#define U_HTTP_HOST_STREQ(str) (U_http_host_len ? U_STREQ(u_clientimage_info.http_info.host, U_http_host_len, str) : false) #define U_HTTP_REFERER_STREQ(str) (u_clientimage_info.http_info.referer_len ? U_STREQ(u_clientimage_info.http_info.referer,u_clientimage_info.http_info.referer_len, str) : false) -#define U_HTTP_USER_AGENT_STREQ(str) (u_clientimage_info.http_info.user_agent_len ? U_STREQ(u_clientimage_info.http_info.user_agent,u_clientimage_info.http_info.user_agent_len, str) : false) - -#define U_HTTP_URI_MEMEQ(str) memcmp(u_clientimage_info.http_info.uri, U_CONSTANT_TO_PARAM(str)) == 0 +#define U_HTTP_URI_MEMEQ(str) (memcmp(u_clientimage_info.http_info.uri, U_CONSTANT_TO_PARAM(str)) == 0) #define U_HTTP_URI_STREQ(str) U_STREQ(u_clientimage_info.http_info.uri, u_clientimage_info.http_info.uri_len, str) #define U_HTTP_CTYPE_MEMEQ(str) (U_http_content_type_len ? memcmp(u_clientimage_info.http_info.content_type, U_CONSTANT_TO_PARAM(str)) == 0 : false) @@ -317,6 +314,9 @@ enum HttpRequestType { #define U_HTTP_QUERY_MEMEQ(str) (u_clientimage_info.http_info.query_len ? memcmp(u_clientimage_info.http_info.query, U_CONSTANT_TO_PARAM(str)) == 0 : false) #define U_HTTP_QUERY_STREQ(str) (u_clientimage_info.http_info.query_len ? U_STREQ(u_clientimage_info.http_info.query, u_clientimage_info.http_info.query_len, str) : false) +#define U_HTTP_USER_AGENT_MEMEQ(str) (u_clientimage_info.http_info.user_agent_len ? memcmp(u_clientimage_info.http_info.user_agent,U_CONSTANT_TO_PARAM(str)) == 0 : false) +#define U_HTTP_USER_AGENT_STREQ(str) (u_clientimage_info.http_info.user_agent_len ? U_STREQ(u_clientimage_info.http_info.user_agent,u_clientimage_info.http_info.user_agent_len, str) : false) + /** * The hostname of your server from header's request. * The difference between U_HTTP_HOST_.. and U_HTTP_VHOST_.. is that diff --git a/include/ulib/json/value.h b/include/ulib/json/value.h index 9d5a776ea..debfc01c4 100644 --- a/include/ulib/json/value.h +++ b/include/ulib/json/value.h @@ -64,7 +64,6 @@ class UValueIter; class U_EXPORT UValue { public: - // Check for memory error U_MEMORY_TEST @@ -83,13 +82,13 @@ class U_EXPORT UValue { REAL_VALUE = 0, // double value INT_VALUE = 1, // signed integer value UINT_VALUE = 2, // unsigned integer value - TRUE_VALUE = 3, // bool value - FALSE_VALUE = 4, // bool value + TRUE_VALUE = 3, // bool value + FALSE_VALUE = 4, // bool value STRING_VALUE = 5, // string value UTF_VALUE = 6, // string value (need to be emitted) ARRAY_VALUE = 7, // array value (ordered list) OBJECT_VALUE = 8, // object value (collection of name/value pairs) - NULL_VALUE = 9 // null value + NULL_VALUE = 9 // null value } ValueType; static int jsonParseFlags; @@ -239,6 +238,15 @@ class U_EXPORT UValue { U_RETURN(type); } + bool empty() const + { + U_TRACE_NO_PARAM(0, "UValue::empty()") + + if (value.ival == 0ULL) U_RETURN(true); + + U_RETURN(false); + } + bool isNull() const { U_TRACE_NO_PARAM(0+256, "UValue::isNull()") @@ -326,27 +334,26 @@ class U_EXPORT UValue { U_RETURN(false); } - bool isArrayOrObject() const { return isArrayOrObject(value.ival); } - - bool isStringUTF() const { return isStringUTF(value.ival); } - bool isStringOrUTF() const { return isStringOrUTF(value.ival); } - union jval getKey() const { return pkey; } union jval getValue() const { return value; } int getInt() const { return value.sint; } unsigned int getUInt() const { return value.uint; } + UString getString() { return getString(value.ival); } + + int getTag() const { return getTag(value.ival); } bool getBool() const { return (getTag() == TRUE_VALUE); } double getDouble() const { return value.real; } - int getTag() const { return getTag(value.ival); } - long long getNumber() const { return (isDouble() ? llrint(value.real) : (long long)(long)getPayload()); } - - UString getString() { return getString(value.ival); } + bool isStringUTF() const { return isStringUTF(value.ival); } + bool isStringOrUTF() const { return isStringOrUTF(value.ival); } + bool isArrayOrObject() const { return isArrayOrObject(value.ival); } static uint32_t getStringSize(const union jval value) { return getString(value.ival).size(); } + long long getNumber() const { return (isDouble() ? llrint(value.real) : (long long)(long)getPayload()); } + // manage values in array or object UValue* at(uint32_t pos) const __pure; @@ -354,8 +361,8 @@ class U_EXPORT UValue { UValue* at(const UString& key) const { return at(U_STRING_TO_PARAM(key)); } - UValue& operator[](uint32_t pos) const { return *at(pos); } - UValue& operator[](const UString& key) const { return *at(U_STRING_TO_PARAM(key)); } + UValue& operator[](uint32_t pos) const { return *at(pos); } + UValue& operator[](const UString& key) const { return *at(U_STRING_TO_PARAM(key)); } UValue& operator[](const UStringRep* key) const { return *at(U_STRING_TO_PARAM(*key)); } bool isMemberExist(const UString& key) const { return (at(U_STRING_TO_PARAM(key)) != 0); } @@ -436,54 +443,36 @@ class U_EXPORT UValue { U_INTERNAL_DUMP("result(%u) = %V", result.size(), result.rep) } - template void toJSON(UJsonTypeHandler t) - { - U_TRACE(0, "UValue::toJSON(%p)", &t) - - t.toJSON(*this); - } - - template static void toJSON1(UJsonTypeHandler member) - { - U_TRACE(0, "UValue::toJSON1(%p)", &member) - - UValue* node; - - U_NEW(UValue, node, UValue((UValue*)0)); - - member.toJSON(*node); - - if (pnode) pnode->next = node; - else phead = node; - - pnode = node; - } - - template static void toJSON(const UString& name, UJsonTypeHandler member) + template void toJSON(const UString& name, UJsonTypeHandler member) { U_TRACE(0, "UValue::toJSON(%V,%p)", name.rep, &member) - toJSON1(member); + addJSON(name, member); - name.hold(); + U_INTERNAL_DUMP("pnode = %p", pnode) - pnode->pkey.ival = getJsonValue(STRING_VALUE, name.rep); - } + if (pnode->toNode() == 0) + { + int tag = pnode->getTag(); - template void fromJSON(UJsonTypeHandler t) - { - U_TRACE(0, "UValue::fromJSON(%p)", &t) + if (tag == ARRAY_VALUE || + tag == OBJECT_VALUE) + { + U_INTERNAL_DUMP("pnode->next = %p", pnode->next) - t.fromJSON(*this); + pnode->value.ival = UValue::getJsonValue(tag, pnode->next); + pnode->next = 0; + } + } } template void fromJSON(const UString& name, UJsonTypeHandler member) { U_TRACE(0, "UValue::fromJSON(%V,%p)", name.rep, &member) - UValue* json = at(name); + UValue* node = at(name); - if (json) member.fromJSON(*json); + if (node) member.fromJSON(*node); else member.clear(); } @@ -589,7 +578,6 @@ class U_EXPORT UValue { union jval pkey, // only if binded to an object value; - static UValue* phead; static UValue* pnode; static uint32_t size; static char* pstringify; // buffer to stringify json @@ -682,19 +670,24 @@ class U_EXPORT UValue { } } - void stringify() const; - void prettify(uint32_t indent) const; + void setTag(int tag) + { + U_TRACE(0, "UValue::setTag(%d)", tag) - uint64_t getPayload() const { return getPayload(value.ival); } + value.ival = getJsonValue(tag, (void*)getPayload()); + } + + void stringify() const; + void prettify(uint32_t indent) const; UValue* toNode() const { return toNode(value.ival); } + uint64_t getPayload() const { return getPayload(value.ival); } + static UValue* toNode(uint64_t val) { U_TRACE(0, "UValue::toNode(0x%x)", val) - U_ASSERT(isArrayOrObject(val)) - UValue* ptr = (UValue*)getPayload(val); U_RETURN_POINTER(ptr, UValue); @@ -804,6 +797,37 @@ class U_EXPORT UValue { static UString getString(uint64_t value); + template void addJSON(UJsonTypeHandler member) + { + U_TRACE(0, "UValue::addJSON(%p)", &member) + + UValue* node; + + U_NEW(UValue, node, UValue((UValue*)0)); + + U_INTERNAL_DUMP("pnode = %p", pnode) + + if (pnode) pnode->next = node; + else + { + value.ival = UValue::getJsonValue(isArray() ? UValue::ARRAY_VALUE : UValue::OBJECT_VALUE, node); + } + + member.toJSON(*(pnode = node)); + pnode = node; + } + + template void addJSON(const UString& name, UJsonTypeHandler member) + { + U_TRACE(0, "UValue::addJSON(%V,%p)", name.rep, &member) + + name.hold(); + + addJSON(member); + + pnode->pkey.ival = getJsonValue(STRING_VALUE, name.rep); + } + private: static int jread_skip(UTokenizer& tok) U_NO_EXPORT; static int jreadFindToken(UTokenizer& tok) U_NO_EXPORT; @@ -885,6 +909,9 @@ class U_EXPORT UJsonTypeHandler_Base { U_DISALLOW_ASSIGN(UJsonTypeHandler_Base) }; +#define U_JSON_METHOD_HANDLER(name_object_member, type_object_member) \ + U_STRING_FROM_CONSTANT(#name_object_member), UJsonTypeHandler(name_object_member) + #define U_JSON_TYPE_HANDLER(class_name, name_object_member, type_object_member) \ U_STRING_FROM_CONSTANT(#name_object_member), UJsonTypeHandler(((class_name*)pval)->name_object_member) @@ -895,9 +922,9 @@ class U_EXPORT UJsonTypeHandler_Base { * * class Person { * public: + * int age; * UString lastName; * UString firstName; - * int age; * }; * * The UJsonTypeHandler must provide a custom toJSON and fromJSON method: @@ -908,18 +935,41 @@ class U_EXPORT UJsonTypeHandler_Base { * * void toJSON(UValue& json) * { - * json.toJSON(U_JSON_TYPE_HANDLER(Person, lastName, UString)); - * json.toJSON(U_JSON_TYPE_HANDLER(Person, firstName, UString)); - * json.toJSON(U_JSON_TYPE_HANDLER(Person, age, int)); + * json.toJSON(U_JSON_TYPE_HANDLER(Person, age, int)); + * json.toJSON(U_JSON_TYPE_HANDLER(Person, lastName, UString)); + * json.toJSON(U_JSON_TYPE_HANDLER(Person, firstName, UString)); * } * * void fromJSON(UValue& json) * { - * json.fromJSON(U_JSON_TYPE_HANDLER(Person, lastName, UString)); - * json.fromJSON(U_JSON_TYPE_HANDLER(Person, firstName, UString)); - * json.fromJSON(U_JSON_TYPE_HANDLER(Person, age, int)); + * json.fromJSON(U_JSON_TYPE_HANDLER(Person, age, int)); + * json.fromJSON(U_JSON_TYPE_HANDLER(Person, lastName, UString)); + * json.fromJSON(U_JSON_TYPE_HANDLER(Person, firstName, UString)); * } * }; + * + * or in alternative add this methods to the (simplified) class: + * + * void Person::clear() + * { + * age = 0; + * lastName.clear(); + * firstName.clear(); + * } + * + * void Person::toJSON(UValue& json) + * { + * json.toJSON(U_JSON_METHOD_HANDLER(age, int)); + * json.toJSON(U_JSON_METHOD_HANDLER( lastName, UString)); + * json.toJSON(U_JSON_METHOD_HANDLER(firstName, UString)); + * } + * + * void Person::fromJSON(UValue& json) + * { + * json.fromJSON(U_JSON_METHOD_HANDLER(age, int)); + * json.fromJSON(U_JSON_METHOD_HANDLER( lastName, UString)); + * json.fromJSON(U_JSON_METHOD_HANDLER(firstName, UString)); + * } */ template class U_EXPORT UJsonTypeHandler : public UJsonTypeHandler_Base { @@ -929,10 +979,28 @@ template class U_EXPORT UJsonTypeHandler : public UJsonTypeHandler_Bas // SERVICES - void clear(); + void clear() + { + U_TRACE(0, "UJsonTypeHandler::clear()") + + ((T*)pval)->clear(); // method alternative + } + + void toJSON(UValue& json) + { + U_TRACE(0, "UJsonTypeHandler::toJSON(%p)", &json) + + json.setTag(UValue::OBJECT_VALUE); - void toJSON(UValue& json); - void fromJSON(UValue& json); + ((T*)pval)->toJSON(json); // method alternative + } + + void fromJSON(UValue& json) + { + U_TRACE(0, "UJsonTypeHandler::fromJSON(%p)", &json) + + ((T*)pval)->fromJSON(json); // method alternative + } private: U_DISALLOW_ASSIGN(UJsonTypeHandler) @@ -940,7 +1008,7 @@ template class U_EXPORT UJsonTypeHandler : public UJsonTypeHandler_Bas // manage object <=> JSON representation -template inline bool JSON_parse(const UString& str, T& obj) +template bool JSON_parse(const UString& str, T& obj) { U_TRACE(0, "JSON_parse(%p,%p)", &str, &obj) @@ -948,7 +1016,7 @@ template inline bool JSON_parse(const UString& str, T& obj) if (json.parse(str)) { - json.fromJSON(UJsonTypeHandler(obj)); + UJsonTypeHandler(obj).fromJSON(json); U_RETURN(true); } @@ -956,15 +1024,15 @@ template inline bool JSON_parse(const UString& str, T& obj) U_RETURN(false); } -template inline void JSON_stringify(UString& result, UValue& json, T& obj) +template void JSON_stringify(UString& result, UValue& json, T& obj) { U_TRACE(0, "JSON_stringify(%V,%p,%p)", result.rep, &json, &obj) - UValue::pnode = 0; + U_ASSERT(json.empty()) - json.toJSON(UJsonTypeHandler(obj)); + UValue::pnode = 0; - if (UValue::pnode) json.value.ival = UValue::getJsonValue(UValue::OBJECT_VALUE, UValue::phead); + UJsonTypeHandler(obj).toJSON(json); UValue::stringify(result, json); } @@ -975,6 +1043,11 @@ template <> class U_EXPORT UJsonTypeHandler : public UJsonTypeHandler_Base public: explicit UJsonTypeHandler(null&) : UJsonTypeHandler_Base(0) {} + void clear() + { + U_TRACE(0, "UJsonTypeHandler::clear()") + } + void toJSON(UValue& json) { U_TRACE(0, "UJsonTypeHandler::toJSON(%p)", &json) @@ -1459,7 +1532,8 @@ template class U_EXPORT UJsonTypeHandler > : public UJsonT { U_TRACE(0, "UJsonTypeHandler::toJSON(%p)", &json) - UValue::phead = 0; + json.setTag(UValue::ARRAY_VALUE); + uvector* pvec = (uvector*)pval; if (pvec->_length) @@ -1467,21 +1541,16 @@ template class U_EXPORT UJsonTypeHandler > : public UJsonT const void** ptr = pvec->vec; const void** end = pvec->vec + pvec->_length; - for (; ptr < end; ++ptr) - { - UValue::toJSON1(UJsonTypeHandler(*(T*)(*ptr))); - } + do { json.addJSON(UJsonTypeHandler(*(T*)(*ptr))); } while (++ptr < end); } - - json.value.ival = UValue::getJsonValue(UValue::ARRAY_VALUE, UValue::phead); - - UValue::pnode = 0; } void fromJSON(UValue& json) { U_TRACE(0, "UJsonTypeHandler::fromJSON(%p)", &json) + U_ASSERT(((uvector*)pval)->empty()) + UValue* element = json.toNode(); while (element) @@ -1514,25 +1583,47 @@ template <> class U_EXPORT UJsonTypeHandler > : public UJsonTyp ((UVector*)pval)->clear(); } - void toJSON(UValue& json) { ((UJsonTypeHandler*)this)->toJSON(json); } + void toJSON(UValue& json) + { + U_TRACE(0, "UJsonTypeHandler>::toJSON(%p)", &json) + + json.setTag(UValue::ARRAY_VALUE); + + uvectorbase* pvec = (uvectorbase*)pval; + + if (pvec->_length) + { + const void** ptr = pvec->vec; + const void** end = pvec->vec + pvec->_length; + + do { + UString item((const UStringRep*)(*ptr)); + + json.addJSON(UJsonTypeHandler(item)); + } + while (++ptr < end); + } + } void fromJSON(UValue& json) { U_TRACE(0, "UJsonTypeHandler>::fromJSON(%p)", &json) - UValue* element = json.toNode(); + U_ASSERT(((UVector*)pval)->empty()) - while (element) + UValue* pelement = json.toNode(); + + while (pelement) { UString item; - U_DUMP("element = %p element->next = %p element->type = (%d,%S)", element, element->next, element->getTag(), UValue::getDataTypeDescription(element->getTag())) + U_DUMP("pelement = %p pelement->next = %p pelement->type = (%d,%S)", pelement, pelement->next, pelement->getTag(), UValue::getDataTypeDescription(pelement->getTag())) - element->fromJSON(UJsonTypeHandler(item)); + UJsonTypeHandler(item).fromJSON(*pelement); ((UVector*)pval)->push_back(item); - element = element->next; + pelement = pelement->next; } } }; @@ -1554,45 +1645,46 @@ template class U_EXPORT UJsonTypeHandler > : public UJson { U_TRACE(0, "UJsonTypeHandler::toJSON(%p)", &json) - UValue::phead = 0; + json.setTag(UValue::OBJECT_VALUE); + uhashmap* pmap = (uhashmap*)pval; if (pmap->first()) { do { - UValue::toJSON(pmap->getKey(), UJsonTypeHandler(*(pmap->elem()))); + json.addJSON(pmap->getKey(), UJsonTypeHandler(*(pmap->elem()))); } while (pmap->next()); } - - json.value.ival = UValue::getJsonValue(UValue::OBJECT_VALUE, UValue::phead); } void fromJSON(UValue& json) { U_TRACE(0, "UJsonTypeHandler::fromJSON(%p)", &json) - UValue* element = json.toNode(); + U_ASSERT(((uhashmap*)pval)->empty()) - while (element) + UValue* pelement = json.toNode(); + + while (pelement) { T* pitem; U_NEW(T, pitem, T); - U_DUMP("element = %p element->next = %p element->type = (%d,%S)", element, element->next, element->getTag(), UValue::getDataTypeDescription(element->getTag())) + U_DUMP("pelement = %p pelement->next = %p pelement->type = (%d,%S)", pelement, pelement->next, pelement->getTag(), UValue::getDataTypeDescription(pelement->getTag())) - element->fromJSON(UJsonTypeHandler(*pitem)); + UJsonTypeHandler(*pitem).fromJSON(*pelement); - UStringRep* rep = (UStringRep*)UValue::getPayload(element->pkey.ival); + UStringRep* rep = (UStringRep*)UValue::getPayload(pelement->pkey.ival); - U_INTERNAL_DUMP("element->pkey(%p) = %V", rep, rep) + U_INTERNAL_DUMP("pelement->pkey(%p) = %V", rep, rep) ((uhashmap*)pval)->lookup(rep); ((uhashmap*)pval)->insertAfterFind(rep, pitem); - element = element->next; + pelement = pelement->next; } } }; @@ -1603,31 +1695,57 @@ template <> class U_EXPORT UJsonTypeHandler > : public UJsonTy explicit UJsonTypeHandler(UHashMap& val) : UJsonTypeHandler(*((uhashmap*)&val)) {} - void toJSON(UValue& json) { ((UJsonTypeHandler*)this)->toJSON(json); } + void clear() + { + U_TRACE(0, "UJsonTypeHandler>::clear()") + + ((UHashMap*)pval)->clear(); + } + + void toJSON(UValue& json) + { + U_TRACE(0, "UJsonTypeHandler>::toJSON(%p)", &json) + + json.setTag(UValue::OBJECT_VALUE); + + UHashMap* pmap = (UHashMap*)pval; + + if (pmap->first()) + { + do { + UString item((const UStringRep*)pmap->elem()); + + json.addJSON(pmap->getKey(), UJsonTypeHandler(item)); + } + while (pmap->next()); + } + } void fromJSON(UValue& json) { U_TRACE(0, "UJsonTypeHandler>::fromJSON(%p)", &json) - UValue* element = json.toNode(); + U_ASSERT(((UHashMap*)pval)->empty()) - while (element) + UValue* pelement = json.toNode(); + + while (pelement) { UString item; - U_DUMP("element = %p element->next = %p element->type = (%d,%S)", element, element->next, element->getTag(), UValue::getDataTypeDescription(element->getTag())) + U_DUMP("pelement = %p pelement->next = %p pelement->type = (%d,%S)", pelement, pelement->next, pelement->getTag(), UValue::getDataTypeDescription(pelement->getTag())) - element->fromJSON(UJsonTypeHandler(item)); + UJsonTypeHandler(item).fromJSON(*pelement); - UStringRep* rep = (UStringRep*)UValue::getPayload(element->pkey.ival); + UStringRep* rep = (UStringRep*)UValue::getPayload(pelement->pkey.ival); - U_INTERNAL_DUMP("element->pkey(%p) = %V", rep, rep) + U_INTERNAL_DUMP("pelement->pkey(%p) = %V", rep, rep) - ((uhashmap*)pval)->lookup(rep); + ((UHashMap*)pval)->lookup(rep); - ((uhashmap*)pval)->insertAfterFind(rep, item.rep); + ((uhashmapbase*)pval)->insertAfterFind(rep, item.rep); - element = element->next; + pelement = pelement->next; } } }; @@ -1651,36 +1769,32 @@ template class U_EXPORT UJsonTypeHandler > : public UJs { U_TRACE(0, "UJsonTypeHandler::toJSON(%p)", &json) - UValue::phead = 0; - stdvector* pvec = (stdvector*)pval; + json.setTag(UValue::ARRAY_VALUE); - for (uint32_t i = 0, n = pvec->size(); i < n; ++i) - { - UValue::toJSON1(UJsonTypeHandler(pvec->at(i))); - } - - json.value.ival = UValue::getJsonValue(UValue::ARRAY_VALUE, UValue::phead); + stdvector* pvec = (stdvector*)pval; - UValue::pnode = 0; + for (uint32_t i = 0, n = pvec->size(); i < n; ++i) json.addJSON(UJsonTypeHandler(pvec->at(i))); } void fromJSON(UValue& json) { U_TRACE(0, "UJsonTypeHandler::fromJSON(%p)", &json) - UValue* element = json.toNode(); + // U_ASSERT(((stdvector*)pval)->empty()) - while (element) + UValue* pelement = json.toNode(); + + while (pelement) { T item; - U_DUMP("element = %p element->next = %p element->type = (%d,%S)", element, element->next, element->getTag(), UValue::getDataTypeDescription(element->getTag())) + U_DUMP("pelement = %p pelement->next = %p pelement->type = (%d,%S)", pelement, pelement->next, pelement->getTag(), UValue::getDataTypeDescription(pelement->getTag())) - element->fromJSON(UJsonTypeHandler(item)); + UJsonTypeHandler(item).fromJSON(*pelement); ((stdvector*)pval)->push_back(item); - element = element->next; + pelement = pelement->next; } } }; diff --git a/include/ulib/net/server/server.h b/include/ulib/net/server/server.h index 25de8ecef..5a71d4c10 100644 --- a/include/ulib/net/server/server.h +++ b/include/ulib/net/server/server.h @@ -560,7 +560,7 @@ class U_EXPORT UServer_Base : public UEventFd { static UServer_Base* pthis; static UString* cenvironment; static UString* senvironment; - static bool flag_sigterm, monitoring_process, set_realtime_priority, public_address, binsert, set_tcp_keep_alive, called_from_handlerTime; + static bool monitoring_process, set_realtime_priority, public_address, binsert, set_tcp_keep_alive, called_from_handlerTime; static uint32_t vplugin_size; static UVector* vplugin_name; diff --git a/include/ulib/notifier.h b/include/ulib/notifier.h index f545aa0f6..6be58f561 100644 --- a/include/ulib/notifier.h +++ b/include/ulib/notifier.h @@ -201,10 +201,10 @@ class U_EXPORT UNotifier { #endif protected: - static bool bread; static int nfd_ready; // the number of file descriptors ready for the requested I/O static UEventFd** lo_map_fd; static UEventFd* handler_event; + static bool bread, flag_sigterm; static UGenericHashMap* hi_map_fd; // maps a fd to a node pointer static uint32_t bepollet_threshold, lo_map_fd_len; diff --git a/include/ulib/orm/orm.h b/include/ulib/orm/orm.h index 96f8eb6c3..375688ca6 100644 --- a/include/ulib/orm/orm.h +++ b/include/ulib/orm/orm.h @@ -135,9 +135,9 @@ class U_EXPORT UOrmTypeHandler_Base { * * class Person { * public: - * UString _lastName; - * UString _firstName; - * int _age; + * int age; + * UString lastName; + * UString firstName; * }; * * The UOrmTypeHandler must provide a custom bindParam and bindResult method: @@ -147,22 +147,22 @@ class U_EXPORT UOrmTypeHandler_Base { * explicit UOrmTypeHandler(Person& val) : UOrmTypeHandler_Base(&val) * * void bindParam(UOrmStatement* stmt) - * { + * { * // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Age INTEGER(3)) * - * stmt->bindParam(U_ORM_TYPE_HANDLER(Person, _lastName, UString)); - * stmt->bindParam(U_ORM_TYPE_HANDLER(Person, _firstName, UString)); - * stmt->bindParam(U_ORM_TYPE_HANDLER(Person, _age, int)); - * } + * stmt->bindParam(U_ORM_TYPE_HANDLER(Person, age, int)); + * stmt->bindParam(U_ORM_TYPE_HANDLER(Person, lastName, UString)); + * stmt->bindParam(U_ORM_TYPE_HANDLER(Person, firstName, UString)); + * } * * void bindResult(UOrmStatement* stmt) - * { + * { * // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Age INTEGER(3)) * - * stmt->bindResult(U_ORM_TYPE_HANDLER(Person, _lastName, UString)); - * stmt->bindResult(U_ORM_TYPE_HANDLER(Person, _firstName, UString)); - * stmt->bindResult(U_ORM_TYPE_HANDLER(Person, _age, int)); - * } + * stmt->bindResult(U_ORM_TYPE_HANDLER(Person, age, int)); + * stmt->bindResult(U_ORM_TYPE_HANDLER(Person, lastName, UString)); + * stmt->bindResult(U_ORM_TYPE_HANDLER(Person, firstName, UString)); + * } * }; */ diff --git a/include/ulib/string.h b/include/ulib/string.h index 450d0e2c4..11d90bd57 100644 --- a/include/ulib/string.h +++ b/include/ulib/string.h @@ -684,6 +684,40 @@ class U_EXPORT UStringRep { _length -= 2; } + bool needQuote() const + { + U_TRACE_NO_PARAM(0, "UStringRep::needQuote()") + + U_CHECK_MEMORY + + if (_length == 0) U_RETURN(true); + + for (const unsigned char* s = (const unsigned char*)str, *_end = s + _length; s < _end; ++s) + { + unsigned char c = *s; + + if (c == '"' || + c == '\\' || + u__isspace(c)) + { + U_RETURN(true); + } + } + + U_RETURN(false); + } + + uint32_t getSpaceToDump() const + { + U_TRACE_NO_PARAM(0, "UStringRep::getSpaceToDump()") + + U_CHECK_MEMORY + + if (needQuote() == false) U_RETURN(_length); + + U_RETURN(_length + U_CONSTANT_SIZE("")); + } + static uint32_t fold(uint32_t pos, uint32_t off, uint32_t sz) { U_TRACE(0, "UStringRep::fold(%u,%u,%u)", pos, off, sz) @@ -1765,6 +1799,10 @@ class U_EXPORT UString { void unQuote(); + bool needQuote() const { return rep->needQuote(); } + + uint32_t getSpaceToDump() const { return rep->getSpaceToDump(); } + // set uniq void duplicate() const; @@ -1865,7 +1903,7 @@ class U_EXPORT UString { U_TRACE(0, "UString::setFromNumber32(%u)", number) # ifdef DEBUG - vsnprintf_check("4294967295"); + vsnprintf_check("1234567890"); U_ASSERT(uniq()) # endif @@ -1882,7 +1920,7 @@ class U_EXPORT UString { U_TRACE(0, "UString::setFromNumber32s(%d)", number) # ifdef DEBUG - vsnprintf_check("4294967295"); + vsnprintf_check("1234567890"); U_ASSERT(uniq()) # endif diff --git a/include/ulib/utility/http2.h b/include/ulib/utility/http2.h index 852033cce..6b9afbc4f 100644 --- a/include/ulib/utility/http2.h +++ b/include/ulib/utility/http2.h @@ -21,6 +21,7 @@ #define HTTP2_FRAME_HEADER_SIZE 9 // The number of bytes of the frame header #define HTTP2_DEFAULT_WINDOW_SIZE 65535 #define HTTP2_HEADER_TABLE_OFFSET 62 +#define HTTP2_MAX_CONCURRENT_STREAMS 128 #define HTTP2_HEADER_TABLE_ENTRY_SIZE_OFFSET 32 #define HTTP2_CONNECTION_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" // (24 bytes) @@ -80,10 +81,8 @@ class U_EXPORT UHTTP2 { }; struct Stream { - uint32_t id, - state, - clength; UString headers, body; + uint32_t id, state, clength; }; class Connection { @@ -103,8 +102,8 @@ class U_EXPORT UHTTP2 { UHashMap itable; // headers request HpackDynamicTable idyntbl, odyntbl; // hpack dynamic table (request, response) // streams - uint32_t stream_idx, max_processed_stream_id; - Stream streams[100]; + uint32_t max_processed_stream_id; + Stream streams[HTTP2_MAX_CONCURRENT_STREAMS]; #ifdef DEBUG UHashMap dtable; HpackDynamicTable ddyntbl; @@ -122,9 +121,7 @@ class U_EXPORT UHTTP2 { inp_window = out_window = HTTP2_DEFAULT_WINDOW_SIZE; - state = CONN_STATE_IDLE; peer_settings = settings; - stream_idx = max_processed_stream_id = 0; } @@ -165,11 +162,6 @@ class U_EXPORT UHTTP2 { CONTINUATION = 0x09 }; - enum WaitFor { - WAIT_CONTINUATION = 0x01, - WAIT_WINDOW_UPDATE = 0x02 - }; - enum FrameFlagsId { FLAG_NONE = 0x00, FLAG_ACK = 0x01, // 1 @@ -194,8 +186,7 @@ class U_EXPORT UHTTP2 { ENHANCE_YOUR_CALM = 0x0b, INADEQUATE_SECURITY = 0x0c, HTTP_1_1_REQUIRED = 0x0d, - ERROR_NOCLOSE = 0xfe, - ERROR_INCOMPLETE = 0xff + ERROR_INCOMPLETE = 0x0e }; enum HuffDecodeFlag { @@ -246,20 +237,17 @@ class U_EXPORT UHTTP2 { }; static Stream* pStream; - static UString* wbuffer; static FrameHeader frame; - static Settings settings; - static uint32_t wait_for; static Stream* pStreamEnd; - static Stream* pStreamStart; static int nerror, hpack_errno; static Connection* vConnection; static Connection* pConnection; - static const char* upgrade_settings; - static bool bcontinue100, bsetting_ack, bsetting_send; + static const Settings settings; + static uint32_t wait_for_continuation; + static bool bcontinue100, bsetting_ack, bsetting_send, bnghttp2; - static bool priority_exclusive; static uint8_t priority_weight; // 0 if not set + static bool priority_exclusive; static uint32_t priority_dependency; // 0 if not set static const HuffSym huff_sym_table[]; @@ -275,7 +263,6 @@ class U_EXPORT UHTTP2 { { U_TRACE_NO_PARAM(0, "UHTTP2::dtor()") - delete wbuffer; delete[] vConnection; } @@ -285,18 +272,55 @@ class U_EXPORT UHTTP2 { static void manageData(); static void sendGoAway(); static bool initRequest(); - static void sendContinue(); + static void writeResponse(); static void decodeHeaders(); static void manageHeaders(); - static void writeResponse(); static void handlerRequest(); static void handlerResponse(); static void sendResetStream(); static void sendWindowUpdate(); - static void reset(UClientImage_Base* pclient); + static void handlerDelete(UClientImage_Base* pclient); static void updateSetting(unsigned char* ptr, uint32_t len); - static void writeData(struct iovec* iov, bool bdata, bool flag, bool bnghttp2); + static void writeData(struct iovec* iov, bool bdata, bool flag); + + static void resetDataRead() + { + U_TRACE_NO_PARAM(0, "UHTTP2::resetDataRead()") + + UClientImage_Base::rstart = 0; + + UClientImage_Base::rbuffer->setEmptyForce(); + } + + static void clear() + { + U_TRACE_NO_PARAM(0, "UHTTP2::clear()") + + for (pStream = pConnection->streams; pStream <= pStreamEnd; ++pStream) + { + pStream->body.clear(); + pStream->headers.clear(); + + pStream->id = + pStream->state = + pStream->clength = 0; + } + +# ifdef DEBUG + for (pStreamEnd = (pConnection->streams+HTTP2_MAX_CONCURRENT_STREAMS); pStream < pStreamEnd; ++pStream) + { + // U_INTERNAL_DUMP("pStream index = %u", pStream - pConnection->streams) + + U_ASSERT(pStream->body.empty()) + U_ASSERT(pStream->headers.empty()) + + U_INTERNAL_ASSERT_EQUALS(pStream->id, 0) + U_INTERNAL_ASSERT_EQUALS(pStream->clength, 0) + U_INTERNAL_ASSERT_EQUALS(pStream->state, STREAM_STATE_IDLE) + } +# endif + } static void readPriority(unsigned char* ptr) { @@ -315,15 +339,6 @@ class U_EXPORT UHTTP2 { if (priority_dependency == frame.stream_id) nerror = PROTOCOL_ERROR; } - static void resetDataRead() - { - U_TRACE_NO_PARAM(0, "UHTTP2::resetDataRead()") - - UClientImage_Base::rstart = 0; - - UClientImage_Base::rbuffer->setEmptyForce(); - } - static void setEncoding(const UString& x) { U_TRACE(0, "UHTTP2::setEncoding(%V)", x.rep) @@ -363,6 +378,19 @@ class U_EXPORT UHTTP2 { static void updateSetting(const UString& data) { updateSetting((unsigned char*)U_STRING_TO_PARAM(data)); } + static void writev(struct iovec* iov, int iovcnt, uint32_t count) + { + U_TRACE(0, "UHTTP2::writev(%p,%d,%u)", iov, iovcnt, count) + + U_DUMP_IOVEC(iov,iovcnt) + +# if defined(USE_LIBSSL) || defined(_MSWINDOWS_) + (void) USocketExt::writev( UServer_Base::csocket, iov, iovcnt, count, 0); +# else + (void) USocketExt::_writev(UServer_Base::csocket, iov, iovcnt, count, 0); +# endif + } + static bool setIndexStaticTable(UHashMap* table, const char* key, uint32_t length) { U_TRACE(0, "UHTTP2::setIndexStaticTable(%p,%.*S,%u)", table, length, key, length) @@ -378,19 +406,6 @@ class U_EXPORT UHTTP2 { U_RETURN(true); // NB: ignore case... } - static void writev(struct iovec* iov, int iovcnt, uint32_t count) - { - U_TRACE(0, "UHTTP2::writev(%p,%d,%u)", iov, iovcnt, count) - - U_DUMP_IOVEC(iov,iovcnt) - -# if defined(USE_LIBSSL) || defined(_MSWINDOWS_) - (void) USocketExt::writev( UServer_Base::csocket, iov, iovcnt, count, 0); -# else - (void) USocketExt::_writev(UServer_Base::csocket, iov, iovcnt, count, 0); -# endif - } - // HPACK (HTTP headers compression) static bool isHpackError() diff --git a/include/ulib/utility/string_ext.h b/include/ulib/utility/string_ext.h index df3aed897..6dee89497 100644 --- a/include/ulib/utility/string_ext.h +++ b/include/ulib/utility/string_ext.h @@ -92,7 +92,7 @@ class U_EXPORT UStringExt { { U_TRACE(0, "UStringExt::numberToString(%u)", n) - UString x(10U); + UString x(22U); x.setFromNumber32(n); @@ -318,7 +318,7 @@ class U_EXPORT UStringExt { { U_TRACE_NO_PARAM(0, "UStringExt::getPidProcess()") - UString value(10U); + UString value(22U); U_MEMCPY(value.data(), u_pid_str, u_pid_str_len); diff --git a/src/ulib/container/hash_map.cpp b/src/ulib/container/hash_map.cpp index 04e0c1192..604578d2f 100644 --- a/src/ulib/container/hash_map.cpp +++ b/src/ulib/container/hash_map.cpp @@ -779,16 +779,16 @@ uint32_t UHashMap::loadFromData(const char* ptr, uint32_t sz) str.setFromData(&ptr, _end - ptr, terminator); } - if (str.empty()) - { - U_WARNING("UHashMap::loadFromData() has found a key(%u) = %V without value", _key.size(), _key.rep); - } - else + if (str) { U_INTERNAL_ASSERT(str.isNullTerminated()) insert(_key, str); + + continue; } + + U_WARNING("UHashMap::loadFromData() has found a key(%u) = %V without value", _key.size(), _key.rep); } U_INTERNAL_DUMP("ptr - _start = %lu", ptr - _start) @@ -800,6 +800,55 @@ uint32_t UHashMap::loadFromData(const char* ptr, uint32_t sz) U_RETURN(sz); } +uint32_t UHashMap::getSpaceToDump() const +{ + U_TRACE_NO_PARAM(0, "UHashMap::getSpaceToDump()") + + U_CHECK_MEMORY + + U_INTERNAL_DUMP("_length = %u", _length) + + uint32_t space = U_CONSTANT_SIZE("[\n]"); + + if (_length) + { + uint32_t sz; + UHashMapNode* pnode; + UHashMapNode* pnext; + + for (uint32_t _index = 0; _index < _capacity; ++_index) + { + pnode = table[_index]; + + if (pnode == 0) continue; + +loop: U_INTERNAL_ASSERT_POINTER(pnode) + + sz = pnode->key->size(); + + U_INTERNAL_DUMP("pnode->key(%u) = %p %V", sz, pnode->key, pnode->key) + + U_INTERNAL_ASSERT_MAJOR(sz, 0) + + space += sz + 1 + ((UStringRep*)pnode->elem)->getSpaceToDump() + 1; + + if (pnode->next) + { + pnext = pnode->next; + + U_INTERNAL_ASSERT_POINTER(pnext) + U_INTERNAL_ASSERT_DIFFERS(pnode, pnext) + + pnode = pnext; + + goto loop; + } + } + } + + U_RETURN(space); +} + bool UHashMap::empty() { U_TRACE_NO_PARAM(0, "UHashMap::empty()") diff --git a/src/ulib/debug/debug_common.cpp b/src/ulib/debug/debug_common.cpp index 86059a1db..1cb428a0f 100644 --- a/src/ulib/debug/debug_common.cpp +++ b/src/ulib/debug/debug_common.cpp @@ -254,7 +254,7 @@ __noreturn void U_EXPORT u_debug_exec(const char* pathname, char* const argv[], if (flag_trace_active == false) { char buf[64]; - uint32_t bytes_written = u__snprintf(buf, sizeof(buf), U_CONSTANT_TO_PARAM("%W%N%W: %WWARNING: %W"),BRIGHTCYAN,RESET,YELLOW,RESET); + uint32_t bytes_written = u__snprintf(buf, sizeof(buf), U_CONSTANT_TO_PARAM("%W%N%W: %WWARNING: %W"), BRIGHTCYAN, RESET, YELLOW, RESET); (void) write(STDERR_FILENO, buf, bytes_written); (void) write(STDERR_FILENO, buffer, iov[1].iov_len); @@ -264,8 +264,19 @@ __noreturn void U_EXPORT u_debug_exec(const char* pathname, char* const argv[], if (flag_trace_active == false) { + uint32_t i; + (void) write(STDERR_FILENO, buffer, iov[1].iov_len); (void) write(STDERR_FILENO, iov[2].iov_base, iov[2].iov_len); + + for (i = 0; argv[i]; ++i) (void) write(STDERR_FILENO, buffer, u__snprintf(buffer, sizeof(buffer), U_CONSTANT_TO_PARAM("argv[%2u] = %p %S"), i, argv[i], argv[i])); + (void) write(STDERR_FILENO, buffer, u__snprintf(buffer, sizeof(buffer), U_CONSTANT_TO_PARAM("argv[%2u] = %p %S"), i, argv[i], argv[i])); + + if (envp) + { + for (i = 0; envp[i]; ++i) (void) write(STDERR_FILENO, buffer, u__snprintf(buffer, sizeof(buffer), U_CONSTANT_TO_PARAM("envp[%2u] = %p %S"), i, envp[i], envp[i])); + (void) write(STDERR_FILENO, buffer, u__snprintf(buffer, sizeof(buffer), U_CONSTANT_TO_PARAM("envp[%2u] = %p %S"), i, envp[i], envp[i])); + } } else { diff --git a/src/ulib/json/value.cpp b/src/ulib/json/value.cpp index a4d1cebe1..45d10e3c1 100644 --- a/src/ulib/json/value.cpp +++ b/src/ulib/json/value.cpp @@ -17,7 +17,6 @@ int UValue::jsonParseFlags; char* UValue::pstringify; -UValue* UValue::phead; UValue* UValue::pnode; uint32_t UValue::size; #ifdef DEBUG @@ -118,6 +117,8 @@ void UValue::clear() case_double: value.ival = 0ULL; + U_ASSERT(empty()) + return; case_string: @@ -821,7 +822,7 @@ loop: while (u__isspace(*s)) ++s; } dquote: - // U_INTERNAL_DUMP("c = %C", *s) + // U_INTERNAL_DUMP("c = %C", *s) if (u__isvalidchar((c = *s)) == false) break; diff --git a/src/ulib/net/server/client_image.cpp b/src/ulib/net/server/client_image.cpp index c13c88844..abba48309 100644 --- a/src/ulib/net/server/client_image.cpp +++ b/src/ulib/net/server/client_image.cpp @@ -473,7 +473,7 @@ void UClientImage_Base::handlerDelete() #ifndef U_HTTP2_DISABLE U_INTERNAL_DUMP("U_http_version = %C", U_http_version) - if (U_http_version == '2') UHTTP2::reset(this); + if (U_http_version == '2') UHTTP2::handlerDelete(this); #endif if (bsocket_open) socket->close(); @@ -857,9 +857,6 @@ void UClientImage_Base::manageReadBufferResize(uint32_t n) if (U_http_info.user_agent_len) U_http_info.user_agent += diff; if (U_http_accept_language_len) U_http_info.accept_language += diff; -# ifndef U_HTTP2_DISABLE - if (U_http2_settings_len) UHTTP2::upgrade_settings += diff; -# endif if (U_http_websocket_len) UWebSocket::upgrade_settings += diff; U_INTERNAL_DUMP("host = %.*S", U_HTTP_HOST_TO_TRACE) diff --git a/src/ulib/net/server/plugin/mod_nocat.cpp b/src/ulib/net/server/plugin/mod_nocat.cpp index 7062a9330..f33803fda 100644 --- a/src/ulib/net/server/plugin/mod_nocat.cpp +++ b/src/ulib/net/server/plugin/mod_nocat.cpp @@ -1316,7 +1316,7 @@ bool UNoCatPlugIn::setPeerLabel() { // typedef struct { uint32_t area, leasetime; time_t expiration; } __attribute__((packed)) tdbdata; - UString area(10U); + UString area(32U); uint32_t id = *(uint32_t*)value.data(); if (id == 16777215) (void) area.assign(U_CONSTANT_TO_PARAM("ffffff")); diff --git a/src/ulib/net/server/plugin/usp/businesses.usp b/src/ulib/net/server/plugin/usp/businesses.usp index 24567f7c7..08946f7be 100644 --- a/src/ulib/net/server/plugin/usp/businesses.usp +++ b/src/ulib/net/server/plugin/usp/businesses.usp @@ -2,6 +2,7 @@ prototype for Victor Stewart --> diff --git a/src/ulib/net/server/server.cpp b/src/ulib/net/server/server.cpp index 1c46a4c6b..d47301988 100644 --- a/src/ulib/net/server/server.cpp +++ b/src/ulib/net/server/server.cpp @@ -89,7 +89,6 @@ bool UServer_Base::bssl; bool UServer_Base::bipc; bool UServer_Base::binsert; bool UServer_Base::flag_loop; -bool UServer_Base::flag_sigterm; bool UServer_Base::public_address; bool UServer_Base::monitoring_process; bool UServer_Base::set_tcp_keep_alive; @@ -2729,8 +2728,9 @@ RETSIGTYPE UServer_Base::handlerForSigTERM(int signo) { U_TRACE(0, "[SIGTERM] UServer_Base::handlerForSigTERM(%d)", signo) - flag_loop = false; - flag_sigterm = true; + flag_loop = false; + + UNotifier::flag_sigterm = true; U_INTERNAL_ASSERT_POINTER(proc) @@ -2763,10 +2763,9 @@ RETSIGTYPE UServer_Base::handlerForSigTERM(int signo) # endif # endif -# ifndef U_LOG_DISABLE +# ifndef U_LOG_DISABLE if (isLog()) logMemUsage("SIGTERM"); -# endif - +# endif U_EXIT(0); } } @@ -3728,7 +3727,7 @@ void UServer_Base::run() #ifndef U_LOG_DISABLE if (isLog() && - flag_sigterm) + UNotifier::flag_sigterm) { logMemUsage("SIGTERM"); } diff --git a/src/ulib/notifier.cpp b/src/ulib/notifier.cpp index 9981f4090..9f7d696c9 100644 --- a/src/ulib/notifier.cpp +++ b/src/ulib/notifier.cpp @@ -130,6 +130,7 @@ fd_set UNotifier::fd_set_read; fd_set UNotifier::fd_set_write; # endif bool UNotifier::bread; +bool UNotifier::flag_sigterm; U_NO_EXPORT void UNotifier::notifyHandlerEvent() { @@ -1243,7 +1244,8 @@ int UNotifier::waitForEvent(int timeoutMS) if (ret > 0) U_RETURN(ret); if (ret == 0) { - u_errno = errno == EAGAIN; + errno = + u_errno = EAGAIN; U_RETURN(0); } @@ -1252,7 +1254,9 @@ int UNotifier::waitForEvent(int timeoutMS) { UInterrupt::checkForEventSignalPending(); - goto loop; + U_INTERNAL_DUMP("flag_sigterm = %b", flag_sigterm) + + if (flag_sigterm == false) goto loop; } U_RETURN(-1); @@ -1418,7 +1422,9 @@ int UNotifier::read(int fd, char* buffer, int count, int timeoutMS) { UInterrupt::checkForEventSignalPending(); - goto loop; + U_INTERNAL_DUMP("flag_sigterm = %b", flag_sigterm) + + if (flag_sigterm == false) goto loop; } } diff --git a/src/ulib/process.cpp b/src/ulib/process.cpp index da41310e3..7ca3b6d23 100644 --- a/src/ulib/process.cpp +++ b/src/ulib/process.cpp @@ -468,7 +468,7 @@ pid_t UProcess::execute(const char* pathname, char* argv[], char* envp[], bool f pid_t pid; -# ifdef HAVE_POSIX_SPAWN +# ifdef HAVE_POSIX_SPAWN posix_spawn_file_actions_t action; (void) U_SYSCALL(posix_spawn_file_actions_init, "%p", &action); @@ -512,7 +512,7 @@ pid_t UProcess::execute(const char* pathname, char* argv[], char* envp[], bool f (void) U_SYSCALL(posix_spawn, "%p,%S,%p,%p,%p,%p", &pid, pathname, &action, 0, argv, envp); (void) U_SYSCALL(posix_spawn_file_actions_destroy, "%p", &action); -# else +# else pid = U_VFORK(); if (pid == 0) // child @@ -525,7 +525,7 @@ pid_t UProcess::execute(const char* pathname, char* argv[], char* envp[], bool f // parent if (u_exec_failed) U_RETURN(-1); -# endif +# endif U_RETURN(pid); } diff --git a/src/ulib/string.cpp b/src/ulib/string.cpp index 0ad477a91..ec6b8eab8 100644 --- a/src/ulib/string.cpp +++ b/src/ulib/string.cpp @@ -2355,26 +2355,7 @@ void UStringRep::write(ostream& os) const { U_TRACE(0, "UStringRep::write(%p)", &os) - bool need_quote = (_length == 0); - - if (need_quote == false) - { - for (const unsigned char* s = (const unsigned char*)str, *_end = s + _length; s < _end; ++s) - { - unsigned char c = *s; - - if (c == '"' || - c == '\\' || - u__isspace(c)) - { - need_quote = true; - - break; - } - } - } - - if (need_quote == false) os.write(str, _length); + if (needQuote() == false) os.write(str, _length); else { os.put('"'); diff --git a/src/ulib/utility/http2.cpp b/src/ulib/utility/http2.cpp index c0f2eaf51..3e61173a7 100644 --- a/src/ulib/utility/http2.cpp +++ b/src/ulib/utility/http2.cpp @@ -12,24 +12,21 @@ // ============================================================================ #include -#include #include // huff_sym_table, huff_decode_table int UHTTP2::nerror; int UHTTP2::hpack_errno; +bool UHTTP2::bnghttp2; bool UHTTP2::bcontinue100; bool UHTTP2::bsetting_ack; bool UHTTP2::bsetting_send; bool UHTTP2::priority_exclusive; uint8_t UHTTP2::priority_weight; // 0 if not set -UString* UHTTP2::wbuffer; uint32_t UHTTP2::priority_dependency; // 0 if not set uint32_t UHTTP2::hash_static_table[61]; -uint32_t UHTTP2::wait_for; -const char* UHTTP2::upgrade_settings; +uint32_t UHTTP2::wait_for_continuation; UHTTP2::Stream* UHTTP2::pStream; UHTTP2::Stream* UHTTP2::pStreamEnd; -UHTTP2::Stream* UHTTP2::pStreamStart; UHTTP2::FrameHeader UHTTP2::frame; UHTTP2::Connection* UHTTP2::vConnection; UHTTP2::Connection* UHTTP2::pConnection; @@ -55,26 +52,26 @@ UHTTP2::HpackError UHTTP2::hpack_error[8]; #define HTTP2_MAX_WINDOW_SIZE (2147483647-1) #define HTTP2_DEFAULT_WINDOW_SIZE 65535 -// ============================ +// ===================================================== // SETTINGS FRAME -// ============================ +// ===================================================== // frame size = 12 (2*6) // settings frame // no flags // stream id = 0 -// ============================ -// max_concurrent_streams = 100 +// ===================================================== +// max_concurrent_streams = HTTP2_MAX_CONCURRENT_STREAMS // max_frame_size = 16777215 -// ============================ +// ===================================================== // 9 + 12 = 21 bytes -// ============================ +// ===================================================== #define HTTP2_SETTINGS_BIN \ "\x00\x00\x06" \ "\x04" \ "\x00" \ "\x00\x00\x00\x00" \ - "\x00\x03" "\x00\x00\x00\x64" + "\x00\x03" "\x00\x00\x00\x80" // "\x00\x05" "\x00\xff\xff\xff" (h2spec(4.2) fail with this..????) // ================================ @@ -97,10 +94,10 @@ UHTTP2::HpackError UHTTP2::hpack_error[8]; #define UINT32_MAX 4294967295U #endif -UHTTP2::Settings UHTTP2::settings = { +const UHTTP2::Settings UHTTP2::settings = { /* header_table_size = */ 4096, - /* enable_push = */ 0, - /* max_concurrent_streams = */ 100, + /* enable_push = */ 1, + /* max_concurrent_streams = */ UINT32_MAX, /* initial_window_size = (HTTP2_DEFAULT_WINDOW_SIZE) */ 65535, /* max_frame_size = */ 16384, /* max_header_list_size */ UINT32_MAX @@ -115,19 +112,23 @@ UHTTP2::Connection::Connection() : itable(53, setIndexStaticTable) reset(); + state = CONN_STATE_IDLE; + (void) memset(&idyntbl, 0, sizeof(HpackDynamicTable)); (void) memset(&odyntbl, 0, sizeof(HpackDynamicTable)); idyntbl.hpack_capacity = idyntbl.hpack_max_capacity = odyntbl.hpack_capacity = - odyntbl.hpack_max_capacity = settings.header_table_size; + odyntbl.hpack_max_capacity = 4096; + + for (uint32_t i = 0; i < HTTP2_MAX_CONCURRENT_STREAMS; ++i) (void) U_SYSCALL(memset, "%p,%d,%u", &(streams[i].id), 0, sizeof(uint32_t) * 3); #ifdef DEBUG (void) memset(&ddyntbl, 0, sizeof(HpackDynamicTable)); ddyntbl.hpack_capacity = - ddyntbl.hpack_max_capacity = settings.header_table_size; + ddyntbl.hpack_max_capacity = 4096; #endif } @@ -272,8 +273,6 @@ void UHTTP2::ctor() hpack_static_table[60].name = UString::str_www_authenticate; hash_static_table[60] = UString::str_www_authenticate->hashIgnoreCase(); - U_NEW(UString, wbuffer, UString(U_CAPACITY)); - #ifdef DEBUG if (btest) { @@ -668,7 +667,7 @@ void UHTTP2::addHpackDynTblEntry(HpackDynamicTable* dyntbl, const UString& name, dyntbl->hpack_size += size_add; - // grow the entries if full + // if full grow the entries if (dyntbl->num_entries == dyntbl->entry_capacity) { @@ -738,10 +737,10 @@ void UHTTP2::decodeHeaders(UHashMap* table, HpackDynamicTable* dyntbl, (int)((char*)&&case_13-(char*)&&cdefault), /* 13 :status 404 */ (int)((char*)&&case_14-(char*)&&cdefault), /* 14 :status 500 */ 0,/* 15 accept-charset */ - (int)((char*)&&case_16-(char*)&&cdefault), /* 16 accept-encoding */ - (int)((char*)&&case_17-(char*)&&cdefault), /* 17 accept-language */ + (int)((char*)&&case_16-(char*)&&cdefault), /* 16 accept-encoding */ + (int)((char*)&&case_17-(char*)&&cdefault), /* 17 accept-language */ 0,/* 18 accept-ranges */ - (int)((char*)&&case_19-(char*)&&cdefault), /* 19 accept */ + (int)((char*)&&case_19-(char*)&&cdefault), /* 19 accept */ 0,/* 20 access-control-allow-origin */ 0,/* 21 age */ 0,/* 22 allow */ @@ -1666,8 +1665,6 @@ insertd: binsert_dynamic_table = false; if (pseudo_header_mask != (METHOD | SCHEME| PATH)) nerror = PROTOCOL_ERROR; } -// U_DUMP_CONTAINER(*table) - U_INTERNAL_DUMP("num_entries = %u entry_capacity = %u entry_start_index = %u hpack_size = %u hpack_capacity = %u hpack_max_capacity = %u", dyntbl->num_entries, dyntbl->entry_capacity, dyntbl->entry_start_index, dyntbl->hpack_size, dyntbl->hpack_capacity, dyntbl->hpack_max_capacity) } @@ -1678,18 +1675,19 @@ void UHTTP2::decodeHeaders() U_DUMP("pStream->id = %u pStream->state = (%u, %s)", pStream->id, pStream->state, getStreamStatusDescription()) + UHashMap* table = &(pConnection->itable); + + U_ASSERT(table->empty()) U_INTERNAL_ASSERT_EQUALS(U_http_version, '2') U_INTERNAL_ASSERT_EQUALS(U_http_info.uri_len, 0) U_INTERNAL_ASSERT_MAJOR(pStream->state, STREAM_STATE_OPEN) - UHashMap* table = &(pConnection->itable); - - table->clear(); - decodeHeaders(table, &(pConnection->idyntbl), (unsigned char*)pStream->headers.data(), (unsigned char*)pStream->headers.pend()); if (nerror == NO_ERROR) { + U_DUMP_CONTAINER(*table); + U_INTERNAL_DUMP("Host = %.*S", U_HTTP_HOST_TO_TRACE) U_INTERNAL_DUMP("Range = %.*S", U_HTTP_RANGE_TO_TRACE) U_INTERNAL_DUMP("Accept = %.*S", U_HTTP_ACCEPT_TO_TRACE) @@ -1699,30 +1697,6 @@ void UHTTP2::decodeHeaders() U_INTERNAL_DUMP("Content-Type = %.*S", U_HTTP_CTYPE_TO_TRACE) U_INTERNAL_DUMP("Accept-Language = %.*S", U_HTTP_ACCEPT_LANGUAGE_TO_TRACE) - U_INTERNAL_DUMP("U_http_method_type = %B U_http_method_num = %d", U_http_method_type, U_http_method_num) - - if (pStream->clength > UHTTP::limit_request_body) - { - U_INTERNAL_DUMP("pStream->clength = %u UHTTP::limit_request_body = %u", pStream->clength, UHTTP::limit_request_body) - - nerror = ERROR_NOCLOSE; - - U_http_info.nResponseCode = HTTP_ENTITY_TOO_LARGE; - - UHTTP::setResponse(); - - return; - } - - if (pStream->clength != pStream->body.size()) - { - U_INTERNAL_DUMP("pStream->clength = %u pStream->body(%u) = %V", pStream->clength, pStream->body.size(), pStream->body.rep) - - nerror = ERROR_INCOMPLETE; - - return; - } - U_INTERNAL_DUMP("U_http_is_accept_gzip = %b", U_http_is_accept_gzip) if (U_http_is_accept_gzip == false) @@ -1731,6 +1705,8 @@ void UHTTP2::decodeHeaders() UString value = table->at(UString::str_accept_encoding->rep); + U_INTERNAL_DUMP("value = %V", value.rep) + if (value) setEncoding(value); } @@ -1742,8 +1718,12 @@ void UHTTP2::decodeHeaders() UString value = table->at(UString::str_path->rep); + U_INTERNAL_DUMP("value = %V", value.rep) + if (value) setURI(value); } + + U_INTERNAL_DUMP("U_http_method_type = %B U_http_method_num = %d", U_http_method_type, U_http_method_num) } } @@ -1751,6 +1731,12 @@ void UHTTP2::manageHeaders() { U_TRACE_NO_PARAM(0, "UHTTP2::manageHeaders()") + U_ASSERT(pStream->body.empty()) + U_ASSERT(pStream->headers.empty()) + U_INTERNAL_ASSERT_EQUALS(pStream->id, 0) + U_INTERNAL_ASSERT_EQUALS(pStream->clength, 0) + U_INTERNAL_ASSERT_EQUALS(pStream->state, STREAM_STATE_IDLE) + uint32_t padlen, sz; unsigned char* ptr = frame.payload; unsigned char* endptr = ptr + frame.length; @@ -1796,162 +1782,155 @@ void UHTTP2::manageHeaders() ptr += 5; } - if (nerror == NO_ERROR) + U_INTERNAL_DUMP("nerror = %u", nerror) + + U_INTERNAL_ASSERT_EQUALS(nerror, NO_ERROR) + + if (pStream->headers.empty() == false) { U_INTERNAL_DUMP("pStream->headers = %V", pStream->headers.rep) - if (pStream->headers.empty() == false) + if ((frame.flags & FLAG_END_STREAM) != 0) { - if ((frame.flags & FLAG_END_STREAM) != 0) - { - // check for trailer part + // check for trailer part - UHashMap* table = &(pConnection->itable); + UHashMap* table = &(pConnection->itable); - table->hash = u_hash_ignore_case((unsigned char*)U_CONSTANT_TO_PARAM("trailer")); + table->hash = u_hash_ignore_case((unsigned char*)U_CONSTANT_TO_PARAM("trailer")); - UString trailer = table->at(U_CONSTANT_TO_PARAM("trailer")); + UString trailer = table->at(U_CONSTANT_TO_PARAM("trailer")); - U_INTERNAL_DUMP("trailer = %V", trailer.rep) + U_INTERNAL_DUMP("trailer = %V", trailer.rep) - if (trailer) - { - UHashMap tmp; + if (trailer) + { + UHashMap tmp; - decodeHeaders(&tmp, &(pConnection->idyntbl), ptr, endptr); + decodeHeaders(&tmp, &(pConnection->idyntbl), ptr, endptr); - UString trailer_value = table->at(trailer.rep); + UString trailer_value = table->at(trailer.rep); - U_INTERNAL_DUMP("trailer_value = %V", trailer_value.rep) + U_INTERNAL_DUMP("trailer_value = %V", trailer_value.rep) - if (trailer_value) goto next; - } + if (trailer_value) goto next; } - - nerror = PROTOCOL_ERROR; - - return; - } - -next: sz = (endptr - ptr); - - if ((frame.flags & FLAG_END_HEADERS) != 0) - { - (void) pStream->headers.replace((const char*)ptr, sz); - - return; } - // we must wait for CONTINUATION frames for the same stream... - - U_INTERNAL_ASSERT_EQUALS(frame.flags & FLAG_END_HEADERS, 0) + nerror = PROTOCOL_ERROR; - UString bufferHeaders(ptr, sz, sz * 2); + return; + } -wait_for_CONTINUATION: - wait_for |= WAIT_CONTINUATION; +next: + sz = (endptr - ptr); - readFrame(); + pStream->state = ((frame.flags & FLAG_END_STREAM) != 0 ? STREAM_STATE_HALF_CLOSED : STREAM_STATE_OPEN); - wait_for &= ~WAIT_CONTINUATION; + if ((frame.flags & FLAG_END_HEADERS) != 0) + { + (void) pStream->headers.replace((const char*)ptr, sz); - U_DUMP("nerror = %d frame.type = (%u, %s)", nerror, frame.type, getFrameTypeDescription(frame.type)) + return; + } - if (nerror == NO_ERROR) - { - U_INTERNAL_ASSERT_EQUALS(frame.type, CONTINUATION) + // we must wait for CONTINUATION frames for the same stream... - (void) bufferHeaders.append((const char*)frame.payload, frame.length); + U_INTERNAL_ASSERT_EQUALS(frame.flags & FLAG_END_HEADERS, 0) - if ((frame.flags & FLAG_END_HEADERS) == 0) goto wait_for_CONTINUATION; + pStream->headers = UString(ptr, sz, sz * 2); - pStream->headers = bufferHeaders; - } - } + wait_for_continuation = frame.stream_id; } void UHTTP2::setStream() { U_TRACE_NO_PARAM(0, "UHTTP2::setStream()") - U_INTERNAL_DUMP("pStream->id = %u frame.stream_id = %u pConnection->max_processed_stream_id = %u", pStream->id, frame.stream_id, pConnection->max_processed_stream_id) - - if ((frame.stream_id & 1) == 0) goto err; + U_DUMP("frame.stream_id = %u pConnection->max_processed_stream_id = %u pStream->id = %u pStream->state = (%u, %s)", + frame.stream_id, pConnection->max_processed_stream_id, pStream->id, pStream->state, getStreamStatusDescription()) - if (pStream->id != frame.stream_id) + if (pStream->id == frame.stream_id) { - if (pStream->id <= 1) +chk: if (pStream->state < STREAM_STATE_HALF_CLOSED) { - if (frame.type != HEADERS) goto err; - - pStream->body.clear(); - pStream->headers.clear(); - -headers: manageHeaders(); - - if (nerror != NO_ERROR) goto err; + if ((frame.flags & FLAG_END_STREAM) != 0) pStream->state = STREAM_STATE_HALF_CLOSED; + else + { + if (frame.type == HEADERS) nerror = PROTOCOL_ERROR; + } + } + else + { + if (frame.type != WINDOW_UPDATE) nerror = STREAM_CLOSED; + } - pStream->id = frame.stream_id; - pStream->state = ((frame.flags & FLAG_END_STREAM) != 0 ? STREAM_STATE_HALF_CLOSED : STREAM_STATE_OPEN); - pStream->clength = 0; + return; + } - if (pConnection->max_processed_stream_id < frame.stream_id) pConnection->max_processed_stream_id = frame.stream_id; + if (pStream->id == 0) + { + if (frame.stream_id <= pConnection->max_processed_stream_id) + { + nerror = STREAM_CLOSED; return; } - if (frame.stream_id > pConnection->max_processed_stream_id) +manage_headers: + if (frame.type != HEADERS) { - if (frame.type != HEADERS || - pStream->state != STREAM_STATE_HALF_CLOSED) - { - nerror = ((wait_for & WAIT_CONTINUATION) != 0 ? PROTOCOL_ERROR : REFUSED_STREAM); + nerror = PROTOCOL_ERROR; - return; - } + return; + } - ++pStream; + manageHeaders(); - goto headers; - } + U_INTERNAL_DUMP("nerror = %u", nerror) - while (++pStream < pStreamEnd) + if (nerror == NO_ERROR) { - U_DUMP("pStream->id = %u pStream->state = (%u, %s)", pStream->id, pStream->state, getStreamStatusDescription()) + pStream->id = frame.stream_id; - if (pStream->id == frame.stream_id) goto chk; + if (pConnection->max_processed_stream_id < frame.stream_id) pConnection->max_processed_stream_id = frame.stream_id; } -err: nerror = PROTOCOL_ERROR; - return; } - if (wait_for == 0) + if (frame.stream_id > pConnection->max_processed_stream_id) { -chk: if (frame.type == RST_STREAM) pStream->state = STREAM_STATE_CLOSED; - else + if (++pStream >= (pConnection->streams+HTTP2_MAX_CONCURRENT_STREAMS)) { - if (pStream->state >= STREAM_STATE_HALF_CLOSED) - { - if (frame.type != WINDOW_UPDATE) nerror = STREAM_CLOSED; - } - else - { - if ((frame.flags & FLAG_END_STREAM) != 0) pStream->state = STREAM_STATE_HALF_CLOSED; - } + nerror = REFUSED_STREAM; + + return; } + + ++pStreamEnd; + + goto manage_headers; } + + for (pStream = pConnection->streams; pStream <= pStreamEnd; ++pStream) + { + U_DUMP("pStream->id = %u pStream->state = (%u, %s)", pStream->id, pStream->state, getStreamStatusDescription()) + + if (pStream->id == frame.stream_id) goto chk; + } + + nerror = PROTOCOL_ERROR; } void UHTTP2::readFrame() { U_TRACE_NO_PARAM(0, "UHTTP2::readFrame()") - U_INTERNAL_ASSERT_POINTER(pStream) U_INTERNAL_ASSERT_EQUALS(nerror, NO_ERROR) int32_t len; + uint32_t error; + const char* descr; const unsigned char* ptr; loop: @@ -1961,7 +1940,7 @@ void UHTTP2::readFrame() { if (UClientImage_Base::rbuffer->size() == UClientImage_Base::rstart) resetDataRead(); - if (USocketExt::read(UServer_Base::csocket, *UClientImage_Base::rbuffer, U_SINGLE_READ, UServer_Base::timeoutMS, UHTTP::request_read_timeout) == false) + if (USocketExt::read(UServer_Base::csocket, *UClientImage_Base::rbuffer, U_SINGLE_READ, U_SSL_TIMEOUT_MS, UHTTP::request_read_timeout) == false) { nerror = ERROR_INCOMPLETE; @@ -1976,12 +1955,12 @@ void UHTTP2::readFrame() ptr[2]; frame.type = ptr[3]; frame.flags = ptr[4]; - frame.stream_id = ntohl(*(uint32_t*)(ptr+5) & 0x7fffffff); + frame.stream_id = ntohl(*(uint32_t*)(ptr+5)) & 0x7fffffff; U_DUMP("frame { length = %d stream_id = %d type = (%u, %s) flags = %d %B } = %#.*S", frame.length, frame.stream_id, frame.type, getFrameTypeDescription(frame.type), frame.flags, frame.flags, frame.length, ptr + HTTP2_FRAME_HEADER_SIZE) - U_INTERNAL_ASSERT_EQUALS(frame.length, ntohl(*(uint32_t*)ptr & 0x00ffffff) >> 8) + U_INTERNAL_ASSERT_EQUALS(frame.length, (ntohl(*(uint32_t*)ptr) & 0x00ffffff) >> 8) if (frame.length > pConnection->peer_settings.max_frame_size) { @@ -2006,7 +1985,7 @@ void UHTTP2::readFrame() UClientImage_Base::rstart = 0; } - if (USocketExt::read(UServer_Base::csocket, *UClientImage_Base::rbuffer, len, UServer_Base::timeoutMS, UHTTP::request_read_timeout) == false) + if (USocketExt::read(UServer_Base::csocket, *UClientImage_Base::rbuffer, len, U_SSL_TIMEOUT_MS, UHTTP::request_read_timeout) == false) { nerror = ERROR_INCOMPLETE; @@ -2017,6 +1996,38 @@ void UHTTP2::readFrame() frame.payload = (unsigned char*) UClientImage_Base::rbuffer->c_pointer(UClientImage_Base::rstart); UClientImage_Base::rstart += (uint32_t)frame.length; + U_INTERNAL_DUMP("wait_for_continuation = %u", wait_for_continuation) + + if (frame.type > CONTINUATION) + { + if (wait_for_continuation == 0) goto ret; // The endpoint MUST discard frames that have unknown or unsupported types + + nerror = PROTOCOL_ERROR; + + goto end; + } + + if (wait_for_continuation) + { + if (frame.type == CONTINUATION) + { + if (wait_for_continuation == frame.stream_id) + { + (void) pStream->headers.append((const char*)frame.payload, frame.length); + + if ((frame.flags & FLAG_END_HEADERS) == 0) goto loop; + + wait_for_continuation = 0; + + return; + } + } + + nerror = PROTOCOL_ERROR; + + goto end; + } + if (frame.type == SETTINGS) { if (frame.stream_id) @@ -2037,8 +2048,6 @@ void UHTTP2::readFrame() bsetting_ack = true; - U_DUMP("pConnection->inp_window = %d pStream->state = (%u, %s)", pConnection->inp_window, pStream->state, getStreamStatusDescription()) - goto ret; } @@ -2064,17 +2073,6 @@ void UHTTP2::readFrame() goto ret; } - U_INTERNAL_DUMP("wait_for = %u %B", wait_for, wait_for) - - if (frame.type > CONTINUATION) - { - if ((wait_for & WAIT_CONTINUATION) == 0) goto ret; // The endpoint MUST discard frames that have unknown or unsupported types - - nerror = PROTOCOL_ERROR; - - goto end; - } - if (frame.stream_id == 0) // stream ID zero is for connection-oriented stuff { if (frame.type == WINDOW_UPDATE) @@ -2087,7 +2085,7 @@ void UHTTP2::readFrame() goto end; } - uint32_t window_size_increment = ntohl(*(uint32_t*)frame.payload & 0x7fffffff); + uint32_t window_size_increment = ntohl(*(uint32_t*)frame.payload) & 0x7fffffff; U_INTERNAL_DUMP("pConnection->out_window = %d window_size_increment = %u", pConnection->out_window, window_size_increment) @@ -2112,9 +2110,8 @@ void UHTTP2::readFrame() if (frame.type == GOAWAY) { - uint32_t error = ntohl(*(uint32_t*)(frame.payload+4)); - - const char* descr = getFrameErrorCodeDescription(error); + error = ntohl(*(uint32_t*)(frame.payload+4)); + descr = getFrameErrorCodeDescription(error); U_DEBUG("Received GOAWAY frame with error (%u, %s)", error, descr) @@ -2151,6 +2148,13 @@ void UHTTP2::readFrame() goto end; } + if ((frame.stream_id & 1) == 0) + { + nerror = PROTOCOL_ERROR; + + goto end; + } + if (frame.type == PRIORITY) { if (frame.length != 5) nerror = FRAME_SIZE_ERROR; @@ -2169,32 +2173,20 @@ void UHTTP2::readFrame() goto end; } - if (frame.type == RST_STREAM) - { - uint32_t error; - const char* descr; + setStream(); - if (frame.length != 4) - { - nerror = FRAME_SIZE_ERROR; + U_INTERNAL_DUMP("nerror = %u", nerror) - goto end; - } + if (nerror != NO_ERROR) goto end; - descr = getFrameErrorCodeDescription((error = ntohl(*(uint32_t*)frame.payload))); - - U_DEBUG("Received RST_STREAM frame for stream %u with error (%u, %s)", frame.stream_id, error, descr) + if (wait_for_continuation) + { + U_INTERNAL_DUMP("wait_for_continuation = %u", wait_for_continuation) - U_SRV_LOG("received RST_STREAM frame for stream %u with error (%u, %s)", frame.stream_id, error, descr); + goto loop; } - U_DUMP("frame.type = (%u, %s) pStream->state = (%u, %s)", frame.type, getFrameTypeDescription(frame.type), pStream->state, getStreamStatusDescription()) - - setStream(); - - U_INTERNAL_DUMP("nerror = %d", nerror) - - if (nerror != NO_ERROR) goto err; + if (frame.type == HEADERS) goto ret; if (frame.type == DATA) { @@ -2211,7 +2203,7 @@ void UHTTP2::readFrame() { nerror = PROTOCOL_ERROR; - return; + goto end; } (void) pStream->body.append((const char*)frame.payload+1, frame.length-padlen-1); @@ -2238,8 +2230,7 @@ void UHTTP2::readFrame() if (frame.type == WINDOW_UPDATE) { - if ((wait_for & WAIT_WINDOW_UPDATE) == 0 && - pStream->state != STREAM_STATE_OPEN) + if (pStream->state == STREAM_STATE_IDLE) { nerror = PROTOCOL_ERROR; @@ -2249,53 +2240,46 @@ void UHTTP2::readFrame() goto window_update; } - if ((wait_for & WAIT_WINDOW_UPDATE) != 0) + if (frame.type == RST_STREAM) { - nerror = PROTOCOL_ERROR; + if (frame.length != 4) + { + nerror = FRAME_SIZE_ERROR; - goto end; - } + goto end; + } - if ((wait_for & WAIT_CONTINUATION) != 0) - { - if (frame.type == CONTINUATION) return; + error = ntohl(*(uint32_t*)frame.payload); + descr = getFrameErrorCodeDescription(error); - nerror = PROTOCOL_ERROR; + U_DEBUG("Received RST_STREAM frame for stream %u with error (%u, %s)", frame.stream_id, error, descr) - goto end; - } + U_SRV_LOG("received RST_STREAM frame for stream %u with error (%u, %s)", frame.stream_id, error, descr); - if (frame.type != HEADERS && - frame.type != RST_STREAM) - { - nerror = PROTOCOL_ERROR; + pStream->state = STREAM_STATE_CLOSED; - goto end; + goto ret; } -ret: - U_DUMP("bsetting_ack = %b UClientImage_Base::rbuffer->size() = %u UClientImage_Base::rstart = %u pStream->state = (%u, %s)", - bsetting_ack, UClientImage_Base::rbuffer->size(), UClientImage_Base::rstart, pStream->state, getStreamStatusDescription()) + U_DUMP("frame.type = (%u, %s)", frame.type, getFrameTypeDescription(frame.type)) - if (bsetting_ack == false || // we wait for SETTINGS ack... - pStream->state <= STREAM_STATE_OPEN || - UClientImage_Base::rbuffer->size() > UClientImage_Base::rstart) - { - goto loop; - } + nerror = PROTOCOL_ERROR; + +end: + U_INTERNAL_DUMP("nerror = %u", nerror) - if (frame.type == RST_STREAM) nerror = ERROR_NOCLOSE; + resetDataRead(); return; -err: - U_INTERNAL_DUMP("nerror = %d wait_for = %u %B", nerror, wait_for, wait_for) +ret: + U_INTERNAL_DUMP("pConnection->out_window = %d UClientImage_Base::rbuffer->size() = %u UClientImage_Base::rstart = %u", + pConnection->out_window, UClientImage_Base::rbuffer->size(), UClientImage_Base::rstart) - if (wait_for == 0) + if (pConnection->out_window == 0 || + UClientImage_Base::rbuffer->size() > UClientImage_Base::rstart) { -end: U_INTERNAL_DUMP("nerror = %d", nerror) - - resetDataRead(); + goto loop; } } @@ -2416,13 +2400,15 @@ void UHTTP2::handlerResponse() U_INTERNAL_DUMP("ext(%u) = %#V", sz2, UHTTP::ext->rep) - wbuffer->setBuffer(800U + sz1 + sz2); + UClientImage_Base::wbuffer->setBuffer(800U + sz1 + sz2); - unsigned char* dst = (unsigned char*) wbuffer->data(); + unsigned char* dst = (unsigned char*)UClientImage_Base::wbuffer->data(); if (U_http_info.nResponseCode == HTTP_NOT_IMPLEMENTED || U_http_info.nResponseCode == HTTP_OPTIONS_RESPONSE) { + UClientImage_Base::body->clear(); // clean body to avoid writev() in response... + UClientImage_Base::setCloseConnection(); /** @@ -2599,14 +2585,14 @@ void UHTTP2::handlerResponse() dst += 4; } - wbuffer->size_adjust((const char*)dst); + UClientImage_Base::wbuffer->size_adjust((const char*)dst); - U_INTERNAL_ASSERT_MINOR(wbuffer->size(), pConnection->peer_settings.max_frame_size) + U_INTERNAL_ASSERT_MINOR(UClientImage_Base::wbuffer->size(), pConnection->peer_settings.max_frame_size) } -void UHTTP2::writeData(struct iovec* iov, bool bdata, bool flag, bool bnghttp2) +void UHTTP2::writeData(struct iovec* iov, bool bdata, bool flag) { - U_TRACE(0, "UHTTP2::writeData(%p,%b,%b,%b)", iov, bdata, flag, bnghttp2) + U_TRACE(0, "UHTTP2::writeData(%p,%b,%b,%b)", iov, bdata, flag) char* ptr = (char*)iov[3].iov_base; char* ptr2 = (char*)iov[2].iov_base; @@ -2621,8 +2607,8 @@ void UHTTP2::writeData(struct iovec* iov, bool bdata, bool flag, bool bnghttp2) ptr2[4] = flag; U_DUMP("frame data response { length = %d stream_id = %d type = (%d, %s) flags = %d } = %#.*S", - ntohl(*(uint32_t*) ptr2 & 0x00ffffff) >> 8, - ntohl(*(uint32_t*)(ptr2+5) & 0x7fffffff), ptr2[3], getFrameTypeDescription(ptr2[3]), ptr2[4], ntohl(*(uint32_t*)ptr2 & 0x00ffffff) >> 8, ptr) + (ntohl(*(uint32_t*) ptr2) & 0x00ffffff) >> 8, + ntohl(*(uint32_t*)(ptr2+5)) & 0x7fffffff, ptr2[3], getFrameTypeDescription(ptr2[3]), ptr2[4], (ntohl(*(uint32_t*)ptr2) & 0x00ffffff) >> 8, ptr) if (bdata) writev(iov, 4, HTTP2_FRAME_HEADER_SIZE+sz1+HTTP2_FRAME_HEADER_SIZE+sz2); else writev(iov+2, 2, HTTP2_FRAME_HEADER_SIZE+sz2); @@ -2726,27 +2712,32 @@ void UHTTP2::writeResponse() char buffer2[HTTP2_FRAME_HEADER_SIZE]; char* ptr = UClientImage_Base::body->data(); - char* ptr0 = wbuffer->data(); + char* ptr0 = UClientImage_Base::wbuffer->data(); char* ptr1 = buffer1; - uint32_t sz0 = wbuffer->size(), - body_sz = (UHTTP::isHEAD() ? 0 : UClientImage_Base::body->size()); + uint32_t sz0 = UClientImage_Base::wbuffer->size(), + body_sz = UClientImage_Base::body->size(); struct iovec iov_vec[4] = { { (caddr_t)buffer1, HTTP2_FRAME_HEADER_SIZE }, { (caddr_t)ptr0, sz0 }, { (caddr_t)buffer2, HTTP2_FRAME_HEADER_SIZE }, { (caddr_t)ptr, body_sz } }; + U_INTERNAL_DUMP("UClientImage_Base::wbuffer(%u) = %V", sz0, UClientImage_Base::wbuffer->rep) + + U_INTERNAL_ASSERT_MAJOR((int)pStream->id, 0) + U_INTERNAL_ASSERT(*UClientImage_Base::wbuffer) + *(uint32_t*) ptr1 = htonl(sz0 << 8); ptr1[3] = HEADERS; ptr1[4] = FLAG_END_HEADERS | (body_sz == 0); // FLAG_END_STREAM (1) *(uint32_t*)(ptr1+5) = htonl(pStream->id); U_DUMP("frame header response { length = %d stream_id = %d type = (%d, %s) flags = %d } = %#.*S", - ntohl(*(uint32_t*) ptr1 & 0x00ffffff) >> 8, - ntohl(*(uint32_t*)(ptr1+5) & 0x7fffffff), ptr1[3], getFrameTypeDescription(ptr1[3]), ptr1[4], ntohl(*(uint32_t*)ptr1 & 0x00ffffff) >> 8, ptr0) + (ntohl(*(uint32_t*) ptr1) & 0x00ffffff) >> 8, + ntohl(*(uint32_t*)(ptr1+5)) & 0x7fffffff, ptr1[3], getFrameTypeDescription(ptr1[3]), ptr1[4], (ntohl(*(uint32_t*)ptr1) & 0x00ffffff) >> 8, ptr0) - U_SRV_LOG_WITH_ADDR("send response (HTTP2,id:%u,bytes:%u) %#.*S to", pStream->id, sz0+body_sz, sz0, ptr0); + U_SRV_LOG_WITH_ADDR("send response (%s,id:%u,bytes:%u) %#.*S to", bnghttp2 ? "nghttp2" : "HTTP2", pStream->id, sz0+body_sz, sz0, ptr0); if (body_sz == 0) { @@ -2761,8 +2752,6 @@ void UHTTP2::writeResponse() U_INTERNAL_ASSERT_MAJOR(body_sz, 0) - bool bnghttp2 = (u_clientimage_info.http_info.user_agent_len && memcmp(u_clientimage_info.http_info.user_agent, U_CONSTANT_TO_PARAM("nghttp2")) == 0); - char* ptr2 = buffer2; *(uint32_t*)(ptr2+5) = htonl(pStream->id); @@ -2771,7 +2760,7 @@ void UHTTP2::writeResponse() { pConnection->out_window -= body_sz; - writeData(iov_vec, true, FLAG_END_STREAM, bnghttp2); + writeData(iov_vec, true, FLAG_END_STREAM); return; } @@ -2788,23 +2777,24 @@ void UHTTP2::writeResponse() iov_vec[3].iov_len = pConnection->out_window; pConnection->out_window = 0; - writeData(iov_vec, true, false, bnghttp2); + writeData(iov_vec, true, false); } // we must wait for a Window Update frame if the current window size is not sufficient... -loop: - wait_for |= WAIT_WINDOW_UPDATE; +#ifdef DEBUG + Stream* pStreamEndOld = pStreamEnd; +#endif +loop: readFrame(); - wait_for &= ~WAIT_WINDOW_UPDATE; - - U_INTERNAL_DUMP("nerror = %d", nerror) + U_INTERNAL_DUMP("nerror = %u", nerror) if (nerror == NO_ERROR) { U_INTERNAL_ASSERT_MAJOR(pConnection->out_window, 0) + U_INTERNAL_ASSERT_EQUALS(pStreamEnd, pStreamEndOld) iov_vec[3].iov_base = ptr; @@ -2815,7 +2805,7 @@ void UHTTP2::writeResponse() iov_vec[3].iov_len = pConnection->out_window; pConnection->out_window = 0; - writeData(iov_vec, false, false, bnghttp2); + writeData(iov_vec, false, false); goto loop; } @@ -2823,8 +2813,51 @@ void UHTTP2::writeResponse() iov_vec[3].iov_len = body_sz; pConnection->out_window -= body_sz; - writeData(iov_vec, false, true, bnghttp2); + writeData(iov_vec, false, true); + } +} + +void UHTTP2::handlerDelete(UClientImage_Base* pclient) +{ + U_TRACE(0, "UHTTP2::handlerDelete(%p)", pclient) + + U_INTERNAL_ASSERT_EQUALS(U_http_version, '2') + + uint32_t idx = (pclient - UServer_Base::vClientImage); + + U_INTERNAL_DUMP("idx = %u", idx) + + U_INTERNAL_ASSERT_MINOR(idx, UNotifier::max_connection) + + pConnection = vConnection + idx; + +#ifdef DEBUG + U_DUMP("pConnection->state = (%u, %s) pConnection->max_processed_stream_id = %u", pConnection->state, getConnectionStatusDescription(), pConnection->max_processed_stream_id) + + for (pStream = pConnection->streams, pStreamEnd = (pStream+HTTP2_MAX_CONCURRENT_STREAMS); pStream < pStreamEnd; ++pStream) + { + U_ASSERT(pStream->body.empty()) + U_ASSERT(pStream->headers.empty()) + + U_INTERNAL_ASSERT_EQUALS(pStream->id, 0) + U_INTERNAL_ASSERT_EQUALS(pStream->clength, 0) + U_INTERNAL_ASSERT_EQUALS(pStream->state, STREAM_STATE_IDLE) + } +#endif + + pConnection->state = CONN_STATE_IS_CLOSING; + + U_INTERNAL_DUMP("pclient->socket->iState = %u", pclient->socket->iState) + + if (pclient->socket->isTimeout()) + { + nerror = NO_ERROR; + + sendGoAway(); } + + clearHpackDynTbl(&(pConnection->idyntbl)); + clearHpackDynTbl(&(pConnection->odyntbl)); } bool UHTTP2::initRequest() @@ -2841,36 +2874,31 @@ bool UHTTP2::initRequest() pConnection = (vConnection + sz); - U_DUMP("pConnection->state = (%u, %s) pConnection->stream_idx = %u pConnection->max_processed_stream_id = %u", - pConnection->state, getConnectionStatusDescription(), pConnection->stream_idx, pConnection->max_processed_stream_id) + U_DUMP("pConnection->state = (%u, %s) pConnection->max_processed_stream_id = %u", pConnection->state, getConnectionStatusDescription(), pConnection->max_processed_stream_id) - pStreamEnd = pConnection->streams + 100; + pConnection->itable.clear(); // NB: we can't clear it before because UClientImage_Base::getRequestUri() depend on it... - if (pConnection->state == CONN_STATE_OPEN) - { - pStream = - pStreamStart = pConnection->streams + pConnection->stream_idx; + pStream = + pStreamEnd = pConnection->streams; - U_DUMP("pStream->state = (%u, %s)", pStream->state, getStreamStatusDescription()) + U_DUMP("pStream->state = (%u, %s)", pStream->state, getStreamStatusDescription()) - U_RETURN(true); - } + U_ASSERT(pStream->body.empty()) + U_ASSERT(pStream->headers.empty()) - pStream = - pStreamStart = pConnection->streams; + U_INTERNAL_ASSERT_EQUALS(pStream->id, 0) + U_INTERNAL_ASSERT_EQUALS(pStream->clength, 0) + U_INTERNAL_ASSERT_EQUALS(pStream->state, STREAM_STATE_IDLE) - pStream->id = - pStream->state = 0; + if (pConnection->state != CONN_STATE_IDLE) + { + if (pConnection->state == CONN_STATE_OPEN) U_RETURN(true); - if (pConnection->state != CONN_STATE_IDLE) pConnection->reset(); + pConnection->reset(); + } pConnection->state = CONN_STATE_OPEN; - U_INTERNAL_DUMP("HTTP2-Settings(%u): = %.*S U_http_method_type = %d %B", U_http2_settings_len, U_http2_settings_len, upgrade_settings, U_http_method_type, U_http_method_type) - - U_INTERNAL_ASSERT_EQUALS(pStream->id, 0) - U_INTERNAL_ASSERT_EQUALS(pStream->state, STREAM_STATE_IDLE) - U_RETURN(false); } @@ -2881,269 +2909,283 @@ void UHTTP2::handlerRequest() uint32_t sz; const char* ptr; - nerror = - hpack_errno = 0; + bnghttp2 = + bsetting_ack = + bsetting_send = false; - if ((bsetting_ack = - bsetting_send = initRequest())) - { - goto read_frame; - } + nerror = + hpack_errno = + wait_for_continuation = 0; - if (U_http2_settings_len) + if (initRequest() == false) { - if (USocketExt::write(UServer_Base::csocket, U_CONSTANT_TO_PARAM(HTTP2_CONNECTION_UPGRADE HTTP2_SETTINGS_BIN), UServer_Base::timeoutMS) != - U_CONSTANT_SIZE(HTTP2_CONNECTION_UPGRADE HTTP2_SETTINGS_BIN)) + if (U_http2_settings_len) { - U_ClientImage_state = U_PLUGIN_HANDLER_ERROR; + bsetting_send = true; - return; - } + (void) USocketExt::write(UServer_Base::csocket, U_CONSTANT_TO_PARAM(HTTP2_CONNECTION_UPGRADE HTTP2_SETTINGS_BIN), U_SSL_TIMEOUT_MS); - bsetting_send = true; + U_INTERNAL_ASSERT_MAJOR(U_http_info.startHeader, 2) + U_INTERNAL_ASSERT_RANGE(1,UClientImage_Base::uri_offset,64) - UString buffer_settings(U_CAPACITY); + sz = U_http_info.startHeader - UClientImage_Base::uri_offset + U_CONSTANT_SIZE(" HTTP/1.1\r\n"); - UBase64::decodeUrl(upgrade_settings, U_http2_settings_len, buffer_settings); + if (sz > sizeof(UClientImage_Base::cbuffer)) goto err; - if (buffer_settings.empty()) - { - U_ClientImage_state = U_PLUGIN_HANDLER_ERROR; + U_INTERNAL_DUMP("UClientImage_Base::wbuffer(%u) = %V", UClientImage_Base::wbuffer->size(), UClientImage_Base::wbuffer->rep) - return; - } + if (*UClientImage_Base::wbuffer) UHTTP2::updateSetting(*UClientImage_Base::wbuffer); - updateSetting(buffer_settings); + U_INTERNAL_DUMP("U_http_info.uri(%u) = %.*S U_http_method_type = %B U_http_method_num = %d", + U_http_info.uri_len, U_http_info.uri_len, U_http_info.uri, U_http_method_type, U_http_method_num) - U_INTERNAL_ASSERT_MAJOR(U_http_info.startHeader, 2) - U_INTERNAL_ASSERT_RANGE(1,UClientImage_Base::uri_offset,64) + if (U_http_method_type == HTTP_OPTIONS) + { + uint32_t endHeader = U_http_info.endHeader; - sz = U_http_info.startHeader - UClientImage_Base::uri_offset + U_CONSTANT_SIZE(" HTTP/1.1\r\n"); + U_HTTP_INFO_RESET(0); - if (sz > sizeof(UClientImage_Base::cbuffer)) - { - U_ClientImage_state = U_PLUGIN_HANDLER_ERROR; + U_http_version = '2'; + U_http_info.endHeader = endHeader; - return; - } - - /** - * The HTTP/1.1 request that is sent prior to upgrade is assigned a stream identifier of 1 with default priority values. - * Stream 1 is implicitly "half-closed" from the client toward the server, since the request is completed as an HTTP/1.1 - * request. After commencing the HTTP/2 connection, stream 1 is used for the response - */ + goto next; + } - pStream->id = - pConnection->max_processed_stream_id = 1; + /** + * The HTTP/1.1 request that is sent prior to upgrade is assigned a stream identifier of 1 with default priority values. + * Stream 1 is implicitly "half-closed" from the client toward the server, since the request is completed as an HTTP/1.1 + * request. After commencing the HTTP/2 connection, stream 1 is used for the response + */ - pStream->state = STREAM_STATE_HALF_CLOSED; - pStream->clength = 0; + pStream->id = + pConnection->max_processed_stream_id = 1; - pStream->body.clear(); - pStream->headers.clear(); + pStream->state = STREAM_STATE_HALF_CLOSED; - U_INTERNAL_DUMP("U_http_info.uri(%u) = %.*S U_http_method_type = %B U_http_method_num = %d", - U_http_info.uri_len, U_http_info.uri_len, U_http_info.uri, U_http_method_type, U_http_method_num) + if ((pStream->clength = U_http_info.clength)) + { + pStream->body.setBuffer(U_http_info.clength); - if (U_http_method_type == HTTP_OPTIONS) - { - uint32_t endHeader = U_http_info.endHeader; + // NB: check for 'Expect: 100-continue' (as curl does)... - U_HTTP_INFO_RESET(0); + bcontinue100 = (UClientImage_Base::request->find("Expect: 100-continue", U_http_info.startHeader, + U_CONSTANT_SIZE("Expect: 100-continue"), U_http_info.endHeader - U_CONSTANT_SIZE(U_CRLF2) - U_http_info.startHeader) != U_NOT_FOUND); + } - U_http_version = '2'; - U_http_info.endHeader = endHeader; + U_http_info.uri = UClientImage_Base::cbuffer; - goto next; + U_MEMCPY(UClientImage_Base::cbuffer, UClientImage_Base::request->c_pointer(UClientImage_Base::uri_offset), sz); } - if (UHTTP::isPOSTorPUTorPATCH()) - { - U_INTERNAL_DUMP("U_http_info.clength = %u UHTTP::limit_request_body = %u", U_http_info.clength, UHTTP::limit_request_body) +next: U_INTERNAL_ASSERT(UClientImage_Base::request->same(*UClientImage_Base::rbuffer)) - if (U_http_info.clength > UHTTP::limit_request_body) - { - nerror = ERROR_NOCLOSE; + UClientImage_Base::request->clear(); - U_http_info.nResponseCode = HTTP_ENTITY_TOO_LARGE; + // maybe we have read more data than necessary... - UHTTP::setResponse(); + sz = UClientImage_Base::rbuffer->size(); - return; - } + U_INTERNAL_ASSERT_MAJOR(sz, 0) - (void) pStream->body.reserve((pStream->clength = U_http_info.clength)); + if (sz > U_http_info.endHeader) UClientImage_Base::rstart = U_http_info.endHeader; + else + { + // we wait for HTTP2_CONNECTION_PREFACE... - // NB: check for 'Expect: 100-continue' (as curl does)... + UClientImage_Base::rbuffer->setEmptyForce(); - bcontinue100 = UClientImage_Base::request->find("Expect: 100-continue", U_http_info.startHeader, - U_CONSTANT_SIZE("Expect: 100-continue"), U_http_info.endHeader - U_CONSTANT_SIZE(U_CRLF2) - U_http_info.startHeader) != U_NOT_FOUND; - } + if (USocketExt::read(UServer_Base::csocket, *UClientImage_Base::rbuffer, U_SINGLE_READ, U_SSL_TIMEOUT_MS, UHTTP::request_read_timeout) == false) goto err; - U_http_info.uri = UClientImage_Base::cbuffer; + UClientImage_Base::rstart = 0; - U_MEMCPY(UClientImage_Base::cbuffer, UClientImage_Base::request->c_pointer(UClientImage_Base::uri_offset), sz); - } + sz = UClientImage_Base::rbuffer->size(); + } -next: - U_INTERNAL_ASSERT(UClientImage_Base::request->same(*UClientImage_Base::rbuffer)) + ptr = UClientImage_Base::rbuffer->c_pointer(UClientImage_Base::rstart); - UClientImage_Base::request->clear(); + if (u_get_unalignedp64(ptr) != U_MULTICHAR_CONSTANT64( 'P', 'R','I',' ', '*', ' ', 'H', 'T') || + u_get_unalignedp64(ptr+8) != U_MULTICHAR_CONSTANT64( 'T', 'P','/','2', '.', '0','\r','\n') || + u_get_unalignedp64(ptr+16) != U_MULTICHAR_CONSTANT64('\r','\n','S','M','\r','\n','\r','\n')) + { + goto err; + } - // maybe we have read more data than necessary... + UClientImage_Base::rstart += U_CONSTANT_SIZE(HTTP2_CONNECTION_PREFACE); + } - sz = UClientImage_Base::rbuffer->size(); +read_request: + readFrame(); - U_INTERNAL_ASSERT_MAJOR(sz, 0) + U_INTERNAL_DUMP("nerror = %u", nerror) - if (sz > U_http_info.endHeader) UClientImage_Base::rstart = U_http_info.endHeader; - else + if (nerror == NO_ERROR) { - // we wait for HTTP2_CONNECTION_PREFACE... - - UClientImage_Base::rbuffer->setEmptyForce(); + U_INTERNAL_DUMP("bsetting_ack = %b bsetting_send = %b pStream = %p pStreamEnd = %p", bsetting_ack, bsetting_send, pStream, pStreamEnd) - if (UNotifier::waitForRead(UServer_Base::csocket->iSockDesc, U_TIMEOUT_MS) != 1 || - USocketExt::read(UServer_Base::csocket, *UClientImage_Base::rbuffer, U_SINGLE_READ, 0) == false) - { - U_ClientImage_state = U_PLUGIN_HANDLER_ERROR; + U_INTERNAL_ASSERT_RANGE(pConnection->streams,pStream,pStreamEnd) - return; - } + sz = pStream->headers.size(); - UClientImage_Base::rstart = 0; + U_INTERNAL_DUMP("sz = %u", sz) - sz = UClientImage_Base::rbuffer->size(); - } + if (sz == 0) + { + U_INTERNAL_ASSERT_EQUALS(pStream, pConnection->streams) + U_INTERNAL_ASSERT_EQUALS(pStreamEnd, pConnection->streams) - ptr = UClientImage_Base::rbuffer->c_pointer(UClientImage_Base::rstart); + U_INTERNAL_DUMP("U_http_info.uri_len = %u U_http2_settings_len = %u", U_http_info.uri_len, U_http2_settings_len) - if (u_get_unalignedp64(ptr) != U_MULTICHAR_CONSTANT64( 'P', 'R','I',' ', '*', ' ', 'H', 'T') || - u_get_unalignedp64(ptr+8) != U_MULTICHAR_CONSTANT64( 'T', 'P','/','2', '.', '0','\r','\n') || - u_get_unalignedp64(ptr+16) != U_MULTICHAR_CONSTANT64('\r','\n','S','M','\r','\n','\r','\n')) - { - U_ClientImage_state = U_PLUGIN_HANDLER_ERROR; + if (U_http2_settings_len) + { + // NB: not OPTION upgrade... - return; - } + U_INTERNAL_ASSERT(bsetting_send) - UClientImage_Base::rstart += U_CONSTANT_SIZE(HTTP2_CONNECTION_PREFACE); + if (bsetting_ack == false) goto read_request; // we must wait for SETTINGS ack... + + goto process_request; + } -read_frame: - readFrame(); + U_INTERNAL_ASSERT_EQUALS(U_http_info.uri_len, 0) - U_INTERNAL_DUMP("nerror = %d", nerror) + if (bsetting_send) goto read_request; // NB: OPTION upgrade, we need a HEADERS frame... - if (nerror == ERROR_NOCLOSE) - { - UClientImage_Base::setRequestProcessed(); + UClientImage_Base::setRequestProcessed(); - return; - } + return; + } - if (nerror == NO_ERROR) - { - U_INTERNAL_DUMP("bsetting_ack = %b bsetting_send = %b pStreamStart = %p pStream = %p pStreamEnd = %p", - bsetting_ack, bsetting_send, pStreamStart, pStream, pStreamEnd) + if (bsetting_ack == false) goto read_request; // we must wait for SETTINGS ack... U_INTERNAL_ASSERT(bsetting_ack) U_INTERNAL_ASSERT(bsetting_send) - U_INTERNAL_ASSERT_RANGE(pStreamStart,pStream,pStreamEnd) - U_INTERNAL_DUMP("bcontinue100 = %b pStream->headers = %V", bcontinue100, pStream->headers.rep) + pStream = pConnection->streams; + +loop: U_DUMP("pStream->id = %d pStream->state = (%u, %s) pStream->headers(%u) = %V pStream->clength = %u pStream->body(%u) = %V", + pStream->id, pStream->state, getStreamStatusDescription(), sz, pStream->headers.rep, pStream->clength, pStream->body.size(), pStream->body.rep) - if (pStream->headers.empty()) + if (pStream->state <= STREAM_STATE_OPEN) goto read_request; + + if ((int)pStream->id > 0) { - U_INTERNAL_DUMP("U_http_info.uri_len = %u", U_http_info.uri_len) + decodeHeaders(); // parse header block - if (U_http_info.uri_len == 0) - { - UClientImage_Base::setRequestProcessed(); + U_INTERNAL_DUMP("nerror = %u", nerror) - return; - } + if (nerror != NO_ERROR) goto err; - if (bcontinue100) +process_request: + if (pStream == pConnection->streams && + U_HTTP_USER_AGENT_MEMEQ("nghttp2")) { - bcontinue100 = false; + bnghttp2 = true; // NB: to avoid GOAWAY frame with error (6, FRAME_SIZE_ERROR) in writeResponse()... + } - sendContinue(); +# ifndef U_LOG_DISABLE + if (sz) U_SRV_LOG_WITH_ADDR("received request (%s,id:%u,bytes:%u) %#.*S from", bnghttp2 ? "nghttp2" : "HTTP2", pStream->id, sz, sz, pStream->headers.data()); +# endif - goto read_frame; // wait for DATA frames - } - } - else - { - pConnection->stream_idx = (pStream - pConnection->streams); + U_INTERNAL_DUMP("pStream->clength = %u UHTTP::limit_request_body = %u", pStream->clength, UHTTP::limit_request_body) - U_INTERNAL_DUMP("pConnection->stream_idx = %u pStream->clength = %u pStream->body(%u) = %V", pConnection->stream_idx, pStream->clength, pStream->body.size(), pStream->body.rep) + if (pStream->clength > UHTTP::limit_request_body) + { + U_http_info.nResponseCode = HTTP_ENTITY_TOO_LARGE; - if (pStream->clength == 0) + UHTTP::setResponse(); + } + else { -loop: decodeHeaders(); // parse header block + U_INTERNAL_DUMP("bcontinue100 = %b", bcontinue100) - U_INTERNAL_DUMP("nerror = %d", nerror) + if (bcontinue100) + { + bcontinue100 = false; - if (nerror != NO_ERROR) goto err; + U_INTERNAL_ASSERT(UServer_Base::csocket->isOpen()) - U_SRV_LOG_WITH_ADDR("received request (HTTP2,id:%u,bytes:%u) %#.*S from", pStream->id, pStream->headers.size(), pStream->headers.size(), pStream->headers.data()); - } + char buffer[HTTP2_FRAME_HEADER_SIZE+5] = { 0, 0, 5, + HEADERS, + FLAG_END_HEADERS, + 0, 0, 0, 0, + 8, 3, '1', '0', '0' }; - if (bcontinue100) - { - bcontinue100 = false; + ptr = buffer; - sendContinue(); + *(uint32_t*)(ptr+5) = htonl(pStream->id); - goto read_frame; // wait for DATA frames - } - - U_http_info.clength = pStream->clength; + (void) USocketExt::write(UServer_Base::csocket, buffer, sizeof(buffer), U_SSL_TIMEOUT_MS); - UString::swap(pStream->body, *UClientImage_Base::body); - } + goto read_request; // wait for DATA frames + } - U_ClientImage_state = UHTTP::manageRequest(); + if (pStream->clength != pStream->body.size()) + { + if (pStream->body.empty()) goto read_request; // wait for DATA frames - U_INTERNAL_DUMP("U_ClientImage_state = %u %B", U_ClientImage_state, U_ClientImage_state) + nerror = PROTOCOL_ERROR; + + goto err; + } - if (U_ClientImage_state == U_PLUGIN_HANDLER_ERROR) goto err; + U_http_info.clength = pStream->clength; - if (UClientImage_Base::isRequestNeedProcessing()) - { - U_ClientImage_state = UClientImage_Base::callerHandlerRequest(); + *UClientImage_Base::body = pStream->body; - if (UServer_Base::csocket->isClosed()) goto end; - } + if (UHTTP::manageRequest() == U_PLUGIN_HANDLER_ERROR) goto err; + } + + if (UHTTP::isHEAD()) UClientImage_Base::body->clear(); + + writeResponse(); - writeResponse(); + U_INTERNAL_DUMP("nerror = %u", nerror) - U_INTERNAL_DUMP("nerror = %d", nerror) + if (nerror != NO_ERROR) goto err; + } - if (nerror != NO_ERROR) goto err; + pStream->id = -pStream->id; - if (pStream > pStreamStart) + while (++pStream <= pStreamEnd) { - // TODO (manage priority) + if ((int)pStream->id > 0) + { + sz = pStream->headers.size(); - --pStream; + U_INTERNAL_DUMP("sz = %u", sz) - // ------------------------------ - // U_http_info.uri - // .... - // U_http_info.nResponseCode - // .... - // ------------------------------ - U_HTTP_INFO_RESET(0); + U_INTERNAL_ASSERT_MAJOR(sz, 0) - U_http_version = '2'; + // ------------------------------ + // U_http_info.uri + // .... + // U_http_info.nResponseCode + // .... + // ------------------------------ + U_HTTP_INFO_RESET(0); - UClientImage_Base::body->clear(); + U_http_version = '2'; - goto loop; + pConnection->itable.clear(); + + goto loop; + } } - return; + UClientImage_Base::body->clear(); + UClientImage_Base::wbuffer->clear(); + +# ifdef DEBUG + for (pStream = pConnection->streams; pStream <= pStreamEnd; ++pStream) + { + U_DUMP("pStream->id = %d pStream->state = (%u, %s) pStream->headers = %V pStream->clength = %u pStream->body(%u) = %V", + pStream->id, pStream->state, getStreamStatusDescription(), pStream->headers.rep, pStream->clength, pStream->body.size(), pStream->body.rep) + + U_INTERNAL_ASSERT_MINOR((int)pStream->id, 0) + } +# endif + + goto end; } err: @@ -3154,41 +3196,10 @@ loop: decodeHeaders(); // parse header block UClientImage_Base::close(); } -end: U_ClientImage_state = U_PLUGIN_HANDLER_ERROR; -} - -void UHTTP2::reset(UClientImage_Base* pclient) -{ - U_TRACE(0, "UHTTP2::reset(%p)", pclient) - U_INTERNAL_ASSERT_EQUALS(U_http_version, '2') - - uint32_t idx = (pclient - UServer_Base::vClientImage); - - U_INTERNAL_DUMP("idx = %u U_http2_settings_len = %u", idx, U_http2_settings_len) - - U_INTERNAL_ASSERT_MINOR(idx, UNotifier::max_connection) - - pConnection = vConnection + idx; - - U_DUMP("pConnection->state = (%d, %s)", pConnection->state, getConnectionStatusDescription()) - - pConnection->itable.clear(); - - pConnection->state = CONN_STATE_IS_CLOSING; - - clearHpackDynTbl(&(pConnection->idyntbl)); - clearHpackDynTbl(&(pConnection->odyntbl)); - - U_INTERNAL_DUMP("pclient->socket->iState = %u", pclient->socket->iState) - - if (pclient->socket->isTimeout()) - { - nerror = NO_ERROR; - - sendGoAway(); - } +end: + clear(); } #define U_HTTP2_ENTRY(n) n: descr = #n; break @@ -3215,6 +3226,7 @@ const char* UHTTP2::getFrameErrorCodeDescription(uint32_t error) case U_HTTP2_ENTRY(ENHANCE_YOUR_CALM); // 11: The endpoint detected that its peer is exhibiting a behavior that might be generating excessive load case U_HTTP2_ENTRY(INADEQUATE_SECURITY); // 12: The underlying transport has properties that do not meet minimum security requirements case U_HTTP2_ENTRY(HTTP_1_1_REQUIRED); // 13: The endpoint requires that HTTP/1.1 be used instead of HTTP/2 + case U_HTTP2_ENTRY(ERROR_INCOMPLETE); default: descr = "Error type unknown"; } @@ -3318,25 +3330,6 @@ void UHTTP2::clearHpackDynTbl(HpackDynamicTable* dyntbl) } } -void UHTTP2::sendContinue() -{ - U_TRACE_NO_PARAM(0, "UHTTP2::sendContinue()") - - U_INTERNAL_ASSERT(UServer_Base::csocket->isOpen()) - - char buffer[HTTP2_FRAME_HEADER_SIZE+5] = { 0, 0, 5, - HEADERS, - FLAG_END_HEADERS, - 0, 0, 0, 0, - 8, 3, '1', '0', '0' }; - - char* ptr = buffer; - - *(uint32_t*)(ptr+5) = htonl(pStream->id); - - (void) USocketExt::write(UServer_Base::csocket, buffer, sizeof(buffer), UServer_Base::timeoutMS); -} - void UHTTP2::sendWindowUpdate() { U_TRACE_NO_PARAM(0, "UHTTP2::sendWindowUpdate()") @@ -3361,7 +3354,7 @@ void UHTTP2::sendWindowUpdate() *(uint32_t*)(ptr+HTTP2_FRAME_HEADER_SIZE+4+5) = htonl(pStream->id); *(uint32_t*)(ptr+HTTP2_FRAME_HEADER_SIZE+4+9) = htonl(pConnection->inp_window); - (void) USocketExt::write(UServer_Base::csocket, (const char*)buffer, sizeof(buffer), UServer_Base::timeoutMS); + (void) USocketExt::write(UServer_Base::csocket, (const char*)buffer, sizeof(buffer), U_SSL_TIMEOUT_MS); } void UHTTP2::sendPing() @@ -3382,7 +3375,7 @@ void UHTTP2::sendPing() if (*(uint64_t*)frame.payload) U_MEMCPY(buffer+HTTP2_FRAME_HEADER_SIZE, frame.payload, 8); - (void) USocketExt::write(UServer_Base::csocket, buffer, sizeof(buffer), UServer_Base::timeoutMS); + (void) USocketExt::write(UServer_Base::csocket, buffer, sizeof(buffer), U_SSL_TIMEOUT_MS); } void UHTTP2::sendGoAway() @@ -3412,7 +3405,7 @@ void UHTTP2::sendGoAway() pConnection->state = CONN_STATE_IS_CLOSING; - (void) USocketExt::write(UServer_Base::csocket, buffer, sizeof(buffer), UServer_Base::timeoutMS); + (void) USocketExt::write(UServer_Base::csocket, buffer, sizeof(buffer), U_SSL_TIMEOUT_MS); } void UHTTP2::sendResetStream() @@ -3440,7 +3433,7 @@ void UHTTP2::sendResetStream() pStream->state = STREAM_STATE_CLOSED; - (void) USocketExt::write(UServer_Base::csocket, buffer, sizeof(buffer), UServer_Base::timeoutMS); + (void) USocketExt::write(UServer_Base::csocket, buffer, sizeof(buffer), U_SSL_TIMEOUT_MS); } #ifdef DEBUG @@ -3557,7 +3550,6 @@ U_EXPORT const char* UHTTP2::Connection::dump(bool reset) const *UObjectIO::os << "state " << state << '\n' << "inp_window " << inp_window << '\n' << "out_window " << out_window << '\n' - << "stream_idx " << stream_idx << '\n' << "max_processed_stream_id " << max_processed_stream_id << '\n' << "itable (UHashMap " << (void*)&itable << ')'; diff --git a/src/ulib/utility/string_ext.cpp b/src/ulib/utility/string_ext.cpp index d1222814a..85274b5ee 100644 --- a/src/ulib/utility/string_ext.cpp +++ b/src/ulib/utility/string_ext.cpp @@ -228,7 +228,7 @@ UString UStringExt::substitute(const char* s, uint32_t n, const char* a, uint32_ void* p; uint32_t start = 0, len, capacity = (n / n1); - if (capacity == 0) capacity = 10U; + if (capacity == 0) capacity = 22U; if (n2) capacity *= n2; if (capacity > (256U * 1024U * 1024U)) capacity = (256U * 1024U * 1024U); // worst case... diff --git a/src/ulib/utility/uhttp.cpp b/src/ulib/utility/uhttp.cpp index 29ea2336d..6f56dd4cc 100644 --- a/src/ulib/utility/uhttp.cpp +++ b/src/ulib/utility/uhttp.cpp @@ -2738,7 +2738,7 @@ U_NO_EXPORT void UHTTP::checkRequestForHeader() const char* pend; const char* ptr = UClientImage_Base::request->data(); - + if (U_http_info.endHeader) { U_INTERNAL_ASSERT_EQUALS(U_ClientImage_data_missing, false) @@ -2941,7 +2941,7 @@ U_NO_EXPORT void UHTTP::checkRequestForHeader() U_http_version = '2'; U_http2_settings_len = pn-ptr1; - UHTTP2::upgrade_settings = ptr1; + UBase64::decodeUrl(ptr1, U_http2_settings_len, *UClientImage_Base::wbuffer); # endif goto next; @@ -3863,7 +3863,9 @@ int UHTTP::handlerREAD() UClientImage_Base::size_request = (U_http_info.endHeader ? U_http_info.endHeader : U_http_info.startHeader + U_CONSTANT_SIZE(U_CRLF2)); - return manageRequest(); + U_ClientImage_state = manageRequest(); + + U_RETURN(U_ClientImage_state); } int UHTTP::manageRequest() @@ -4367,6 +4369,8 @@ set_uri: U_http_info.uri = alias->data(); UWebSocket::sendAccept() == false) { setBadRequest(); + + U_RETURN(U_PLUGIN_HANDLER_FINISHED); } #endif @@ -4388,6 +4392,26 @@ set_uri: U_http_info.uri = alias->data(); if (UClientImage_Base::isRequestFileCacheProcessed()) handlerResponse(); +#ifndef U_HTTP2_DISABLE + U_INTERNAL_DUMP("UClientImage_Base::size_request = %u", UClientImage_Base::size_request) + + if (UClientImage_Base::size_request == 0) + { + U_INTERNAL_DUMP("U_ClientImage_state = %u %B", U_ClientImage_state, U_ClientImage_state) + + U_INTERNAL_ASSERT(UServer_Base::csocket->isOpen()) + U_INTERNAL_ASSERT_EQUALS(U_ClientImage_data_missing, false) + U_INTERNAL_ASSERT_DIFFERS(U_ClientImage_state, U_PLUGIN_HANDLER_ERROR) + + if (UClientImage_Base::isRequestNeedProcessing()) + { + U_ClientImage_state = UClientImage_Base::callerHandlerRequest(); + + if (UServer_Base::csocket->isClosed()) U_RETURN(U_PLUGIN_HANDLER_ERROR); + } + } +#endif + U_RETURN(U_PLUGIN_HANDLER_FINISHED); } @@ -6201,12 +6225,7 @@ void UHTTP::handlerResponse() U_INTERNAL_DUMP("U_http_version = %C", U_http_version) #ifndef U_HTTP2_DISABLE - if (U_http_version == '2') - { - UClientImage_Base::wbuffer->clear(); - - UHTTP2::handlerResponse(); - } + if (U_http_version == '2') UHTTP2::handlerResponse(); else #endif { @@ -6215,6 +6234,8 @@ void UHTTP::handlerResponse() if (U_http_info.nResponseCode == HTTP_NOT_IMPLEMENTED || U_http_info.nResponseCode == HTTP_OPTIONS_RESPONSE) { + UClientImage_Base::body->clear(); // clean body to avoid writev() in response... + U_INTERNAL_DUMP("U_http_method_not_implemented = %b", U_http_method_not_implemented) if (U_http_method_not_implemented) @@ -6279,7 +6300,8 @@ void UHTTP::handlerResponse() UClientImage_Base::wbuffer->setBuffer(200U + sz1 + sz2); - base = ptr = UClientImage_Base::wbuffer->data(); + ptr = + base = UClientImage_Base::wbuffer->data(); if (sz1) { diff --git a/tests/debug/test_objectIO.cpp b/tests/debug/test_objectIO.cpp index 43f2b61a9..8fde01ba9 100644 --- a/tests/debug/test_objectIO.cpp +++ b/tests/debug/test_objectIO.cpp @@ -36,7 +36,7 @@ ostream& operator<<(ostream& os, const Prima& l) { U_TRACE(5, "Prima::operator<<()") - U_DUMP_CONTAINER(l) // per simulare ricorsione in U_OBJECT_TO_TRACE() + U_DUMP_OBJECT(l) // per simulare ricorsione in U_OBJECT_TO_TRACE() os << l.a; @@ -74,7 +74,7 @@ ostream& operator<<(ostream& os, const Seconda& l) Prima a1; - U_DUMP_CONTAINER(a1) // per simulare ricorsione in U_OBJECT_TO_TRACE() + U_DUMP_OBJECT(a1) // per simulare ricorsione in U_OBJECT_TO_TRACE() os << l.b; @@ -99,8 +99,9 @@ int U_EXPORT main(int argc, char** argv) U_SET_LOCATION_INFO; Seconda b, b1; - U_DUMP_CONTAINER(a) - U_DUMP_CONTAINER(b) + U_DUMP_OBJECT(a) + U_DUMP_OBJECT(b) + U_INTERNAL_DUMP("Prima = %O Seconda = %O", U_OBJECT_TO_TRACE(a), U_OBJECT_TO_TRACE(b)) U_SET_LOCATION_INFO; diff --git a/tests/examples/businesses.test b/tests/examples/businesses.test index ab742ea5c..4954189d2 100755 --- a/tests/examples/businesses.test +++ b/tests/examples/businesses.test @@ -50,8 +50,9 @@ DIR_CMD="../../examples/userver" start_prg_background userver_tcp -c inp/webserver.cfg -send_req $NCAT localhost 8080 inp/http/businesses1.req businesses 2 -send_req $NCAT localhost 8080 inp/http/businesses2.req businesses 2 + send_req $NCAT localhost 8080 inp/http/businesses1.req businesses 2 + send_req $NCAT localhost 8080 inp/http/businesses2.req businesses 2 + send_req $NCAT localhost 8080 inp/http/businesses3.req businesses 2 kill_prg userver_tcp TERM diff --git a/tests/examples/inp/http/businesses3.req b/tests/examples/inp/http/businesses3.req new file mode 100644 index 000000000..64d131ef1 --- /dev/null +++ b/tests/examples/inp/http/businesses3.req @@ -0,0 +1,7 @@ +POST /servlet/businesses HTTP/1.1 +Host: localhost:8080 +Connection: Keep-Alive +Content-Type: application/jsonrequest +Content-Length: 29 + +{"type":"startup","authed":0} diff --git a/tests/examples/ok/businesses.ok b/tests/examples/ok/businesses.ok index a4d5a193d..2654af8b6 100644 --- a/tests/examples/ok/businesses.ok +++ b/tests/examples/ok/businesses.ok @@ -1,13 +1,21 @@ HTTP/1.1 200 OK -Date: Sat, 10 Sep 2016 16:16:24 GMT +Date: Sun, 29 Jan 2017 14:54:34 GMT Server: ULib Connection: close Content-Length: 56 Content-Type: application/json {"name":"","rating":"","address":"","phone":"","url":""}HTTP/1.1 200 OK -Date: Sat, 10 Sep 2016 16:16:24 GMT +Date: Sun, 29 Jan 2017 14:54:34 GMT Server: ULib Connection: close Content-Length: 0 +HTTP/1.1 200 OK +Date: Sun, 29 Jan 2017 14:54:34 GMT +Server: ULib +Connection: close +Content-Length: 87 +Content-Type: application/json + +{"type":"startup","token":"","fbPermissions":["public_profile","user_friends","email"]} \ No newline at end of file diff --git a/tests/ulib/test_json.cpp b/tests/ulib/test_json.cpp index fc5655ea2..a701096de 100644 --- a/tests/ulib/test_json.cpp +++ b/tests/ulib/test_json.cpp @@ -13,20 +13,15 @@ class Request { U_MEMORY_ALLOCATOR U_MEMORY_DEALLOCATOR - UString token, type, radius, location; + UHashMap table; + UString radius, location; + UVector fbPermissions; Request() { U_TRACE_REGISTER_OBJECT(5, Request, "") } - Request(const Request& r) : token(r.token), type(r.type), radius(r.radius), location(r.location) - { - U_TRACE_REGISTER_OBJECT(5, Request, "%p", &r) - - U_MEMORY_TEST_COPY(r) - } - ~Request() { U_TRACE_UNREGISTER_OBJECT(5, Request) @@ -36,19 +31,39 @@ class Request { { U_TRACE_NO_PARAM(5, "Request::clear()") - token.clear(); - type.clear(); - radius.clear(); + table.clear(); + radius.clear(); location.clear(); + fbPermissions.clear(); + } + + void toJSON(UValue& json) + { + U_TRACE(5, "Request::toJSON(%p)", &json) + + json.toJSON(U_JSON_METHOD_HANDLER(table, UHashMap)); + json.toJSON(U_JSON_METHOD_HANDLER(radius, UString)); + json.toJSON(U_JSON_METHOD_HANDLER(location, UString)); + json.toJSON(U_JSON_METHOD_HANDLER(fbPermissions, UVector)); + } + + void fromJSON(UValue& json) + { + U_TRACE(5, "Request::fromJSON(%p)", &json) + + json.fromJSON(U_JSON_METHOD_HANDLER(table, UHashMap)); + json.fromJSON(U_JSON_METHOD_HANDLER(radius, UString)); + json.fromJSON(U_JSON_METHOD_HANDLER(location, UString)); + json.fromJSON(U_JSON_METHOD_HANDLER(fbPermissions, UVector)); } #ifdef DEBUG const char* dump(bool breset) const { - *UObjectIO::os << "token (UString " << (void*)&token << ")\n" - << "type (UString " << (void*)&type << ")\n" - << "radius (UString " << (void*)&radius << ")\n" - << "location (UString " << (void*)&location << ')'; + *UObjectIO::os << "table (UHashMap " << (void*)&table << ")\n" + << "radius (UString " << (void*)&radius << ")\n" + << "location (UString " << (void*)&location << ")\n" + << "fbPermissions (UVector " << (void*)&fbPermissions << ')'; if (breset) { @@ -65,31 +80,141 @@ class Request { Request& operator=(const Request&) { return *this; } }; -// JSON TEMPLATE SPECIALIZATIONS +class Response { +public: + // Check for memory error + U_MEMORY_TEST + + // Allocator e Deallocator + U_MEMORY_ALLOCATOR + U_MEMORY_DEALLOCATOR + + UVector fbPermissions; + UString type, token; + UHashMap table; + + Response(): type(U_STRING_FROM_CONSTANT("startup")) + { + U_TRACE_REGISTER_OBJECT(5, Response, "") + } + + ~Response() + { + U_TRACE_UNREGISTER_OBJECT(5, Response) + } + + void clear() + { + U_TRACE_NO_PARAM(5, "Response::clear()") + + fbPermissions.clear(); + type.clear(); + token.clear(); + table.clear(); + } + + void toJSON(UValue& json) + { + U_TRACE(5, "Response::toJSON(%p)", &json) + + json.toJSON(U_JSON_METHOD_HANDLER(fbPermissions, UVector)); + json.toJSON(U_JSON_METHOD_HANDLER(type, UString)); + json.toJSON(U_JSON_METHOD_HANDLER(token, UString)); + json.toJSON(U_JSON_METHOD_HANDLER(table, UHashMap)); + } + + void fromJSON(UValue& json) + { + U_TRACE(5, "Response::fromJSON(%p)", &json) + + json.fromJSON(U_JSON_METHOD_HANDLER(fbPermissions, UVector)); + json.fromJSON(U_JSON_METHOD_HANDLER(type, UString)); + json.fromJSON(U_JSON_METHOD_HANDLER(token, UString)); + json.fromJSON(U_JSON_METHOD_HANDLER(table, UHashMap)); + } + +#ifdef DEBUG + const char* dump(bool breset) const + { + *UObjectIO::os << "fbPermissions (UVector " << (void*)&fbPermissions << ")\n" + << "type (UString " << (void*)&type << ")\n" + << "token (UString " << (void*)&token << ")\n" + << "table (UHashMap " << (void*)&table << ')'; + + if (breset) + { + UObjectIO::output(); + + return UObjectIO::buffer_output; + } + + return 0; + } +#endif +}; -template <> class U_EXPORT UJsonTypeHandler : public UJsonTypeHandler_Base { +class Multiple { public: - explicit UJsonTypeHandler(Request& val) : UJsonTypeHandler_Base(&val) {} + // Check for memory error + U_MEMORY_TEST + + // Allocator e Deallocator + U_MEMORY_ALLOCATOR + U_MEMORY_DEALLOCATOR + + Request request; + Response response; + + Multiple() + { + U_TRACE_REGISTER_OBJECT(5, Multiple, "") + } + + ~Multiple() + { + U_TRACE_UNREGISTER_OBJECT(5, Multiple) + } + + void clear() + { + U_TRACE_NO_PARAM(5, "Multiple::clear()") + + request.clear(); + response.clear(); + } void toJSON(UValue& json) { - U_TRACE(0, "UJsonTypeHandler::toJSON(%p)", &json) + U_TRACE(5, "Multiple::toJSON(%p)", &json) - json.toJSON(U_JSON_TYPE_HANDLER(Request, token, UString)); - json.toJSON(U_JSON_TYPE_HANDLER(Request, type, UString)); - json.toJSON(U_JSON_TYPE_HANDLER(Request, radius, UString)); - json.toJSON(U_JSON_TYPE_HANDLER(Request, location, UString)); + json.toJSON(U_JSON_METHOD_HANDLER(request, Request)); + json.toJSON(U_JSON_METHOD_HANDLER(response, Response)); } void fromJSON(UValue& json) { - U_TRACE(0, "UJsonTypeHandler::fromJSON(%p)", &json) + U_TRACE(5, "Multiple::fromJSON(%p)", &json) - json.fromJSON(U_JSON_TYPE_HANDLER(Request, token, UString)); - json.fromJSON(U_JSON_TYPE_HANDLER(Request, type, UString)); - json.fromJSON(U_JSON_TYPE_HANDLER(Request, radius, UString)); - json.fromJSON(U_JSON_TYPE_HANDLER(Request, location, UString)); + json.fromJSON(U_JSON_METHOD_HANDLER(request, Request)); + json.fromJSON(U_JSON_METHOD_HANDLER(response, Response)); } + +#ifdef DEBUG + const char* dump(bool breset) const + { + *UObjectIO::os << "request (Request " << (void*)&request << ")\n" + << "response (Response " << (void*)&response << ')'; + + if (breset) + { + UObjectIO::output(); + + return UObjectIO::buffer_output; + } + + return 0; + } +#endif }; // Do a query and print the results @@ -200,58 +325,137 @@ static void testMap() U_INTERNAL_ASSERT(ok) } -static void testObject() +static void testRequest() { - U_TRACE_NO_PARAM(5, "testObject()") + U_TRACE_NO_PARAM(5, "testRequest()") UValue json_obj; Request request; - UString result, reqJson = U_STRING_FROM_CONSTANT("{\"token\":\"A619828KAIJ6D3\",\"type\":\"localesData\",\"radius\":\"near\",\"location\":\"40.7831 N, 73.9712 W\"}"); + const char* dump; + UString result, reqJson = U_STRING_FROM_CONSTANT("{\"table\":{\"type\":\"localesData\",\"token\":\"A619828KAIJ6D3\"},\"radius\":\"near\",\"location\":\"40.7831 N, 73.9712 W\",\"fbPermissions\":[\"public_profile\",\"user_friends\",\"email\"]}"); bool ok = JSON_parse(reqJson, request); U_INTERNAL_ASSERT(ok) - U_DUMP_OBJECT(request) - - U_INTERNAL_ASSERT_EQUALS(request.token, "A619828KAIJ6D3") - U_INTERNAL_ASSERT_EQUALS(request.type, "localesData") U_INTERNAL_ASSERT_EQUALS(request.radius, "near") U_INTERNAL_ASSERT_EQUALS(request.location, "40.7831 N, 73.9712 W") - ok = JSON_parse(reqJson, request); + dump = UObject2String >(request.table); + + U_INTERNAL_DUMP("dump(%u) = %.*S)", UObjectIO::buffer_output_len, UObjectIO::buffer_output_len, dump) + + ok = U_STREQ(dump, UObjectIO::buffer_output_len, "[\ntype\tlocalesData\ntoken\tA619828KAIJ6D3\n]"); U_INTERNAL_ASSERT(ok) - U_DUMP_OBJECT(request) + dump = UObject2String >(request.fbPermissions); - U_INTERNAL_ASSERT_EQUALS(request.token, "A619828KAIJ6D3") - U_INTERNAL_ASSERT_EQUALS(request.type, "localesData") - U_INTERNAL_ASSERT_EQUALS(request.radius, "near") - U_INTERNAL_ASSERT_EQUALS(request.location, "40.7831 N, 73.9712 W") + U_INTERNAL_DUMP("dump(%u) = %.*S)", UObjectIO::buffer_output_len, UObjectIO::buffer_output_len, dump) + + ok = U_STREQ(dump, UObjectIO::buffer_output_len, "( public_profile user_friends email )"); + + U_INTERNAL_ASSERT(ok) JSON_stringify(result, json_obj, request); U_ASSERT_EQUALS( result, reqJson ) +} + +static void testResponse() +{ + U_TRACE_NO_PARAM(5, "testResponse()") - request.clear(); + UValue json_obj; + const char* dump; + Response response; + UString result, reqJson = U_STRING_FROM_CONSTANT("{\"fbPermissions\":[\"public_profile\",\"user_friends\",\"email\"],\"type\":\"startup\",\"token\":\"\",\"table\":{\"type\":\"localesData\",\"token\":\"A619828KAIJ6D3\"}}"); - ok = JSON_parse((reqJson = U_STRING_FROM_CONSTANT("{\"type\":\"localesData\",\"radius\":\"near\",\"location\":\"40.7831 N, 73.9712 W\"}")), request); + bool ok = JSON_parse(reqJson, response); U_INTERNAL_ASSERT(ok) - U_DUMP_OBJECT(request) + U_INTERNAL_ASSERT_EQUALS(response.token, "") + U_INTERNAL_ASSERT_EQUALS(response.type, "startup") - U_INTERNAL_ASSERT_EQUALS(request.token, "") - U_INTERNAL_ASSERT_EQUALS(request.type, "localesData") - U_INTERNAL_ASSERT_EQUALS(request.radius, "near") - U_INTERNAL_ASSERT_EQUALS(request.location, "40.7831 N, 73.9712 W") + dump = UObject2String >(response.fbPermissions); - result.clear(); + U_INTERNAL_DUMP("dump(%u) = %.*S)", UObjectIO::buffer_output_len, UObjectIO::buffer_output_len, dump) - JSON_stringify(result, json_obj, request); + ok = U_STREQ(dump, UObjectIO::buffer_output_len, "( public_profile user_friends email )"); + + U_INTERNAL_ASSERT(ok) + + dump = UObject2String >(response.table); + + U_INTERNAL_DUMP("dump(%u) = %.*S)", UObjectIO::buffer_output_len, UObjectIO::buffer_output_len, dump) + + ok = U_STREQ(dump, UObjectIO::buffer_output_len, "[\ntype\tlocalesData\ntoken\tA619828KAIJ6D3\n]"); + + U_INTERNAL_ASSERT(ok) + + JSON_stringify(result, json_obj, response); - U_ASSERT_EQUALS( result, "{\"token\":\"\",\"type\":\"localesData\",\"radius\":\"near\",\"location\":\"40.7831 N, 73.9712 W\"}" ) + U_ASSERT_EQUALS( result, reqJson ) +} + +static void testMultiple() +{ + U_TRACE_NO_PARAM(5, "testMultiple()") + + UValue json_obj; + const char* dump; + Multiple multiple; + UString result, reqJson = U_STRING_FROM_CONSTANT("{" + "\"request\":{\"table\":{\"type\":\"localesData\",\"token\":\"A619828KAIJ6D3\"},\"radius\":\"near\",\"location\":\"40.7831 N, 73.9712 W\",\"fbPermissions\":[\"public_profile\",\"user_friends\",\"email\"]}," + "\"response\":{\"fbPermissions\":[\"public_profile\",\"user_friends\",\"email\"],\"type\":\"startup\",\"token\":\"\",\"table\":{\"type\":\"localesData\",\"token\":\"A619828KAIJ6D3\"}}" + "}"); + + bool ok = JSON_parse(reqJson, multiple); + + U_INTERNAL_ASSERT(ok) + + U_INTERNAL_ASSERT_EQUALS(multiple.request.radius, "near") + U_INTERNAL_ASSERT_EQUALS(multiple.request.location, "40.7831 N, 73.9712 W") + + dump = UObject2String >(multiple.request.table); + + U_INTERNAL_DUMP("dump(%u) = %.*S)", UObjectIO::buffer_output_len, UObjectIO::buffer_output_len, dump) + + ok = U_STREQ(dump, UObjectIO::buffer_output_len, "[\ntype\tlocalesData\ntoken\tA619828KAIJ6D3\n]"); + + U_INTERNAL_ASSERT(ok) + + dump = UObject2String >(multiple.request.fbPermissions); + + U_INTERNAL_DUMP("dump(%u) = %.*S)", UObjectIO::buffer_output_len, UObjectIO::buffer_output_len, dump) + + ok = U_STREQ(dump, UObjectIO::buffer_output_len, "( public_profile user_friends email )"); + + U_INTERNAL_ASSERT(ok) + + U_INTERNAL_ASSERT_EQUALS(multiple.response.token, "") + U_INTERNAL_ASSERT_EQUALS(multiple.response.type, "startup") + + dump = UObject2String >(multiple.response.fbPermissions); + + U_INTERNAL_DUMP("dump(%u) = %.*S)", UObjectIO::buffer_output_len, UObjectIO::buffer_output_len, dump) + + ok = U_STREQ(dump, UObjectIO::buffer_output_len, "( public_profile user_friends email )"); + + U_INTERNAL_ASSERT(ok) + + dump = UObject2String >(multiple.response.table); + + U_INTERNAL_DUMP("dump(%u) = %.*S)", UObjectIO::buffer_output_len, UObjectIO::buffer_output_len, dump) + + ok = U_STREQ(dump, UObjectIO::buffer_output_len, "[\ntype\tlocalesData\ntoken\tA619828KAIJ6D3\n]"); + + U_INTERNAL_ASSERT(ok) + + JSON_stringify(result, json_obj, multiple); + + U_ASSERT_EQUALS( result, reqJson ) } int @@ -279,7 +483,11 @@ U_EXPORT main (int argc, char* argv[]) return -1; */ - testObject(); + testMap(); + testVector(); + testRequest(); + testResponse(); + testMultiple(); content = UFile::contentOf(U_STRING_FROM_CONSTANT("inp/json/prova.json")); @@ -530,9 +738,6 @@ U_EXPORT main (int argc, char* argv[]) U_INTERNAL_ASSERT(ok) #endif - testMap(); - testVector(); - while (cin >> filename) { content = UFile::contentOf(filename);