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
36
TODO.txt
36
TODO.txt
|
@ -11,40 +11,12 @@ Key:
|
||||||
Milestone: v0.3.0
|
Milestone: v0.3.0
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
[~] Move configuration to database
|
[~] /_telodendria/admin/config endpoint
|
||||||
[x] Token permissions
|
[x] Replace entire config
|
||||||
[x] Initial configuration
|
[ ] Update only certain values
|
||||||
[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
|
|
||||||
|
|
||||||
[ ] Documentation
|
[ ] 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)
|
[ ] 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
|
[x] Debug memory leaks with caching
|
||||||
[ ] Debug OpenSSL
|
[ ] Debug OpenSSL
|
||||||
|
@ -53,12 +25,10 @@ Milestone: v0.3.0
|
||||||
[~] 4: Account management
|
[~] 4: Account management
|
||||||
[~] Deactivate
|
[~] Deactivate
|
||||||
[x] Make sure UserLogin() fails if user is deactivated.
|
[x] Make sure UserLogin() fails if user is deactivated.
|
||||||
[x] Change password
|
|
||||||
[~] Whoami
|
[~] Whoami
|
||||||
[ ] Attach device id to user object
|
[ ] Attach device id to user object
|
||||||
[ ] Use UserAuthenticate()
|
[ ] Use UserAuthenticate()
|
||||||
[~] 9: User Data
|
[~] 9: User Data
|
||||||
[x] 5: Capabilities negotiation
|
|
||||||
[ ] 10: Security (Rate Limiting)
|
[ ] 10: Security (Rate Limiting)
|
||||||
|
|
||||||
Milestone: v0.4.0
|
Milestone: v0.4.0
|
||||||
|
|
|
@ -652,7 +652,8 @@ HeaderParse(Stream * stream, HeaderExpr * expr)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Cope with preprocessor macro expansions at the top level. */
|
/* Cope with preprocessor macro expansions at the top
|
||||||
|
* level. */
|
||||||
expr->type = HP_UNKNOWN;
|
expr->type = HP_UNKNOWN;
|
||||||
strncpy(expr->data.text, word, HEADER_EXPR_MAX);
|
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);
|
Free(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
int
|
||||||
JsonEncodeString(const char *str, Stream * out)
|
JsonEncodeString(const char *str, Stream * out)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
char c;
|
char c;
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
StreamPutc(out, '"');
|
StreamPutc(out, '"');
|
||||||
|
length++;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while ((c = str[i]) != '\0')
|
while ((c = str[i]) != '\0')
|
||||||
|
@ -351,21 +353,27 @@ JsonEncodeString(const char *str, Stream * out)
|
||||||
case '/':
|
case '/':
|
||||||
StreamPutc(out, '\\');
|
StreamPutc(out, '\\');
|
||||||
StreamPutc(out, c);
|
StreamPutc(out, c);
|
||||||
|
length += 2;
|
||||||
break;
|
break;
|
||||||
case '\b':
|
case '\b':
|
||||||
StreamPuts(out, "\\b");
|
StreamPuts(out, "\\b");
|
||||||
|
length += 2;
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
StreamPuts(out, "\\t");
|
StreamPuts(out, "\\t");
|
||||||
|
length += 2;
|
||||||
break;
|
break;
|
||||||
case '\n':
|
case '\n':
|
||||||
StreamPuts(out, "\\n");
|
StreamPuts(out, "\\n");
|
||||||
|
length += 2;
|
||||||
break;
|
break;
|
||||||
case '\f':
|
case '\f':
|
||||||
StreamPuts(out, "\\f");
|
StreamPuts(out, "\\f");
|
||||||
|
length += 2;
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
StreamPuts(out, "\\r");
|
StreamPuts(out, "\\r");
|
||||||
|
length += 2;
|
||||||
break;
|
break;
|
||||||
default: /* Assume UTF-8 input */
|
default: /* Assume UTF-8 input */
|
||||||
/*
|
/*
|
||||||
|
@ -381,11 +389,12 @@ JsonEncodeString(const char *str, Stream * out)
|
||||||
*/
|
*/
|
||||||
if (c <= 0x001F)
|
if (c <= 0x001F)
|
||||||
{
|
{
|
||||||
StreamPrintf(out, "\\u%04x", c);
|
length += StreamPrintf(out, "\\u%04x", c);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StreamPutc(out, c);
|
StreamPutc(out, c);
|
||||||
|
length++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -393,6 +402,9 @@ JsonEncodeString(const char *str, Stream * out)
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamPutc(out, '"');
|
StreamPutc(out, '"');
|
||||||
|
length++;
|
||||||
|
|
||||||
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
|
@ -567,67 +579,76 @@ JsonDecodeString(Stream * in)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
int
|
||||||
JsonEncodeValue(JsonValue * value, Stream * out, int level)
|
JsonEncodeValue(JsonValue * value, Stream * out, int level)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t len;
|
size_t len;
|
||||||
Array *arr;
|
Array *arr;
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
switch (value->type)
|
switch (value->type)
|
||||||
{
|
{
|
||||||
case JSON_OBJECT:
|
case JSON_OBJECT:
|
||||||
JsonEncode(value->as.object, out, level >= 0 ? level : level);
|
length += JsonEncode(value->as.object, out, level >= 0 ? level : level);
|
||||||
break;
|
break;
|
||||||
case JSON_ARRAY:
|
case JSON_ARRAY:
|
||||||
arr = value->as.array;
|
arr = value->as.array;
|
||||||
len = ArraySize(arr);
|
len = ArraySize(arr);
|
||||||
|
|
||||||
StreamPutc(out, '[');
|
StreamPutc(out, '[');
|
||||||
|
length++;
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
if (level >= 0)
|
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)
|
if (i < len - 1)
|
||||||
{
|
{
|
||||||
StreamPutc(out, ',');
|
StreamPutc(out, ',');
|
||||||
|
length++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level >= 0)
|
if (level >= 0)
|
||||||
{
|
{
|
||||||
StreamPrintf(out, "\n%*s", level, "");
|
length += StreamPrintf(out, "\n%*s", level, "");
|
||||||
}
|
}
|
||||||
StreamPutc(out, ']');
|
StreamPutc(out, ']');
|
||||||
|
length++;
|
||||||
break;
|
break;
|
||||||
case JSON_STRING:
|
case JSON_STRING:
|
||||||
JsonEncodeString(value->as.string, out);
|
length += JsonEncodeString(value->as.string, out);
|
||||||
break;
|
break;
|
||||||
case JSON_INTEGER:
|
case JSON_INTEGER:
|
||||||
StreamPrintf(out, "%ld", value->as.integer);
|
length += StreamPrintf(out, "%ld", value->as.integer);
|
||||||
break;
|
break;
|
||||||
case JSON_FLOAT:
|
case JSON_FLOAT:
|
||||||
StreamPrintf(out, "%f", value->as.floating);
|
length += StreamPrintf(out, "%f", value->as.floating);
|
||||||
break;
|
break;
|
||||||
case JSON_BOOLEAN:
|
case JSON_BOOLEAN:
|
||||||
if (value->as.boolean)
|
if (value->as.boolean)
|
||||||
{
|
{
|
||||||
StreamPuts(out, "true");
|
StreamPuts(out, "true");
|
||||||
|
length += 4;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StreamPuts(out, "false");
|
StreamPuts(out, "false");
|
||||||
|
length += 5;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSON_NULL:
|
case JSON_NULL:
|
||||||
StreamPuts(out, "null");
|
StreamPuts(out, "null");
|
||||||
|
length += 4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -637,10 +658,11 @@ JsonEncode(HashMap * object, Stream * out, int level)
|
||||||
size_t count;
|
size_t count;
|
||||||
char *key;
|
char *key;
|
||||||
JsonValue *value;
|
JsonValue *value;
|
||||||
|
int length;
|
||||||
|
|
||||||
if (!object || !out)
|
if (!object)
|
||||||
{
|
{
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
|
@ -649,10 +671,16 @@ JsonEncode(HashMap * object, Stream * out, int level)
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The total number of bytes written */
|
||||||
|
length = 0;
|
||||||
|
|
||||||
StreamPutc(out, '{');
|
StreamPutc(out, '{');
|
||||||
|
length++;
|
||||||
|
|
||||||
if (level >= 0)
|
if (level >= 0)
|
||||||
{
|
{
|
||||||
StreamPutc(out, '\n');
|
StreamPutc(out, '\n');
|
||||||
|
length++;
|
||||||
}
|
}
|
||||||
|
|
||||||
index = 0;
|
index = 0;
|
||||||
|
@ -661,26 +689,31 @@ JsonEncode(HashMap * object, Stream * out, int level)
|
||||||
if (level >= 0)
|
if (level >= 0)
|
||||||
{
|
{
|
||||||
StreamPrintf(out, "%*s", level + 2, "");
|
StreamPrintf(out, "%*s", level + 2, "");
|
||||||
|
length += level + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonEncodeString(key, out);
|
length += JsonEncodeString(key, out);
|
||||||
|
|
||||||
StreamPutc(out, ':');
|
StreamPutc(out, ':');
|
||||||
|
length++;
|
||||||
if (level >= 0)
|
if (level >= 0)
|
||||||
{
|
{
|
||||||
StreamPutc(out, ' ');
|
StreamPutc(out, ' ');
|
||||||
|
length++;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonEncodeValue(value, out, level >= 0 ? level + 2 : level);
|
length += JsonEncodeValue(value, out, level >= 0 ? level + 2 : level);
|
||||||
|
|
||||||
if (index < count - 1)
|
if (index < count - 1)
|
||||||
{
|
{
|
||||||
StreamPutc(out, ',');
|
StreamPutc(out, ',');
|
||||||
|
length++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level >= 0)
|
if (level >= 0)
|
||||||
{
|
{
|
||||||
StreamPutc(out, '\n');
|
StreamPutc(out, '\n');
|
||||||
|
length++;
|
||||||
}
|
}
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
|
@ -689,10 +722,12 @@ JsonEncode(HashMap * object, Stream * out, int level)
|
||||||
if (level >= 0)
|
if (level >= 0)
|
||||||
{
|
{
|
||||||
StreamPrintf(out, "%*s", level, "");
|
StreamPrintf(out, "%*s", level, "");
|
||||||
|
length += level;
|
||||||
}
|
}
|
||||||
StreamPutc(out, '}');
|
StreamPutc(out, '}');
|
||||||
|
length++;
|
||||||
|
|
||||||
return 1;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -93,9 +93,14 @@ MatrixHttpHandler(HttpServerContext * context, void *argp)
|
||||||
*/
|
*/
|
||||||
if (response)
|
if (response)
|
||||||
{
|
{
|
||||||
|
char *contentLen = StrInt(JsonEncode(response, NULL, JSON_DEFAULT));
|
||||||
|
|
||||||
HttpResponseHeader(context, "Content-Type", "application/json");
|
HttpResponseHeader(context, "Content-Type", "application/json");
|
||||||
|
HttpResponseHeader(context, "Content-Length", contentLen);
|
||||||
HttpSendHeaders(context);
|
HttpSendHeaders(context);
|
||||||
|
|
||||||
|
Free(contentLen);
|
||||||
|
|
||||||
stream = HttpServerStream(context);
|
stream = HttpServerStream(context);
|
||||||
JsonEncode(response, stream, JSON_DEFAULT);
|
JsonEncode(response, stream, JSON_DEFAULT);
|
||||||
JsonFree(response);
|
JsonFree(response);
|
||||||
|
|
18
src/Str.c
18
src/Str.c
|
@ -258,3 +258,21 @@ StrRandom(size_t len)
|
||||||
str[len] = '\0';
|
str[len] = '\0';
|
||||||
return str;
|
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;
|
int ret;
|
||||||
|
|
||||||
if (!stream || !fmt)
|
if (!fmt)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -264,9 +264,12 @@ StreamVprintf(Stream * stream, const char *fmt, va_list ap)
|
||||||
ret = vfprintf(fp, fmt, ap);
|
ret = vfprintf(fp, fmt, ap);
|
||||||
fclose(fp);
|
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
|
free(buf); /* Allocated by stdlib, not Memory
|
||||||
|
|
|
@ -247,8 +247,12 @@ extern void JsonFree(HashMap *);
|
||||||
* CanonicalJson encoder. This will typically be used for encoding
|
* CanonicalJson encoder. This will typically be used for encoding
|
||||||
* object keys; to encode values, just use
|
* object keys; to encode values, just use
|
||||||
* .Fn JsonEncodeValue .
|
* .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
|
* 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 .
|
* .Va JSON_PRETTY .
|
||||||
* To get minified output, set it to
|
* To get minified output, set it to
|
||||||
* .Va JSON_DEFAULT .
|
* .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
|
* Encode a JSON object as it would appear in JSON output, writing it
|
||||||
* to the given output stream. This function is recursive; it will
|
* to the given output stream. This function is recursive; it will
|
||||||
* serialize everything accessible from the passed object. The third
|
* serialize everything accessible from the passed object. The third
|
||||||
* parameter has the same behavior as described above.
|
* 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);
|
extern int JsonEncode(HashMap *, Stream *, int);
|
||||||
|
|
||||||
|
|
|
@ -88,4 +88,12 @@ extern int StrBlank(const char *str);
|
||||||
*/
|
*/
|
||||||
extern char * StrRandom(size_t);
|
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 */
|
#endif /* TELODENDRIA_STR_H */
|
||||||
|
|
|
@ -321,7 +321,7 @@ main(int argc, char **argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
StreamPrintf(StreamStderr(), "Warning: Unknown expression: %s\n",
|
StreamPrintf(StreamStderr(), "Warning: Unknown expression: %s\n",
|
||||||
expr.data.text);
|
expr.data.text);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
StreamPrintf(StreamStderr(), "Unknown header type: %d\n", expr.type);
|
StreamPrintf(StreamStderr(), "Unknown header type: %d\n", expr.type);
|
||||||
|
|
Loading…
Reference in a new issue