28#if HTTP_LIB == PEBL_HAPPY
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
43 #define vsnprintf _vsnprintf
66const char* GetWinsockErrorString(
int err );
80 int e = WSAGetLastError();
81 const char* msg = GetWinsockErrorString( e );
83 const char* msg = strerror( errno );
85 throw Wobbly(
"%s: %s", context, msg );
91const char* GetWinsockErrorString(
int err )
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";
158 FD_SET( sock, &fds );
164 int r = select( sock+1, &fds,
NULL,
NULL, &tv);
168 if( FD_ISSET( sock, &fds ) )
179 struct hostent *host;
180 static struct in_addr saddr;
183 saddr.s_addr = inet_addr(address);
184 if (saddr.s_addr != -1)
187 host = gethostbyname(address);
189 return (
struct in_addr *) *host->h_addr_list;
230 m_ResponseBeginCB(0),
232 m_ResponseCompleteCB(0),
257 in_addr* addr =
atoaddr( m_Host.c_str() );
259 throw Wobbly(
"Invalid network 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;
267 m_Sock = socket( AF_INET, SOCK_STREAM, 0 );
273 if(
::connect( m_Sock, (sockaddr
const*)&address,
sizeof(address) ) < 0 )
282 ::closesocket( m_Sock );
290 while( !m_Outstanding.empty() )
292 delete m_Outstanding.front();
293 m_Outstanding.pop_front();
305 const char* headers[],
306 const unsigned char* body,
310 bool gotcontentlength =
false;
317 const char** h = headers;
320 const char* name = *h++;
321 const char* value = *h++;
322 assert( value != 0 );
324 if( 0==strcasecmp( name,
"content-length" ) )
325 gotcontentlength =
true;
331 if( body && !gotcontentlength )
336 const char** h = headers;
339 const char* name = *h++;
340 const char* value = *h++;
347 send( body, bodysize );
356 if( m_State != IDLE )
357 throw Wobbly(
"Request already issued" );
359 m_State = REQ_STARTED;
362 sprintf( req,
"%s %s HTTP/1.1", method, url );
363 m_Buffer.push_back( req );
368 putheader(
"Accept-Encoding",
"identity");
372 m_Outstanding.push_back( r );
378 if( m_State != REQ_STARTED )
379 throw Wobbly(
"putheader() failed" );
380 m_Buffer.push_back(
string(header) +
": " +
string( value ) );
386 sprintf( buf,
"%d", numericvalue );
392 if( m_State != REQ_STARTED )
393 throw Wobbly(
"Cannot send header" );
396 m_Buffer.push_back(
"" );
399 vector< string>::const_iterator it;
400 for( it = m_Buffer.begin(); it != m_Buffer.end(); ++it )
401 msg += (*it) +
"\r\n";
406 send( (
const unsigned char*)msg.c_str(), msg.size() );
418 while( numbytes > 0 )
421 int n =
::send( m_Sock, (
const char*)buf, numbytes, 0 );
423 int n =
::send( m_Sock, buf, numbytes, 0 );
435 if( m_Outstanding.empty() )
443 unsigned char buf[ 2048 ];
444 int a = recv( m_Sock, (
char*)buf,
sizeof(buf), 0 );
452 Response* r = m_Outstanding.front();
456 m_Outstanding.pop_front();
464 while( used < a && !m_Outstanding.empty() )
467 Response* r = m_Outstanding.front();
468 int u = r->
pump( &buf[used], a-used );
474 m_Outstanding.pop_front();
499 m_Connection( conn ),
500 m_State( STATUSLINE ),
515 std::string lname( name );
518 std::map< std::string, std::string >::const_iterator it = m_Headers.find( lname );
519 if( it == m_Headers.end() )
522 return it->second.c_str();
529 assert( m_State != STATUSLINE );
537 assert( m_State != STATUSLINE );
538 return m_Reason.c_str();
546 if( m_State == COMPLETE )
550 if( m_State == BODY &&
558 throw Wobbly(
"Connection closed unexpectedly" );
566 assert( datasize != 0 );
567 int count = datasize;
569 while(
count > 0 && m_State != COMPLETE )
571 if( m_State == STATUSLINE ||
572 m_State == HEADERS ||
573 m_State == TRAILERS ||
574 m_State == CHUNKLEN ||
575 m_State == CHUNKEND )
580 char c = (char)*data++;
588 ProcessStatusLine( m_LineBuf );
591 ProcessHeaderLine( m_LineBuf );
594 ProcessTrailerLine( m_LineBuf );
597 ProcessChunkLenLine( m_LineBuf );
601 assert( m_Chunked ==
true );
617 else if( m_State == BODY )
621 bytesused = ProcessDataChunked( data,
count );
623 bytesused = ProcessDataNonChunked( data,
count );
630 return datasize -
count;
635void Response::ProcessChunkLenLine( std::string
const& line )
638 m_ChunkLeft = strtol( line.c_str(),
NULL, 16 );
640 if( m_ChunkLeft == 0 )
644 m_HeaderAccum.clear();
655int Response::ProcessDataChunked(
const unsigned char* data,
int count )
670 assert( m_ChunkLeft >= 0);
671 if( m_ChunkLeft == 0 )
681int Response::ProcessDataNonChunked(
const unsigned char* data,
int count )
687 int remaining = m_Length - m_BytesRead;
699 if( m_Length != -1 && m_BytesRead == m_Length )
706void Response::Finish()
716void Response::ProcessStatusLine( std::string
const& line )
718 const char* p = line.c_str();
721 while( *p && *p ==
' ' )
725 while( *p && *p !=
' ' )
726 m_VersionString += *p++;
727 while( *p && *p ==
' ' )
732 while( *p && *p !=
' ' )
734 while( *p && *p ==
' ' )
741 m_Status = atoi( status.c_str() );
742 if( m_Status < 100 || m_Status > 999 )
743 throw Wobbly(
"BadStatusLine (%s)", line.c_str() );
751 if( m_VersionString ==
"HTTP:/1.0" )
753 else if( 0==m_VersionString.compare( 0,7,
"HTTP/1." ) )
756 throw Wobbly(
"UnknownProtocol (%s)", m_VersionString.c_str() );
762 m_HeaderAccum.clear();
767void Response::FlushHeader()
769 if( m_HeaderAccum.empty() )
772 const char* p = m_HeaderAccum.c_str();
776 while( *p && *p !=
':' )
777 header += tolower( *p++ );
784 while( *p && (*p ==
' ' || *p==
'\t') )
789 m_Headers[ header ] = value;
792 m_HeaderAccum.clear();
796void Response::ProcessHeaderLine( std::string
const& line )
798 const char* p = line.c_str();
806 m_State = STATUSLINE;
816 while( *p && isspace( *p ) )
819 m_HeaderAccum +=
' ';
831void Response::ProcessTrailerLine( std::string
const& line )
846void Response::BeginBody()
854 const char* trenc =
getheader(
"transfer-encoding" );
855 if( trenc && 0==strcasecmp( trenc,
"chunked") )
861 m_WillClose = CheckClose();
864 const char* contentlen =
getheader(
"content-length" );
865 if( contentlen && !m_Chunked )
867 m_Length = atoi( contentlen );
873 ( m_Status >= 100 && m_Status < 200 ) ||
882 if( !m_WillClose && !m_Chunked && m_Length == -1 )
908bool Response::CheckClose()
910 if( m_Version == 11 )
914 const char* conn =
getheader(
"connection" );
915 if( conn && 0==strcasecmp( conn,
"close" ) )
ResponseBegin_CB m_ResponseBeginCB
void request(const char *method, const char *url, const char *headers[]=0, const unsigned char *body=0, int bodysize=0)
ResponseComplete_CB m_ResponseCompleteCB
void putheader(const char *header, const char *value)
Connection(const char *host, int port)
ResponseData_CB m_ResponseDataCB
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 pump(const unsigned char *data, int datasize)
void notifyconnectionclosed()
const char * getheader(const char *name) const
const char * getreason() const
Response(const char *method, Connection &conn)
Wobbly(const char *fmt,...)
void(* ResponseBegin_CB)(const Response *r, void *userdata)
void BailOnSocketError(const char *context)
bool datawaiting(int sock)
void(* ResponseData_CB)(const Response *r, void *userdata, const unsigned char *data, int numbytes)
struct in_addr * atoaddr(const char *address)
void(* ResponseComplete_CB)(const Response *r, void *userdata)