httpget.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. /*
  2. ** JNetLib
  3. ** Copyright (C) 2000-2007 Nullsoft, Inc.
  4. ** Author: Justin Frankel
  5. ** File: httpget.cpp - JNL HTTP GET implementation
  6. ** License: see jnetlib.h
  7. */
  8. #include "netinc.h"
  9. #include "util.h"
  10. #include "httpget.h"
  11. #include "foundation/error.h"
  12. #ifdef USE_SSL
  13. #include "sslconnection.h"
  14. #endif
  15. #include <stdio.h>
  16. #define STRSAFE_NO_DEPRECATE
  17. #include "nu/strsafe.h"
  18. #include "nu/AutoLock.h"
  19. char *JNL_HTTPGet::g_proxy = 0;
  20. static nu::LockGuard proxy_guard;
  21. char *JNL_HTTPGet::get_proxy()
  22. {
  23. nu::AutoLock auto_lock( proxy_guard );
  24. if ( g_proxy )
  25. return _strdup( g_proxy );
  26. else
  27. return 0;
  28. }
  29. void JNL_HTTPGet::set_proxy( const char *proxy )
  30. {
  31. nu::AutoLock auto_lock( proxy_guard );
  32. free( g_proxy );
  33. if ( proxy )
  34. g_proxy = _strdup( proxy );
  35. else
  36. g_proxy = 0;
  37. }
  38. JNL_HTTPGet::JNL_HTTPGet( size_t recvbufsize, size_t sendbufsize )
  39. {
  40. persistent = false;
  41. accept_all_reply_codes = false;
  42. zlibStream = 0;
  43. allowCompression = false;
  44. m_dns = JNL_AUTODNS;
  45. m_con = NULL;
  46. m_http_proxylpinfo = 0;
  47. m_http_proxyhost = 0;
  48. m_http_proxyport = 0;
  49. m_sendbufsize = sendbufsize;
  50. m_sendheaders = NULL;
  51. reinit();
  52. m_recvbufsize = recvbufsize;
  53. char *p = get_proxy();
  54. if ( p )
  55. {
  56. char *r = NULL;
  57. do_parse_url( p, &m_http_proxyhost, &m_http_proxyport, &r, &m_http_proxylpinfo );
  58. free( r );
  59. free( p );
  60. }
  61. }
  62. JNL_HTTPGet::~JNL_HTTPGet()
  63. {
  64. deinit();
  65. free( m_sendheaders );
  66. free( m_http_proxylpinfo );
  67. free( m_http_proxyhost );
  68. }
  69. void JNL_HTTPGet::reinit()
  70. {
  71. m_errstr = 0;
  72. m_recvheaders = NULL;
  73. m_recvheaders_size = 0;
  74. m_http_state = 0;
  75. m_http_port = 0;
  76. m_http_url = 0;
  77. m_reply = 0;
  78. m_http_host = m_http_lpinfo = m_http_request = NULL;
  79. }
  80. void JNL_HTTPGet::deinit( bool full )
  81. {
  82. if ( !persistent || full || ( m_con && m_con->get_state() == JNL_Connection::STATE_ERROR ) )
  83. {
  84. delete m_con;
  85. m_con = NULL;
  86. }
  87. free( m_recvheaders );
  88. free( m_http_url );
  89. free( m_http_host );
  90. free( m_http_lpinfo );
  91. free( m_http_request );
  92. free( m_errstr );
  93. free( m_reply );
  94. if ( zlibStream )
  95. inflateEnd( zlibStream );
  96. free( zlibStream );
  97. zlibStream = 0;
  98. reinit();
  99. }
  100. void JNL_HTTPGet::set_sendbufsize(size_t sendbufsize)
  101. {
  102. m_sendbufsize = sendbufsize;
  103. }
  104. int JNL_HTTPGet::set_recv_buffer_size( size_t new_buffer_size )
  105. {
  106. if ( m_con )
  107. {
  108. int ret = m_con->set_recv_buffer_size( new_buffer_size );
  109. if ( ret == NErr_NoAction )// this will get returned if new_buffer_size is smaller than existing.
  110. return NErr_Success;
  111. else if ( ret != NErr_Success )
  112. return ret;
  113. }
  114. m_recvbufsize = new_buffer_size;
  115. return NErr_Success;
  116. }
  117. void JNL_HTTPGet::addheader( const char *header )
  118. {
  119. if ( strstr( header, "\r" ) || strstr( header, "\n" ) )
  120. return;
  121. if ( !m_sendheaders )
  122. {
  123. size_t len = strlen( header ) + 3;
  124. m_sendheaders = (char *)malloc( len );
  125. if ( m_sendheaders )
  126. {
  127. char *itr = m_sendheaders;
  128. StringCchCopyExA( itr, len, header, &itr, &len, 0 );
  129. StringCchCatExA( itr, len, "\r\n", &itr, &len, 0 );
  130. }
  131. }
  132. else
  133. {
  134. size_t len = strlen( header ) + strlen( m_sendheaders ) + 1 + 2;
  135. char *t = (char *)malloc( len );
  136. if ( t )
  137. {
  138. char *newHeaders = t;
  139. StringCchCopyExA( t, len, m_sendheaders, &t, &len, 0 );
  140. StringCchCatExA( t, len, header, &t, &len, 0 );
  141. StringCchCatExA( t, len, "\r\n", &t, &len, 0 );
  142. free( m_sendheaders );
  143. m_sendheaders = newHeaders;
  144. }
  145. }
  146. }
  147. void JNL_HTTPGet::addheadervalue( const char *header, const char *value )
  148. {
  149. size_t additional = strlen( header ) + 2 + strlen( value ) + 2 + 1;
  150. if ( !m_sendheaders )
  151. {
  152. m_sendheaders = (char *)malloc( additional );
  153. if ( m_sendheaders )
  154. {
  155. char *p = m_sendheaders;
  156. StringCchCopyExA( p, additional, header, &p, &additional, 0 );
  157. StringCchCatExA( p, additional, ": ", &p, &additional, 0 );
  158. StringCchCatExA( p, additional, value, &p, &additional, 0 );
  159. StringCchCatExA( p, additional, "\r\n", &p, &additional, 0 );
  160. }
  161. }
  162. else
  163. {
  164. size_t alloc_len = strlen( m_sendheaders ) + additional;
  165. char *t = (char *)malloc( alloc_len );
  166. if ( t )
  167. {
  168. char *p = t;
  169. StringCchCopyExA( p, alloc_len, m_sendheaders, &p, &alloc_len, 0 );
  170. StringCchCatExA( p, alloc_len, header, &p, &alloc_len, 0 );
  171. StringCchCatExA( p, alloc_len, ": ", &p, &alloc_len, 0 );
  172. StringCchCatExA( p, alloc_len, value, &p, &alloc_len, 0 );
  173. StringCchCatExA( p, alloc_len, "\r\n", &p, &alloc_len, 0 );
  174. free( m_sendheaders );
  175. m_sendheaders = t;
  176. }
  177. }
  178. }
  179. void JNL_HTTPGet::do_encode_mimestr( char *in, char *out )
  180. {
  181. char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  182. int shift = 0;
  183. int accum = 0;
  184. while ( in && *in )
  185. {
  186. if ( *in )
  187. {
  188. accum <<= 8;
  189. shift += 8;
  190. accum |= *in++;
  191. }
  192. while ( shift >= 6 )
  193. {
  194. shift -= 6;
  195. *out++ = alphabet[ ( accum >> shift ) & 0x3F ];
  196. }
  197. }
  198. if ( shift == 4 )
  199. {
  200. *out++ = alphabet[ ( accum & 0xF ) << 2 ];
  201. *out++ = '=';
  202. }
  203. else if ( shift == 2 )
  204. {
  205. *out++ = alphabet[ ( accum & 0x3 ) << 4 ];
  206. *out++ = '=';
  207. *out++ = '=';
  208. }
  209. *out++ = 0;
  210. }
  211. void JNL_HTTPGet::connect( const char *url, int ver, const char *requestmethod )
  212. {
  213. deinit( false );
  214. m_http_url = _strdup( url );
  215. do_parse_url( m_http_url, &m_http_host, &m_http_port, &m_http_request, &m_http_lpinfo );
  216. if ( !m_http_host || !m_http_host[ 0 ] || !m_http_port )
  217. {
  218. m_http_state = -1;
  219. seterrstr( "invalid URL" );
  220. return;
  221. }
  222. size_t sendbufferlen = 0;
  223. if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] )
  224. sendbufferlen += strlen( requestmethod ) + 1 /* GET */ + strlen( m_http_request ) + 9 /* HTTP/1.0 */ + 2;
  225. else
  226. {
  227. sendbufferlen += strlen( requestmethod ) + 1 /* GET */ + strlen( m_http_url ) + 9 /* HTTP/1.0 */ + 2;
  228. if ( m_http_proxylpinfo && m_http_proxylpinfo[ 0 ] )
  229. sendbufferlen += 58 + strlen( m_http_proxylpinfo ) * 2; // being safe here
  230. }
  231. sendbufferlen += 5 /* Host: */ + strlen( m_http_host ) + 2;
  232. if ( m_http_port != 80 )
  233. sendbufferlen += 6;
  234. if ( m_http_lpinfo && m_http_lpinfo[ 0 ] )
  235. sendbufferlen += 46 + strlen( m_http_lpinfo ) * 2; // being safe here
  236. if ( m_sendheaders )
  237. sendbufferlen += strlen( m_sendheaders );
  238. size_t strLen = sendbufferlen + 1024;
  239. char *str = (char *)calloc( strLen, sizeof( char ) );
  240. char *connectString = str;
  241. if ( !str )
  242. {
  243. seterrstr( "error allocating memory" );
  244. m_http_state = -1;
  245. }
  246. if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] )
  247. {
  248. StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s %s HTTP/1.%d\r\n", requestmethod, m_http_request, ver % 10 );
  249. }
  250. else
  251. {
  252. char *myp = NULL;
  253. if ( strncasecmp( m_http_url, "uvox://", 7 ) == 0 )
  254. {
  255. myp = m_http_url + 7;
  256. StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 );
  257. }
  258. else if ( strncasecmp( m_http_url, "unsv://", 7 ) == 0 )
  259. {
  260. myp = m_http_url + 7;
  261. StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 );
  262. }
  263. else if ( strncasecmp( m_http_url, "uasf://", 7 ) == 0 )
  264. {
  265. myp = m_http_url + 7;
  266. StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s http://%s HTTP/1.%d\r\n", requestmethod, myp, ver % 10 );
  267. }
  268. else
  269. StringCchPrintfExA( str, strLen, &str, &strLen, 0, "%s %s HTTP/1.%d\r\n", requestmethod, m_http_url, ver % 10 );
  270. }
  271. if ( m_http_port == 80 )
  272. StringCchPrintfExA( str, strLen, &str, &strLen, 0, "Host: %s\r\n", m_http_host );
  273. else
  274. StringCchPrintfExA( str, strLen, &str, &strLen, 0, "Host: %s:%d\r\n", m_http_host, m_http_port );
  275. if ( m_http_lpinfo && m_http_lpinfo[ 0 ] )
  276. {
  277. StringCchCatExA( str, strLen, "Authorization: Basic ", &str, &strLen, 0 );
  278. do_encode_mimestr( m_http_lpinfo, str );
  279. StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 );
  280. }
  281. if ( m_http_proxylpinfo && m_http_proxylpinfo[ 0 ] )
  282. {
  283. StringCchCatExA( str, strLen, "Proxy-Authorization: Basic ", &str, &strLen, 0 );
  284. do_encode_mimestr( m_http_proxylpinfo, str );
  285. StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 );
  286. }
  287. if ( allowCompression )
  288. StringCchCatExA( str, strLen, "Accept-Encoding: gzip\r\n", &str, &strLen, 0 );
  289. if ( m_sendheaders )
  290. StringCchCatExA( str, strLen, m_sendheaders, &str, &strLen, 0 );
  291. StringCchCatExA( str, strLen, "\r\n", &str, &strLen, 0 );
  292. int a = (int)m_recvbufsize;
  293. if ( a < 4096 )
  294. a = 4096;
  295. if ( !m_con )
  296. {
  297. //m_con=new JNL_Connection(m_dns,strlen(str)+4,a);
  298. /*
  299. ** Joshua Teitelbaum delta 1/15/2006
  300. */
  301. #ifdef USE_SSL
  302. /*
  303. ** Joshua Teitelbaum 1/27/2006
  304. ** Check for secure
  305. */
  306. if ( !_strnicmp( m_http_url, "https:", strlen( "https:" ) ) )
  307. {
  308. size_t send_buffer_size = strlen( connectString ) + 4;
  309. if ( send_buffer_size < 8192 )
  310. send_buffer_size = 8192;
  311. send_buffer_size += m_sendbufsize;
  312. if ( m_sendbufsize == 0 && !strcmp( requestmethod, "POST" ) )
  313. send_buffer_size += 8192; // some extra room for posting data if it wasn't explicitly defined
  314. m_con = new JNL_SSL_Connection( NULL, m_dns, send_buffer_size, a );
  315. }
  316. else
  317. {
  318. #endif
  319. size_t send_buffer_size = strlen( connectString ) + 4 + m_sendbufsize;
  320. if ( m_sendbufsize == 0 && !strcmp( requestmethod, "POST" ) )
  321. send_buffer_size += 8192; // some extra room for posting data if it wasn't explicitly defined
  322. m_con = new JNL_Connection( m_dns, send_buffer_size, a );
  323. #ifdef USE_SSL
  324. }
  325. #endif
  326. if ( m_con )
  327. {
  328. if ( !m_http_proxyhost || !m_http_proxyhost[ 0 ] )
  329. m_con->connect( m_http_host, m_http_port );
  330. else
  331. m_con->connect( m_http_proxyhost, m_http_proxyport );
  332. m_con->send_string( connectString );
  333. }
  334. else
  335. {
  336. m_http_state = -1;
  337. seterrstr( "could not create connection object" );
  338. }
  339. }
  340. else
  341. {
  342. m_con->reuse();
  343. m_con->send_string( connectString );
  344. }
  345. free(connectString);
  346. }
  347. void JNL_HTTPGet::do_parse_url( const char *url, char **host, unsigned short *port, char **req, char **lp )
  348. {
  349. char *l_port = 0;
  350. JNL::parse_url( url, &l_port, host, port, req, lp );
  351. if ( !*port )
  352. {
  353. if ( l_port )
  354. {
  355. addrinfo *res;
  356. addrinfo hints;
  357. memset( &hints, 0, sizeof( hints ) );
  358. hints.ai_family = PF_UNSPEC;
  359. hints.ai_flags = 0;
  360. hints.ai_socktype = SOCK_STREAM;
  361. if ( getaddrinfo( 0, l_port, &hints, &res ) == 0 )
  362. {
  363. if ( res->ai_family == AF_INET )
  364. *port = htons( ( (sockaddr_in *)res->ai_addr )->sin_port );
  365. else if ( res->ai_family == AF_INET6 )
  366. *port = htons( ( (sockaddr_in6 *)res->ai_addr )->sin6_port );
  367. else // wtf?
  368. *port = 80;
  369. }
  370. else
  371. *port = 80;
  372. }
  373. else
  374. *port = 80;
  375. }
  376. if ( l_port )
  377. free( l_port );
  378. if ( !*req )
  379. *req = _strdup( "/" );
  380. }
  381. const char *JNL_HTTPGet::getallheaders()
  382. {
  383. // double null terminated, null delimited list
  384. if (m_recvheaders)
  385. return m_recvheaders;
  386. else
  387. return "\0\0";
  388. }
  389. const char *JNL_HTTPGet::getheader( const char *headername )
  390. {
  391. char *ret = NULL;
  392. if ( headername[ 0 ] == 0 || !m_recvheaders )
  393. return NULL;
  394. size_t headername_size = strlen( headername );
  395. char *buf = (char *)malloc( headername_size + 2 );
  396. #ifdef _WIN32
  397. StringCchCopyA( buf, headername_size + 2, headername );
  398. #elif defined(__APPLE__)
  399. strlcpy( buf, headername, headername_size + 2 );
  400. #else
  401. strncpy( buf, headername, headername_size + 1 );
  402. buf[ headername_size + 1 ] = 0;
  403. #endif
  404. if ( buf[ headername_size - 1 ] != ':' )
  405. {
  406. buf[ headername_size++ ] = ':';
  407. buf[ headername_size ] = 0;
  408. }
  409. char *p = m_recvheaders;
  410. while ( p && *p )
  411. {
  412. if ( !strncasecmp( buf, p, headername_size ) )
  413. {
  414. ret = p + headername_size;
  415. while ( ret && *ret && *ret == ' ' )
  416. ret++;
  417. break;
  418. }
  419. p += strlen( p ) + 1;
  420. }
  421. free( buf );
  422. return ret;
  423. }
  424. int JNL_HTTPGet::run()
  425. {
  426. int cnt = 0;
  427. if ( m_http_state == -1 || !m_con )
  428. return HTTPGET_RUN_ERROR; // error
  429. run_again:
  430. m_con->run();
  431. if ( m_con->get_state() == JNL_Connection::STATE_ERROR )
  432. {
  433. seterrstr( m_con->get_errstr() );
  434. return HTTPGET_RUN_ERROR;
  435. }
  436. if ( m_con->get_state() == JNL_Connection::STATE_CLOSED )
  437. return HTTPGET_RUN_CONNECTION_CLOSED;
  438. if ( m_http_state == 0 ) // connected, waiting for reply
  439. {
  440. if ( m_con->recv_lines_available() > 0 )
  441. {
  442. char buf[ 4096 ] = { 0 };
  443. m_con->recv_line( buf, 4096 );
  444. buf[ 4095 ] = 0;
  445. if ( m_reply && getreplycode() == 100 )
  446. {
  447. free( m_reply );
  448. m_reply = 0;
  449. goto run_again;
  450. }
  451. m_reply = _strdup( buf );
  452. int code = getreplycode();
  453. if ( code >= 200 && code <= 206 )
  454. m_http_state = 2; // proceed to read headers normally
  455. else if ( code == 301 || code == 302 || code == 303 || code == 307 )
  456. {
  457. m_http_state = 1; // redirect city
  458. }
  459. else if ( code != 100 ) // in case of HTTP 100 Continue code, we'll keep looping
  460. {
  461. if ( accept_all_reply_codes )
  462. {
  463. m_http_state = 2; // proceed to read headers normally
  464. }
  465. else
  466. {
  467. seterrstr( buf );
  468. m_http_state = -1;
  469. return HTTPGET_RUN_ERROR;
  470. }
  471. }
  472. cnt = 0;
  473. }
  474. else if ( !cnt++ )
  475. goto run_again;
  476. }
  477. if ( m_http_state == 1 ) // redirect
  478. {
  479. char *loc = 0;
  480. while ( m_con->recv_lines_available() > 0 )
  481. {
  482. char buf[ 4096 ] = { 0 };
  483. m_con->recv_line( buf, 4096 );
  484. buf[ 4095 ] = 0;
  485. if ( !buf[ 0 ] )
  486. {
  487. if ( !loc )
  488. {
  489. m_http_state = -1;
  490. return HTTPGET_RUN_ERROR;
  491. }
  492. else
  493. break;
  494. }
  495. if ( !strncasecmp( buf, "Location:", 9 ) )
  496. {
  497. char *p = buf + 9;
  498. while ( p && *p && *p == ' ' ) p++;
  499. if ( p && *p )
  500. {
  501. // TODO need to make this match the request type
  502. loc = _strdup( p );
  503. }
  504. }
  505. }
  506. if ( loc )
  507. {
  508. connect( loc );
  509. free( loc );
  510. return HTTPGET_RUN_OK;
  511. }
  512. }
  513. /* ----- read headers ----- */
  514. if ( m_http_state == 2 )
  515. {
  516. if ( !cnt++ && m_con->recv_lines_available() < 1 )
  517. goto run_again;
  518. while ( m_con->recv_lines_available() > 0 )
  519. {
  520. char buf[ 8192 ] = { 0 };
  521. m_con->recv_line( buf, 8192 );
  522. buf[ 8191 ] = 0;
  523. if ( !buf[ 0 ] )
  524. {
  525. const char *compression = getheader( "Content-Encoding" );
  526. if ( compression && !strcmp( compression, "gzip" ) )
  527. {
  528. zlibStream = (z_stream *)malloc( sizeof( z_stream ) );
  529. zlibStream->next_in = Z_NULL;
  530. zlibStream->avail_in = Z_NULL;
  531. zlibStream->next_out = Z_NULL;
  532. zlibStream->avail_out = Z_NULL;
  533. zlibStream->zalloc = (alloc_func)0;
  534. zlibStream->zfree = (free_func)0;
  535. zlibStream->opaque = 0;
  536. int z_err = inflateInit2( zlibStream, 15 + 16 /* +16 for gzip */ );
  537. if ( z_err != Z_OK )
  538. {
  539. free( zlibStream );
  540. zlibStream = 0;
  541. }
  542. }
  543. else
  544. {
  545. if ( zlibStream )
  546. {
  547. free( zlibStream );
  548. zlibStream = 0;
  549. }
  550. }
  551. m_http_state = 3;
  552. break;
  553. }
  554. if ( !m_recvheaders )
  555. {
  556. m_recvheaders_size = strlen( buf ) + 1;
  557. if ( m_recvheaders_size == 0 || m_recvheaders_size == (size_t)-1 ) // check for overflow
  558. {
  559. m_http_state = -1;
  560. return HTTPGET_RUN_ERROR;
  561. }
  562. m_recvheaders = (char *)malloc( m_recvheaders_size + 1 );
  563. if ( m_recvheaders )
  564. {
  565. strcpy( m_recvheaders, buf ); // safe because we malloc'd specifically above
  566. m_recvheaders[ m_recvheaders_size ] = 0;
  567. }
  568. else
  569. {
  570. m_http_state = -1;
  571. return HTTPGET_RUN_ERROR;
  572. }
  573. }
  574. else
  575. {
  576. size_t oldsize = m_recvheaders_size;
  577. m_recvheaders_size += strlen( buf ) + 1;
  578. if ( m_recvheaders_size + 1 < oldsize ) // check for overflow
  579. {
  580. m_http_state = -1;
  581. return HTTPGET_RUN_ERROR;
  582. }
  583. char *n = (char *)realloc( m_recvheaders, m_recvheaders_size + 1 );
  584. if ( !n )
  585. {
  586. m_http_state = -1;
  587. return HTTPGET_RUN_ERROR;
  588. }
  589. strcpy( n + oldsize, buf ); // safe because we malloc specifially for the size
  590. n[ m_recvheaders_size ] = 0; // double null terminate
  591. m_recvheaders = n;
  592. }
  593. }
  594. }
  595. return HTTPGET_RUN_OK;
  596. }
  597. int JNL_HTTPGet::get_status() // returns 0 if connecting, 1 if reading headers, 2 if reading content, -1 if error.
  598. {
  599. if ( m_http_state < 0 )
  600. return HTTPGET_STATUS_ERROR;
  601. if ( m_http_state < 2 )
  602. return HTTPGET_STATUS_CONNECTING;
  603. if ( m_http_state == 2 )
  604. return HTTPGET_STATUS_READING_HEADERS;
  605. if ( m_http_state == 3 )
  606. return HTTPGET_STATUS_READING_CONTENT;
  607. return HTTPGET_STATUS_ERROR;
  608. }
  609. int JNL_HTTPGet::getreplycode() // returns 0 if none yet, otherwise returns http reply code.
  610. {
  611. if ( !m_reply )
  612. return 0;
  613. char *p = m_reply;
  614. while ( p && *p && *p != ' ' )
  615. p++; // skip over HTTP/x.x
  616. if ( !p || !*p )
  617. return 0;
  618. return atoi( ++p );
  619. }
  620. size_t JNL_HTTPGet::bytes_available()
  621. {
  622. if ( m_con && m_http_state == 3 )
  623. return m_con->recv_bytes_available();
  624. return 0;
  625. }
  626. size_t JNL_HTTPGet::get_bytes( char *buf, size_t len )
  627. {
  628. if ( m_con && m_http_state == 3 )
  629. {
  630. if ( zlibStream )
  631. {
  632. // TODO: benski> we need to pick a better buffer size
  633. // either alloca() and use the passed in length
  634. // or malloc a buffer based on the constructor-initted buffer size
  635. char temp[ 8192 ] = { 0 };
  636. int size = (int)m_con->peek_bytes( temp, 8192 );
  637. if ( size )
  638. {
  639. zlibStream->next_in = reinterpret_cast<Bytef *>( temp );
  640. zlibStream->avail_in = (uInt)size;
  641. zlibStream->next_out = reinterpret_cast<Bytef *>( buf );
  642. zlibStream->avail_out = (uInt)len;
  643. int zlib_err = inflate( zlibStream, Z_SYNC_FLUSH );
  644. if ( zlib_err == Z_OK || zlib_err == Z_STREAM_END )
  645. {
  646. m_con->recv_bytes( 0, size - zlibStream->avail_in ); // since we only peeked above
  647. return len - zlibStream->avail_out;
  648. }
  649. else
  650. return 0; // TODO: should we do something else here?
  651. }
  652. }
  653. else
  654. return m_con->recv_bytes( buf, len );
  655. }
  656. return 0;
  657. }
  658. size_t JNL_HTTPGet::peek_bytes( char *buf, size_t len )
  659. {
  660. if ( m_con && m_http_state == 3 )
  661. {
  662. if ( zlibStream )
  663. return 0; // TODO: benski> how are we going to do peek_bytes, since the inflater saves state?
  664. else
  665. return m_con->peek_bytes( buf, len );
  666. }
  667. return 0;
  668. }
  669. uint64_t JNL_HTTPGet::content_length()
  670. {
  671. const char *p = getheader( "content-length" );
  672. if ( p && *p )
  673. return strtoull( p, 0, 10 );
  674. else
  675. {
  676. // TODO need to check this further for reliability!
  677. // Helps to handle responses without content-length
  678. if ( m_recvheaders_size > 0 && bytes_available() > 0 )
  679. return bytes_available() - m_recvheaders_size;
  680. }
  681. return 0;
  682. }
  683. void JNL_HTTPGet::seterrstr( const char *str )
  684. {
  685. if ( m_errstr )
  686. free( m_errstr );
  687. m_errstr = _strdup( str );
  688. }
  689. void JNL_HTTPGet::AllowCompression()
  690. {
  691. allowCompression = true;
  692. }
  693. void JNL_HTTPGet::reset_headers()
  694. {
  695. if ( m_sendheaders )
  696. {
  697. free( m_sendheaders );
  698. m_sendheaders = 0;
  699. }
  700. }
  701. void JNL_HTTPGet::set_accept_all_reply_codes()
  702. {
  703. accept_all_reply_codes = true;
  704. }
  705. void JNL_HTTPGet::set_persistent()
  706. {
  707. persistent = true;
  708. }