PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
happyhttp.cpp
Go to the documentation of this file.
1/*
2 * HappyHTTP - a simple HTTP library
3 * Version 0.1
4 *
5 * Copyright (c) 2006 Ben Campbell
6 *
7 * This software is provided 'as-is', without any express or implied
8 * warranty. In no event will the authors be held liable for any damages
9 * arising from the use of this software.
10 *
11 * Permission is granted to anyone to use this software for any purpose,
12 * including commercial applications, and to alter it and redistribute it
13 * freely, subject to the following restrictions:
14 *
15 * 1. The origin of this software must not be misrepresented; you must not
16 * claim that you wrote the original software. If you use this software in a
17 * product, an acknowledgment in the product documentation would be
18 * appreciated but is not required.
19 *
20 * 2. Altered source versions must be plainly marked as such, and must not
21 * be misrepresented as being the original software.
22 *
23 * 3. This notice may not be removed or altered from any source distribution.
24 *
25 */
26
27
28#if HTTP_LIB == PEBL_HAPPY
29
30#include "happyhttp.h"
31
32#ifndef WIN32
33// #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <netdb.h> // for gethostbyname()
38 #include <errno.h>
39#endif
40
41#ifdef WIN32
42 #include <winsock2.h>
43 #define vsnprintf _vsnprintf
44#endif
45
46#include <cstdio>
47#include <cstring>
48#include <cstdarg>
49#include <assert.h>
50
51#include <string>
52#include <vector>
53#include <string>
54#include <algorithm>
55
56
57
58
59using namespace std;
60
61
62namespace happyhttp
63{
64
65#ifdef WIN32
66const char* GetWinsockErrorString( int err );
67#endif
68
69
70//---------------------------------------------------------------------
71// Helper functions
72//---------------------------------------------------------------------
73
74
75
76void BailOnSocketError( const char* context )
77{
78#ifdef WIN32
79
80 int e = WSAGetLastError();
81 const char* msg = GetWinsockErrorString( e );
82#else
83 const char* msg = strerror( errno );
84#endif
85 throw Wobbly( "%s: %s", context, msg );
86}
87
88
89#ifdef WIN32
90
91const char* GetWinsockErrorString( int err )
92{
93 switch( err)
94 {
95 case 0: return "No error";
96 case WSAEINTR: return "Interrupted system call";
97 case WSAEBADF: return "Bad file number";
98 case WSAEACCES: return "Permission denied";
99 case WSAEFAULT: return "Bad address";
100 case WSAEINVAL: return "Invalid argument";
101 case WSAEMFILE: return "Too many open sockets";
102 case WSAEWOULDBLOCK: return "Operation would block";
103 case WSAEINPROGRESS: return "Operation now in progress";
104 case WSAEALREADY: return "Operation already in progress";
105 case WSAENOTSOCK: return "Socket operation on non-socket";
106 case WSAEDESTADDRREQ: return "Destination address required";
107 case WSAEMSGSIZE: return "Message too long";
108 case WSAEPROTOTYPE: return "Protocol wrong type for socket";
109 case WSAENOPROTOOPT: return "Bad protocol option";
110 case WSAEPROTONOSUPPORT: return "Protocol not supported";
111 case WSAESOCKTNOSUPPORT: return "Socket type not supported";
112 case WSAEOPNOTSUPP: return "Operation not supported on socket";
113 case WSAEPFNOSUPPORT: return "Protocol family not supported";
114 case WSAEAFNOSUPPORT: return "Address family not supported";
115 case WSAEADDRINUSE: return "Address already in use";
116 case WSAEADDRNOTAVAIL: return "Can't assign requested address";
117 case WSAENETDOWN: return "Network is down";
118 case WSAENETUNREACH: return "Network is unreachable";
119 case WSAENETRESET: return "Net connection reset";
120 case WSAECONNABORTED: return "Software caused connection abort";
121 case WSAECONNRESET: return "Connection reset by peer";
122 case WSAENOBUFS: return "No buffer space available";
123 case WSAEISCONN: return "Socket is already connected";
124 case WSAENOTCONN: return "Socket is not connected";
125 case WSAESHUTDOWN: return "Can't send after socket shutdown";
126 case WSAETOOMANYREFS: return "Too many references, can't splice";
127 case WSAETIMEDOUT: return "Connection timed out";
128 case WSAECONNREFUSED: return "Connection refused";
129 case WSAELOOP: return "Too many levels of symbolic links";
130 case WSAENAMETOOLONG: return "File name too long";
131 case WSAEHOSTDOWN: return "Host is down";
132 case WSAEHOSTUNREACH: return "No route to host";
133 case WSAENOTEMPTY: return "Directory not empty";
134 case WSAEPROCLIM: return "Too many processes";
135 case WSAEUSERS: return "Too many users";
136 case WSAEDQUOT: return "Disc quota exceeded";
137 case WSAESTALE: return "Stale NFS file handle";
138 case WSAEREMOTE: return "Too many levels of remote in path";
139 case WSASYSNOTREADY: return "Network system is unavailable";
140 case WSAVERNOTSUPPORTED: return "Winsock version out of range";
141 case WSANOTINITIALISED: return "WSAStartup not yet called";
142 case WSAEDISCON: return "Graceful shutdown in progress";
143 case WSAHOST_NOT_FOUND: return "Host not found";
144 case WSANO_DATA: return "No host data of that type was found";
145 }
146
147 return "unknown";
148};
149
150#endif // WIN32
151
152
153// return true if socket has data waiting to be read
154bool datawaiting( int sock )
155{
156 fd_set fds;
157 FD_ZERO( &fds );
158 FD_SET( sock, &fds );
159
160 struct timeval tv;
161 tv.tv_sec = 0;
162 tv.tv_usec = 0;
163
164 int r = select( sock+1, &fds, NULL, NULL, &tv);
165 if (r < 0)
166 BailOnSocketError( "select" );
167
168 if( FD_ISSET( sock, &fds ) )
169 return true;
170 else
171 return false;
172}
173
174
175// Try to work out address from string
176// returns 0 if bad
177struct in_addr *atoaddr( const char* address)
178{
179 struct hostent *host;
180 static struct in_addr saddr;
181
182 // First try nnn.nnn.nnn.nnn form
183 saddr.s_addr = inet_addr(address);
184 if (saddr.s_addr != -1)
185 return &saddr;
186
187 host = gethostbyname(address);
188 if( host )
189 return (struct in_addr *) *host->h_addr_list;
190
191 return 0;
192}
193
194
195
196
197
198
199
200//---------------------------------------------------------------------
201//
202// Exception class
203//
204//---------------------------------------------------------------------
205
206
207Wobbly::Wobbly( const char* fmt, ... )
208{
209 va_list ap;
210 va_start( ap,fmt);
211 int n = vsnprintf( m_Message, MAXLEN, fmt, ap );
212 va_end( ap );
213 if(n==MAXLEN)
214 m_Message[MAXLEN-1] = '\0';
215}
216
217
218
219
220
221
222
223
224//---------------------------------------------------------------------
225//
226// Connection
227//
228//---------------------------------------------------------------------
229Connection::Connection( const char* host, int port ) :
230 m_ResponseBeginCB(0),
231 m_ResponseDataCB(0),
232 m_ResponseCompleteCB(0),
233 m_UserData(0),
234 m_State( IDLE ),
235 m_Host( host ),
236 m_Port( port ),
237 m_Sock(-1)
238{
239}
240
241
243 ResponseBegin_CB begincb,
244 ResponseData_CB datacb,
245 ResponseComplete_CB completecb,
246 void* userdata )
247{
248 m_ResponseBeginCB = begincb;
249 m_ResponseDataCB = datacb;
250 m_ResponseCompleteCB = completecb;
251 m_UserData = userdata;
252}
253
254
256{
257 in_addr* addr = atoaddr( m_Host.c_str() );
258 if( !addr )
259 throw Wobbly( "Invalid network address" );
260
261 sockaddr_in address;
262 memset( (char*)&address, 0, sizeof(address) );
263 address.sin_family = AF_INET;
264 address.sin_port = htons( m_Port );
265 address.sin_addr.s_addr = addr->s_addr;
266
267 m_Sock = socket( AF_INET, SOCK_STREAM, 0 );
268 if( m_Sock < 0 )
269 BailOnSocketError( "socket()" );
270
271// printf("Connecting to %s on port %d.\n",inet_ntoa(*addr), port);
272
273 if( ::connect( m_Sock, (sockaddr const*)&address, sizeof(address) ) < 0 )
274 BailOnSocketError( "connect()" );
275}
276
277
279{
280#ifdef WIN32
281 if( m_Sock >= 0 )
282 ::closesocket( m_Sock );
283#else
284 if( m_Sock >= 0 )
285 ::close( m_Sock );
286#endif
287 m_Sock = -1;
288
289 // discard any incomplete responses
290 while( !m_Outstanding.empty() )
291 {
292 delete m_Outstanding.front();
293 m_Outstanding.pop_front();
294 }
295}
296
297
302
303void Connection::request( const char* method,
304 const char* url,
305 const char* headers[],
306 const unsigned char* body,
307 int bodysize )
308{
309
310 bool gotcontentlength = false; // already in headers?
311
312 // check headers for content-length
313 // TODO: check for "Host" and "Accept-Encoding" too
314 // and avoid adding them ourselves in putrequest()
315 if( headers )
316 {
317 const char** h = headers;
318 while( *h )
319 {
320 const char* name = *h++;
321 const char* value = *h++;
322 assert( value != 0 ); // name with no value!
323
324 if( 0==strcasecmp( name, "content-length" ) )
325 gotcontentlength = true;
326 }
327 }
328
329 putrequest( method, url );
330
331 if( body && !gotcontentlength )
332 putheader( "Content-Length", bodysize );
333
334 if( headers )
335 {
336 const char** h = headers;
337 while( *h )
338 {
339 const char* name = *h++;
340 const char* value = *h++;
341 putheader( name, value );
342 }
343 }
344 endheaders();
345
346 if( body )
347 send( body, bodysize );
348
349}
350
351
352
353
354void Connection::putrequest( const char* method, const char* url )
355{
356 if( m_State != IDLE )
357 throw Wobbly( "Request already issued" );
358
359 m_State = REQ_STARTED;
360
361 char req[ 512 ];
362 sprintf( req, "%s %s HTTP/1.1", method, url );
363 m_Buffer.push_back( req );
364
365 putheader( "Host", m_Host.c_str() ); // required for HTTP1.1
366
367 // don't want any fancy encodings please
368 putheader("Accept-Encoding", "identity");
369
370 // Push a new response onto the queue
371 Response *r = new Response( method, *this );
372 m_Outstanding.push_back( r );
373}
374
375
376void Connection::putheader( const char* header, const char* value )
377{
378 if( m_State != REQ_STARTED )
379 throw Wobbly( "putheader() failed" );
380 m_Buffer.push_back( string(header) + ": " + string( value ) );
381}
382
383void Connection::putheader( const char* header, int numericvalue )
384{
385 char buf[32];
386 sprintf( buf, "%d", numericvalue );
387 putheader( header, buf );
388}
389
391{
392 if( m_State != REQ_STARTED )
393 throw Wobbly( "Cannot send header" );
394 m_State = IDLE;
395
396 m_Buffer.push_back( "" );
397
398 string msg;
399 vector< string>::const_iterator it;
400 for( it = m_Buffer.begin(); it != m_Buffer.end(); ++it )
401 msg += (*it) + "\r\n";
402
403 m_Buffer.clear();
404
405// printf( "%s", msg.c_str() );
406 send( (const unsigned char*)msg.c_str(), msg.size() );
407}
408
409
410
411void Connection::send( const unsigned char* buf, int numbytes )
412{
413// fwrite( buf, 1,numbytes, stdout );
414
415 if( m_Sock < 0 )
416 connect();
417
418 while( numbytes > 0 )
419 {
420#ifdef WIN32
421 int n = ::send( m_Sock, (const char*)buf, numbytes, 0 );
422#else
423 int n = ::send( m_Sock, buf, numbytes, 0 );
424#endif
425 if( n<0 )
426 BailOnSocketError( "send()" );
427 numbytes -= n;
428 buf += n;
429 }
430}
431
432
434{
435 if( m_Outstanding.empty() )
436 return; // no requests outstanding
437
438 assert( m_Sock >0 ); // outstanding requests but no connection!
439
440 if( !datawaiting( m_Sock ) )
441 return; // recv will block
442
443 unsigned char buf[ 2048 ];
444 int a = recv( m_Sock, (char*)buf, sizeof(buf), 0 );
445 if( a<0 )
446 BailOnSocketError( "recv()" );
447
448 if( a== 0 )
449 {
450 // connection has closed
451
452 Response* r = m_Outstanding.front();
454 assert( r->completed() );
455 delete r;
456 m_Outstanding.pop_front();
457
458 // any outstanding requests will be discarded
459 close();
460 }
461 else
462 {
463 int used = 0;
464 while( used < a && !m_Outstanding.empty() )
465 {
466
467 Response* r = m_Outstanding.front();
468 int u = r->pump( &buf[used], a-used );
469
470 // delete response once completed
471 if( r->completed() )
472 {
473 delete r;
474 m_Outstanding.pop_front();
475 }
476 used += u;
477 }
478
479 // NOTE: will lose bytes if response queue goes empty
480 // (but server shouldn't be sending anything if we don't have
481 // anything outstanding anyway)
482 assert( used == a ); // all bytes should be used up by here.
483 }
484}
485
486
487
488
489
490
491//---------------------------------------------------------------------
492//
493// Response
494//
495//---------------------------------------------------------------------
496
497
498Response::Response( const char* method, Connection& conn ) :
499 m_Connection( conn ),
500 m_State( STATUSLINE ),
501 m_Method( method ),
502 m_Version( 0 ),
503 m_Status(0),
504 m_BytesRead(0),
505 m_Chunked(false),
506 m_ChunkLeft(0),
507 m_Length(-1),
508 m_WillClose(false)
509{
510}
511
512
513const char* Response::getheader( const char* name ) const
514{
515 std::string lname( name );
516// std::transform( lname.begin(), lname.end(), lname.begin(), tolower );
517
518 std::map< std::string, std::string >::const_iterator it = m_Headers.find( lname );
519 if( it == m_Headers.end() )
520 return 0;
521 else
522 return it->second.c_str();
523}
524
525
527{
528 // only valid once we've got the statusline
529 assert( m_State != STATUSLINE );
530 return m_Status;
531}
532
533
534const char* Response::getreason() const
535{
536 // only valid once we've got the statusline
537 assert( m_State != STATUSLINE );
538 return m_Reason.c_str();
539}
540
541
542
543// Connection has closed
545{
546 if( m_State == COMPLETE )
547 return;
548
549 // eof can be valid...
550 if( m_State == BODY &&
551 !m_Chunked &&
552 m_Length == -1 )
553 {
554 Finish(); // we're all done!
555 }
556 else
557 {
558 throw Wobbly( "Connection closed unexpectedly" );
559 }
560}
561
562
563
564int Response::pump( const unsigned char* data, int datasize )
565{
566 assert( datasize != 0 );
567 int count = datasize;
568
569 while( count > 0 && m_State != COMPLETE )
570 {
571 if( m_State == STATUSLINE ||
572 m_State == HEADERS ||
573 m_State == TRAILERS ||
574 m_State == CHUNKLEN ||
575 m_State == CHUNKEND )
576 {
577 // we want to accumulate a line
578 while( count > 0 )
579 {
580 char c = (char)*data++;
581 --count;
582 if( c == '\n' )
583 {
584 // now got a whole line!
585 switch( m_State )
586 {
587 case STATUSLINE:
588 ProcessStatusLine( m_LineBuf );
589 break;
590 case HEADERS:
591 ProcessHeaderLine( m_LineBuf );
592 break;
593 case TRAILERS:
594 ProcessTrailerLine( m_LineBuf );
595 break;
596 case CHUNKLEN:
597 ProcessChunkLenLine( m_LineBuf );
598 break;
599 case CHUNKEND:
600 // just soak up the crlf after body and go to next state
601 assert( m_Chunked == true );
602 m_State = CHUNKLEN;
603 break;
604 default:
605 break;
606 }
607 m_LineBuf.clear();
608 break; // break out of line accumulation!
609 }
610 else
611 {
612 if( c != '\r' ) // just ignore CR
613 m_LineBuf += c;
614 }
615 }
616 }
617 else if( m_State == BODY )
618 {
619 int bytesused = 0;
620 if( m_Chunked )
621 bytesused = ProcessDataChunked( data, count );
622 else
623 bytesused = ProcessDataNonChunked( data, count );
624 data += bytesused;
625 count -= bytesused;
626 }
627 }
628
629 // return number of bytes used
630 return datasize - count;
631}
632
633
634
635void Response::ProcessChunkLenLine( std::string const& line )
636{
637 // chunklen in hex at beginning of line
638 m_ChunkLeft = strtol( line.c_str(), NULL, 16 );
639
640 if( m_ChunkLeft == 0 )
641 {
642 // got the whole body, now check for trailing headers
643 m_State = TRAILERS;
644 m_HeaderAccum.clear();
645 }
646 else
647 {
648 m_State = BODY;
649 }
650}
651
652
653// handle some body data in chunked mode
654// returns number of bytes used.
655int Response::ProcessDataChunked( const unsigned char* data, int count )
656{
657 assert( m_Chunked );
658
659 int n = count;
660 if( n>m_ChunkLeft )
661 n = m_ChunkLeft;
662
663 // invoke callback to pass out the data
664 if( m_Connection.m_ResponseDataCB )
665 (m_Connection.m_ResponseDataCB)( this, m_Connection.m_UserData, data, n );
666
667 m_BytesRead += n;
668
669 m_ChunkLeft -= n;
670 assert( m_ChunkLeft >= 0);
671 if( m_ChunkLeft == 0 )
672 {
673 // chunk completed! now soak up the trailing CRLF before next chunk
674 m_State = CHUNKEND;
675 }
676 return n;
677}
678
679// handle some body data in non-chunked mode.
680// returns number of bytes used.
681int Response::ProcessDataNonChunked( const unsigned char* data, int count )
682{
683 int n = count;
684 if( m_Length != -1 )
685 {
686 // we know how many bytes to expect
687 int remaining = m_Length - m_BytesRead;
688 if( n > remaining )
689 n = remaining;
690 }
691
692 // invoke callback to pass out the data
693 if( m_Connection.m_ResponseDataCB )
694 (m_Connection.m_ResponseDataCB)( this, m_Connection.m_UserData, data, n );
695
696 m_BytesRead += n;
697
698 // Finish if we know we're done. Else we're waiting for connection close.
699 if( m_Length != -1 && m_BytesRead == m_Length )
700 Finish();
701
702 return n;
703}
704
705
706void Response::Finish()
707{
708 m_State = COMPLETE;
709
710 // invoke the callback
711 if( m_Connection.m_ResponseCompleteCB )
712 (m_Connection.m_ResponseCompleteCB)( this, m_Connection.m_UserData );
713}
714
715
716void Response::ProcessStatusLine( std::string const& line )
717{
718 const char* p = line.c_str();
719
720 // skip any leading space
721 while( *p && *p == ' ' )
722 ++p;
723
724 // get version
725 while( *p && *p != ' ' )
726 m_VersionString += *p++;
727 while( *p && *p == ' ' )
728 ++p;
729
730 // get status code
731 std::string status;
732 while( *p && *p != ' ' )
733 status += *p++;
734 while( *p && *p == ' ' )
735 ++p;
736
737 // rest of line is reason
738 while( *p )
739 m_Reason += *p++;
740
741 m_Status = atoi( status.c_str() );
742 if( m_Status < 100 || m_Status > 999 )
743 throw Wobbly( "BadStatusLine (%s)", line.c_str() );
744
745/*
746 printf( "version: '%s'\n", m_VersionString.c_str() );
747 printf( "status: '%d'\n", m_Status );
748 printf( "reason: '%s'\n", m_Reason.c_str() );
749*/
750
751 if( m_VersionString == "HTTP:/1.0" )
752 m_Version = 10;
753 else if( 0==m_VersionString.compare( 0,7,"HTTP/1." ) )
754 m_Version = 11;
755 else
756 throw Wobbly( "UnknownProtocol (%s)", m_VersionString.c_str() );
757 // TODO: support for HTTP/0.9
758
759
760 // OK, now we expect headers!
761 m_State = HEADERS;
762 m_HeaderAccum.clear();
763}
764
765
766// process accumulated header data
767void Response::FlushHeader()
768{
769 if( m_HeaderAccum.empty() )
770 return; // no flushing required
771
772 const char* p = m_HeaderAccum.c_str();
773
774 std::string header;
775 std::string value;
776 while( *p && *p != ':' )
777 header += tolower( *p++ );
778
779 // skip ':'
780 if( *p )
781 ++p;
782
783 // skip space
784 while( *p && (*p ==' ' || *p=='\t') )
785 ++p;
786
787 value = p; // rest of line is value
788
789 m_Headers[ header ] = value;
790// printf("header: ['%s': '%s']\n", header.c_str(), value.c_str() );
791
792 m_HeaderAccum.clear();
793}
794
795
796void Response::ProcessHeaderLine( std::string const& line )
797{
798 const char* p = line.c_str();
799 if( line.empty() )
800 {
801 FlushHeader();
802 // end of headers
803
804 // HTTP code 100 handling (we ignore 'em)
805 if( m_Status == CONTINUE )
806 m_State = STATUSLINE; // reset parsing, expect new status line
807 else
808 BeginBody(); // start on body now!
809 return;
810 }
811
812 if( isspace(*p) )
813 {
814 // it's a continuation line - just add it to previous data
815 ++p;
816 while( *p && isspace( *p ) )
817 ++p;
818
819 m_HeaderAccum += ' ';
820 m_HeaderAccum += p;
821 }
822 else
823 {
824 // begin a new header
825 FlushHeader();
826 m_HeaderAccum = p;
827 }
828}
829
830
831void Response::ProcessTrailerLine( std::string const& line )
832{
833 // TODO: handle trailers?
834 // (python httplib doesn't seem to!)
835 if( line.empty() )
836 Finish();
837
838 // just ignore all the trailers...
839}
840
841
842
843// OK, we've now got all the headers read in, so we're ready to start
844// on the body. But we need to see what info we can glean from the headers
845// first...
846void Response::BeginBody()
847{
848
849 m_Chunked = false;
850 m_Length = -1; // unknown
851 m_WillClose = false;
852
853 // using chunked encoding?
854 const char* trenc = getheader( "transfer-encoding" );
855 if( trenc && 0==strcasecmp( trenc, "chunked") )
856 {
857 m_Chunked = true;
858 m_ChunkLeft = -1; // unknown
859 }
860
861 m_WillClose = CheckClose();
862
863 // length supplied?
864 const char* contentlen = getheader( "content-length" );
865 if( contentlen && !m_Chunked )
866 {
867 m_Length = atoi( contentlen );
868 }
869
870 // check for various cases where we expect zero-length body
871 if( m_Status == NO_CONTENT ||
872 m_Status == NOT_MODIFIED ||
873 ( m_Status >= 100 && m_Status < 200 ) || // 1xx codes have no body
874 m_Method == "HEAD" )
875 {
876 m_Length = 0;
877 }
878
879
880 // if we're not using chunked mode, and no length has been specified,
881 // assume connection will close at end.
882 if( !m_WillClose && !m_Chunked && m_Length == -1 )
883 m_WillClose = true;
884
885
886
887 // Invoke the user callback, if any
888 if( m_Connection.m_ResponseBeginCB )
889 (m_Connection.m_ResponseBeginCB)( this, m_Connection.m_UserData );
890
891/*
892 printf("---------BeginBody()--------\n");
893 printf("Length: %d\n", m_Length );
894 printf("WillClose: %d\n", (int)m_WillClose );
895 printf("Chunked: %d\n", (int)m_Chunked );
896 printf("ChunkLeft: %d\n", (int)m_ChunkLeft );
897 printf("----------------------------\n");
898*/
899 // now start reading body data!
900 if( m_Chunked )
901 m_State = CHUNKLEN;
902 else
903 m_State = BODY;
904}
905
906
907// return true if we think server will automatically close connection
908bool Response::CheckClose()
909{
910 if( m_Version == 11 )
911 {
912 // HTTP1.1
913 // the connection stays open unless "connection: close" is specified.
914 const char* conn = getheader( "connection" );
915 if( conn && 0==strcasecmp( conn, "close" ) )
916 return true;
917 else
918 return false;
919 }
920
921 // Older HTTP
922 // keep-alive header indicates persistant connection
923 if( getheader( "keep-alive" ) )
924 return false;
925
926 // TODO: some special case handling for Akamai and netscape maybe?
927 // (see _check_close() in python httplib.py for details)
928
929 return true;
930}
931
932
933
934} // end namespace happyhttp
935
936
937#endif
#define NULL
Definition BinReloc.cpp:317
ResponseBegin_CB m_ResponseBeginCB
Definition happyhttp.h:217
friend class Response
Definition happyhttp.h:147
void request(const char *method, const char *url, const char *headers[]=0, const unsigned char *body=0, int bodysize=0)
ResponseComplete_CB m_ResponseCompleteCB
Definition happyhttp.h:219
void putheader(const char *header, const char *value)
Connection(const char *host, int port)
ResponseData_CB m_ResponseDataCB
Definition happyhttp.h:218
void setcallbacks(ResponseBegin_CB begincb, ResponseData_CB datacb, ResponseComplete_CB completecb, void *userdata)
void send(const unsigned char *buf, int numbytes)
void putrequest(const char *method, const char *url)
int getstatus() const
int pump(const unsigned char *data, int datasize)
bool completed() const
Definition happyhttp.h:252
void notifyconnectionclosed()
const char * getheader(const char *name) const
const char * getreason() const
Response(const char *method, Connection &conn)
char m_Message[MAXLEN]
Definition happyhttp.h:133
Wobbly(const char *fmt,...)
void(* ResponseBegin_CB)(const Response *r, void *userdata)
Definition happyhttp.h:54
void BailOnSocketError(const char *context)
Definition happyhttp.cpp:76
bool datawaiting(int sock)
void(* ResponseData_CB)(const Response *r, void *userdata, const unsigned char *data, int numbytes)
Definition happyhttp.h:55
@ NOT_MODIFIED
Definition happyhttp.h:82
struct in_addr * atoaddr(const char *address)
void(* ResponseComplete_CB)(const Response *r, void *userdata)
Definition happyhttp.h:56
int count
Definition test.cpp:12