wac_network_web_server.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. /*
  2. ** JNetLib
  3. ** Copyright (C) 2000-2003 Nullsoft, Inc.
  4. ** Author: Justin Frankel
  5. ** File: wac_network_web_server.cpp - Generic simple webserver baseclass
  6. ** License: see jnetlib.h
  7. ** see test.cpp for an example of how to use this class
  8. */
  9. #include "wac_network_web_server.h"
  10. #include <zlib.h>
  11. #include "../nu/strsafe.h"
  12. #ifndef MIN
  13. #define MIN(a,b) (((a) < (b)) ? (a) : (b))
  14. #endif
  15. class WS_ItemList
  16. {
  17. public:
  18. WS_ItemList()
  19. {
  20. m_size = 0; m_list = 0;
  21. }
  22. ~WS_ItemList()
  23. {
  24. ::free( m_list );
  25. }
  26. void *Add( void *i );
  27. void *Get( int w );
  28. void Del( int idx );
  29. int GetSize( void )
  30. {
  31. return m_size;
  32. }
  33. protected:
  34. void **m_list;
  35. int m_size;
  36. };
  37. class WS_conInst
  38. {
  39. public:
  40. WS_conInst( WAC_Network_Connection *c, JNL_Listen *l, int which_port );
  41. ~WS_conInst();
  42. void close();
  43. // these will be used by WebServerBaseClass::onConnection yay
  44. wa::Components::WAC_Network_HTTP_Server m_serv;
  45. api_pagegenerator *m_pagegen;
  46. api_onconncb *connCb;
  47. JNL_Listen *m_listener;
  48. int m_port; // port this came in on
  49. time_t m_connect_time;
  50. z_stream *zlibStream;
  51. };
  52. void *WS_ItemList::Add( void *i )
  53. {
  54. if ( !m_list || !( m_size & 31 ) )
  55. m_list = ( void ** )::realloc( m_list, sizeof( void * ) * ( m_size + 32 ) );
  56. m_list[ m_size++ ] = i;
  57. return i;
  58. }
  59. void *WS_ItemList::Get( int w )
  60. {
  61. if ( w >= 0 && w < m_size )
  62. return m_list[ w ];
  63. return NULL;
  64. }
  65. void WS_ItemList::Del( int idx )
  66. {
  67. if ( m_list && idx >= 0 && idx < m_size )
  68. {
  69. m_size--;
  70. if ( idx != m_size )
  71. ::memcpy( m_list + idx, m_list + idx + 1, sizeof( void * ) * ( m_size - idx ) );
  72. if ( !( m_size & 31 ) && m_size ) // resize down
  73. {
  74. m_list = ( void ** )::realloc( m_list, sizeof( void * ) * m_size );
  75. }
  76. }
  77. }
  78. 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 )
  79. {
  80. zlibStream = 0;
  81. m_pagegen = 0;
  82. time( &m_connect_time );
  83. }
  84. WS_conInst::~WS_conInst()
  85. {
  86. if ( connCb && m_pagegen )
  87. connCb->destroyConnection( m_pagegen );
  88. if ( zlibStream )
  89. deflateEnd( zlibStream );
  90. free( zlibStream );
  91. }
  92. void WS_conInst::close()
  93. {
  94. if ( connCb && m_pagegen )
  95. connCb->destroyConnection( m_pagegen );
  96. m_pagegen = 0;
  97. if ( zlibStream )
  98. deflateEnd( zlibStream );
  99. free( zlibStream );
  100. zlibStream = 0;
  101. time( &m_connect_time );
  102. }
  103. WebServerBaseClass::~WebServerBaseClass()
  104. {
  105. int x;
  106. for ( x = 0; x < m_connections->GetSize(); x++ )
  107. {
  108. delete (WS_conInst *)m_connections->Get( x );
  109. }
  110. delete m_connections;
  111. for ( x = 0; x < m_listeners->GetSize(); x++ )
  112. {
  113. delete (JNL_Listen *)m_listeners->Get( x );
  114. }
  115. delete m_listeners;
  116. }
  117. WebServerBaseClass::WebServerBaseClass()
  118. {
  119. allowCompression = false;
  120. m_connections = new WS_ItemList;
  121. m_listeners = new WS_ItemList;
  122. m_listener_rot = 0;
  123. m_timeout_s = 15;
  124. m_max_con = 100;
  125. extraDeflateBuffer = NULL;
  126. extraDeflateBufferSize = 0;
  127. extraDeflateBufferUsed = 0;
  128. connectionCallback = NULL;
  129. }
  130. void WebServerBaseClass::initForFactory()
  131. {
  132. if ( m_connections )
  133. {
  134. for ( int x = 0; x < m_connections->GetSize(); x++ )
  135. delete (WS_conInst *)m_connections->Get( x );
  136. delete m_connections;
  137. }
  138. if ( m_listeners )
  139. {
  140. for ( int x = 0; x < m_listeners->GetSize(); x++ )
  141. delete (JNL_Listen *)m_listeners->Get( x );
  142. delete m_listeners;
  143. }
  144. m_connections = new WS_ItemList;
  145. m_listeners = new WS_ItemList;
  146. m_listener_rot = 0;
  147. m_timeout_s = 15;
  148. m_max_con = 100;
  149. }
  150. void WebServerBaseClass::setMaxConnections( int max_con )
  151. {
  152. m_max_con = max_con;
  153. }
  154. void WebServerBaseClass::setRequestTimeout( int timeout_s )
  155. {
  156. m_timeout_s = timeout_s;
  157. }
  158. void WebServerBaseClass::AllowCompression()
  159. {
  160. allowCompression = true;
  161. }
  162. int WebServerBaseClass::addListenPort( int port, unsigned long which_interface )
  163. {
  164. removeListenPort( port );
  165. sockaddr_in address;
  166. memset( &address, 0, sizeof( address ) );
  167. address.sin_family = AF_INET;
  168. address.sin_port = htons( (unsigned short)port );
  169. address.sin_addr.s_addr = which_interface ? which_interface : INADDR_ANY;
  170. JNL_Listen *p = new JNL_Listen();
  171. p->Initialize( port, (sockaddr *)&address, PF_INET );
  172. m_listeners->Add( (void *)p );
  173. if ( p->is_error() )
  174. return -1;
  175. return 0;
  176. }
  177. void WebServerBaseClass::removeListenPort( int port )
  178. {
  179. for ( int x = 0; x < m_listeners->GetSize(); x++ )
  180. {
  181. JNL_Listen *p = (JNL_Listen *)m_listeners->Get( x );
  182. if ( p->get_port() == port )
  183. {
  184. delete p;
  185. m_listeners->Del( x );
  186. break;
  187. }
  188. }
  189. }
  190. void WebServerBaseClass::removeListenIdx( int idx )
  191. {
  192. if ( idx >= 0 && idx < m_listeners->GetSize() )
  193. {
  194. JNL_Listen *p = (JNL_Listen *)m_listeners->Get( idx );
  195. delete p;
  196. m_listeners->Del( idx );
  197. }
  198. }
  199. int WebServerBaseClass::getListenPort( int idx, int *err )
  200. {
  201. JNL_Listen *p = (JNL_Listen *)m_listeners->Get( idx );
  202. if ( p )
  203. {
  204. if ( err )
  205. *err = p->is_error();
  206. return p->get_port();
  207. }
  208. return 0;
  209. }
  210. void WebServerBaseClass::attachConnection( WAC_Network_Connection *con, JNL_Listen *listener )
  211. {
  212. m_connections->Add( ( void * )new WS_conInst( con, listener, listener->get_port() ) );
  213. }
  214. void WebServerBaseClass::run( void )
  215. {
  216. int nl;
  217. if ( m_connections->GetSize() < m_max_con && ( nl = m_listeners->GetSize() ) )
  218. {
  219. JNL_Listen *l = (JNL_Listen *)m_listeners->Get( m_listener_rot++ % nl );
  220. WAC_Network_Connection *c = l->get_connect( PACKET_SIZE, PACKET_SIZE );
  221. if ( c )
  222. attachConnection( c, l );
  223. }
  224. for ( int x = 0; x < m_connections->GetSize(); x++ )
  225. {
  226. WS_conInst *this_con = (WS_conInst *)m_connections->Get( x );
  227. switch ( run_connection( this_con ) )
  228. {
  229. case RUN_CONNECTION_DONE:
  230. {
  231. bool keep_alive = false;
  232. if ( this_con->m_serv.get_keep_alive() )
  233. {
  234. const char *connection_status = this_con->m_serv.getheader( "Connection" );
  235. if ( !connection_status || _stricmp( connection_status, "close" ) )
  236. keep_alive = true;
  237. }
  238. if ( !keep_alive )
  239. {
  240. delete this_con;
  241. m_connections->Del( x-- );
  242. }
  243. else
  244. {
  245. this_con->close();
  246. this_con->m_serv.reset();
  247. }
  248. }
  249. break;
  250. case RUN_CONNECTION_ERROR:
  251. case RUN_CONNECTION_TIMEOUT:
  252. delete this_con;
  253. m_connections->Del( x-- );
  254. break;
  255. }
  256. }
  257. }
  258. void WebServerBaseClass::SetConnectionCallback( api_onconncb *_connectionCallback )
  259. {
  260. _connectionCallback->caller = this;
  261. connectionCallback = _connectionCallback;
  262. }
  263. void WebServerBaseClass::deflatehelper( WS_conInst *con, char *buf, int l, bool flush )
  264. {
  265. char temp[ 8192 ] = { 0 };
  266. con->zlibStream->next_in = (Bytef *)buf;
  267. con->zlibStream->avail_in = l;
  268. con->zlibStream->avail_out = 0;
  269. int sent = 0;
  270. if ( flush )
  271. l = con->m_serv.bytes_cansend();
  272. while ( ( !flush && con->zlibStream->avail_in ) || ( flush && !con->zlibStream->avail_out ) )
  273. {
  274. con->zlibStream->next_out = (Bytef *)temp;
  275. con->zlibStream->avail_out = 8192;
  276. 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
  277. if ( sent + 8192 - (int)con->zlibStream->avail_out > l )
  278. {
  279. if ( !extraDeflateBuffer )
  280. {
  281. extraDeflateBufferSize = 8192 - con->zlibStream->avail_out;
  282. extraDeflateBuffer = (char *)malloc( extraDeflateBufferSize );
  283. memcpy( extraDeflateBuffer, temp, extraDeflateBufferSize );
  284. }
  285. else
  286. {
  287. // TODO need to sort out this to handle realloc failures...
  288. extraDeflateBuffer = (char *)realloc( extraDeflateBuffer, extraDeflateBufferSize + 8192 - con->zlibStream->avail_out );
  289. memcpy( extraDeflateBuffer + extraDeflateBufferSize, temp, 8192 - con->zlibStream->avail_out );
  290. extraDeflateBufferSize += 8192 - con->zlibStream->avail_out;
  291. }
  292. }
  293. else
  294. {
  295. con->m_serv.write_bytes( temp, 8192 - con->zlibStream->avail_out );
  296. sent += 8192 - con->zlibStream->avail_out;
  297. }
  298. if ( r != Z_OK )
  299. break;
  300. }
  301. }
  302. int WebServerBaseClass::run_connection( WS_conInst *con )
  303. {
  304. // TODO: add a Run() method to WC_conInst, passing in connectionCallback
  305. int s = con->m_serv.run();
  306. if ( s < 0 )
  307. {
  308. // m_serv.geterrorstr()
  309. return RUN_CONNECTION_ERROR;
  310. }
  311. if ( s < 2 )
  312. {
  313. // return 1 if we timed out
  314. if ( time( NULL ) - con->m_connect_time > m_timeout_s )
  315. return RUN_CONNECTION_TIMEOUT;
  316. else
  317. return RUN_CONNECTION_CONTINUE;
  318. }
  319. if ( s < 3 )
  320. {
  321. con->connCb = connectionCallback;
  322. con->m_pagegen = connectionCallback->onConnection( &con->m_serv, con->m_port );
  323. const char *compression0 = allowCompression ? con->m_serv.getheader( "Accept-Encoding" ) : 0;
  324. if ( compression0 )
  325. {
  326. // the compression0 string is a comma (and possibly space) separated list of acceptable formats
  327. char *compression = _strdup( compression0 );
  328. bool supportsGzip = false, supportsDeflate = false;
  329. size_t l = strlen( compression );
  330. char *p = compression;
  331. while ( p && *p )
  332. {
  333. if ( *p == ' ' || *p == ',' ) *p = 0; p++;
  334. }
  335. for ( char *q = compression; q < compression + l; q++ )
  336. {
  337. if ( !_stricmp( q, "deflate" ) )
  338. {
  339. supportsDeflate = true;
  340. q += 7;
  341. }
  342. if ( !_stricmp( q, "gzip" ) )
  343. {
  344. supportsGzip = true;
  345. q += 4;
  346. }
  347. }
  348. free( compression );
  349. int windowBits = 0;
  350. if ( supportsGzip )
  351. {
  352. windowBits = 15 + 16; /* +16 for gzip */
  353. compression = _strdup( "gzip" );
  354. }
  355. else if ( supportsDeflate )
  356. {
  357. windowBits = 15;
  358. compression = _strdup( "deflate" );
  359. }
  360. if ( windowBits )
  361. {
  362. con->zlibStream = (z_stream *)malloc( sizeof( z_stream ) );
  363. con->zlibStream->next_in = Z_NULL;
  364. con->zlibStream->avail_in = Z_NULL;
  365. con->zlibStream->next_out = Z_NULL;
  366. con->zlibStream->avail_out = Z_NULL;
  367. con->zlibStream->zalloc = (alloc_func)0;
  368. con->zlibStream->zfree = (free_func)0;
  369. con->zlibStream->opaque = 0;
  370. int z_err = deflateInit2( con->zlibStream,
  371. Z_DEFAULT_COMPRESSION,
  372. Z_DEFLATED,
  373. 15 + 16 /* +16 for gzip */,
  374. 8,
  375. Z_DEFAULT_STRATEGY );
  376. if ( z_err != Z_OK )
  377. {
  378. free( con->zlibStream );
  379. con->zlibStream = 0;
  380. }
  381. char temp_header[ 1024 ] = "Content-Encoding:";
  382. StringCchCatA( temp_header, 1024, compression );
  383. con->m_serv.set_reply_header( temp_header );
  384. }
  385. }
  386. return RUN_CONNECTION_CONTINUE;
  387. }
  388. if ( s < 4 )
  389. {
  390. if ( !con->m_pagegen )
  391. {
  392. return con->m_serv.bytes_inqueue() ? RUN_CONNECTION_CONTINUE : RUN_CONNECTION_DONE;
  393. }
  394. int l = con->m_serv.bytes_cansend();
  395. if ( l > 0 )
  396. {
  397. if ( con->zlibStream && extraDeflateBufferSize )
  398. {
  399. int len = MIN( extraDeflateBufferSize - extraDeflateBufferUsed, l );
  400. con->m_serv.write_bytes( extraDeflateBuffer + extraDeflateBufferUsed, len );
  401. extraDeflateBufferUsed += len;
  402. if ( extraDeflateBufferUsed == extraDeflateBufferSize )
  403. {
  404. extraDeflateBufferUsed = 0;
  405. extraDeflateBufferSize = 0;
  406. free( extraDeflateBuffer );
  407. extraDeflateBuffer = NULL;
  408. }
  409. l -= len;
  410. if ( l == 0 )
  411. return con->m_serv.bytes_inqueue() ? RUN_CONNECTION_CONTINUE : RUN_CONNECTION_DONE;
  412. }
  413. char buf[ PACKET_SIZE ] = { 0 };
  414. if ( l > sizeof( buf ) )
  415. l = sizeof( buf );
  416. l = con->m_pagegen->GetData( buf, l );
  417. if ( l < ( con->m_pagegen->IsNonBlocking() ? 0 : 1 ) ) // if nonblocking, this is l < 0, otherwise it's l<1
  418. {
  419. if ( con->zlibStream )
  420. {
  421. deflatehelper( con, 0, 0, true );
  422. /*
  423. char temp[8192] = {0};
  424. con->zlibStream->next_in = 0;
  425. con->zlibStream->avail_in = 0;
  426. con->zlibStream->avail_out = 0;
  427. while(!con->zlibStream->avail_out)
  428. {
  429. con->zlibStream->next_out = (Bytef *)temp;
  430. con->zlibStream->avail_out = 8192;
  431. int r = deflate(con->zlibStream, Z_FINISH);
  432. con->m_serv.write_bytes(temp, 8192 - con->zlibStream->avail_out);
  433. if(r != Z_OK) break;
  434. }*/
  435. }
  436. return con->m_serv.bytes_inqueue() ? RUN_CONNECTION_CONTINUE : RUN_CONNECTION_DONE;
  437. }
  438. if ( l > 0 )
  439. {
  440. if ( con->zlibStream )
  441. deflatehelper( con, buf, l, false );
  442. else
  443. con->m_serv.write_bytes( buf, l );
  444. }
  445. }
  446. return RUN_CONNECTION_CONTINUE;
  447. }
  448. return RUN_CONNECTION_DONE; // we're done by this point
  449. }
  450. void WebServerBaseClass::url_encode( char *in, char *out, int max_out )
  451. {
  452. while ( in && *in && max_out > 4 )
  453. {
  454. if ( ( *in >= 'A' && *in <= 'Z' ) ||
  455. ( *in >= 'a' && *in <= 'z' ) ||
  456. ( *in >= '0' && *in <= '9' ) || *in == '.' || *in == '_' || *in == '-' )
  457. {
  458. *out++ = *in++;
  459. max_out--;
  460. }
  461. else
  462. {
  463. int i = *in++;
  464. *out++ = '%';
  465. int b = ( i >> 4 ) & 15;
  466. if ( b < 10 )
  467. *out++ = '0' + b;
  468. else
  469. *out++ = 'A' + b - 10;
  470. b = i & 15;
  471. if ( b < 10 )
  472. *out++ = '0' + b;
  473. else
  474. *out++ = 'A' + b - 10;
  475. max_out -= 3;
  476. }
  477. }
  478. *out = 0;
  479. }
  480. void WebServerBaseClass::url_decode( char *in, char *out, int maxlen )
  481. {
  482. while ( in && *in && maxlen > 1 )
  483. {
  484. if ( *in == '+' )
  485. {
  486. in++;
  487. *out++ = ' ';
  488. }
  489. else if ( *in == '%' && in[ 1 ] != '%' && in[ 1 ] )
  490. {
  491. int a = 0;
  492. int b = 0;
  493. for ( b = 0; b < 2; b++ )
  494. {
  495. int r = in[ 1 + b ];
  496. if ( r >= '0' && r <= '9' )
  497. r -= '0';
  498. else if ( r >= 'a' && r <= 'z' )
  499. r -= 'a' - 10;
  500. else if ( r >= 'A' && r <= 'Z' )
  501. r -= 'A' - 10;
  502. else
  503. break;
  504. a *= 16;
  505. a += r;
  506. }
  507. if ( b < 2 )
  508. *out++ = *in++;
  509. else
  510. {
  511. *out++ = a;
  512. in += 3;
  513. }
  514. }
  515. else
  516. *out++ = *in++;
  517. maxlen--;
  518. }
  519. *out = 0;
  520. }
  521. void WebServerBaseClass::base64decode( char *src, char *dest, int destsize )
  522. {
  523. int accum = 0;
  524. int nbits = 0;
  525. while ( src && *src )
  526. {
  527. int x = 0;
  528. char c = *src++;
  529. if ( c >= 'A' && c <= 'Z' )
  530. x = c - 'A';
  531. else if ( c >= 'a' && c <= 'z' )
  532. x = c - 'a' + 26;
  533. else if ( c >= '0' && c <= '9' )
  534. x = c - '0' + 52;
  535. else if ( c == '+' )
  536. x = 62;
  537. else if ( c == '/' )
  538. x = 63;
  539. else
  540. break;
  541. accum <<= 6;
  542. accum |= x;
  543. nbits += 6;
  544. while ( nbits >= 8 )
  545. {
  546. if ( --destsize <= 0 )
  547. break;
  548. nbits -= 8;
  549. *dest++ = (char)( ( accum >> nbits ) & 0xff );
  550. }
  551. }
  552. *dest = 0;
  553. }
  554. void WebServerBaseClass::base64encode( char *in, char *out )
  555. {
  556. char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  557. int shift = 0;
  558. int accum = 0;
  559. while ( in && *in )
  560. {
  561. if ( *in )
  562. {
  563. accum <<= 8;
  564. shift += 8;
  565. accum |= *in++;
  566. }
  567. while ( shift >= 6 )
  568. {
  569. shift -= 6;
  570. *out++ = alphabet[ ( accum >> shift ) & 0x3F ];
  571. }
  572. }
  573. if ( shift == 4 )
  574. {
  575. *out++ = alphabet[ ( accum & 0xF ) << 2 ];
  576. *out++ = '=';
  577. }
  578. else if ( shift == 2 )
  579. {
  580. *out++ = alphabet[ ( accum & 0x3 ) << 4 ];
  581. *out++ = '=';
  582. *out++ = '=';
  583. }
  584. *out++ = 0;
  585. }
  586. int WebServerBaseClass::parseAuth( char *auth_header, char *out, int out_len ) //returns 0 on unknown auth, 1 on basic
  587. {
  588. char *authstr = auth_header;
  589. *out = 0;
  590. if ( !auth_header || !*auth_header )
  591. return 0;
  592. while ( authstr && *authstr && *authstr == ' ' )
  593. {
  594. authstr++;
  595. }
  596. if ( _strnicmp( authstr, "basic ", 6 ) )
  597. return 0;
  598. authstr += 6;
  599. while ( authstr && *authstr && *authstr == ' ' )
  600. authstr++;
  601. base64decode( authstr, out, out_len );
  602. return 1;
  603. }
  604. #ifdef CBCLASS
  605. #undef CBCLASS
  606. #endif
  607. #define CBCLASS WebServerBaseClass
  608. START_DISPATCH
  609. VCB( API_WEBSERV_RUN, run )
  610. VCB( API_WEBSERV_SETMAXCONNECTIONS, setMaxConnections )
  611. VCB( API_WEBSERV_SETREQUESTTIMEOUT, setRequestTimeout )
  612. CB( API_WEBSERV_ADDLISTENPORT, addListenPort )
  613. CB( API_WEBSERV_GETLISTENPORT, getListenPort )
  614. VCB( API_WEBSERV_REMOVELISTENPORT, removeListenPort )
  615. VCB( API_WEBSERV_REMOVELISTENIDX, removeListenIdx )
  616. VCB( API_WEBSERV_ATTACHCONNECTION, attachConnection )
  617. VCB( API_WEBSERV_URLENCODE, wasabi_url_encode )
  618. VCB( API_WEBSERV_URLDECODE, wasabi_url_decode )
  619. VCB( API_WEBSERV_BASE64ENCODE, wasabi_base64encode )
  620. VCB( API_WEBSERV_BASE64DECODE, wasabi_base64decode )
  621. CB( API_WEBSERV_PARSEAUTH, wasabi_parseAuth )
  622. VCB( API_WEBSERV_SETCONNECTIONCB, SetConnectionCallback );
  623. VCB( API_WEBSERV_ALLOWCOMPRESSION, AllowCompression )
  624. END_DISPATCH