starting work on request/response handling
This commit is contained in:
parent
bfbba84b5c
commit
3470d4bebc
54
common/include/request.h
Normal file
54
common/include/request.h
Normal 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
252
common/src/request.c
Normal 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);
|
@ -93,7 +93,7 @@ static enum error_t world_serialise_parts(
|
||||
|
||||
struct json_t *world_json = json_pack(WORLD_JSON_FMT,
|
||||
"entities", entities_json,
|
||||
"registered_entities", registered_entities_json,
|
||||
"registered-entities", registered_entities_json,
|
||||
"height", height, "width", width
|
||||
);
|
||||
|
||||
@ -158,7 +158,7 @@ static enum error_t world_deserialise_parts(
|
||||
|
||||
int err = json_unpack(json, WORLD_JSON_FMT,
|
||||
"entities", entities_json,
|
||||
"registered_entities", registered_entities_json,
|
||||
"registered-entities", registered_entities_json,
|
||||
"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;
|
||||
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);
|
||||
if (str == NULL) return ERR_JSON_SERIALISE;
|
||||
if (str == NULL) {
|
||||
json_decref(json);
|
||||
return ERR_JSON_SERIALISE;
|
||||
}
|
||||
|
||||
json_decref(json);
|
||||
|
||||
*strptr = str;
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -296,8 +305,12 @@ enum error_t world_deserialise_str(struct world_t *self, char const *str) {
|
||||
if (json == NULL) return ERR_JSON_DESERIALISE;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
```
|
@ -9,6 +9,7 @@
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <error.h>
|
||||
#include <request.h>
|
||||
|
||||
#include "opts.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 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 };
|
||||
err = world_serialise_buf(&data->world, obuf, 8192);
|
||||
if (err != ERR_OK) return err;
|
||||
|
3
server/src/request.c
Normal file
3
server/src/request.c
Normal file
@ -0,0 +1,3 @@
|
||||
// request.c
|
||||
|
||||
#include "request.h"
|
8
server/src/request.h
Normal file
8
server/src/request.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef REQUEST_H
|
||||
#define REQUEST_H
|
||||
|
||||
#include <error.h>
|
||||
|
||||
enum error_t handle_request(void);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user