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