2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Inetfile/Chttpget.cpp $
15 * HTTP Client class (get only)
18 * Revision 1.7 2002/06/17 06:33:09 relnev
19 * ryan's struct patch for gcc 2.95
21 * Revision 1.6 2002/06/09 04:41:21 relnev
22 * added copyright header
24 * Revision 1.5 2002/06/02 04:26:34 relnev
27 * Revision 1.4 2002/05/26 20:32:24 theoddone33
28 * Fix some minor stuff
30 * Revision 1.3 2002/05/26 20:20:54 relnev
35 * Revision 1.2 2002/05/07 03:16:45 theoddone33
36 * The Great Newline Fix
38 * Revision 1.1.1.1 2002/05/03 03:28:09 root
42 * 5 8/24/99 1:49a Dave
43 * Fixed client-side afterburner stuttering. Added checkbox for no version
44 * checking on PXO join. Made button info passing more friendly between
47 * 4 8/22/99 1:19p Dave
48 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
49 * which d3d cards are detected.
51 * 21 8/21/99 6:33p Kevin
54 * 20 8/21/99 6:48a Jeff
57 * 19 8/20/99 3:01p Kevin
58 * Added support for Proxies (I hope!)
60 * 18 8/15/99 6:38p Jeff
63 * 17 8/15/99 6:26p Kevin
65 * 16 4/14/99 1:20a Jeff
66 * fixed case mismatched #includes
68 * 15 3/03/99 12:28a Nate
69 * sped up something or other when the connection is done
71 * 14 2/03/99 4:20p Kevin
72 * Got multiplayer working with .mn3 files, and setup autodownloading
74 * 13 1/27/99 5:49p Kevin
76 * 12 1/27/99 5:38p Kevin
78 * 11 12/30/98 12:15p Kevin
79 * Auto Mission Download system
81 * 10 10/12/98 4:59p Kevin
82 * Added delay to thread when cancelled...
84 * 9 10/12/98 4:49p Nate
87 * 8 10/12/98 1:54p Nate
90 * 7 10/12/98 11:30a Kevin
93 * 6 10/08/98 12:59p Nate
96 * 5 10/08/98 9:57a Kevin
97 * made transfer cancellable
99 * 4 7/31/98 12:19p Nate
100 * Fixed http abort problem.
102 * 3 7/31/98 11:57a Kevin
103 * Added new functions for getting state
105 * 2 6/01/98 10:10a Kevin
106 * Added DLL connection interface and auto update DLL
108 * 1 5/27/98 9:52a Kevin
110 * 1 5/25/98 5:31p Kevin
124 #include <sys/types.h>
125 #include <sys/socket.h>
126 #include <netinet/in.h>
127 #include <arpa/inet.h>
129 #include <sys/ioctl.h>
131 #include <sys/time.h>
143 #include "inetgetfile.h"
144 #include "chttpget.h"
148 inline void Sleep(int millis)
152 tv.tv_usec = millis*1000;
153 select(0,NULL,NULL,NULL,&tv);
157 #define NW_AGHBN_CANCEL 1
158 #define NW_AGHBN_LOOKUP 2
159 #define NW_AGHBN_READ 3
162 void __cdecl http_gethostbynameworker(void *parm);
164 void *http_gethostbynameworker(void *parm);
167 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname);
170 void HTTPObjThread( void * obj )
172 void *HTTPObjThread( void * obj )
175 ((ChttpGet *)obj)->WorkerThread();
176 ((ChttpGet *)obj)->m_Aborted = true;
177 //OutputDebugString("http transfer exiting....\n");
184 void ChttpGet::AbortGet()
187 OutputDebugString("Aborting....\n");
190 while(!m_Aborted) Sleep(50); //Wait for the thread to end
192 OutputDebugString("Aborted....\n");
196 ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport)
198 m_ProxyEnabled = true;
200 m_ProxyPort = proxyport;
201 GetFile(URL,localfile);
204 ChttpGet::ChttpGet(char *URL,char *localfile)
206 m_ProxyEnabled = false;
207 GetFile(URL,localfile);
211 void ChttpGet::GetFile(char *URL,char *localfile)
213 m_DataSock = INVALID_SOCKET;
216 m_State = HTTP_STATE_STARTUP;;
220 strncpy(m_URL,URL,MAX_URL_LEN-1);
221 m_URL[MAX_URL_LEN-1] = 0;
223 LOCALFILE = fopen(localfile,"wb");
224 if(NULL == LOCALFILE)
226 m_State = HTTP_STATE_CANT_WRITE_FILE;
229 m_DataSock = socket(AF_INET, SOCK_STREAM, 0);
230 if(INVALID_SOCKET == m_DataSock)
232 m_State = HTTP_STATE_SOCKET_ERROR;
240 ioctlsocket( m_DataSock, FIONBIO, &arg );
242 ioctl( m_DataSock, FIONBIO, &arg );
246 if(strnicmp(URL,"http:",5)==0)
254 //There shouldn't be any : in this string
257 m_State = HTTP_STATE_URL_PARSING_ERROR;
260 //read the filename by searching backwards for a /
261 //then keep reading until you find the first /
262 //when you found it, you have the host and dir
263 char *filestart = NULL;
264 char *dirstart = NULL;
265 for(int i = strlen(pURL);i>=0;i--)
271 filestart = pURL+i+1;
273 strcpy(m_szFilename,filestart);
281 if((dirstart==NULL) || (filestart==NULL))
283 m_State = HTTP_STATE_URL_PARSING_ERROR;
288 strcpy(m_szDir,dirstart);//,(filestart-dirstart));
289 //m_szDir[(filestart-dirstart)] = NULL;
290 strncpy(m_szHost,pURL,(dirstart-pURL));
291 m_szHost[(dirstart-pURL)-1] = '\0';
294 if(NULL==_beginthread(HTTPObjThread,0,this))
296 m_State = HTTP_STATE_INTERNAL_ERROR;
300 #elif defined(__LINUX__)
302 if(!inet_LoadThreadLib())
304 m_State = HTTP_STATE_INTERNAL_ERROR;
307 if(df_pthread_create(&thread,NULL,HTTPObjThread,this)!=0)
309 m_State = HTTP_STATE_INTERNAL_ERROR;
317 ChttpGet::~ChttpGet()
319 if(m_DataSock != INVALID_SOCKET)
321 shutdown(m_DataSock,2);
323 closesocket(m_DataSock);
330 int ChttpGet::GetStatus()
335 unsigned int ChttpGet::GetBytesIn()
340 unsigned int ChttpGet::GetTotalBytes()
342 return m_iBytesTotal;
346 void ChttpGet::WorkerThread()
348 char szCommand[1000];
357 if(m_State != HTTP_STATE_CONNECTED)
362 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);
363 send(m_DataSock,szCommand,strlen(szCommand),0);
365 if(strnicmp("HTTP/",p,5)==0)
368 pcode = strchr(p,' ')+1;
371 m_State = HTTP_STATE_UNKNOWN_ERROR;
381 m_State = HTTP_STATE_UNKNOWN_ERROR;
393 m_State = HTTP_STATE_UNKNOWN_ERROR;
402 if(strnicmp(p,"Content-Length:",strlen("Content-Length:"))==0)
404 char *s = strchr(p,' ')+1;
416 m_iBytesTotal = atoi(p);
426 m_State = HTTP_STATE_FILE_NOT_FOUND;
432 m_State = HTTP_STATE_UNKNOWN_ERROR;
438 int ChttpGet::ConnectSocket()
443 SOCKADDR_IN hostaddr;
448 ip = inet_addr((const char *)m_szHost);
453 http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_szHost);
458 http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_szHost);
461 rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_szHost);
469 m_State = HTTP_STATE_HOST_NOT_FOUND;
475 se = getservbyname("http", NULL);
480 hostaddr.sin_port = htons(80);
484 hostaddr.sin_port = se->s_port;
486 hostaddr.sin_family = AF_INET;
488 memcpy(&hostaddr.sin_addr,&ip,4);
492 //This is on a proxy, so we need to make sure to connect to the proxy machine
493 ip = inet_addr((const char *)m_ProxyIP);
497 http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_ProxyIP);
503 http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_ProxyIP);
506 rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_ProxyIP);
514 m_State = HTTP_STATE_HOST_NOT_FOUND;
519 //Use either the proxy port or 80 if none specified
520 hostaddr.sin_port = htons((ushort)(m_ProxyPort ? m_ProxyPort : 80));
521 //Copy the proxy address...
522 memcpy(&hostaddr.sin_addr,&ip,4);
525 //Now we will connect to the host
531 int serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
532 int cerr = WSAGetLastError();
535 while((cerr==WSAEALREADY)||(cerr==WSAEINVAL)||(cerr==WSAEWOULDBLOCK))
538 FD_SET( m_DataSock, &wfds );
539 if(select(0,NULL,&wfds,NULL,&timeout))
546 serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
549 cerr = WSAGetLastError();
561 m_State = HTTP_STATE_CANT_CONNECT;
564 m_State = HTTP_STATE_CONNECTED;
568 char *ChttpGet::GetHTTPLine()
572 unsigned int igotcrlf = 0;
573 memset(recv_buffer,0,1000);
577 bool gotdata = false;
580 iBytesRead = recv(m_DataSock,chunk,1,0);
582 if(SOCKET_ERROR == iBytesRead)
584 int error = WSAGetLastError();
585 if(WSAEWOULDBLOCK==error)
603 //This should always read a 0x0a
606 iBytesRead = recv(m_DataSock,chunk,1,0);
608 if(SOCKET_ERROR == iBytesRead)
610 int error = WSAGetLastError();
611 if(WSAEWOULDBLOCK==error)
630 strcat(recv_buffer,chunk);
638 unsigned int ChttpGet::ReadDataChannel()
640 char sDataBuffer[4096]; // Data-storage buffer for the data channel
641 int nBytesRecv = 0; // Bytes received from the data channel
647 timeout.tv_usec = 500;
649 m_State = HTTP_STATE_RECEIVING;
653 FD_SET( m_DataSock, &wfds );
655 if((m_iBytesTotal)&&(m_iBytesIn==m_iBytesTotal))
659 select(0,&wfds,NULL,NULL,&timeout);
665 nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer,sizeof(sDataBuffer), 0);
671 if(SOCKET_ERROR == nBytesRecv)
673 int error = WSAGetLastError();
674 if(WSAEWOULDBLOCK==error)
680 m_iBytesIn += nBytesRecv;
683 fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
684 //Write sDataBuffer, nBytesRecv
688 }while (nBytesRecv > 0);
690 // Close the file and check for error returns.
691 if (nBytesRecv == SOCKET_ERROR)
693 //Ok, we got a socket error -- xfer aborted?
694 m_State = HTTP_STATE_RECV_FAILED;
699 //OutputDebugString("HTTP File complete!\n");
701 m_State = HTTP_STATE_FILE_RECEIVED;
707 typedef struct _async_dns_lookup
709 unsigned int ip; //resolved host. Write only to worker thread.
710 char * host;//host name to resolve. read only to worker thread
711 bool done; //write only to the worker thread. Signals that the operation is complete
712 bool error; //write only to worker thread. Thread sets this if the name doesn't resolve
713 bool abort; //read only to worker thread. If this is set, don't fill in the struct.
716 async_dns_lookup httpaslu;
717 async_dns_lookup *http_lastaslu = NULL;
720 void __cdecl http_gethostbynameworker(void *parm);
722 void *http_gethostbynameworker(void *parm);
725 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname)
728 if(command==NW_AGHBN_LOOKUP)
731 http_lastaslu->abort = true;
733 async_dns_lookup *newaslu;
734 newaslu = (async_dns_lookup *)malloc(sizeof(async_dns_lookup));
735 memset(&newaslu->ip,0,sizeof(unsigned int));
736 newaslu->host = hostname;
737 newaslu->done = false;
738 newaslu->error = false;
739 newaslu->abort = false;
740 http_lastaslu = newaslu;
741 httpaslu.done = false;
744 _beginthread(http_gethostbynameworker,0,newaslu);
746 #elif defined(__LINUX__)
748 if(!inet_LoadThreadLib())
753 df_pthread_create(&thread,NULL,http_gethostbynameworker,newaslu);
758 else if(command==NW_AGHBN_CANCEL)
761 http_lastaslu->abort = true;
762 http_lastaslu = NULL;
764 else if(command==NW_AGHBN_READ)
770 //free(http_lastaslu);
771 http_lastaslu = NULL;
772 memcpy(ip,&httpaslu.ip,sizeof(unsigned int));
775 else if(httpaslu.error)
778 http_lastaslu = NULL;
787 // This is the worker thread which does the lookup.
789 void __cdecl http_gethostbynameworker(void *parm)
791 void *http_gethostbynameworker(void *parm)
795 df_pthread_detach(df_pthread_self());
797 async_dns_lookup *lookup = (async_dns_lookup *)parm;
798 HOSTENT *he = gethostbyname(lookup->host);
801 lookup->error = true;
808 else if(!lookup->abort)
810 memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int));
812 memcpy(&httpaslu,lookup,sizeof(async_dns_lookup));