httping.c (6022B)
1#include "httping.h" 2#include <errno.h> 3#include <stdint.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7#include "resolve.h" 8 9#ifndef TCC 10#include <unistd.h> 11#include <sys/types.h> 12#else 13#include "tccheader.h" 14#endif 15 16#if defined( WIN32 ) || defined( WINDOWS ) 17 #ifdef TCC 18 #else 19 #include <ws2tcpip.h> 20 #endif 21 #define SOL_TCP IPPROTO_TCP 22#else 23 #include <sys/socket.h> 24 #include <netinet/in.h> 25 #include <netinet/tcp.h> 26 #include <netdb.h> 27#endif 28 29#include "rawdraw/os_generic.h" 30#include "error_handling.h" 31 32#ifndef MSG_NOSIGNAL 33 #define MSG_NOSIGNAL 0 34#endif 35 36#define HTTPTIMEOUT 3.0 37 38//Callbacks (when started/received) 39void HTTPingCallbackStart( int seqno ); 40void HTTPingCallbackGot( int seqno ); 41 42//Don't dynamically allocate resources here, since execution may be stopped arbitrarily. 43void DoHTTPing( const char * addy, double minperiod, int * seqnoptr, volatile double * timeouttime, int * socketptr, volatile int * getting_host_by_name, const char * device) 44{ 45#if defined(WIN32) || defined(WINDOWS) 46 (void) device; // option is not available for windows. Suppress unused warning. 47 48 WSADATA wsaData; 49 int r = WSAStartup(MAKEWORD(2,2), &wsaData); 50 if( r ) 51 { 52 ERRM( "Fault in WSAStartup\n" ); 53 exit( -2 ); 54 } 55#endif 56 struct sockaddr_in6 serveraddr; 57 socklen_t serveraddr_len; 58 int serverresolve; 59 int httpsock; 60 int addylen = strlen(addy); 61 char hostname[addylen+1]; 62 memcpy( hostname, addy, addylen + 1 ); 63 char * eportmarker = strchr( hostname, ':' ); 64 char * eurl = strchr( hostname, '/' ); 65 66 int portno = 80; 67 68 if( eportmarker ) 69 { 70 portno = atoi( eportmarker+1 ); 71 *eportmarker = 0; 72 } 73 else 74 { 75 if( eurl ) 76 *eurl = 0; 77 } 78 79 /* gethostbyname: get the server's DNS entry */ 80 serveraddr_len = sizeof(serveraddr); 81 *getting_host_by_name = 1; 82 serverresolve = resolveName((struct sockaddr*) &serveraddr, &serveraddr_len, hostname, AF_UNSPEC); 83 *getting_host_by_name = 0; 84 85 if (serverresolve != 1) { 86 ERRMB("ERROR, no such host as \"%s\"\n", hostname); 87 goto fail; 88 } 89 90 /* build the server's Internet address */ 91 serveraddr.sin6_port = htons(portno); 92 93reconnect: 94 *socketptr = httpsock = socket(serveraddr.sin6_family, SOCK_STREAM, 0); 95 if (httpsock < 0) 96 { 97 ERRMB( "Error opening socket\n" ); 98 return; 99 } 100 101 int sockVal = 1; 102 // using char* for sockVal for windows 103 if (setsockopt(httpsock, SOL_TCP, TCP_NODELAY, (char*) &sockVal, 4) != 0) 104 { 105 ERRM( "Error: Failed to set TCP_NODELAY\n"); 106 // not a critical error, we can continue 107 } 108 109#if !defined( WIN32 ) && !defined( WINDOWS ) 110 if(device) 111 { 112 if( setsockopt(httpsock, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device) +1) != 0) 113 { 114 ERRM("Error: Failed to set Device option.\n"); 115 exit( -1 ); 116 } 117 } 118#endif // not windows 119 120 /* connect: create a connection with the server */ 121 if (connect(httpsock, (struct sockaddr*)&serveraddr, serveraddr_len) < 0) 122 { 123 ERRMB( "%s: ERROR connecting: %s (%d)\n", hostname, strerror(errno), errno ); 124 goto fail; 125 } 126 127#ifdef __APPLE__ 128 int opt = 1; 129 setsockopt(httpsock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); 130#endif 131 132 errbuffer[0] = 0; 133 134 while( 1 ) 135 { 136 char buf[8192]; 137 138 int n = sprintf( buf, "HEAD %s HTTP/1.1\r\nConnection: keep-alive\r\nHost: %s\r\n\r\n", eurl?eurl:"/favicon.ico", hostname ); 139 140 (*seqnoptr) ++; 141 HTTPingCallbackStart( *seqnoptr ); 142 143 int rs = send( httpsock, buf, n, MSG_NOSIGNAL ); 144 double starttime = *timeouttime = OGGetAbsoluteTime(); 145 int breakout = 0; 146 if( rs != n ) breakout = 1; 147 int endstate = 0; 148 while( !breakout ) 149 { 150#ifdef WIN32 151 n = recv( httpsock, buf, sizeof(buf)-1, 0); 152#else 153 n = recv( httpsock, buf, sizeof(buf)-1, MSG_PEEK); 154 if( n > 0 ) n = read( httpsock, buf, sizeof(buf)-1); 155 else if( n == 0 ) break; //FIN received 156#endif 157 158 159 if( n < 0 ) return; 160 161 int i; 162 for( i = 0; i < n; i++ ) 163 { 164 char c = buf[i]; 165 switch( endstate ) 166 { 167 case 0: if( c == '\r' ) endstate++; break; 168 case 1: if( c == '\n' ) endstate++; else endstate = 0; break; 169 case 2: if( c == '\r' ) endstate++; else endstate = 0; break; 170 case 3: if( c == '\n' && i == n-1) breakout = 1; else endstate = 0; break; 171 } 172 } 173 } 174 *timeouttime = OGGetAbsoluteTime(); 175 176 HTTPingCallbackGot( *seqnoptr ); 177 178 double delay_time = minperiod - (*timeouttime - starttime); 179 if( delay_time > 0 ) 180 usleep( (int)(delay_time * 1000000) ); 181 if( !breakout ) { 182#ifdef WIN32 183 closesocket( httpsock ); 184#else 185 close( httpsock ); 186#endif 187 goto reconnect; 188 } 189 } 190fail: 191 return; 192} 193 194 195struct HTTPPingLaunch 196{ 197 const char * addy; 198 double minperiod; 199 const char * device; 200 201 volatile double timeout_time; 202 volatile int failed; 203 int seqno; 204 int socket; 205 volatile int getting_host_by_name; 206}; 207 208static void * DeployPing( void * v ) 209{ 210 struct HTTPPingLaunch *hpl = (struct HTTPPingLaunch*)v; 211 hpl->socket = 0; 212 hpl->getting_host_by_name = 0; 213 DoHTTPing( hpl->addy, hpl->minperiod, &hpl->seqno, &hpl->timeout_time, &hpl->socket, &hpl->getting_host_by_name, hpl->device ); 214 hpl->failed = 1; 215 return 0; 216} 217 218 219static void * PingRunner( void * v ) 220{ 221 struct HTTPPingLaunch *hpl = (struct HTTPPingLaunch*)v; 222 hpl->seqno = 0; 223 while( 1 ) 224 { 225 hpl->timeout_time = OGGetAbsoluteTime(); 226 og_thread_t thd = OGCreateThread( DeployPing, hpl ); 227 while( 1 ) 228 { 229 double Now = OGGetAbsoluteTime(); 230 double usl = hpl->timeout_time - Now + HTTPTIMEOUT; 231 if( usl > 0 ) usleep( (int)(usl*1000000 + 1000)); 232 else usleep( 10000 ); 233 234 if( hpl->timeout_time + HTTPTIMEOUT < Now && !hpl->getting_host_by_name ) //Can't terminate in the middle of a gethostbyname operation otherwise bad things can happen. 235 { 236 if( hpl->socket ) 237 { 238#ifdef WIN32 239 closesocket( hpl->socket ); 240#else 241 close( hpl->socket ); 242#endif 243 } 244 245 OGCancelThread( thd ); 246 break; 247 } 248 } 249 } 250 return 0; 251} 252 253int StartHTTPing( const char * addy, double minperiod, const char * device) 254{ 255 struct HTTPPingLaunch *hpl = malloc( sizeof( struct HTTPPingLaunch ) ); 256 hpl->addy = addy; 257 hpl->minperiod = minperiod; 258 hpl->device = device; 259 OGCreateThread( PingRunner, hpl ); 260 return 0; 261} 262 263 264