123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745 |
- /*
- ** JNetLib
- ** Copyright (C) 2000-2003 Nullsoft, Inc.
- ** Author: Justin Frankel
- ** File: wac_network_web_server.cpp - Generic simple webserver baseclass
- ** License: see jnetlib.h
- ** see test.cpp for an example of how to use this class
- */
- #include "wac_network_web_server.h"
- #include <zlib.h>
- #include "../nu/strsafe.h"
- #ifndef MIN
- #define MIN(a,b) (((a) < (b)) ? (a) : (b))
- #endif
- class WS_ItemList
- {
- public:
- WS_ItemList()
- {
- m_size = 0; m_list = 0;
- }
- ~WS_ItemList()
- {
- ::free( m_list );
- }
- void *Add( void *i );
- void *Get( int w );
- void Del( int idx );
- int GetSize( void )
- {
- return m_size;
- }
- protected:
- void **m_list;
- int m_size;
- };
- class WS_conInst
- {
- public:
- WS_conInst( WAC_Network_Connection *c, JNL_Listen *l, int which_port );
- ~WS_conInst();
- void close();
- // these will be used by WebServerBaseClass::onConnection yay
- wa::Components::WAC_Network_HTTP_Server m_serv;
- api_pagegenerator *m_pagegen;
- api_onconncb *connCb;
- JNL_Listen *m_listener;
- int m_port; // port this came in on
- time_t m_connect_time;
- z_stream *zlibStream;
- };
- void *WS_ItemList::Add( void *i )
- {
- if ( !m_list || !( m_size & 31 ) )
- m_list = ( void ** )::realloc( m_list, sizeof( void * ) * ( m_size + 32 ) );
- m_list[ m_size++ ] = i;
- return i;
- }
- void *WS_ItemList::Get( int w )
- {
- if ( w >= 0 && w < m_size )
- return m_list[ w ];
- return NULL;
- }
- void WS_ItemList::Del( int idx )
- {
- if ( m_list && idx >= 0 && idx < m_size )
- {
- m_size--;
- if ( idx != m_size )
- ::memcpy( m_list + idx, m_list + idx + 1, sizeof( void * ) * ( m_size - idx ) );
- if ( !( m_size & 31 ) && m_size ) // resize down
- {
- m_list = ( void ** )::realloc( m_list, sizeof( void * ) * m_size );
- }
- }
- }
- WS_conInst::WS_conInst( WAC_Network_Connection *c, JNL_Listen *l, int which_port ) : m_serv( c ), m_listener( l ), m_port( which_port ), connCb( 0 )
- {
- zlibStream = 0;
- m_pagegen = 0;
- time( &m_connect_time );
- }
- WS_conInst::~WS_conInst()
- {
- if ( connCb && m_pagegen )
- connCb->destroyConnection( m_pagegen );
- if ( zlibStream )
- deflateEnd( zlibStream );
- free( zlibStream );
- }
- void WS_conInst::close()
- {
- if ( connCb && m_pagegen )
- connCb->destroyConnection( m_pagegen );
- m_pagegen = 0;
- if ( zlibStream )
- deflateEnd( zlibStream );
- free( zlibStream );
- zlibStream = 0;
- time( &m_connect_time );
- }
- WebServerBaseClass::~WebServerBaseClass()
- {
- int x;
- for ( x = 0; x < m_connections->GetSize(); x++ )
- {
- delete (WS_conInst *)m_connections->Get( x );
- }
- delete m_connections;
- for ( x = 0; x < m_listeners->GetSize(); x++ )
- {
- delete (JNL_Listen *)m_listeners->Get( x );
- }
- delete m_listeners;
- }
- WebServerBaseClass::WebServerBaseClass()
- {
- allowCompression = false;
- m_connections = new WS_ItemList;
- m_listeners = new WS_ItemList;
- m_listener_rot = 0;
- m_timeout_s = 15;
- m_max_con = 100;
- extraDeflateBuffer = NULL;
- extraDeflateBufferSize = 0;
- extraDeflateBufferUsed = 0;
- connectionCallback = NULL;
- }
- void WebServerBaseClass::initForFactory()
- {
- if ( m_connections )
- {
- for ( int x = 0; x < m_connections->GetSize(); x++ )
- delete (WS_conInst *)m_connections->Get( x );
- delete m_connections;
- }
- if ( m_listeners )
- {
- for ( int x = 0; x < m_listeners->GetSize(); x++ )
- delete (JNL_Listen *)m_listeners->Get( x );
- delete m_listeners;
- }
- m_connections = new WS_ItemList;
- m_listeners = new WS_ItemList;
- m_listener_rot = 0;
- m_timeout_s = 15;
- m_max_con = 100;
- }
- void WebServerBaseClass::setMaxConnections( int max_con )
- {
- m_max_con = max_con;
- }
- void WebServerBaseClass::setRequestTimeout( int timeout_s )
- {
- m_timeout_s = timeout_s;
- }
- void WebServerBaseClass::AllowCompression()
- {
- allowCompression = true;
- }
- int WebServerBaseClass::addListenPort( int port, unsigned long which_interface )
- {
- removeListenPort( port );
- sockaddr_in address;
- memset( &address, 0, sizeof( address ) );
- address.sin_family = AF_INET;
- address.sin_port = htons( (unsigned short)port );
- address.sin_addr.s_addr = which_interface ? which_interface : INADDR_ANY;
- JNL_Listen *p = new JNL_Listen();
- p->Initialize( port, (sockaddr *)&address, PF_INET );
- m_listeners->Add( (void *)p );
- if ( p->is_error() )
- return -1;
- return 0;
- }
- void WebServerBaseClass::removeListenPort( int port )
- {
- for ( int x = 0; x < m_listeners->GetSize(); x++ )
- {
- JNL_Listen *p = (JNL_Listen *)m_listeners->Get( x );
- if ( p->get_port() == port )
- {
- delete p;
- m_listeners->Del( x );
- break;
- }
- }
- }
- void WebServerBaseClass::removeListenIdx( int idx )
- {
- if ( idx >= 0 && idx < m_listeners->GetSize() )
- {
- JNL_Listen *p = (JNL_Listen *)m_listeners->Get( idx );
- delete p;
- m_listeners->Del( idx );
- }
- }
- int WebServerBaseClass::getListenPort( int idx, int *err )
- {
- JNL_Listen *p = (JNL_Listen *)m_listeners->Get( idx );
- if ( p )
- {
- if ( err )
- *err = p->is_error();
- return p->get_port();
- }
- return 0;
- }
- void WebServerBaseClass::attachConnection( WAC_Network_Connection *con, JNL_Listen *listener )
- {
- m_connections->Add( ( void * )new WS_conInst( con, listener, listener->get_port() ) );
- }
- void WebServerBaseClass::run( void )
- {
- int nl;
- if ( m_connections->GetSize() < m_max_con && ( nl = m_listeners->GetSize() ) )
- {
- JNL_Listen *l = (JNL_Listen *)m_listeners->Get( m_listener_rot++ % nl );
- WAC_Network_Connection *c = l->get_connect( PACKET_SIZE, PACKET_SIZE );
- if ( c )
- attachConnection( c, l );
- }
- for ( int x = 0; x < m_connections->GetSize(); x++ )
- {
- WS_conInst *this_con = (WS_conInst *)m_connections->Get( x );
- switch ( run_connection( this_con ) )
- {
- case RUN_CONNECTION_DONE:
- {
- bool keep_alive = false;
- if ( this_con->m_serv.get_keep_alive() )
- {
- const char *connection_status = this_con->m_serv.getheader( "Connection" );
- if ( !connection_status || _stricmp( connection_status, "close" ) )
- keep_alive = true;
- }
- if ( !keep_alive )
- {
- delete this_con;
- m_connections->Del( x-- );
- }
- else
- {
- this_con->close();
- this_con->m_serv.reset();
- }
- }
- break;
- case RUN_CONNECTION_ERROR:
- case RUN_CONNECTION_TIMEOUT:
- delete this_con;
- m_connections->Del( x-- );
- break;
- }
- }
- }
- void WebServerBaseClass::SetConnectionCallback( api_onconncb *_connectionCallback )
- {
- _connectionCallback->caller = this;
- connectionCallback = _connectionCallback;
- }
- void WebServerBaseClass::deflatehelper( WS_conInst *con, char *buf, int l, bool flush )
- {
- char temp[ 8192 ] = { 0 };
- con->zlibStream->next_in = (Bytef *)buf;
- con->zlibStream->avail_in = l;
- con->zlibStream->avail_out = 0;
- int sent = 0;
- if ( flush )
- l = con->m_serv.bytes_cansend();
- while ( ( !flush && con->zlibStream->avail_in ) || ( flush && !con->zlibStream->avail_out ) )
- {
- con->zlibStream->next_out = (Bytef *)temp;
- con->zlibStream->avail_out = 8192;
- int r = deflate( con->zlibStream, flush ? Z_FINISH : Z_NO_FLUSH ); // TODO: if avail_in != 0, we might have to use Z_SYNC_FLUSH instead
- if ( sent + 8192 - (int)con->zlibStream->avail_out > l )
- {
- if ( !extraDeflateBuffer )
- {
- extraDeflateBufferSize = 8192 - con->zlibStream->avail_out;
- extraDeflateBuffer = (char *)malloc( extraDeflateBufferSize );
- memcpy( extraDeflateBuffer, temp, extraDeflateBufferSize );
- }
- else
- {
- // TODO need to sort out this to handle realloc failures...
- extraDeflateBuffer = (char *)realloc( extraDeflateBuffer, extraDeflateBufferSize + 8192 - con->zlibStream->avail_out );
- memcpy( extraDeflateBuffer + extraDeflateBufferSize, temp, 8192 - con->zlibStream->avail_out );
- extraDeflateBufferSize += 8192 - con->zlibStream->avail_out;
- }
- }
- else
- {
- con->m_serv.write_bytes( temp, 8192 - con->zlibStream->avail_out );
- sent += 8192 - con->zlibStream->avail_out;
- }
- if ( r != Z_OK )
- break;
- }
- }
- int WebServerBaseClass::run_connection( WS_conInst *con )
- {
- // TODO: add a Run() method to WC_conInst, passing in connectionCallback
- int s = con->m_serv.run();
- if ( s < 0 )
- {
- // m_serv.geterrorstr()
- return RUN_CONNECTION_ERROR;
- }
- if ( s < 2 )
- {
- // return 1 if we timed out
- if ( time( NULL ) - con->m_connect_time > m_timeout_s )
- return RUN_CONNECTION_TIMEOUT;
- else
- return RUN_CONNECTION_CONTINUE;
- }
- if ( s < 3 )
- {
- con->connCb = connectionCallback;
- con->m_pagegen = connectionCallback->onConnection( &con->m_serv, con->m_port );
- const char *compression0 = allowCompression ? con->m_serv.getheader( "Accept-Encoding" ) : 0;
- if ( compression0 )
- {
- // the compression0 string is a comma (and possibly space) separated list of acceptable formats
- char *compression = _strdup( compression0 );
- bool supportsGzip = false, supportsDeflate = false;
- size_t l = strlen( compression );
- char *p = compression;
- while ( p && *p )
- {
- if ( *p == ' ' || *p == ',' ) *p = 0; p++;
- }
- for ( char *q = compression; q < compression + l; q++ )
- {
- if ( !_stricmp( q, "deflate" ) )
- {
- supportsDeflate = true;
- q += 7;
- }
- if ( !_stricmp( q, "gzip" ) )
- {
- supportsGzip = true;
- q += 4;
- }
- }
- free( compression );
- int windowBits = 0;
- if ( supportsGzip )
- {
- windowBits = 15 + 16; /* +16 for gzip */
- compression = _strdup( "gzip" );
- }
- else if ( supportsDeflate )
- {
- windowBits = 15;
- compression = _strdup( "deflate" );
- }
- if ( windowBits )
- {
- con->zlibStream = (z_stream *)malloc( sizeof( z_stream ) );
- con->zlibStream->next_in = Z_NULL;
- con->zlibStream->avail_in = Z_NULL;
- con->zlibStream->next_out = Z_NULL;
- con->zlibStream->avail_out = Z_NULL;
- con->zlibStream->zalloc = (alloc_func)0;
- con->zlibStream->zfree = (free_func)0;
- con->zlibStream->opaque = 0;
- int z_err = deflateInit2( con->zlibStream,
- Z_DEFAULT_COMPRESSION,
- Z_DEFLATED,
- 15 + 16 /* +16 for gzip */,
- 8,
- Z_DEFAULT_STRATEGY );
- if ( z_err != Z_OK )
- {
- free( con->zlibStream );
- con->zlibStream = 0;
- }
- char temp_header[ 1024 ] = "Content-Encoding:";
- StringCchCatA( temp_header, 1024, compression );
- con->m_serv.set_reply_header( temp_header );
- }
- }
- return RUN_CONNECTION_CONTINUE;
- }
- if ( s < 4 )
- {
- if ( !con->m_pagegen )
- {
- return con->m_serv.bytes_inqueue() ? RUN_CONNECTION_CONTINUE : RUN_CONNECTION_DONE;
- }
- int l = con->m_serv.bytes_cansend();
- if ( l > 0 )
- {
- if ( con->zlibStream && extraDeflateBufferSize )
- {
- int len = MIN( extraDeflateBufferSize - extraDeflateBufferUsed, l );
- con->m_serv.write_bytes( extraDeflateBuffer + extraDeflateBufferUsed, len );
- extraDeflateBufferUsed += len;
- if ( extraDeflateBufferUsed == extraDeflateBufferSize )
- {
- extraDeflateBufferUsed = 0;
- extraDeflateBufferSize = 0;
- free( extraDeflateBuffer );
- extraDeflateBuffer = NULL;
- }
- l -= len;
- if ( l == 0 )
- return con->m_serv.bytes_inqueue() ? RUN_CONNECTION_CONTINUE : RUN_CONNECTION_DONE;
- }
- char buf[ PACKET_SIZE ] = { 0 };
- if ( l > sizeof( buf ) )
- l = sizeof( buf );
- l = con->m_pagegen->GetData( buf, l );
- if ( l < ( con->m_pagegen->IsNonBlocking() ? 0 : 1 ) ) // if nonblocking, this is l < 0, otherwise it's l<1
- {
- if ( con->zlibStream )
- {
- deflatehelper( con, 0, 0, true );
- /*
- char temp[8192] = {0};
- con->zlibStream->next_in = 0;
- con->zlibStream->avail_in = 0;
- con->zlibStream->avail_out = 0;
- while(!con->zlibStream->avail_out)
- {
- con->zlibStream->next_out = (Bytef *)temp;
- con->zlibStream->avail_out = 8192;
- int r = deflate(con->zlibStream, Z_FINISH);
- con->m_serv.write_bytes(temp, 8192 - con->zlibStream->avail_out);
- if(r != Z_OK) break;
- }*/
- }
- return con->m_serv.bytes_inqueue() ? RUN_CONNECTION_CONTINUE : RUN_CONNECTION_DONE;
- }
- if ( l > 0 )
- {
- if ( con->zlibStream )
- deflatehelper( con, buf, l, false );
- else
- con->m_serv.write_bytes( buf, l );
- }
- }
- return RUN_CONNECTION_CONTINUE;
- }
- return RUN_CONNECTION_DONE; // we're done by this point
- }
- void WebServerBaseClass::url_encode( char *in, char *out, int max_out )
- {
- while ( in && *in && max_out > 4 )
- {
- if ( ( *in >= 'A' && *in <= 'Z' ) ||
- ( *in >= 'a' && *in <= 'z' ) ||
- ( *in >= '0' && *in <= '9' ) || *in == '.' || *in == '_' || *in == '-' )
- {
- *out++ = *in++;
- max_out--;
- }
- else
- {
- int i = *in++;
- *out++ = '%';
- int b = ( i >> 4 ) & 15;
- if ( b < 10 )
- *out++ = '0' + b;
- else
- *out++ = 'A' + b - 10;
- b = i & 15;
- if ( b < 10 )
- *out++ = '0' + b;
- else
- *out++ = 'A' + b - 10;
- max_out -= 3;
- }
- }
- *out = 0;
- }
- void WebServerBaseClass::url_decode( char *in, char *out, int maxlen )
- {
- while ( in && *in && maxlen > 1 )
- {
- if ( *in == '+' )
- {
- in++;
- *out++ = ' ';
- }
- else if ( *in == '%' && in[ 1 ] != '%' && in[ 1 ] )
- {
- int a = 0;
- int b = 0;
- for ( b = 0; b < 2; b++ )
- {
- int r = in[ 1 + b ];
- if ( r >= '0' && r <= '9' )
- r -= '0';
- else if ( r >= 'a' && r <= 'z' )
- r -= 'a' - 10;
- else if ( r >= 'A' && r <= 'Z' )
- r -= 'A' - 10;
- else
- break;
- a *= 16;
- a += r;
- }
- if ( b < 2 )
- *out++ = *in++;
- else
- {
- *out++ = a;
- in += 3;
- }
- }
- else
- *out++ = *in++;
- maxlen--;
- }
- *out = 0;
- }
- void WebServerBaseClass::base64decode( char *src, char *dest, int destsize )
- {
- int accum = 0;
- int nbits = 0;
- while ( src && *src )
- {
- int x = 0;
- char c = *src++;
- if ( c >= 'A' && c <= 'Z' )
- x = c - 'A';
- else if ( c >= 'a' && c <= 'z' )
- x = c - 'a' + 26;
- else if ( c >= '0' && c <= '9' )
- x = c - '0' + 52;
- else if ( c == '+' )
- x = 62;
- else if ( c == '/' )
- x = 63;
- else
- break;
- accum <<= 6;
- accum |= x;
- nbits += 6;
- while ( nbits >= 8 )
- {
- if ( --destsize <= 0 )
- break;
- nbits -= 8;
- *dest++ = (char)( ( accum >> nbits ) & 0xff );
- }
- }
- *dest = 0;
- }
- void WebServerBaseClass::base64encode( char *in, char *out )
- {
- char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- int shift = 0;
- int accum = 0;
- while ( in && *in )
- {
- if ( *in )
- {
- accum <<= 8;
- shift += 8;
- accum |= *in++;
- }
- while ( shift >= 6 )
- {
- shift -= 6;
- *out++ = alphabet[ ( accum >> shift ) & 0x3F ];
- }
- }
- if ( shift == 4 )
- {
- *out++ = alphabet[ ( accum & 0xF ) << 2 ];
- *out++ = '=';
- }
- else if ( shift == 2 )
- {
- *out++ = alphabet[ ( accum & 0x3 ) << 4 ];
- *out++ = '=';
- *out++ = '=';
- }
- *out++ = 0;
- }
- int WebServerBaseClass::parseAuth( char *auth_header, char *out, int out_len ) //returns 0 on unknown auth, 1 on basic
- {
- char *authstr = auth_header;
- *out = 0;
- if ( !auth_header || !*auth_header )
- return 0;
- while ( authstr && *authstr && *authstr == ' ' )
- {
- authstr++;
- }
- if ( _strnicmp( authstr, "basic ", 6 ) )
- return 0;
- authstr += 6;
- while ( authstr && *authstr && *authstr == ' ' )
- authstr++;
- base64decode( authstr, out, out_len );
- return 1;
- }
- #ifdef CBCLASS
- #undef CBCLASS
- #endif
- #define CBCLASS WebServerBaseClass
- START_DISPATCH
- VCB( API_WEBSERV_RUN, run )
- VCB( API_WEBSERV_SETMAXCONNECTIONS, setMaxConnections )
- VCB( API_WEBSERV_SETREQUESTTIMEOUT, setRequestTimeout )
- CB( API_WEBSERV_ADDLISTENPORT, addListenPort )
- CB( API_WEBSERV_GETLISTENPORT, getListenPort )
- VCB( API_WEBSERV_REMOVELISTENPORT, removeListenPort )
- VCB( API_WEBSERV_REMOVELISTENIDX, removeListenIdx )
- VCB( API_WEBSERV_ATTACHCONNECTION, attachConnection )
- VCB( API_WEBSERV_URLENCODE, wasabi_url_encode )
- VCB( API_WEBSERV_URLDECODE, wasabi_url_decode )
- VCB( API_WEBSERV_BASE64ENCODE, wasabi_base64encode )
- VCB( API_WEBSERV_BASE64DECODE, wasabi_base64decode )
- CB( API_WEBSERV_PARSEAUTH, wasabi_parseAuth )
- VCB( API_WEBSERV_SETCONNECTIONCB, SetConnectionCallback );
- VCB( API_WEBSERV_ALLOWCOMPRESSION, AllowCompression )
- END_DISPATCH
|