2005-02-28 07:16:19 +00:00
# include "quakedef.h"
2004-08-23 01:40:25 +00:00
# ifdef WEBSERVER
# include "iweb.h"
2005-11-29 13:14:15 +00:00
# include "netinc.h"
2004-08-23 01:40:25 +00:00
2005-11-29 13:14:15 +00:00
//FIXME: Before any admins use this for any serious usage, make the server send bits of file slowly.
2004-08-23 01:40:25 +00:00
static qboolean httpserverinitied = false ;
2006-03-14 02:50:56 +00:00
qboolean httpserverfailed = false ;
2004-08-23 01:40:25 +00:00
static int httpserversocket ;
2013-08-06 02:19:06 +00:00
static int httpserverport ;
# ifdef WEBSVONLY
static int natpmpsocket = INVALID_SOCKET ;
static int natpmptime ;
# include <mmsystem.h>
static void sendnatpmp ( int port )
{
struct sockaddr_in router ;
struct
{
qbyte ver ;
qbyte op ;
short reserved1 ;
short privport ; short pubport ;
int mapping_expectancy ;
} pmpreqmsg ;
int curtime = timeGetTime ( ) ;
if ( natpmpsocket = = INVALID_SOCKET )
{
unsigned long _true = true ;
natpmpsocket = socket ( AF_INET , SOCK_DGRAM , 0 ) ;
if ( natpmpsocket = = INVALID_SOCKET )
return ;
ioctlsocket ( natpmpsocket , FIONBIO , & _true ) ;
}
else if ( curtime - natpmptime < 0 )
return ;
natpmptime = curtime + 60 * 1000 ;
memset ( & router , 0 , sizeof ( router ) ) ;
router . sin_family = AF_INET ;
router . sin_port = htons ( 5351 ) ;
router . sin_addr . S_un . S_un_b . s_b1 = 192 ;
router . sin_addr . S_un . S_un_b . s_b2 = 168 ;
router . sin_addr . S_un . S_un_b . s_b3 = 0 ;
router . sin_addr . S_un . S_un_b . s_b4 = 1 ;
pmpreqmsg . ver = 0 ;
pmpreqmsg . op = 0 ;
pmpreqmsg . reserved1 = htons ( 0 ) ;
pmpreqmsg . privport = htons ( port ) ;
pmpreqmsg . pubport = htons ( port ) ;
pmpreqmsg . mapping_expectancy = htons ( 60 * 5 ) ;
sendto ( natpmpsocket , ( void * ) & pmpreqmsg , 2 , 0 , ( struct sockaddr * ) & router , sizeof ( router ) ) ;
pmpreqmsg . op = 2 ;
sendto ( natpmpsocket , ( void * ) & pmpreqmsg , sizeof ( pmpreqmsg ) , 0 , ( struct sockaddr * ) & router , sizeof ( router ) ) ;
}
void checknatpmp ( int port )
{
struct
{
qbyte ver ; qbyte op ; short resultcode ;
int age ;
union
{
struct
{
short privport ; short pubport ;
int mapping_expectancy ;
} ;
qbyte ipv4 [ 4 ] ;
} ;
} pmpreqrep ;
struct sockaddr_in from ;
int fromlen = sizeof ( from ) ;
int len ;
static int oldip = - 1 ;
static short oldport ;
memset ( & pmpreqrep , 0 , sizeof ( pmpreqrep ) ) ;
sendnatpmp ( port ) ;
if ( natpmpsocket = = INVALID_SOCKET )
len = - 1 ;
else
len = recvfrom ( natpmpsocket , ( void * ) & pmpreqrep , sizeof ( pmpreqrep ) , 0 , ( struct sockaddr * ) & from , & fromlen ) ;
if ( len = = 12 & & pmpreqrep . op = = 128 )
{
if ( oldip ! = * ( int * ) pmpreqrep . ipv4 )
{
oldip = * ( int * ) pmpreqrep . ipv4 ;
oldport = 0 ;
IWebPrintf ( " Public ip is %i.%i.%i.%i \n " , pmpreqrep . ipv4 [ 0 ] , pmpreqrep . ipv4 [ 1 ] , pmpreqrep . ipv4 [ 2 ] , pmpreqrep . ipv4 [ 3 ] ) ;
}
}
else if ( len = = 16 & & pmpreqrep . op = = 129 )
{
if ( oldport ! = pmpreqrep . pubport )
{
oldport = pmpreqrep . pubport ;
IWebPrintf ( " Public udp port %i (local %i) \n " , ntohs ( pmpreqrep . pubport ) , ntohs ( pmpreqrep . privport ) ) ;
}
}
else if ( len = = 16 & & pmpreqrep . op = = 130 )
{
if ( oldport ! = pmpreqrep . pubport )
{
oldport = pmpreqrep . pubport ;
IWebPrintf ( " Public tcp port %i (local %i) \n " , ntohs ( pmpreqrep . pubport ) , ntohs ( pmpreqrep . privport ) ) ;
}
}
}
# else
void checknatpmp ( int port )
{
}
# endif
2004-08-23 01:40:25 +00:00
2004-09-20 23:25:38 +00:00
typedef enum { HTTP_WAITINGFORREQUEST , HTTP_SENDING } http_mode_t ;
2004-08-23 01:40:25 +00:00
2007-09-17 20:35:39 +00:00
qboolean HTTP_ServerInit ( int port )
2011-05-19 13:34:07 +00:00
{
2004-08-23 01:40:25 +00:00
struct sockaddr_in address ;
unsigned long _true = true ;
int i ;
if ( ( httpserversocket = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ) ) = = - 1 )
{
2014-02-07 08:38:40 +00:00
IWebPrintf ( " HTTP_ServerInit: socket: %s \n " , strerror ( neterrno ( ) ) ) ;
2006-03-14 02:50:56 +00:00
httpserverfailed = true ;
return false ;
2004-08-23 01:40:25 +00:00
}
if ( ioctlsocket ( httpserversocket , FIONBIO , & _true ) = = - 1 )
{
2014-02-07 08:38:40 +00:00
IWebPrintf ( " HTTP_ServerInit: ioctl FIONBIO: %s \n " , strerror ( neterrno ( ) ) ) ;
2006-03-14 02:50:56 +00:00
httpserverfailed = true ;
return false ;
2004-08-23 01:40:25 +00:00
}
address . sin_family = AF_INET ;
//check for interface binding option
if ( ( i = COM_CheckParm ( " -ip " ) ) ! = 0 & & i < com_argc )
{
address . sin_addr . s_addr = inet_addr ( com_argv [ i + 1 ] ) ;
2013-11-29 14:36:47 +00:00
Con_TPrintf ( " Binding to IP Interface Address of %s \n " ,
2004-08-23 01:40:25 +00:00
inet_ntoa ( address . sin_addr ) ) ;
}
else
address . sin_addr . s_addr = INADDR_ANY ;
if ( port = = PORT_ANY )
address . sin_port = 0 ;
else
address . sin_port = htons ( ( short ) port ) ;
2011-05-19 13:34:07 +00:00
2004-08-23 01:40:25 +00:00
if ( bind ( httpserversocket , ( void * ) & address , sizeof ( address ) ) = = - 1 )
{
closesocket ( httpserversocket ) ;
2009-11-04 21:16:50 +00:00
IWebPrintf ( " HTTP_ServerInit: failed to bind to socket \n " ) ;
2006-03-14 02:50:56 +00:00
httpserverfailed = true ;
return false ;
2004-08-23 01:40:25 +00:00
}
2011-05-19 13:34:07 +00:00
2004-08-23 01:40:25 +00:00
listen ( httpserversocket , 3 ) ;
httpserverinitied = true ;
2006-03-14 09:20:07 +00:00
httpserverfailed = false ;
2013-08-06 02:19:06 +00:00
httpserverport = port ;
2004-08-23 01:40:25 +00:00
IWebPrintf ( " HTTP server is running \n " ) ;
2006-03-14 02:50:56 +00:00
return true ;
2004-08-23 01:40:25 +00:00
}
void HTTP_ServerShutdown ( void )
{
closesocket ( httpserversocket ) ;
IWebPrintf ( " HTTP server closed \n " ) ;
httpserverinitied = false ;
}
typedef struct HTTP_active_connections_s {
2013-03-12 22:47:42 +00:00
SOCKET datasock ;
2005-12-21 03:07:33 +00:00
vfsfile_t * file ;
2004-08-23 01:40:25 +00:00
struct HTTP_active_connections_s * next ;
http_mode_t mode ;
qboolean modeswitched ;
qboolean closeaftertransaction ;
2013-11-28 00:39:49 +00:00
char * closereason ;
2013-06-23 02:17:02 +00:00
qboolean acceptgzip ;
2004-08-23 01:40:25 +00:00
char * inbuffer ;
2013-03-12 22:47:42 +00:00
unsigned int inbuffersize ;
unsigned int inbufferused ;
2004-08-23 01:40:25 +00:00
char * outbuffer ;
2013-03-12 22:47:42 +00:00
unsigned int outbuffersize ;
unsigned int outbufferused ;
2004-08-23 01:40:25 +00:00
} HTTP_active_connections_t ;
static HTTP_active_connections_t * HTTP_ServerConnections ;
static int httpconnectioncount ;
2013-03-12 22:47:42 +00:00
static void ExpandInBuffer ( HTTP_active_connections_t * cl , unsigned int quant , qboolean fixedsize )
2004-08-23 01:40:25 +00:00
{
2013-03-12 22:47:42 +00:00
unsigned int newsize ;
2004-08-23 01:40:25 +00:00
if ( fixedsize )
newsize = quant ;
else
newsize = cl - > inbuffersize + quant ;
if ( newsize < = cl - > inbuffersize )
return ;
cl - > inbuffer = IWebRealloc ( cl - > inbuffer , newsize ) ;
cl - > inbuffersize = newsize ;
}
2013-03-12 22:47:42 +00:00
static void ExpandOutBuffer ( HTTP_active_connections_t * cl , unsigned int quant , qboolean fixedsize )
2004-08-23 01:40:25 +00:00
{
2013-03-12 22:47:42 +00:00
unsigned int newsize ;
2004-08-23 01:40:25 +00:00
if ( fixedsize )
newsize = quant ;
else
newsize = cl - > outbuffersize + quant ;
if ( newsize < = cl - > outbuffersize )
return ;
cl - > outbuffer = IWebRealloc ( cl - > outbuffer , newsize ) ;
cl - > outbuffersize = newsize ;
}
void HTTP_RunExisting ( void )
{
char * content ;
char * msg , * nl ;
2013-06-23 02:17:02 +00:00
char buf2 [ 2560 ] ; //short lived temp buffer.
char resource [ 2560 ] ;
char mode [ 80 ] ;
2004-08-23 01:40:25 +00:00
qboolean hostspecified ;
2013-03-12 22:47:42 +00:00
unsigned int contentlen ;
2004-08-23 01:40:25 +00:00
int HTTPmarkup ; //version
2006-04-05 04:31:05 +00:00
int localerrno ;
2004-08-23 01:40:25 +00:00
2009-05-24 10:11:17 +00:00
HTTP_active_connections_t * * link , * cl ;
2004-08-23 01:40:25 +00:00
2009-05-24 10:11:17 +00:00
link = & HTTP_ServerConnections ;
for ( link = & HTTP_ServerConnections ; * link ; )
2004-08-23 01:40:25 +00:00
{
int ammount , wanted ;
2009-05-24 10:11:17 +00:00
cl = * link ;
2013-11-28 00:39:49 +00:00
if ( cl - > closereason )
2004-08-23 01:40:25 +00:00
{
2013-11-28 00:39:49 +00:00
IWebPrintf ( " Closing connection: %s \n " , cl - > closereason ) ;
2009-05-24 10:11:17 +00:00
* link = cl - > next ;
2004-08-23 01:40:25 +00:00
closesocket ( cl - > datasock ) ;
cl - > datasock = INVALID_SOCKET ;
if ( cl - > inbuffer )
IWebFree ( cl - > inbuffer ) ;
if ( cl - > outbuffer )
IWebFree ( cl - > outbuffer ) ;
if ( cl - > file )
2005-12-22 02:29:11 +00:00
VFS_CLOSE ( cl - > file ) ;
2004-08-23 01:40:25 +00:00
IWebFree ( cl ) ;
httpconnectioncount - - ;
2009-05-24 10:11:17 +00:00
continue ;
2004-08-23 01:40:25 +00:00
}
2009-05-24 10:11:17 +00:00
link = & ( * link ) - > next ;
2004-08-23 01:40:25 +00:00
switch ( cl - > mode )
{
case HTTP_WAITINGFORREQUEST :
if ( cl - > outbufferused )
Sys_Error ( " Persistant connection was waiting for input with unsent output " ) ;
2013-03-12 22:47:42 +00:00
ammount = cl - > inbuffersize - 1 - cl - > inbufferused ;
2004-08-23 01:40:25 +00:00
if ( ammount < 128 )
{
if ( cl - > inbuffersize > 128 * 1024 )
{
2013-11-28 00:39:49 +00:00
cl - > closereason = " headers larger than 128kb " ; //that's just taking the piss.
2004-08-23 01:40:25 +00:00
continue ;
}
ExpandInBuffer ( cl , 1500 , false ) ;
2013-03-12 22:47:42 +00:00
ammount = cl - > inbuffersize - 1 - cl - > inbufferused ;
2004-08-23 01:40:25 +00:00
}
if ( cl - > modeswitched )
{
ammount = 0 ;
}
else
{
//we can't try and recv 0 bytes as we use an expanding buffer
ammount = recv ( cl - > datasock , cl - > inbuffer + cl - > inbufferused , ammount , 0 ) ;
if ( ammount < 0 )
{
2014-02-07 08:38:40 +00:00
if ( neterrno ( ) ! = NET_EWOULDBLOCK ) //they closed on us. Assume end.
2004-08-23 01:40:25 +00:00
{
2013-11-28 00:39:49 +00:00
cl - > closereason = " recv error " ;
2004-08-23 01:40:25 +00:00
}
continue ;
}
if ( ammount = = 0 )
{
2013-11-28 00:39:49 +00:00
cl - > closereason = " peer closed connection " ;
2004-08-23 01:40:25 +00:00
continue ;
}
}
cl - > modeswitched = false ;
cl - > inbufferused + = ammount ;
cl - > inbuffer [ cl - > inbufferused ] = ' \0 ' ;
content = NULL ;
msg = cl - > inbuffer ;
nl = strchr ( msg , ' \n ' ) ;
if ( ! nl )
{
cont :
continue ; //we need more... MORE!!! MORE I TELL YOU!!!!
}
2013-06-23 02:17:02 +00:00
//FIXME: COM_ParseOut recognises // comments, which is not correct.
2004-08-23 01:40:25 +00:00
msg = COM_ParseOut ( msg , mode , sizeof ( mode ) ) ;
msg = COM_ParseOut ( msg , resource , sizeof ( resource ) ) ;
if ( ! * resource )
{
2013-11-28 00:39:49 +00:00
cl - > closereason = " not http " ; //even if they forgot to specify a resource, we didn't find an HTTP so we have no option but to close.
2004-08-23 01:40:25 +00:00
continue ;
}
hostspecified = false ;
if ( ! strnicmp ( resource , " http:// " , 7 ) )
{ //groan... 1.1 compliance requires parsing this correctly, without the client ever specifiying it.
char * slash ; //we don't do multiple hosts.
hostspecified = true ;
slash = strchr ( resource + 7 , ' / ' ) ;
if ( ! slash )
strcpy ( resource , " / " ) ;
else
memmove ( resource , slash , strlen ( slash + 1 ) ) ; //just get rid of the http:// stuff.
}
if ( ! strcmp ( resource , " / " ) )
strcpy ( resource , " /index.html " ) ;
2013-06-23 02:17:02 +00:00
cl - > acceptgzip = false ;
2004-08-23 01:40:25 +00:00
msg = COM_ParseOut ( msg , buf2 , sizeof ( buf2 ) ) ;
contentlen = 0 ;
if ( ! strnicmp ( buf2 , " HTTP/ " , 5 ) )
{
if ( ! strncmp ( buf2 , " HTTP/1.1 " , 8 ) )
2013-11-28 00:39:49 +00:00
{
2004-08-23 01:40:25 +00:00
HTTPmarkup = 3 ;
2013-11-28 00:39:49 +00:00
cl - > closeaftertransaction = false ;
}
2004-08-23 01:40:25 +00:00
else if ( ! strncmp ( buf2 , " HTTP/1 " , 6 ) )
2013-11-28 00:39:49 +00:00
{
2004-08-23 01:40:25 +00:00
HTTPmarkup = 2 ;
2013-11-28 00:39:49 +00:00
cl - > closeaftertransaction = true ;
}
2004-08-23 01:40:25 +00:00
else
{
HTTPmarkup = 1 ; //0.9... lamer.
cl - > closeaftertransaction = true ;
}
//expect X lines containing options.
//then a blank line. Don't continue till we have that.
msg = nl + 1 ;
while ( 1 )
{
if ( * msg = = ' \r ' )
msg + + ;
if ( * msg = = ' \n ' )
{
msg + + ;
break ; //that was our blank line.
}
2011-05-19 13:34:07 +00:00
2004-08-23 01:40:25 +00:00
while ( * msg = = ' ' )
msg + + ;
if ( ! strnicmp ( msg , " Host: " , 6 ) ) //parse needed header fields
hostspecified = true ;
else if ( ! strnicmp ( msg , " Content-Length: " , 16 ) ) //parse needed header fields
2013-03-12 22:47:42 +00:00
contentlen = strtoul ( msg + 16 , NULL , 0 ) ;
2013-06-23 02:17:02 +00:00
else if ( ! strnicmp ( msg , " Accept-Encoding: " , 16 ) ) //parse needed header fields
{
char * e ;
msg + = 16 ;
while ( * msg )
{
if ( * msg = = ' \n ' )
break ;
while ( * msg = = ' ' | | * msg = = ' \t ' )
msg + + ;
if ( * msg = = ' , ' )
{
msg + + ;
continue ;
}
e = msg ;
while ( * e )
{
if ( * e = = ' , ' | | * e = = ' \r ' | | * e = = ' \n ' )
break ;
e + + ;
}
while ( e > msg & & ( e [ - 1 ] = = ' ' | | e [ - 1 ] = = ' \t ' ) )
e - - ;
if ( e - msg = = 4 & & ! strncmp ( msg , " gzip " , 4 ) )
cl - > acceptgzip = true ;
while ( * msg & & * msg ! = ' \n ' & & * msg ! = ' , ' )
msg + + ;
}
}
2004-08-23 01:40:25 +00:00
else if ( ! strnicmp ( msg , " Transfer-Encoding: " , 18 ) ) //parse needed header fields
{
cl - > closeaftertransaction = true ;
goto notimplemented ;
}
else if ( ! strnicmp ( msg , " Connection: close " , 17 ) )
cl - > closeaftertransaction = true ;
2013-11-28 00:39:49 +00:00
else if ( ! strnicmp ( msg , " Connection: Keep-Alive " , 22 ) )
cl - > closeaftertransaction = false ;
2004-08-23 01:40:25 +00:00
while ( * msg ! = ' \n ' )
{
if ( ! * msg )
{
goto cont ;
}
msg + + ;
}
msg + + ;
}
}
else
{
HTTPmarkup = 0 ; //strimmed... totally...
cl - > closeaftertransaction = true ;
//don't bother running to nl.
}
if ( cl - > inbufferused - ( msg - cl - > inbuffer ) < contentlen )
continue ;
cl - > modeswitched = true ;
if ( contentlen )
{
2009-11-04 21:16:50 +00:00
content = IWebMalloc ( contentlen + 1 ) ;
2004-08-23 01:40:25 +00:00
memcpy ( content , msg , contentlen + 1 ) ;
}
memmove ( cl - > inbuffer , cl - > inbuffer + ( msg - cl - > inbuffer + contentlen ) , cl - > inbufferused - ( msg - cl - > inbuffer + contentlen ) ) ;
cl - > inbufferused - = msg - cl - > inbuffer + contentlen ;
if ( HTTPmarkup = = 3 & & ! hostspecified ) //1.1 requires the host to be specified... we ca,just ignore it as we're not routing or imitating two servers. (for complience we need to encourage the client to send - does nothing for compatability or anything, just compliance to spec. not always the same thing)
{
2012-04-09 19:12:12 +00:00
msg = " HTTP/1.1 400 Bad Request \r \n " /*"Content-Type: application/octet-stream\r\n"*/ " Content-Length: 69 \r \n " " Server: " FULLENGINENAME " /0 \r \n " " \r \n " " 400 Bad Request \r \n Your client failed to provide the host header line " ;
2004-08-23 01:40:25 +00:00
ammount = strlen ( msg ) ;
ExpandOutBuffer ( cl , ammount , true ) ;
memcpy ( cl - > outbuffer , msg , ammount ) ;
cl - > outbufferused = ammount ;
cl - > mode = HTTP_SENDING ;
}
else if ( ! stricmp ( mode , " GET " ) | | ! stricmp ( mode , " HEAD " ) | | ! stricmp ( mode , " POST " ) )
2007-09-17 20:35:39 +00:00
{
2012-04-24 07:59:11 +00:00
qboolean gzipped = false ;
2007-09-17 20:35:39 +00:00
if ( * resource ! = ' / ' )
2009-05-24 10:11:17 +00:00
{
resource [ 0 ] = ' / ' ;
2007-09-17 20:35:39 +00:00
resource [ 1 ] = 0 ; //I'm lazy, they need to comply
2009-05-24 10:11:17 +00:00
}
2009-11-04 21:16:50 +00:00
IWebPrintf ( " Download request for \" %s \" \n " , resource + 1 ) ;
2013-06-23 02:17:02 +00:00
2004-08-23 01:40:25 +00:00
if ( ! strnicmp ( mode , " P " , 1 ) ) //when stuff is posted, data is provided. Give an error message if we couldn't do anything with that data.
cl - > file = IWebGenerateFile ( resource + 1 , content , contentlen ) ;
else
2009-05-24 10:11:17 +00:00
{
2012-04-24 07:59:11 +00:00
cl - > file = NULL ;
if ( SV_AllowDownload ( resource + 1 ) )
{
char nbuf [ MAX_OSPATH ] ;
2013-06-23 02:17:02 +00:00
if ( cl - > acceptgzip & & strlen ( resource + 1 ) < sizeof ( nbuf ) - 4 )
2012-04-24 07:59:11 +00:00
{
sprintf ( nbuf , " %s.gz " , resource + 1 ) ;
2013-06-23 02:17:02 +00:00
cl - > file = FS_OpenVFS ( nbuf , " rb " , FS_GAME ) ;
2012-04-24 07:59:11 +00:00
}
if ( cl - > file )
gzipped = true ;
else
cl - > file = FS_OpenVFS ( resource + 1 , " rb " , FS_GAME ) ;
}
2011-05-19 13:34:07 +00:00
2009-05-24 10:11:17 +00:00
if ( ! cl - > file )
{
cl - > file = IWebGenerateFile ( resource + 1 , content , contentlen ) ;
}
}
2004-08-23 01:40:25 +00:00
if ( ! cl - > file )
{
2012-04-24 07:59:11 +00:00
IWebPrintf ( " Download rejected \n " ) ;
2004-08-23 01:40:25 +00:00
if ( HTTPmarkup > = 3 )
2005-11-14 01:32:21 +00:00
msg = " HTTP/1.1 404 Not Found \r \n " " Content-Type: text/plain \r \n " " Content-Length: 15 \r \n " " Server: " FULLENGINENAME " /0 \r \n " " \r \n " " 404 Bad address " ;
2004-08-23 01:40:25 +00:00
else if ( HTTPmarkup = = 2 )
2005-11-14 01:32:21 +00:00
msg = " HTTP/1.0 404 Not Found \r \n " " Content-Type: text/plain \r \n " " Content-Length: 15 \r \n " " Server: " FULLENGINENAME " /0 \r \n " " \r \n " " 404 Bad address " ;
2004-08-23 01:40:25 +00:00
else if ( HTTPmarkup )
msg = " HTTP/0.9 404 Not Found \r \n " " \r \n " " 404 Bad address " ;
else
msg = " <HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY>404 Not Found<BR>The specified file could not be found on the server</HEAD></HTML> " ;
ammount = strlen ( msg ) ;
ExpandOutBuffer ( cl , ammount , true ) ;
memcpy ( cl - > outbuffer , msg , ammount ) ;
cl - > outbufferused = ammount ;
cl - > mode = HTTP_SENDING ;
}
else
{
2013-11-28 00:39:49 +00:00
//fixme: add connection: keep-alive or whatever so that ie3 is happy...
2004-08-23 01:40:25 +00:00
if ( HTTPmarkup > = 3 )
2013-11-28 00:39:49 +00:00
sprintf ( resource , " HTTP/1.1 200 OK \r \n " " %s%s " " Connection: %s \r \n " " Content-Length: %i \r \n " " Server: " FULLENGINENAME " /0 \r \n " " \r \n " , strstr ( resource , " .htm " ) ? " Content-Type: text/html \r \n " : " " , gzipped ? " Content-Encoding: gzip \r \n Cache-Control: public, max-age=86400 \r \n " : " " , cl - > closeaftertransaction ? " close " : " keep-alive " , ( int ) VFS_GETLEN ( cl - > file ) ) ;
2004-08-23 01:40:25 +00:00
else if ( HTTPmarkup = = 2 )
2013-11-28 00:39:49 +00:00
sprintf ( resource , " HTTP/1.0 200 OK \r \n " " %s%s " " Connection: %s \r \n " " Content-Length: %i \r \n " " Server: " FULLENGINENAME " /0 \r \n " " \r \n " , strstr ( resource , " .htm " ) ? " Content-Type: text/html \r \n " : " " , gzipped ? " Content-Encoding: gzip \r \n Cache-Control: public, max-age=86400 \r \n " : " " , cl - > closeaftertransaction ? " close " : " keep-alive " , ( int ) VFS_GETLEN ( cl - > file ) ) ;
2004-08-23 01:40:25 +00:00
else if ( HTTPmarkup )
sprintf ( resource , " HTTP/0.9 200 OK \r \n \r \n " ) ;
else
2004-09-20 23:25:38 +00:00
strcpy ( resource , " " ) ;
2004-08-23 01:40:25 +00:00
msg = resource ;
if ( * mode = = ' H ' | | * mode = = ' h ' )
{
2011-05-19 13:34:07 +00:00
2005-12-22 02:29:11 +00:00
VFS_CLOSE ( cl - > file ) ;
2004-08-23 01:40:25 +00:00
cl - > file = NULL ;
}
ammount = strlen ( msg ) ;
ExpandOutBuffer ( cl , ammount , true ) ;
memcpy ( cl - > outbuffer , msg , ammount ) ;
cl - > outbufferused = ammount ;
cl - > mode = HTTP_SENDING ;
}
}
//PUT/POST must support chunked transfer encoding for 1.1 compliance.
/* else if (!stricmp(mode, "PUT")) //put is replacement of a resource. (file uploads)
{
}
*/
else
{
notimplemented :
if ( HTTPmarkup > = 3 )
msg = " HTTP/1.1 501 Not Implemented \r \n \r \n " ;
else if ( HTTPmarkup = = 2 )
msg = " HTTP/1.0 501 Not Implemented \r \n \r \n " ;
else if ( HTTPmarkup )
msg = " HTTP/0.9 501 Not Implemented \r \n \r \n " ;
else
{
msg = NULL ;
2013-11-28 00:39:49 +00:00
cl - > closereason = " unsupported http version " ;
2004-08-23 01:40:25 +00:00
}
if ( msg )
{
ammount = strlen ( msg ) ;
ExpandOutBuffer ( cl , ammount , true ) ;
memcpy ( cl - > outbuffer , msg , ammount ) ;
cl - > outbufferused = ammount ;
cl - > mode = HTTP_SENDING ;
}
}
if ( content )
2009-11-04 21:16:50 +00:00
IWebFree ( content ) ;
2004-08-23 01:40:25 +00:00
break ;
case HTTP_SENDING :
2009-05-24 10:11:17 +00:00
if ( cl - > outbufferused < 8192 )
2004-08-23 01:40:25 +00:00
{
if ( cl - > file )
{
2009-05-24 10:11:17 +00:00
ExpandOutBuffer ( cl , 32768 , true ) ;
2004-08-23 01:40:25 +00:00
wanted = cl - > outbuffersize - cl - > outbufferused ;
2005-12-22 02:29:11 +00:00
ammount = VFS_READ ( cl - > file , cl - > outbuffer + cl - > outbufferused , wanted ) ;
2004-08-23 01:40:25 +00:00
if ( ! ammount )
{
2005-12-22 02:29:11 +00:00
VFS_CLOSE ( cl - > file ) ;
2004-08-23 01:40:25 +00:00
cl - > file = NULL ;
2012-01-28 10:30:44 +00:00
IWebPrintf ( " Download complete \n " ) ;
2004-08-23 01:40:25 +00:00
}
else
cl - > outbufferused + = ammount ;
}
}
ammount = send ( cl - > datasock , cl - > outbuffer , cl - > outbufferused , 0 ) ;
if ( ammount = = - 1 )
{
2014-02-07 08:38:40 +00:00
localerrno = neterrno ( ) ;
if ( localerrno ! = NET_EWOULDBLOCK )
2004-08-23 01:40:25 +00:00
{
2013-11-28 00:39:49 +00:00
cl - > closereason = " some error when sending " ;
2004-08-23 01:40:25 +00:00
}
}
else if ( ammount | | ! cl - > outbufferused )
{
memcpy ( cl - > outbuffer , cl - > outbuffer + ammount , cl - > outbufferused - ammount ) ;
cl - > outbufferused - = ammount ;
if ( ! cl - > outbufferused & & ! cl - > file )
{
cl - > modeswitched = true ;
cl - > mode = HTTP_WAITINGFORREQUEST ;
if ( cl - > closeaftertransaction )
2013-11-28 00:39:49 +00:00
cl - > closereason = " file sent " ;
2004-08-23 01:40:25 +00:00
}
}
else
2013-11-28 00:39:49 +00:00
cl - > closereason = " peer prematurely closed connection " ;
2004-08-23 01:40:25 +00:00
break ;
/* case HTTP_RECEIVING:
sent = recv ( cl - > datasock , resource , ammount , 0 ) ;
if ( sent = = - 1 )
{
if ( qerrno ! = EWOULDBLOCK ) //they closed on us. Assume end.
{
2005-12-22 02:29:11 +00:00
VFS_CLOSE ( cl - > file ) ;
2004-08-23 01:40:25 +00:00
cl - > file = NULL ;
cl - > close = true ;
continue ;
}
}
if ( sent ! = 0 )
IWebFWrite ( resource , 1 , sent , cl - > file ) ;
break ; */
}
}
}
2007-09-17 20:35:39 +00:00
qboolean HTTP_ServerPoll ( qboolean httpserverwanted , int portnum ) //loop while true
2004-08-23 01:40:25 +00:00
{
2009-05-24 10:11:17 +00:00
struct sockaddr_qstorage from ;
2004-08-23 01:40:25 +00:00
int fromlen ;
int clientsock ;
2007-09-04 20:54:24 +00:00
int _true = true ;
2009-05-24 10:11:17 +00:00
char buf [ 128 ] ;
netadr_t na ;
2004-08-23 01:40:25 +00:00
HTTP_active_connections_t * cl ;
2013-08-06 02:19:06 +00:00
if ( httpserverport ! = portnum & & httpserverinitied )
HTTP_ServerShutdown ( ) ;
2004-08-23 01:40:25 +00:00
if ( ! httpserverinitied )
{
if ( httpserverwanted )
2007-09-17 20:35:39 +00:00
return HTTP_ServerInit ( portnum ) ;
2004-08-23 01:40:25 +00:00
return false ;
}
else if ( ! httpserverwanted )
{
HTTP_ServerShutdown ( ) ;
return false ;
}
2013-08-06 02:19:06 +00:00
checknatpmp ( httpserverport ) ;
2004-08-23 01:40:25 +00:00
if ( httpconnectioncount > 32 )
return false ;
fromlen = sizeof ( from ) ;
clientsock = accept ( httpserversocket , ( struct sockaddr * ) & from , & fromlen ) ;
if ( clientsock = = - 1 )
{
2014-02-07 08:38:40 +00:00
int e = neterrno ( ) ;
if ( e = = NET_EWOULDBLOCK )
2004-08-23 01:40:25 +00:00
{
HTTP_RunExisting ( ) ;
return false ;
}
2014-02-07 08:38:40 +00:00
if ( e = = NET_ECONNABORTED | | e = = NET_ECONNRESET )
2004-08-23 01:40:25 +00:00
{
2013-11-29 14:36:47 +00:00
Con_TPrintf ( " Connection lost or aborted \n " ) ;
2004-08-23 01:40:25 +00:00
return false ;
}
2014-02-07 08:38:40 +00:00
Con_Printf ( " NET_GetPacket: %s \n " , strerror ( e ) ) ;
2004-08-23 01:40:25 +00:00
return false ;
}
2011-05-19 13:34:07 +00:00
if ( ioctlsocket ( clientsock , FIONBIO , ( u_long * ) & _true ) = = - 1 )
2007-09-17 20:35:39 +00:00
{
2014-02-07 08:38:40 +00:00
IWebPrintf ( " HTTP_ServerInit: ioctl FIONBIO: %s \n " , strerror ( neterrno ( ) ) ) ;
2007-09-17 20:35:39 +00:00
closesocket ( clientsock ) ;
return false ;
}
2009-11-04 21:16:50 +00:00
# ifndef WEBSVONLY
2009-05-24 10:11:17 +00:00
SockadrToNetadr ( & from , & na ) ;
2013-05-03 04:28:08 +00:00
IWebPrintf ( " New http connection from %s \n " , NET_AdrToString ( buf , sizeof ( buf ) , & na ) ) ;
2013-11-28 00:39:49 +00:00
# else
IWebPrintf ( " New http connection from %s \n " , inet_ntoa ( ( ( struct sockaddr_in * ) & from ) - > sin_addr ) ) ;
2009-11-04 21:16:50 +00:00
# endif
2007-09-04 20:54:24 +00:00
2004-08-23 01:40:25 +00:00
cl = IWebMalloc ( sizeof ( HTTP_active_connections_t ) ) ;
cl - > datasock = clientsock ;
cl - > next = HTTP_ServerConnections ;
2011-05-19 13:34:07 +00:00
HTTP_ServerConnections = cl ;
2004-08-23 01:40:25 +00:00
httpconnectioncount + + ;
return true ;
}
# endif