ddnet/src/engine/client/http.h

143 lines
3 KiB
C
Raw Normal View History

2018-12-12 08:59:42 +00:00
#ifndef ENGINE_CLIENT_HTTP_H
#define ENGINE_CLIENT_HTTP_H
#include <engine/kernel.h>
#include <engine/shared/jobs.h>
#include <engine/storage.h>
typedef struct _json_value json_value;
typedef void CURL;
enum
{
HTTP_ERROR = -1,
HTTP_QUEUED,
HTTP_RUNNING,
HTTP_DONE,
HTTP_ABORTED,
};
struct CTimeout
{
long ConnectTimeoutMs;
long LowSpeedLimit;
long LowSpeedTime;
};
class CRequest : public IJob
{
// Abort the request with an error if `BeforeInit()` or `AfterInit()`
2019-12-20 21:50:01 +00:00
// returns false. Also abort the request if `OnData()` returns
// something other than `DataSize`.
virtual bool BeforeInit() { return true; }
virtual bool AfterInit(void *pCurl) { return true; }
virtual size_t OnData(char *pData, size_t DataSize) = 0;
virtual void OnProgress() {}
char m_aUrl[256];
CTimeout m_Timeout;
double m_Size;
double m_Current;
int m_Progress;
bool m_LogProgress;
std::atomic<int> m_State;
std::atomic<bool> m_Abort;
static int ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr);
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser);
void Run();
int RunImpl(CURL *pHandle);
protected:
virtual int OnCompletion(int State) { return State; }
public:
CRequest(const char *pUrl, CTimeout Timeout, bool LogProgress = true);
double Current() const { return m_Current; }
double Size() const { return m_Size; }
int Progress() const { return m_Progress; }
int State() const { return m_State; }
void Abort() { m_Abort = true; }
};
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
class CHead : public CRequest
{
virtual size_t OnData(char *pData, size_t DataSize) { return DataSize; }
virtual bool AfterInit(void *pCurl);
public:
Reduce spamminess of http server browser As reported by fokkonaut on Discord. Not sure if we lose relevant information. Previously: [2021-05-28 12:08:54][http]: http https://master2.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:55][http]: task done https://master2.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:55][http]: http https://master2.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: task done https://master2.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][serverbrowse_http]: found master, url='https://master2.ddnet.tw/ddnet/15/servers.json' time=2437ms [2021-05-28 12:08:57][http]: http https://master4.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: task failed. libcurl error: Could not resolve host: master4.ddnet.tw [2021-05-28 12:08:57][http]: http https://master3.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: task failed. libcurl error: Could not resolve host: master3.ddnet.tw [2021-05-28 12:08:57][http]: http https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: task done https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: http https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:58][http]: task done https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:58][serverbrowse_http]: found master, url='https://master1.ddnet.tw/ddnet/15/servers.json' time=201ms [2021-05-28 12:08:58][serverbrowse_http]: determined best master, url='https://master1.ddnet.tw/ddnet/15/servers.json' time=201ms [2021-05-28 12:08:58][http]: http https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:58][http]: task done https://master1.ddnet.tw/ddnet/15/servers.json Now: [2021-05-28 12:29:58][http]: http https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:29:59][http]: task done https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:30:00][serverbrowse_http]: found master, url='https://master2.ddnet.tw/ddnet/15/servers.json' time=799ms [2021-05-28 12:30:00][serverbrowse_http]: found master, url='https://master1.ddnet.tw/ddnet/15/servers.json' time=43ms [2021-05-28 12:30:00][serverbrowse_http]: determined best master, url='https://master1.ddnet.tw/ddnet/15/servers.json' time=43ms
2021-05-28 10:18:53 +00:00
CHead(const char *pUrl, CTimeout Timeout, bool LogProgress = true);
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
~CHead();
};
class CGet : public CRequest
{
virtual size_t OnData(char *pData, size_t DataSize);
size_t m_BufferSize;
size_t m_BufferLength;
unsigned char *m_pBuffer;
public:
Reduce spamminess of http server browser As reported by fokkonaut on Discord. Not sure if we lose relevant information. Previously: [2021-05-28 12:08:54][http]: http https://master2.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:55][http]: task done https://master2.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:55][http]: http https://master2.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: task done https://master2.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][serverbrowse_http]: found master, url='https://master2.ddnet.tw/ddnet/15/servers.json' time=2437ms [2021-05-28 12:08:57][http]: http https://master4.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: task failed. libcurl error: Could not resolve host: master4.ddnet.tw [2021-05-28 12:08:57][http]: http https://master3.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: task failed. libcurl error: Could not resolve host: master3.ddnet.tw [2021-05-28 12:08:57][http]: http https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: task done https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:57][http]: http https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:58][http]: task done https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:58][serverbrowse_http]: found master, url='https://master1.ddnet.tw/ddnet/15/servers.json' time=201ms [2021-05-28 12:08:58][serverbrowse_http]: determined best master, url='https://master1.ddnet.tw/ddnet/15/servers.json' time=201ms [2021-05-28 12:08:58][http]: http https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:08:58][http]: task done https://master1.ddnet.tw/ddnet/15/servers.json Now: [2021-05-28 12:29:58][http]: http https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:29:59][http]: task done https://master1.ddnet.tw/ddnet/15/servers.json [2021-05-28 12:30:00][serverbrowse_http]: found master, url='https://master2.ddnet.tw/ddnet/15/servers.json' time=799ms [2021-05-28 12:30:00][serverbrowse_http]: found master, url='https://master1.ddnet.tw/ddnet/15/servers.json' time=43ms [2021-05-28 12:30:00][serverbrowse_http]: determined best master, url='https://master1.ddnet.tw/ddnet/15/servers.json' time=43ms
2021-05-28 10:18:53 +00:00
CGet(const char *pUrl, CTimeout Timeout, bool LogProgress = true);
~CGet();
size_t ResultSize() const
{
if(!Result())
{
return 0;
}
else
{
return m_BufferSize;
}
}
unsigned char *Result() const;
unsigned char *TakeResult();
json_value *ResultJson() const;
};
class CGetFile : public CRequest
{
virtual size_t OnData(char *pData, size_t DataSize);
virtual bool BeforeInit();
IStorage *m_pStorage;
char m_aDestFull[MAX_PATH_LENGTH];
IOHANDLE m_File;
protected:
char m_aDest[MAX_PATH_LENGTH];
int m_StorageType;
virtual int OnCompletion(int State);
public:
CGetFile(IStorage *pStorage, const char *pUrl, const char *pDest, int StorageType = -2, CTimeout Timeout = CTimeout{4000, 500, 5}, bool LogProgress = true);
const char *Dest() const { return m_aDest; }
};
class CPostJson : public CRequest
{
virtual size_t OnData(char *pData, size_t DataSize) { return DataSize; }
virtual bool AfterInit(void *pCurl);
char m_aJson[1024];
public:
CPostJson(const char *pUrl, CTimeout Timeout, const char *pJson);
};
bool HttpInit(IStorage *pStorage);
void EscapeUrl(char *pBuf, int Size, const char *pStr);
2018-12-12 08:59:42 +00:00
#endif // ENGINE_CLIENT_HTTP_H