tNew code added for non-blocking communication with the metaserver - vaccinewar… | |
git clone git://src.adamsgaard.dk/vaccinewars | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 8deb8cf2d3a6cd75cebbefcb6e8b15bc2c6a02b4 | |
parent de7b9a4dd681c475fa9423484288871b71209512 | |
Author: Ben Webb <[email protected]> | |
Date: Sun, 9 Sep 2001 21:26:38 +0000 | |
New code added for non-blocking communication with the metaserver | |
Diffstat: | |
M src/message.c | 157 +++++++++++++++++++++++++++++… | |
M src/message.h | 31 +++++++++++++++++++++++++++++… | |
2 files changed, 188 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/src/message.c b/src/message.c | |
t@@ -442,6 +442,21 @@ gchar *GetWaitingPlayerMessage(Player *Play) { | |
return GetWaitingMessage(&Play->NetBuf); | |
} | |
+gint CountWaitingMessages(NetworkBuffer *NetBuf) { | |
+/* Returns the number of complete (terminated) messages waiting in the */ | |
+/* given network buffer. This is the number of times that */ | |
+/* GetWaitingMessage() can be safely called without it returning NULL. */ | |
+ ConnBuf *conn; | |
+ gint i,msgs=0; | |
+ | |
+ conn=&NetBuf->ReadBuf; | |
+ | |
+ if (conn->Data) for (i=0;i<conn->DataPresent;i++) { | |
+ if (conn->Data[i]==NetBuf->Terminator) msgs++; | |
+ } | |
+ return msgs; | |
+} | |
+ | |
gchar *GetWaitingMessage(NetworkBuffer *NetBuf) { | |
/* Reads a complete (terminated) message from the network buffer. The */ | |
/* message is removed from the buffer, and returned as a null-terminated */ | |
t@@ -571,6 +586,148 @@ gboolean WriteDataToWire(NetworkBuffer *NetBuf) { | |
return TRUE; | |
} | |
+HttpConnection *OpenHttpConnection(gchar *HostName,unsigned Port, | |
+ gchar *Proxy,unsigned ProxyPort, | |
+ gchar *Method,gchar *Query, | |
+ gchar *Headers,gchar *Body) { | |
+ HttpConnection *conn; | |
+ gchar *ConnectHost; | |
+ unsigned ConnectPort; | |
+ GString *text; | |
+ g_assert(HostName && Method && Query); | |
+ | |
+ conn=g_new0(HttpConnection,1); | |
+ InitNetworkBuffer(&conn->NetBuf,'\n','\r'); | |
+ conn->HostName=g_strdup(HostName); | |
+ if (Proxy && Proxy[0]) conn->Proxy=g_strdup(Proxy); | |
+ conn->Method=g_strdup(Method); | |
+ conn->Query=g_strdup(Query); | |
+ if (Headers && Headers[0]) conn->Headers=g_strdup(Headers); | |
+ if (Body && Body[0]) conn->Body=g_strdup(Body); | |
+ conn->Port = Port; | |
+ conn->ProxyPort = ProxyPort; | |
+ | |
+ if (conn->Proxy) { | |
+ ConnectHost=conn->Proxy; ConnectPort=conn->ProxyPort; | |
+ } else { | |
+ ConnectHost=conn->HostName; ConnectPort=conn->Port; | |
+ } | |
+ | |
+ if (!StartNetworkBufferConnect(&conn->NetBuf,ConnectHost,ConnectPort)) { | |
+ CloseHttpConnection(conn); | |
+ return NULL; | |
+ } | |
+ conn->Tries++; | |
+ conn->StatusCode=0; | |
+ conn->Status=HS_CONNECTING; | |
+ | |
+ text=g_string_new(""); | |
+ | |
+ g_string_sprintf(text,"%s http://%s:%u%s HTTP/1.0", | |
+ conn->Method,conn->HostName,conn->Port,conn->Query); | |
+ QueueMessageForSend(&conn->NetBuf,text->str); | |
+ | |
+ if (conn->Headers) QueueMessageForSend(&conn->NetBuf,conn->Headers); | |
+ QueueMessageForSend(&conn->NetBuf,"\n"); | |
+ if (conn->Body) QueueMessageForSend(&conn->NetBuf,conn->Body); | |
+ | |
+ g_string_free(text,TRUE); | |
+ | |
+ return conn; | |
+} | |
+ | |
+HttpConnection *OpenMetaHttpConnection() { | |
+ gchar *query; | |
+ HttpConnection *retval; | |
+ | |
+ query = g_strdup_printf("%s?output=text&getlist=%d", | |
+ MetaServer.Path,METAVERSION); | |
+ retval = OpenHttpConnection(MetaServer.Name,MetaServer.Port, | |
+ MetaServer.ProxyName,MetaServer.ProxyPort, | |
+ "GET",query,NULL,NULL); | |
+ if (retval) g_print("HTTP connection successfully established\n"); | |
+ g_free(query); | |
+ return retval; | |
+} | |
+ | |
+void CloseHttpConnection(HttpConnection *conn) { | |
+ ShutdownNetworkBuffer(&conn->NetBuf); | |
+ g_free(conn->HostName); | |
+ g_free(conn->Proxy); | |
+ g_free(conn->Method); | |
+ g_free(conn->Query); | |
+ g_free(conn->Headers); | |
+ g_free(conn->Body); | |
+ g_free(conn); | |
+} | |
+ | |
+gchar *ReadHttpResponse(HttpConnection *conn) { | |
+ gchar *msg,**split; | |
+ | |
+ msg=GetWaitingMessage(&conn->NetBuf); | |
+ if (msg) switch(conn->Status) { | |
+ case HS_CONNECTING: /* OK, we should have the HTTP status line */ | |
+ conn->Status=HS_READHEADERS; | |
+ split=g_strsplit(msg," ",2); | |
+ if (split[0] && split[1]) { | |
+ conn->StatusCode=atoi(split[1]); | |
+ g_print("HTTP status code %d returned\n",conn->StatusCode); | |
+ } else g_warning("Invalid HTTP status line %s",msg); | |
+ g_strfreev(split); | |
+ break; | |
+ case HS_READHEADERS: | |
+ if (msg[0]==0) conn->Status=HS_READSEPARATOR; | |
+ break; | |
+ case HS_READSEPARATOR: | |
+ conn->Status=HS_READBODY; | |
+ break; | |
+ case HS_READBODY: /* At present, we do nothing special with the body */ | |
+ break; | |
+ } | |
+ return msg; | |
+} | |
+ | |
+gboolean HandleWaitingMetaServerData(HttpConnection *conn) { | |
+ gchar *msg; | |
+ ServerData *NewServer; | |
+ | |
+/* If we're done reading the headers, only read if the data for a whole | |
+ server is available (8 lines) N.B. "Status" is from the _last_ read */ | |
+ if (conn->Status==HS_READBODY) { | |
+ if (CountWaitingMessages(&conn->NetBuf)<8) return FALSE; | |
+ | |
+ NewServer=g_new0(ServerData,1); | |
+ NewServer->Name=ReadHttpResponse(conn); | |
+ g_print("Server name %s read from metaserver\n",NewServer->Name); | |
+ msg=ReadHttpResponse(conn); | |
+ NewServer->Port=atoi(msg); g_free(msg); | |
+ NewServer->Version=ReadHttpResponse(conn); | |
+ msg=ReadHttpResponse(conn); | |
+ if (msg[0]) NewServer->CurPlayers=atoi(msg); | |
+ else NewServer->CurPlayers=-1; | |
+ g_free(msg); | |
+ msg=ReadHttpResponse(conn); | |
+ NewServer->MaxPlayers=atoi(msg); g_free(msg); | |
+ NewServer->Update=ReadHttpResponse(conn); | |
+ NewServer->Comment=ReadHttpResponse(conn); | |
+ NewServer->UpSince=ReadHttpResponse(conn); | |
+ ServerList=g_slist_append(ServerList,NewServer); | |
+ } else if (conn->Status==HS_READSEPARATOR) { | |
+ /* This should be the first line of the body, the "MetaServer:" line */ | |
+ msg=ReadHttpResponse(conn); | |
+ if (!msg) return FALSE; | |
+ if (strncmp(msg,"MetaServer:",11)!=0) { | |
+ g_warning("Bad reply from metaserver: %s",msg); | |
+ } | |
+ g_free(msg); | |
+ } else { | |
+ msg=ReadHttpResponse(conn); | |
+ if (!msg) return FALSE; | |
+ g_free(msg); | |
+ } | |
+ return TRUE; | |
+} | |
+ | |
gchar *bgets(int fd) { | |
/* Drop-in substitute for fgets; reads a newline-terminated string from */ | |
/* file descriptor fd, into a dynamically-allocated buffer. Returns a */ | |
diff --git a/src/message.h b/src/message.h | |
t@@ -115,6 +115,27 @@ void SendPrintMessage(Player *From,char AICode,Player *To… | |
void SendQuestion(Player *From,char AICode,Player *To,char *Data); | |
#if NETWORKING | |
+/* Keeps track of the progress of an HTTP connection */ | |
+typedef enum _HttpStatus { | |
+ HS_CONNECTING,HS_READHEADERS,HS_READSEPARATOR,HS_READBODY | |
+} HttpStatus; | |
+ | |
+/* A structure used to keep track of an HTTP connection */ | |
+typedef struct _HttpConnection { | |
+ gchar *HostName; /* The machine on which the desired page resides */ | |
+ unsigned Port; /* The port */ | |
+ gchar *Proxy; /* If non-NULL, a web proxy to use */ | |
+ unsigned ProxyPort; /* The port to use for talking to the proxy */ | |
+ gchar *Method; /* e.g. GET, POST */ | |
+ gchar *Query; /* e.g. the path of the desired webpage */ | |
+ gchar *Headers; /* if non-NULL, e.g. Content-Type */ | |
+ gchar *Body; /* if non-NULL, data to send */ | |
+ NetworkBuffer NetBuf; /* The actual network connection itself */ | |
+ gint Tries; /* Number of requests actually sent so far */ | |
+ gint StatusCode; /* 0=no status yet, otherwise an HTTP status code */ | |
+ HttpStatus Status; | |
+} HttpConnection; | |
+ | |
char *StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort, | |
gboolean NonBlocking); | |
char *FinishConnect(int fd); | |
t@@ -142,8 +163,18 @@ gchar *GetWaitingPlayerMessage(Player *Play); | |
gboolean ReadDataFromWire(NetworkBuffer *NetBuf); | |
gboolean WriteDataToWire(NetworkBuffer *NetBuf); | |
void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data); | |
+gint CountWaitingMessages(NetworkBuffer *NetBuf); | |
gchar *GetWaitingMessage(NetworkBuffer *NetBuf); | |
+HttpConnection *OpenHttpConnection(gchar *HostName,unsigned Port, | |
+ gchar *Proxy,unsigned ProxyPort, | |
+ gchar *Method,gchar *Query, | |
+ gchar *Headers,gchar *Body); | |
+HttpConnection *OpenMetaHttpConnection(void); | |
+void CloseHttpConnection(HttpConnection *conn); | |
+gchar *ReadHttpResponse(HttpConnection *conn); | |
+gboolean HandleWaitingMetaServerData(HttpConnection *conn); | |
+ | |
gchar *bgets(int fd); | |
#endif /* NETWORKING */ | |