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.6 2002/06/09 04:41:21 relnev
19 * added copyright header
21 * Revision 1.5 2002/06/02 04:26:34 relnev
24 * Revision 1.4 2002/05/26 20:32:24 theoddone33
25 * Fix some minor stuff
27 * Revision 1.3 2002/05/26 20:20:54 relnev
32 * Revision 1.2 2002/05/07 03:16:45 theoddone33
33 * The Great Newline Fix
35 * Revision 1.1.1.1 2002/05/03 03:28:09 root
39 * 5 8/24/99 1:49a Dave
40 * Fixed client-side afterburner stuttering. Added checkbox for no version
41 * checking on PXO join. Made button info passing more friendly between
44 * 4 8/22/99 1:19p Dave
45 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
46 * which d3d cards are detected.
48 * 21 8/21/99 6:33p Kevin
51 * 20 8/21/99 6:48a Jeff
54 * 19 8/20/99 3:01p Kevin
55 * Added support for Proxies (I hope!)
57 * 18 8/15/99 6:38p Jeff
60 * 17 8/15/99 6:26p Kevin
62 * 16 4/14/99 1:20a Jeff
63 * fixed case mismatched #includes
65 * 15 3/03/99 12:28a Nate
66 * sped up something or other when the connection is done
68 * 14 2/03/99 4:20p Kevin
69 * Got multiplayer working with .mn3 files, and setup autodownloading
71 * 13 1/27/99 5:49p Kevin
73 * 12 1/27/99 5:38p Kevin
75 * 11 12/30/98 12:15p Kevin
76 * Auto Mission Download system
78 * 10 10/12/98 4:59p Kevin
79 * Added delay to thread when cancelled...
81 * 9 10/12/98 4:49p Nate
84 * 8 10/12/98 1:54p Nate
87 * 7 10/12/98 11:30a Kevin
90 * 6 10/08/98 12:59p Nate
93 * 5 10/08/98 9:57a Kevin
94 * made transfer cancellable
96 * 4 7/31/98 12:19p Nate
97 * Fixed http abort problem.
99 * 3 7/31/98 11:57a Kevin
100 * Added new functions for getting state
102 * 2 6/01/98 10:10a Kevin
103 * Added DLL connection interface and auto update DLL
105 * 1 5/27/98 9:52a Kevin
107 * 1 5/25/98 5:31p Kevin
121 #include <sys/types.h>
122 #include <sys/socket.h>
123 #include <netinet/in.h>
124 #include <arpa/inet.h>
126 #include <sys/ioctl.h>
138 #include "inetgetfile.h"
139 #include "chttpget.h"
143 inline void Sleep(int millis)
147 tv.tv_usec = millis*1000;
148 select(0,NULL,NULL,NULL,&tv);
152 #define NW_AGHBN_CANCEL 1
153 #define NW_AGHBN_LOOKUP 2
154 #define NW_AGHBN_READ 3
157 void __cdecl http_gethostbynameworker(void *parm);
159 void *http_gethostbynameworker(void *parm);
162 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname);
165 void HTTPObjThread( void * obj )
167 void *HTTPObjThread( void * obj )
170 ((ChttpGet *)obj)->WorkerThread();
171 ((ChttpGet *)obj)->m_Aborted = true;
172 //OutputDebugString("http transfer exiting....\n");
179 void ChttpGet::AbortGet()
182 OutputDebugString("Aborting....\n");
185 while(!m_Aborted) Sleep(50); //Wait for the thread to end
187 OutputDebugString("Aborted....\n");
191 ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport)
193 m_ProxyEnabled = true;
195 m_ProxyPort = proxyport;
196 GetFile(URL,localfile);
199 ChttpGet::ChttpGet(char *URL,char *localfile)
201 m_ProxyEnabled = false;
202 GetFile(URL,localfile);
206 void ChttpGet::GetFile(char *URL,char *localfile)
208 m_DataSock = INVALID_SOCKET;
211 m_State = HTTP_STATE_STARTUP;;
215 strncpy(m_URL,URL,MAX_URL_LEN-1);
216 m_URL[MAX_URL_LEN-1] = 0;
218 LOCALFILE = fopen(localfile,"wb");
219 if(NULL == LOCALFILE)
221 m_State = HTTP_STATE_CANT_WRITE_FILE;
224 m_DataSock = socket(AF_INET, SOCK_STREAM, 0);
225 if(INVALID_SOCKET == m_DataSock)
227 m_State = HTTP_STATE_SOCKET_ERROR;
235 ioctlsocket( m_DataSock, FIONBIO, &arg );
237 ioctl( m_DataSock, FIONBIO, &arg );
241 if(strnicmp(URL,"http:",5)==0)
249 //There shouldn't be any : in this string
252 m_State = HTTP_STATE_URL_PARSING_ERROR;
255 //read the filename by searching backwards for a /
256 //then keep reading until you find the first /
257 //when you found it, you have the host and dir
258 char *filestart = NULL;
259 char *dirstart = NULL;
260 for(int i = strlen(pURL);i>=0;i--)
266 filestart = pURL+i+1;
268 strcpy(m_szFilename,filestart);
276 if((dirstart==NULL) || (filestart==NULL))
278 m_State = HTTP_STATE_URL_PARSING_ERROR;
283 strcpy(m_szDir,dirstart);//,(filestart-dirstart));
284 //m_szDir[(filestart-dirstart)] = NULL;
285 strncpy(m_szHost,pURL,(dirstart-pURL));
286 m_szHost[(dirstart-pURL)-1] = '\0';
289 if(NULL==_beginthread(HTTPObjThread,0,this))
291 m_State = HTTP_STATE_INTERNAL_ERROR;
295 #elif defined(__LINUX__)
297 if(!inet_LoadThreadLib())
299 m_State = HTTP_STATE_INTERNAL_ERROR;
302 if(df_pthread_create(&thread,NULL,HTTPObjThread,this)!=0)
304 m_State = HTTP_STATE_INTERNAL_ERROR;
312 ChttpGet::~ChttpGet()
314 if(m_DataSock != INVALID_SOCKET)
316 shutdown(m_DataSock,2);
318 closesocket(m_DataSock);
325 int ChttpGet::GetStatus()
330 unsigned int ChttpGet::GetBytesIn()
335 unsigned int ChttpGet::GetTotalBytes()
337 return m_iBytesTotal;
341 void ChttpGet::WorkerThread()
343 char szCommand[1000];
352 if(m_State != HTTP_STATE_CONNECTED)
357 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);
358 send(m_DataSock,szCommand,strlen(szCommand),0);
360 if(strnicmp("HTTP/",p,5)==0)
363 pcode = strchr(p,' ')+1;
366 m_State = HTTP_STATE_UNKNOWN_ERROR;
376 m_State = HTTP_STATE_UNKNOWN_ERROR;
388 m_State = HTTP_STATE_UNKNOWN_ERROR;
397 if(strnicmp(p,"Content-Length:",strlen("Content-Length:"))==0)
399 char *s = strchr(p,' ')+1;
411 m_iBytesTotal = atoi(p);
421 m_State = HTTP_STATE_FILE_NOT_FOUND;
427 m_State = HTTP_STATE_UNKNOWN_ERROR;
433 int ChttpGet::ConnectSocket()
438 SOCKADDR_IN hostaddr;
443 ip = inet_addr((const char *)m_szHost);
448 http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_szHost);
453 http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_szHost);
456 rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_szHost);
464 m_State = HTTP_STATE_HOST_NOT_FOUND;
470 se = getservbyname("http", NULL);
475 hostaddr.sin_port = htons(80);
479 hostaddr.sin_port = se->s_port;
481 hostaddr.sin_family = AF_INET;
483 memcpy(&hostaddr.sin_addr,&ip,4);
487 //This is on a proxy, so we need to make sure to connect to the proxy machine
488 ip = inet_addr((const char *)m_ProxyIP);
492 http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_ProxyIP);
498 http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_ProxyIP);
501 rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_ProxyIP);
509 m_State = HTTP_STATE_HOST_NOT_FOUND;
514 //Use either the proxy port or 80 if none specified
515 hostaddr.sin_port = htons((ushort)(m_ProxyPort ? m_ProxyPort : 80));
516 //Copy the proxy address...
517 memcpy(&hostaddr.sin_addr,&ip,4);
520 //Now we will connect to the host
526 int serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
527 int cerr = WSAGetLastError();
530 while((cerr==WSAEALREADY)||(cerr==WSAEINVAL)||(cerr==WSAEWOULDBLOCK))
533 FD_SET( m_DataSock, &wfds );
534 if(select(0,NULL,&wfds,NULL,&timeout))
541 serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR));
544 cerr = WSAGetLastError();
556 m_State = HTTP_STATE_CANT_CONNECT;
559 m_State = HTTP_STATE_CONNECTED;
563 char *ChttpGet::GetHTTPLine()
567 unsigned int igotcrlf = 0;
568 memset(recv_buffer,0,1000);
572 bool gotdata = false;
575 iBytesRead = recv(m_DataSock,chunk,1,0);
577 if(SOCKET_ERROR == iBytesRead)
579 int error = WSAGetLastError();
580 if(WSAEWOULDBLOCK==error)
598 //This should always read a 0x0a
601 iBytesRead = recv(m_DataSock,chunk,1,0);
603 if(SOCKET_ERROR == iBytesRead)
605 int error = WSAGetLastError();
606 if(WSAEWOULDBLOCK==error)
625 strcat(recv_buffer,chunk);
633 unsigned int ChttpGet::ReadDataChannel()
635 char sDataBuffer[4096]; // Data-storage buffer for the data channel
636 int nBytesRecv = 0; // Bytes received from the data channel
642 timeout.tv_usec = 500;
644 m_State = HTTP_STATE_RECEIVING;
648 FD_SET( m_DataSock, &wfds );
650 if((m_iBytesTotal)&&(m_iBytesIn==m_iBytesTotal))
654 select(0,&wfds,NULL,NULL,&timeout);
660 nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer,sizeof(sDataBuffer), 0);
666 if(SOCKET_ERROR == nBytesRecv)
668 int error = WSAGetLastError();
669 if(WSAEWOULDBLOCK==error)
675 m_iBytesIn += nBytesRecv;
678 fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE);
679 //Write sDataBuffer, nBytesRecv
683 }while (nBytesRecv > 0);
685 // Close the file and check for error returns.
686 if (nBytesRecv == SOCKET_ERROR)
688 //Ok, we got a socket error -- xfer aborted?
689 m_State = HTTP_STATE_RECV_FAILED;
694 //OutputDebugString("HTTP File complete!\n");
696 m_State = HTTP_STATE_FILE_RECEIVED;
702 typedef struct _async_dns_lookup
704 unsigned int ip; //resolved host. Write only to worker thread.
705 char * host;//host name to resolve. read only to worker thread
706 bool done; //write only to the worker thread. Signals that the operation is complete
707 bool error; //write only to worker thread. Thread sets this if the name doesn't resolve
708 bool abort; //read only to worker thread. If this is set, don't fill in the struct.
711 async_dns_lookup httpaslu;
712 async_dns_lookup *http_lastaslu = NULL;
715 void __cdecl http_gethostbynameworker(void *parm);
717 void *http_gethostbynameworker(void *parm);
720 int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname)
723 if(command==NW_AGHBN_LOOKUP)
726 http_lastaslu->abort = true;
728 async_dns_lookup *newaslu;
729 newaslu = (async_dns_lookup *)malloc(sizeof(async_dns_lookup));
730 memset(&newaslu->ip,0,sizeof(unsigned int));
731 newaslu->host = hostname;
732 newaslu->done = false;
733 newaslu->error = false;
734 newaslu->abort = false;
735 http_lastaslu = newaslu;
736 httpaslu.done = false;
739 _beginthread(http_gethostbynameworker,0,newaslu);
741 #elif defined(__LINUX__)
743 if(!inet_LoadThreadLib())
748 df_pthread_create(&thread,NULL,http_gethostbynameworker,newaslu);
753 else if(command==NW_AGHBN_CANCEL)
756 http_lastaslu->abort = true;
757 http_lastaslu = NULL;
759 else if(command==NW_AGHBN_READ)
765 //free(http_lastaslu);
766 http_lastaslu = NULL;
767 memcpy(ip,&httpaslu.ip,sizeof(unsigned int));
770 else if(httpaslu.error)
773 http_lastaslu = NULL;
782 // This is the worker thread which does the lookup.
784 void __cdecl http_gethostbynameworker(void *parm)
786 void *http_gethostbynameworker(void *parm)
790 df_pthread_detach(df_pthread_self());
792 async_dns_lookup *lookup = (async_dns_lookup *)parm;
793 HOSTENT *he = gethostbyname(lookup->host);
796 lookup->error = true;
803 else if(!lookup->abort)
805 memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int));
807 memcpy(&httpaslu,lookup,sizeof(async_dns_lookup));