2 * $Logfile: /Freespace2/code/Inetfile/Chttpget.cpp $
7 * HTTP Client class (get only)
10 * Revision 1.3 2002/05/26 20:20:54 relnev
13 * inetfile/*: complete
15 * Revision 1.2 2002/05/07 03:16:45 theoddone33
16 * The Great Newline Fix
18 * Revision 1.1.1.1 2002/05/03 03:28:09 root
22 * 5 8/24/99 1:49a Dave
23 * Fixed client-side afterburner stuttering. Added checkbox for no version
24 * checking on PXO join. Made button info passing more friendly between
27 * 4 8/22/99 1:19p Dave
28 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
29 * which d3d cards are detected.
31 * 21 8/21/99 6:33p Kevin
34 * 20 8/21/99 6:48a Jeff
37 * 19 8/20/99 3:01p Kevin
38 * Added support for Proxies (I hope!)
40 * 18 8/15/99 6:38p Jeff
43 * 17 8/15/99 6:26p Kevin
45 * 16 4/14/99 1:20a Jeff
46 * fixed case mismatched #includes
48 * 15 3/03/99 12:28a Nate
49 * sped up something or other when the connection is done
51 * 14 2/03/99 4:20p Kevin
52 * Got multiplayer working with .mn3 files, and setup autodownloading
54 * 13 1/27/99 5:49p Kevin
56 * 12 1/27/99 5:38p Kevin
58 * 11 12/30/98 12:15p Kevin
59 * Auto Mission Download system
61 * 10 10/12/98 4:59p Kevin
62 * Added delay to thread when cancelled...
64 * 9 10/12/98 4:49p Nate
67 * 8 10/12/98 1:54p Nate
70 * 7 10/12/98 11:30a Kevin
73 * 6 10/08/98 12:59p Nate
76 * 5 10/08/98 9:57a Kevin
77 * made transfer cancellable
79 * 4 7/31/98 12:19p Nate
80 * Fixed http abort problem.
82 * 3 7/31/98 11:57a Kevin
83 * Added new functions for getting state
85 * 2 6/01/98 10:10a Kevin
86 * Added DLL connection interface and auto update DLL
88 * 1 5/27/98 9:52a Kevin
90 * 1 5/25/98 5:31p Kevin
104 #include <sys/types.h>
105 #include <sys/socket.h>
106 #include <netinet/in.h>
107 #include <arpa/inet.h>
109 #include <sys/ioctl.h>
121 #include "inetgetfile.h"
122 #include "chttpget.h"
126 inline void Sleep(int millis)
130 tv.tv_usec = millis*1000;
131 select(0,NULL,NULL,NULL,&tv);
135 #define NW_AGHBN_CANCEL 1
136 #define NW_AGHBN_LOOKUP 2
137 #define NW_AGHBN_READ 3
140 void __cdecl http_gethostbynameworker(void *parm);
142 void *http_gethostbynameworker(void *parm);
145 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname);
148 void HTTPObjThread( void * obj )
150 void *HTTPObjThread( void * obj )
153 ((ChttpGet *)obj)->WorkerThread();
154 ((ChttpGet *)obj)->m_Aborted = true;
155 //OutputDebugString("http transfer exiting....\n");
162 void ChttpGet::AbortGet()
165 OutputDebugString("Aborting....\n");
168 while(!m_Aborted) Sleep(50); //Wait for the thread to end
170 OutputDebugString("Aborted....\n");
174 ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport)
176 m_ProxyEnabled = true;
178 m_ProxyPort = proxyport;
179 GetFile(URL,localfile);
182 ChttpGet::ChttpGet(char *URL,char *localfile)
184 m_ProxyEnabled = false;
185 GetFile(URL,localfile);
189 void ChttpGet::GetFile(char *URL,char *localfile)
191 m_DataSock = INVALID_SOCKET;
194 m_State = HTTP_STATE_STARTUP;;
198 strncpy(m_URL,URL,MAX_URL_LEN-1);
199 m_URL[MAX_URL_LEN-1] = 0;
201 LOCALFILE = fopen(localfile,"wb");
202 if(NULL == LOCALFILE)
204 m_State = HTTP_STATE_CANT_WRITE_FILE;
207 m_DataSock = socket(AF_INET, SOCK_STREAM, 0);
208 if(INVALID_SOCKET == m_DataSock)
210 m_State = HTTP_STATE_SOCKET_ERROR;
218 ioctlsocket( m_DataSock, FIONBIO, &arg );
220 ioctl( m_DataSock, FIONBIO, &arg );
224 if(strnicmp(URL,"http:",5)==0)
232 //There shouldn't be any : in this string
235 m_State = HTTP_STATE_URL_PARSING_ERROR;
238 //read the filename by searching backwards for a /
239 //then keep reading until you find the first /
240 //when you found it, you have the host and dir
241 char *filestart = NULL;
242 char *dirstart = NULL;
243 for(int i = strlen(pURL);i>=0;i--)
249 filestart = pURL+i+1;
251 strcpy(m_szFilename,filestart);
259 if((dirstart==NULL) || (filestart==NULL))
261 m_State = HTTP_STATE_URL_PARSING_ERROR;
266 strcpy(m_szDir,dirstart);//,(filestart-dirstart));
267 //m_szDir[(filestart-dirstart)] = NULL;
268 strncpy(m_szHost,pURL,(dirstart-pURL));
269 m_szHost[(dirstart-pURL)-1] = '\0';
272 if(NULL==_beginthread(HTTPObjThread,0,this))
274 m_State = HTTP_STATE_INTERNAL_ERROR;
278 #elif defined(__LINUX__)
280 if(!inet_LoadThreadLib())
282 m_State = HTTP_STATE_INTERNAL_ERROR;
285 if(df_pthread_create(&thread,NULL,HTTPObjThread,this)!=0)
287 m_State = HTTP_STATE_INTERNAL_ERROR;
295 ChttpGet::~ChttpGet()
297 if(m_DataSock != INVALID_SOCKET)
299 shutdown(m_DataSock,2);
301 closesocket(m_DataSock);
308 int ChttpGet::GetStatus()
313 unsigned int ChttpGet::GetBytesIn()
318 unsigned int ChttpGet::GetTotalBytes()
320 return m_iBytesTotal;
324 void ChttpGet::WorkerThread()
326 char szCommand[1000];
335 if(m_State != HTTP_STATE_CONNECTED)
340 sprintf(szCommand,"GET %s%s HTTP/1.1\nAccept: */*\nAccept-Encoding: deflate\nHost: %s\n\n\n",m_ProxyEnabled?"":"/",m_ProxyEnabled?m_URL:m_szDir,m_szHost);
341 send(m_DataSock,szCommand,strlen(szCommand),0);
343 if(strnicmp("HTTP/",p,5)==0)
346 pcode = strchr(p,' ')+1;
349 m_State = HTTP_STATE_UNKNOWN_ERROR;
359 m_State = HTTP_STATE_UNKNOWN_ERROR;
371 m_State = HTTP_STATE_UNKNOWN_ERROR;
380 if(strnicmp(p,"Content-Length:",strlen("Content-Length:"))==0)
382 char *s = strchr(p,' ')+1;
394 m_iBytesTotal = atoi(p);
404 m_State = HTTP_STATE_FILE_NOT_FOUND;
410 m_State = HTTP_STATE_UNKNOWN_ERROR;
416 int ChttpGet::ConnectSocket()
421 SOCKADDR_IN hostaddr;
426 ip = inet_addr((const char *)m_szHost);
431 http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_szHost);
436 http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_szHost);
439 rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_szHost);
447 m_State = HTTP_STATE_HOST_NOT_FOUND;
453 se = getservbyname("http", NULL);
458 hostaddr.sin_port = htons(80);
462 hostaddr.sin_port = se->s_port;
464 hostaddr.sin_family = AF_INET;
466 memcpy(&hostaddr.sin_addr,&ip,4);
470 //This is on a proxy, so we need to make sure to connect to the proxy machine
471 ip = inet_addr((const char *)m_ProxyIP);
475 http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_ProxyIP);
481 http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_ProxyIP);
484 rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_ProxyIP);
492 m_State = HTTP_STATE_HOST_NOT_FOUND;
497 //Use either the proxy port or 80 if none specified
498 hostaddr.sin_port = htons((ushort)(m_ProxyPort ? m_ProxyPort : 80));
499 //Copy the proxy address...
500 memcpy(&hostaddr.sin_addr,&ip,4);
503 //Now we will connect to the host
509 int serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
510 int cerr = WSAGetLastError();
513 while((cerr==WSAEALREADY)||(cerr==WSAEINVAL)||(cerr==WSAEWOULDBLOCK))
516 FD_SET( m_DataSock, &wfds );
517 if(select(0,NULL,&wfds,NULL,&timeout))
524 serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
527 cerr = WSAGetLastError();
539 m_State = HTTP_STATE_CANT_CONNECT;
542 m_State = HTTP_STATE_CONNECTED;
546 char *ChttpGet::GetHTTPLine()
548 unsigned int iBytesRead;
550 unsigned int igotcrlf = 0;
551 memset(recv_buffer,0,1000);
555 bool gotdata = false;
558 iBytesRead = recv(m_DataSock,chunk,1,0);
560 if(SOCKET_ERROR == iBytesRead)
562 int error = WSAGetLastError();
563 if(WSAEWOULDBLOCK==error)
581 //This should always read a 0x0a
584 iBytesRead = recv(m_DataSock,chunk,1,0);
586 if(SOCKET_ERROR == iBytesRead)
588 int error = WSAGetLastError();
589 if(WSAEWOULDBLOCK==error)
608 strcat(recv_buffer,chunk);
616 unsigned int ChttpGet::ReadDataChannel()
618 char sDataBuffer[4096]; // Data-storage buffer for the data channel
619 int nBytesRecv = 0; // Bytes received from the data channel
625 timeout.tv_usec = 500;
627 m_State = HTTP_STATE_RECEIVING;
631 FD_SET( m_DataSock, &wfds );
633 if((m_iBytesTotal)&&(m_iBytesIn==m_iBytesTotal))
637 select(0,&wfds,NULL,NULL,&timeout);
643 nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer,sizeof(sDataBuffer), 0);
649 if(SOCKET_ERROR == nBytesRecv)
651 int error = WSAGetLastError();
652 if(WSAEWOULDBLOCK==error)
658 m_iBytesIn += nBytesRecv;
661 fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
662 //Write sDataBuffer, nBytesRecv
666 }while (nBytesRecv > 0);
668 // Close the file and check for error returns.
669 if (nBytesRecv == SOCKET_ERROR)
671 //Ok, we got a socket error -- xfer aborted?
672 m_State = HTTP_STATE_RECV_FAILED;
677 //OutputDebugString("HTTP File complete!\n");
679 m_State = HTTP_STATE_FILE_RECEIVED;
685 typedef struct _async_dns_lookup
687 unsigned int ip; //resolved host. Write only to worker thread.
688 char * host;//host name to resolve. read only to worker thread
689 bool done; //write only to the worker thread. Signals that the operation is complete
690 bool error; //write only to worker thread. Thread sets this if the name doesn't resolve
691 bool abort; //read only to worker thread. If this is set, don't fill in the struct.
694 async_dns_lookup httpaslu;
695 async_dns_lookup *http_lastaslu = NULL;
698 void __cdecl http_gethostbynameworker(void *parm);
700 void *http_gethostbynameworker(void *parm);
703 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname)
706 if(command==NW_AGHBN_LOOKUP)
709 http_lastaslu->abort = true;
711 async_dns_lookup *newaslu;
712 newaslu = (async_dns_lookup *)malloc(sizeof(async_dns_lookup));
713 memset(&newaslu->ip,0,sizeof(unsigned int));
714 newaslu->host = hostname;
715 newaslu->done = false;
716 newaslu->error = false;
717 newaslu->abort = false;
718 http_lastaslu = newaslu;
719 httpaslu.done = false;
722 _beginthread(http_gethostbynameworker,0,newaslu);
724 #elif defined(__LINUX__)
726 if(!inet_LoadThreadLib())
731 df_pthread_create(&thread,NULL,http_gethostbynameworker,newaslu);
736 else if(command==NW_AGHBN_CANCEL)
739 http_lastaslu->abort = true;
740 http_lastaslu = NULL;
742 else if(command==NW_AGHBN_READ)
748 //free(http_lastaslu);
749 http_lastaslu = NULL;
750 memcpy(ip,&httpaslu.ip,sizeof(unsigned int));
753 else if(httpaslu.error)
756 http_lastaslu = NULL;
765 // This is the worker thread which does the lookup.
767 void __cdecl http_gethostbynameworker(void *parm)
769 void *http_gethostbynameworker(void *parm)
773 df_pthread_detach(df_pthread_self());
775 async_dns_lookup *lookup = (async_dns_lookup *)parm;
776 HOSTENT *he = gethostbyname(lookup->host);
779 lookup->error = true;
786 else if(!lookup->abort)
788 memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int));
790 memcpy(&httpaslu,lookup,sizeof(async_dns_lookup));