Add length calculations to JsonEncode() so we can set Content-Length.

This commit is contained in:
Jordan Bancino 2023-05-06 19:23:13 +00:00
parent bbea55be6c
commit 95ceba0645
9 changed files with 108 additions and 56 deletions

View file

@ -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
[~] /_telodendria/admin/config endpoint
[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

View file

@ -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);
}

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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 */

View file

@ -321,7 +321,7 @@ main(int argc, char **argv)
break;
}
StreamPrintf(StreamStderr(), "Warning: Unknown expression: %s\n",
expr.data.text);
expr.data.text);
break;
default:
StreamPrintf(StreamStderr(), "Unknown header type: %d\n", expr.type);