mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-25 05:56:58 +01:00
Implement and test bridge request chunking
- Minor cleanups included
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "v5\_defines.dm"
|
||||
#include "v5\api.dm"
|
||||
#include "v5\bridge.dm"
|
||||
#include "v5\commands.dm"
|
||||
#include "v5\serializers.dm"
|
||||
#include "v5\undefs.dm"
|
||||
|
||||
@@ -5,6 +5,7 @@ This DMAPI implements bridge requests using HTTP GET requests to TGS. It has no
|
||||
- [__interop_version.dm](./__interop_version.dm) contains the version of the API used between the DMAPI and TGS.
|
||||
- [_defines.dm](./_defines.dm) contains constant definitions.
|
||||
- [api.dm](./api.dm) contains the bulk of the API code.
|
||||
- [bridge.dm](./bridge.dm) contains the functions related to making bridge requests.
|
||||
- [commands.dm](./commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
|
||||
- [serializers.dm](./serializers.dm) contains function to help convert interop `/datum`s into a JSON encodable `list()` format.
|
||||
- [undefs.dm](./undefs.dm) Undoes the work of `_defines.dm`.
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#define DMAPI5_BRIDGE_COMMAND_REBOOT 3
|
||||
#define DMAPI5_BRIDGE_COMMAND_KILL 4
|
||||
#define DMAPI5_BRIDGE_COMMAND_CHAT_SEND 5
|
||||
#define DMAPI5_BRIDGE_COMMAND_CHUNK 6
|
||||
|
||||
#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
|
||||
#define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands"
|
||||
@@ -26,6 +27,7 @@
|
||||
|
||||
#define DMAPI5_BRIDGE_RESPONSE_NEW_PORT "newPort"
|
||||
#define DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION "runtimeInformation"
|
||||
#define DMAPI5_BRIDGE_RESPONSE_MISSING_CHUNKS "missingChunks"
|
||||
|
||||
#define DMAPI5_CHAT_MESSAGE_CHANNEL_IDS "channelIds"
|
||||
|
||||
|
||||
+1
-33
@@ -16,6 +16,7 @@
|
||||
var/list/chat_channels
|
||||
|
||||
var/initialized = FALSE
|
||||
var/chunked_requests = 0
|
||||
|
||||
/datum/tgs_api/v5/ApiVersion()
|
||||
return new /datum/tgs_version(
|
||||
@@ -244,39 +245,6 @@
|
||||
|
||||
return TopicResponse("Unknown command: [command]")
|
||||
|
||||
/datum/tgs_api/v5/proc/Bridge(command, list/data)
|
||||
if(!data)
|
||||
data = list()
|
||||
|
||||
data[DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE] = command
|
||||
data[DMAPI5_PARAMETER_ACCESS_IDENTIFIER] = access_identifier
|
||||
|
||||
var/json = json_encode(data)
|
||||
var/encoded_json = url_encode(json)
|
||||
|
||||
// This is an infinite sleep until we get a response
|
||||
var/export_response = world.Export("http://127.0.0.1:[server_port]/Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]")
|
||||
if(!export_response)
|
||||
TGS_ERROR_LOG("Failed export request: [json]")
|
||||
return
|
||||
|
||||
var/response_json = file2text(export_response["CONTENT"])
|
||||
if(!response_json)
|
||||
TGS_ERROR_LOG("Failed export request, missing content!")
|
||||
return
|
||||
|
||||
var/list/bridge_response = json_decode(response_json)
|
||||
if(!bridge_response)
|
||||
TGS_ERROR_LOG("Failed export request, bad json: [response_json]")
|
||||
return
|
||||
|
||||
var/error = bridge_response[DMAPI5_RESPONSE_ERROR_MESSAGE]
|
||||
if(error)
|
||||
TGS_ERROR_LOG("Failed export request, bad request: [error]")
|
||||
return
|
||||
|
||||
return bridge_response
|
||||
|
||||
/datum/tgs_api/v5/OnReboot()
|
||||
var/list/result = Bridge(DMAPI5_BRIDGE_COMMAND_REBOOT)
|
||||
if(!result)
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/datum/tgs_api/v5/proc/Bridge(command, list/data)
|
||||
if(!data)
|
||||
data = list()
|
||||
|
||||
var/single_bridge_request = CreateBridgeRequest(command, data)
|
||||
if(length(single_bridge_request) <= DMAPI5_BRIDGE_REQUEST_LIMIT)
|
||||
return PerformBridgeRequest(single_bridge_request)
|
||||
|
||||
// chunking required
|
||||
var/payload_id = ++chunked_requests
|
||||
|
||||
var/raw_data = CreateBridgeData(command, data, FALSE)
|
||||
var/data_length = length(raw_data)
|
||||
|
||||
var/chunk_count
|
||||
var/list/chunk_requests
|
||||
for(chunk_count = 2; !chunk_requests; ++chunk_count);
|
||||
var/max_chunk_size = -round(-(data_length / chunk_count))
|
||||
if(max_chunk_size > DMAPI5_BRIDGE_REQUEST_LIMIT)
|
||||
continue
|
||||
|
||||
chunk_requests = list()
|
||||
for(var/i in 1 to chunk_count)
|
||||
var/startIndex = 1 + ((i - 1) * max_chunk_size)
|
||||
var/endIndex = min(1 + (i * max_chunk_size), data_length + 1)
|
||||
var/chunk_payload = copytext(raw_data, startIndex, endIndex)
|
||||
var/list/chunk = list("payloadId" = payload_id, "sequenceId" = (i - 1), "totalChunks" = chunk_count, payload = chunk_payload)
|
||||
|
||||
var/chunk_request = CreateBridgeRequest(DMAPI5_BRIDGE_COMMAND_CHUNK, list("chunk" = chunk))
|
||||
if(length(chunk_request) > DMAPI5_BRIDGE_REQUEST_LIMIT)
|
||||
// Screwed by url encoding, no way to preempt it though
|
||||
chunk_requests = null
|
||||
break
|
||||
|
||||
chunk_requests += chunk_request
|
||||
|
||||
var/list/response
|
||||
for(var/bridge_request in chunk_requests)
|
||||
response = PerformBridgeRequest(bridge_request)
|
||||
if(!response)
|
||||
// Abort
|
||||
return
|
||||
|
||||
var/list/missing_sequence_ids = response[DMAPI5_BRIDGE_RESPONSE_MISSING_CHUNKS]
|
||||
if(length(missing_sequence_ids))
|
||||
do
|
||||
TGS_WARNING_LOG("Server is missing some chunks of payload [payload_id]! Sending missing chunks...")
|
||||
if(!istype(missing_sequence_ids))
|
||||
TGS_ERROR_LOG("Did not receive a list() for [DMAPI5_BRIDGE_RESPONSE_MISSING_CHUNKS]!")
|
||||
return
|
||||
|
||||
for(var/missing_sequence_id in missing_sequence_ids)
|
||||
if(!isnum(missing_sequence_id))
|
||||
TGS_ERROR_LOG("Did not receive a num in [DMAPI5_BRIDGE_RESPONSE_MISSING_CHUNKS]!")
|
||||
return
|
||||
|
||||
var/missing_chunk_request = chunk_requests[missing_sequence_id + 1]
|
||||
response = PerformBridgeRequest(missing_chunk_request)
|
||||
if(!response)
|
||||
// Abort
|
||||
return
|
||||
|
||||
missing_sequence_ids = response[DMAPI5_BRIDGE_RESPONSE_MISSING_CHUNKS]
|
||||
while(length(missing_sequence_ids))
|
||||
|
||||
return response
|
||||
|
||||
/datum/tgs_api/v5/proc/CreateBridgeRequest(command, list/data)
|
||||
var/json = CreateBridgeData(command, data, TRUE)
|
||||
var/encoded_json = url_encode(json)
|
||||
|
||||
var/url = "http://127.0.0.1:[server_port]/Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]"
|
||||
return url
|
||||
|
||||
/datum/tgs_api/v5/proc/CreateBridgeData(command, list/data, needs_auth)
|
||||
data[DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE] = command
|
||||
if(needs_auth)
|
||||
data[DMAPI5_PARAMETER_ACCESS_IDENTIFIER] = access_identifier
|
||||
|
||||
var/json = json_encode(data)
|
||||
return json
|
||||
|
||||
/datum/tgs_api/v5/proc/PerformBridgeRequest(bridge_request)
|
||||
// This is an infinite sleep until we get a response
|
||||
var/export_response = world.Export(bridge_request)
|
||||
if(!export_response)
|
||||
TGS_ERROR_LOG("Failed bridge request: [bridge_request]")
|
||||
return
|
||||
|
||||
var/response_json = file2text(export_response["CONTENT"])
|
||||
if(!response_json)
|
||||
TGS_ERROR_LOG("Failed bridge request, missing content!")
|
||||
return
|
||||
|
||||
var/list/bridge_response = json_decode(response_json)
|
||||
if(!bridge_response)
|
||||
TGS_ERROR_LOG("Failed bridge request, bad json: [response_json]")
|
||||
return
|
||||
|
||||
var/error = bridge_response[DMAPI5_RESPONSE_ERROR_MESSAGE]
|
||||
if(error)
|
||||
TGS_ERROR_LOG("Failed bridge request, bad request: [error]")
|
||||
return
|
||||
|
||||
return bridge_response
|
||||
Reference in New Issue
Block a user