forked from Telodendria/Telodendria
Add length calculations to JsonEncode() so we can set Content-Length.
This commit is contained in:
parent
bbea55be6c
commit
95ceba0645
9 changed files with 108 additions and 56 deletions
34
TODO.txt
34
TODO.txt
|
@ -11,40 +11,12 @@ Key:
|
|||
Milestone: v0.3.0
|
||||
-----------------
|
||||
|
||||
[~] Move configuration to database
|
||||
[x] Token permissions
|
||||
[x] Initial configuration
|
||||
[x] If no config, create one-time use registration token that
|
||||
grants user admin privileges.
|
||||
[~] /_telodendria/admin/config endpoint
|
||||
[x] JsonDuplicate()
|
||||
[x] Refactor TelodendriaConfig to just Config
|
||||
[x] Replace entire config
|
||||
[ ] Update only certain values
|
||||
|
||||
[ ] Documentation
|
||||
[x] Array
|
||||
[x] Io
|
||||
[x] Stream
|
||||
[x] Tls
|
||||
[x] HttpClient
|
||||
[x] Uri
|
||||
[x] td
|
||||
[x] tt
|
||||
[x] http-debug-server
|
||||
[x] tp
|
||||
[x] send-patch
|
||||
[x] Log
|
||||
[x] TelodendriaConfig -> Config
|
||||
[x] HashMap
|
||||
[x] HttpRouter
|
||||
[x] Html
|
||||
[x] Str
|
||||
[x] HeaderParser
|
||||
[ ] hdoc(1) and hdoc(5)
|
||||
[x] telodendria-admin
|
||||
[x] telodendria-setup
|
||||
[x] telodendria-config
|
||||
[x] Refactor dev pages so function description and
|
||||
return value are in the same location.
|
||||
|
||||
[x] Debug memory leaks with caching
|
||||
[ ] Debug OpenSSL
|
||||
|
@ -53,12 +25,10 @@ Milestone: v0.3.0
|
|||
[~] 4: Account management
|
||||
[~] Deactivate
|
||||
[x] Make sure UserLogin() fails if user is deactivated.
|
||||
[x] Change password
|
||||
[~] Whoami
|
||||
[ ] Attach device id to user object
|
||||
[ ] Use UserAuthenticate()
|
||||
[~] 9: User Data
|
||||
[x] 5: Capabilities negotiation
|
||||
[ ] 10: Security (Rate Limiting)
|
||||
|
||||
Milestone: v0.4.0
|
||||
|
|
|
@ -652,7 +652,8 @@ HeaderParse(Stream * stream, HeaderExpr * expr)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* Cope with preprocessor macro expansions at the top level. */
|
||||
/* Cope with preprocessor macro expansions at the top
|
||||
* level. */
|
||||
expr->type = HP_UNKNOWN;
|
||||
strncpy(expr->data.text, word, HEADER_EXPR_MAX);
|
||||
}
|
||||
|
|
67
src/Json.c
67
src/Json.c
|
@ -333,13 +333,15 @@ JsonValueFree(JsonValue * value)
|
|||
Free(value);
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
JsonEncodeString(const char *str, Stream * out)
|
||||
{
|
||||
size_t i;
|
||||
char c;
|
||||
int length = 0;
|
||||
|
||||
StreamPutc(out, '"');
|
||||
length++;
|
||||
|
||||
i = 0;
|
||||
while ((c = str[i]) != '\0')
|
||||
|
@ -351,21 +353,27 @@ JsonEncodeString(const char *str, Stream * out)
|
|||
case '/':
|
||||
StreamPutc(out, '\\');
|
||||
StreamPutc(out, c);
|
||||
length += 2;
|
||||
break;
|
||||
case '\b':
|
||||
StreamPuts(out, "\\b");
|
||||
length += 2;
|
||||
break;
|
||||
case '\t':
|
||||
StreamPuts(out, "\\t");
|
||||
length += 2;
|
||||
break;
|
||||
case '\n':
|
||||
StreamPuts(out, "\\n");
|
||||
length += 2;
|
||||
break;
|
||||
case '\f':
|
||||
StreamPuts(out, "\\f");
|
||||
length += 2;
|
||||
break;
|
||||
case '\r':
|
||||
StreamPuts(out, "\\r");
|
||||
length += 2;
|
||||
break;
|
||||
default: /* Assume UTF-8 input */
|
||||
/*
|
||||
|
@ -381,11 +389,12 @@ JsonEncodeString(const char *str, Stream * out)
|
|||
*/
|
||||
if (c <= 0x001F)
|
||||
{
|
||||
StreamPrintf(out, "\\u%04x", c);
|
||||
length += StreamPrintf(out, "\\u%04x", c);
|
||||
}
|
||||
else
|
||||
{
|
||||
StreamPutc(out, c);
|
||||
length++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -393,6 +402,9 @@ JsonEncodeString(const char *str, Stream * out)
|
|||
}
|
||||
|
||||
StreamPutc(out, '"');
|
||||
length++;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static char *
|
||||
|
@ -567,67 +579,76 @@ JsonDecodeString(Stream * in)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
JsonEncodeValue(JsonValue * value, Stream * out, int level)
|
||||
{
|
||||
size_t i;
|
||||
size_t len;
|
||||
Array *arr;
|
||||
int length = 0;
|
||||
|
||||
switch (value->type)
|
||||
{
|
||||
case JSON_OBJECT:
|
||||
JsonEncode(value->as.object, out, level >= 0 ? level : level);
|
||||
length += JsonEncode(value->as.object, out, level >= 0 ? level : level);
|
||||
break;
|
||||
case JSON_ARRAY:
|
||||
arr = value->as.array;
|
||||
len = ArraySize(arr);
|
||||
|
||||
StreamPutc(out, '[');
|
||||
length++;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (level >= 0)
|
||||
{
|
||||
StreamPrintf(out, "\n%*s", level + 2, "");
|
||||
length += StreamPrintf(out, "\n%*s", level + 2, "");
|
||||
}
|
||||
JsonEncodeValue(ArrayGet(arr, i), out, level >= 0 ? level + 2 : level);
|
||||
length += JsonEncodeValue(ArrayGet(arr, i), out, level >= 0 ? level + 2 : level);
|
||||
if (i < len - 1)
|
||||
{
|
||||
StreamPutc(out, ',');
|
||||
length++;
|
||||
}
|
||||
}
|
||||
|
||||
if (level >= 0)
|
||||
{
|
||||
StreamPrintf(out, "\n%*s", level, "");
|
||||
length += StreamPrintf(out, "\n%*s", level, "");
|
||||
}
|
||||
StreamPutc(out, ']');
|
||||
length++;
|
||||
break;
|
||||
case JSON_STRING:
|
||||
JsonEncodeString(value->as.string, out);
|
||||
length += JsonEncodeString(value->as.string, out);
|
||||
break;
|
||||
case JSON_INTEGER:
|
||||
StreamPrintf(out, "%ld", value->as.integer);
|
||||
length += StreamPrintf(out, "%ld", value->as.integer);
|
||||
break;
|
||||
case JSON_FLOAT:
|
||||
StreamPrintf(out, "%f", value->as.floating);
|
||||
length += StreamPrintf(out, "%f", value->as.floating);
|
||||
break;
|
||||
case JSON_BOOLEAN:
|
||||
if (value->as.boolean)
|
||||
{
|
||||
StreamPuts(out, "true");
|
||||
length += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
StreamPuts(out, "false");
|
||||
length += 5;
|
||||
}
|
||||
break;
|
||||
case JSON_NULL:
|
||||
StreamPuts(out, "null");
|
||||
length += 4;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -637,10 +658,11 @@ JsonEncode(HashMap * object, Stream * out, int level)
|
|||
size_t count;
|
||||
char *key;
|
||||
JsonValue *value;
|
||||
int length;
|
||||
|
||||
if (!object || !out)
|
||||
if (!object)
|
||||
{
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
@ -649,10 +671,16 @@ JsonEncode(HashMap * object, Stream * out, int level)
|
|||
count++;
|
||||
}
|
||||
|
||||
/* The total number of bytes written */
|
||||
length = 0;
|
||||
|
||||
StreamPutc(out, '{');
|
||||
length++;
|
||||
|
||||
if (level >= 0)
|
||||
{
|
||||
StreamPutc(out, '\n');
|
||||
length++;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
|
@ -661,26 +689,31 @@ JsonEncode(HashMap * object, Stream * out, int level)
|
|||
if (level >= 0)
|
||||
{
|
||||
StreamPrintf(out, "%*s", level + 2, "");
|
||||
length += level + 2;
|
||||
}
|
||||
|
||||
JsonEncodeString(key, out);
|
||||
length += JsonEncodeString(key, out);
|
||||
|
||||
StreamPutc(out, ':');
|
||||
length++;
|
||||
if (level >= 0)
|
||||
{
|
||||
StreamPutc(out, ' ');
|
||||
length++;
|
||||
}
|
||||
|
||||
JsonEncodeValue(value, out, level >= 0 ? level + 2 : level);
|
||||
length += JsonEncodeValue(value, out, level >= 0 ? level + 2 : level);
|
||||
|
||||
if (index < count - 1)
|
||||
{
|
||||
StreamPutc(out, ',');
|
||||
length++;
|
||||
}
|
||||
|
||||
if (level >= 0)
|
||||
{
|
||||
StreamPutc(out, '\n');
|
||||
length++;
|
||||
}
|
||||
|
||||
index++;
|
||||
|
@ -689,10 +722,12 @@ JsonEncode(HashMap * object, Stream * out, int level)
|
|||
if (level >= 0)
|
||||
{
|
||||
StreamPrintf(out, "%*s", level, "");
|
||||
length += level;
|
||||
}
|
||||
StreamPutc(out, '}');
|
||||
length++;
|
||||
|
||||
return 1;
|
||||
return length;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -93,9 +93,14 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
|
|||
*/
|
||||
if (response)
|
||||
{
|
||||
char *contentLen = StrInt(JsonEncode(response, NULL, JSON_DEFAULT));
|
||||
|
||||
HttpResponseHeader(context, "Content-Type", "application/json");
|
||||
HttpResponseHeader(context, "Content-Length", contentLen);
|
||||
HttpSendHeaders(context);
|
||||
|
||||
Free(contentLen);
|
||||
|
||||
stream = HttpServerStream(context);
|
||||
JsonEncode(response, stream, JSON_DEFAULT);
|
||||
JsonFree(response);
|
||||
|
|
18
src/Str.c
18
src/Str.c
|
@ -258,3 +258,21 @@ StrRandom(size_t len)
|
|||
str[len] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
char *
|
||||
StrInt(long i)
|
||||
{
|
||||
char *str;
|
||||
int len;
|
||||
|
||||
len = snprintf(NULL, 0, "%ld", i);
|
||||
str = Malloc(len + 1);
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(str, len + 1, "%ld", i);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
|
|||
|
||||
int ret;
|
||||
|
||||
if (!stream || !fmt)
|
||||
if (!fmt)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
@ -264,9 +264,12 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
|
|||
ret = vfprintf(fp, fmt, ap);
|
||||
fclose(fp);
|
||||
|
||||
if (ret >= 0)
|
||||
if (ret >= 0 && stream)
|
||||
{
|
||||
ret = StreamPuts(stream, buf);
|
||||
if (StreamPuts(stream, buf) < 0)
|
||||
{
|
||||
ret = -1;
|
||||
};
|
||||
}
|
||||
|
||||
free(buf); /* Allocated by stdlib, not Memory
|
||||
|
|
|
@ -247,8 +247,12 @@ extern void JsonFree(HashMap *);
|
|||
* CanonicalJson encoder. This will typically be used for encoding
|
||||
* object keys; to encode values, just use
|
||||
* .Fn JsonEncodeValue .
|
||||
* .Pp
|
||||
* This function returns the number of bytes written to the stream,
|
||||
* or if the stream is NULL, the number of bytes that would have
|
||||
* been written.
|
||||
*/
|
||||
extern void JsonEncodeString(const char *, Stream *);
|
||||
extern int JsonEncodeString(const char *, Stream *);
|
||||
|
||||
/**
|
||||
* Serialize a JSON value as it would appear in JSON output. This is
|
||||
|
@ -267,14 +271,22 @@ extern void JsonEncodeString(const char *, Stream *);
|
|||
* .Va JSON_PRETTY .
|
||||
* To get minified output, set it to
|
||||
* .Va JSON_DEFAULT .
|
||||
* .Pp
|
||||
* This function returns the number of bytes written to the stream,
|
||||
* or if the stream is NULL, the number of bytes that would have
|
||||
* been written.
|
||||
*/
|
||||
extern void JsonEncodeValue(JsonValue *, Stream *, int);
|
||||
extern int JsonEncodeValue(JsonValue *, Stream *, int);
|
||||
|
||||
/**
|
||||
* Encode a JSON object as it would appear in JSON output, writing it
|
||||
* to the given output stream. This function is recursive; it will
|
||||
* serialize everything accessible from the passed object. The third
|
||||
* parameter has the same behavior as described above.
|
||||
* .Pp
|
||||
* This function returns the number of bytes written to the stream,
|
||||
* or if the stream is NULL, the number of bytes that would have
|
||||
* been written.
|
||||
*/
|
||||
extern int JsonEncode(HashMap *, Stream *, int);
|
||||
|
||||
|
|
|
@ -88,4 +88,12 @@ extern int StrBlank(const char *str);
|
|||
*/
|
||||
extern char * StrRandom(size_t);
|
||||
|
||||
/**
|
||||
* Convert the specified integer into a string, returning the string
|
||||
* on the heap, or NULL if there was a memory allocation error. The
|
||||
* returned string should be freed by the caller after it is no longer
|
||||
* needed.
|
||||
*/
|
||||
extern char * StrInt(long);
|
||||
|
||||
#endif /* TELODENDRIA_STR_H */
|
||||
|
|
Loading…
Reference in a new issue