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 */