tNew code added for non-blocking communication with the metaserver - vaccinewars - be a doctor and try to vaccinate the world
(HTM) git clone git://src.adamsgaard.dk/vaccinewars
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit 8deb8cf2d3a6cd75cebbefcb6e8b15bc2c6a02b4
(DIR) parent de7b9a4dd681c475fa9423484288871b71209512
(HTM) Author: Ben Webb <ben@salilab.org>
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(-)
---
(DIR) 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 */
(DIR) diff --git a/src/message.h b/src/message.h
t@@ -115,6 +115,27 @@ void SendPrintMessage(Player *From,char AICode,Player *To,char *Data);
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 */