starting work on request/response handling

This commit is contained in:
snit 2024-10-17 21:26:25 -05:00
parent bfbba84b5c
commit 3470d4bebc
7 changed files with 361 additions and 58 deletions

54
common/include/request.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef COMMON_REQUEST_H
#define COMMON_REQUEST_H
#include <stdbool.h>
#include "error.h"
#include "world.h"
// ### REQUEST TYPES ### //
enum request_type_t {
REQUEST_NONE,
REQUEST_GET_WORLD_DATA,
};
struct request_body_get_world_data_t {
size_t world_id;
};
struct request_t {
enum request_type_t type;
void *body;
// The body's type depends on the request's type
};
enum error_t request_serialise_str(struct request_t const *, char const **);
enum error_t request_deserialise_str(struct request_t *, char const *);
enum error_t request_serialise_buf(struct request_t const *, char *, size_t);
// ### RESPONSE TYPES ### //
struct response_body_get_world_data_t {
struct world_t world;
};
struct response_t {
bool success;
void *body;
// The body's type depends on the request's type
};
enum error_t response_serialise_str(struct response_t const *, char const **);
enum error_t response_deserialise_str(struct response_t *, char const *);
enum error_t response_serialise_buf(struct response_t const *, char *, size_t);
#endif

252
common/src/request.c Normal file
View File

@ -0,0 +1,252 @@
// request.c
#include <assert.h>
#include <jansson.h>
#include "request.h"
// Request Format Strings
static char const *REQUEST_JSON_FMT = "{si, so}";
static char const *REQUEST_BODY_GET_WORLD_DATA_JSON_FMT = "{sI}";
// Response Format Strings
static enum error_t request_serialise_body_get_world_data(
struct request_body_get_world_data_t const *self,
struct json_t **jsonptr
) {
assert(self != NULL);
assert(jsonptr != NULL);
struct json_t *json = json_pack(REQUEST_BODY_GET_WORLD_DATA_JSON_FMT,
"world-id", self->world_id
);
if (json == NULL) return ERR_JSON_SERIALISE;
*jsonptr = json;
return ERR_OK;
}
static enum error_t request_serialise_body(
struct request_t const *self,
struct json_t **jsonptr
) {
assert(self != NULL);
assert(jsonptr != NULL);
switch (self->type) {
case REQUEST_GET_WORLD_DATA:
return request_serialise_body_get_world_data(self->body, jsonptr);
default: return ERR_JSON_SERIALISE;
}
return ERR_OK;
}
static enum error_t request_serialise_parts(
struct json_t *body_json, int type,
struct json_t **jsonptr
) {
assert(body_json != NULL);
assert(jsonptr != NULL);
struct json_t *json = json_pack(REQUEST_JSON_FMT,
"request-code", type,
"parameters", body_json
);
if (json == NULL) return ERR_JSON_SERIALISE;
*jsonptr = json;
return ERR_OK;
}
static enum error_t request_serialise(
struct request_t const *self,
struct json_t **jsonptr
) {
assert(self != NULL);
assert(jsonptr != NULL);
// Serialise request body
struct json_t *body_json = NULL;
enum error_t err = request_serialise_body(self, &body_json);
if (err != ERR_OK) return err;
// Serialise full structure
struct json_t *request_json = NULL;
err = request_serialise_parts(body_json, self->type, &request_json);
if (err != ERR_OK) {
json_decref(body_json);
return err;
}
*jsonptr = request_json;
return ERR_OK;
}
enum error_t request_serialise_str(
struct request_t const *self,
char const **strptr
) {
assert(self != NULL);
assert(strptr != NULL);
struct json_t *json = NULL;
enum error_t err = request_serialise(self, &json);
if (err != ERR_OK) return err;
const char *str = json_dumps(json, 0);
if (str == NULL) {
json_decref(json);
return ERR_JSON_SERIALISE;
}
json_decref(json);
*strptr = str;
return ERR_OK;
}
enum error_t request_serialise_buf(
struct request_t const *self,
char *buf, size_t len
) {
assert(self != NULL);
assert(buf != NULL);
assert(len > 0);
struct json_t *json = NULL;
enum error_t err = request_serialise(self, &json);
if (err != ERR_OK) return err;
size_t bytes_written = json_dumpb(json, buf, len, 0);
if (bytes_written == 0) {
json_decref(json);
return ERR_JSON_SERIALISE;
}
json_decref(json);
return ERR_OK;
}
static enum error_t request_deserialise_parts(
struct json_t **body_json,
enum request_type_t *type,
struct json_t *json
) {
assert(body_json != NULL);
assert(type != NULL);
assert(json != NULL);
int err = json_unpack(json, REQUEST_JSON_FMT,
"request-code", type,
"parameters", body_json
);
if (err < 0) return ERR_JSON_DESERIALISE;
return ERR_OK;
}
static enum error_t request_deserialise_body_get_world_data(
struct request_body_get_world_data_t *self,
struct json_t *json
) {
assert(self != NULL);
assert(json != NULL);
int err = json_unpack(json, REQUEST_BODY_GET_WORLD_DATA_JSON_FMT,
"world-id", &self->world_id
);
if (err < 0) return ERR_JSON_DESERIALISE;
return ERR_OK;
}
static enum error_t request_deserialise_body(
struct request_t *self,
struct json_t *json
) {
assert(self != NULL);
assert(json != NULL);
switch (self->type) {
case REQUEST_GET_WORLD_DATA:
self->body = malloc(sizeof(struct request_body_get_world_data_t));
if (self->body == NULL) return ERR_ALLOC;
return request_deserialise_body_get_world_data(self->body, json);
default: return ERR_JSON_SERIALISE;
}
}
static enum error_t request_deserialise(
struct request_t *self,
struct json_t *json
) {
assert(self != NULL);
assert(json != NULL);
struct request_t temp_request = { 0 };
struct json_t *body_json = NULL;
enum error_t err = request_deserialise_parts(
&body_json, &temp_request.type, json
);
if (err != ERR_OK) return err;
err = request_deserialise_body(&temp_request, body_json);
if (err != ERR_OK) {
json_decref(body_json);
return err;
}
*self = temp_request;
return ERR_OK;
}
enum error_t request_deserialise_str(struct request_t *self, char const *str) {
assert(self != NULL);
assert(str != NULL);
struct json_t *json = json_loads(str, 0, NULL);
if (json == NULL) return ERR_JSON_DESERIALISE;
enum error_t err = request_deserialise(self, json);
if (err != ERR_OK) {
json_decref(json);
return err;
}
json_decref(json);
return ERR_OK;
}
enum error_t response_serialise_str(
struct response_t const *self,
char const **strptr
);
enum error_t response_serialise_buf(
struct response_t const *self,
char *buf, size_t len
);
enum error_t response_deserialise_str(struct response_t *self, char const *str);

View File

@ -93,7 +93,7 @@ static enum error_t world_serialise_parts(
struct json_t *world_json = json_pack(WORLD_JSON_FMT, struct json_t *world_json = json_pack(WORLD_JSON_FMT,
"entities", entities_json, "entities", entities_json,
"registered_entities", registered_entities_json, "registered-entities", registered_entities_json,
"height", height, "width", width "height", height, "width", width
); );
@ -158,7 +158,7 @@ static enum error_t world_deserialise_parts(
int err = json_unpack(json, WORLD_JSON_FMT, int err = json_unpack(json, WORLD_JSON_FMT,
"entities", entities_json, "entities", entities_json,
"registered_entities", registered_entities_json, "registered-entities", registered_entities_json,
"height", height, "width", width "height", height, "width", width
); );
@ -262,10 +262,15 @@ enum error_t world_serialise_str(struct world_t *self, char const **strptr) {
struct json_t *json; struct json_t *json;
enum error_t err = world_serialise(self, &json); enum error_t err = world_serialise(self, &json);
if (err != ERR_OK) return ERR_JSON_SERIALISE; if (err != ERR_OK) return err;
const char *str = json_dumps(json, 0); const char *str = json_dumps(json, 0);
if (str == NULL) return ERR_JSON_SERIALISE; if (str == NULL) {
json_decref(json);
return ERR_JSON_SERIALISE;
}
json_decref(json);
*strptr = str; *strptr = str;
return ERR_OK; return ERR_OK;
@ -279,11 +284,15 @@ enum error_t world_serialise_buf(struct world_t *self, char *buf, size_t len) {
struct json_t *json; struct json_t *json;
enum error_t err = world_serialise(self, &json); enum error_t err = world_serialise(self, &json);
if (err != ERR_OK) return ERR_JSON_SERIALISE; if (err != ERR_OK) return err;
size_t bytes_written = json_dumpb(json, buf, len, 0); size_t bytes_written = json_dumpb(json, buf, len, 0);
if (bytes_written == 0) return ERR_JSON_SERIALISE; if (bytes_written == 0) {
json_decref(json);
return ERR_JSON_SERIALISE;
}
json_decref(json);
return ERR_OK; return ERR_OK;
} }
@ -296,8 +305,12 @@ enum error_t world_deserialise_str(struct world_t *self, char const *str) {
if (json == NULL) return ERR_JSON_DESERIALISE; if (json == NULL) return ERR_JSON_DESERIALISE;
enum error_t err = world_deserialise(self, json); enum error_t err = world_deserialise(self, json);
if (err != ERR_OK) return ERR_JSON_DESERIALISE; if (err != ERR_OK) {
json_decref(json);
return err;
}
json_decref(json);
return ERR_OK; return ERR_OK;
} }

View File

@ -1,51 +0,0 @@
# DESIGN
This document describes the design of this project. The main purpose is to describe the interface between the client and the server, but I might document other useful information here, too.
## Client/Server Communication
All communication is done through JSON.
### Client Requests
Clients can make requests to the server by sending a JSON object containing two fields:
* "request", which is a string of name of the request being made
* "parameters", which is an object whose structure depends on the request. It contains a named list of the request's parameters
### Server Responses
Upon receiving and handling a request, the server will send a response object with the following fields:
* "success", a boolean that indicates whether the request succeeded
* "response", an object whose structure depends on the request. It contains data relevant to the request
### Example Request and Response
An example request might look as follows:
```
{
"request": "create-world",
"parameters": {
"name": "test world",
"size": {
"height": 10,
"width": 15,
}
}
}
```
A success response might look like:
```
{
"success": true,
"response": {
"world-id": 69420
}
}
```
Or, in the case of an error:
```
{
"success": false,
"response": {
"code": 69
"message": "A world with this name already exists"
}
}
```

View File

@ -9,6 +9,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <error.h> #include <error.h>
#include <request.h>
#include "opts.h" #include "opts.h"
#include "sock.h" #include "sock.h"
@ -25,6 +26,29 @@ static void handle_signal(int signal_no) {
enum error_t game_loop(struct data_t *data) { enum error_t game_loop(struct data_t *data) {
enum error_t err = ERR_OK; enum error_t err = ERR_OK;
// TEMP
struct request_body_get_world_data_t request_body = { 69 };
struct request_t request = { REQUEST_GET_WORLD_DATA, &request_body };
char const *request_json_str = NULL;
err = request_serialise_str(&request, &request_json_str);
if (err) return err;
printf("serialised request: %s\n", request_json_str);
struct request_t request2 = { 0 };
err = request_deserialise_str(&request2, request_json_str);
if (err) return err;
struct request_body_get_world_data_t *request2_body = request2.body;
printf("deserialised request: (%d, %zu)\n",
request2.type,
request2_body->world_id
);
free((void *)request_json_str);
// TEMP
char obuf[8192] = { 0 }; char obuf[8192] = { 0 };
err = world_serialise_buf(&data->world, obuf, 8192); err = world_serialise_buf(&data->world, obuf, 8192);
if (err != ERR_OK) return err; if (err != ERR_OK) return err;

3
server/src/request.c Normal file
View File

@ -0,0 +1,3 @@
// request.c
#include "request.h"

8
server/src/request.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef REQUEST_H
#define REQUEST_H
#include <error.h>
enum error_t handle_request(void);
#endif