Main Page   Modules   Class Hierarchy   Compound List   File List   Compound Members   File Members  

httpstream.cpp

00001 /*  Streaming over http tools.
00002  *  (c) Copyright 2001 Eugen Melinte <ame01@gmx.net>
00003  *
00004  * This source code is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Public License as published 
00006  * by the Free Software Foundation; either version 2 of the License,
00007  * or (at your option) any later version.
00008  *
00009  * This source code is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00012  * Please refer to the GNU Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Public License along with
00015  * this source code; if not, write to:
00016  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  *
00018  * "$Id: httpstream.cpp,v 1.3 2004/05/13 16:56:10 eugene Exp $"
00019  *
00020  */
00021 
00022 
00023 #include <config.h>
00024 
00025 #include <unistd.h>
00026 #include <fcntl.h>
00027 #include <sys/errno.h>
00028 #include <sys/socket.h>
00029 #include <sys/time.h>
00030 #include <sys/types.h>
00031 #include <netdb.h>
00032 #include <sys/uio.h>
00033 #include <netinet/in.h>
00034 #include <arpa/inet.h>
00035 #include <sys/un.h>
00036 
00037 #include <pthread.h>
00038 #include <string.h>
00039 #include <stdlib.h>
00040 #include <ctype.h> /*isdigit*/
00041 
00042 #include <httpstream.h>
00043 
00044 #define SOCKET_ERROR    (-1)
00045 #define INVALID_SOCKET  (-1)
00046 
00047 
00048 static int
00049 rread( int fd, void *buf, int len )
00050 {
00051     int ret;
00052     
00053     do {
00054         ret = read(fd, buf, len);
00055     } while (ret==-1 && errno==EINTR);
00056     return ret;
00057 }
00058 
00059 
00060 static int  
00061 rwrite( int fd, const void *buf, int len )
00062 {
00063     int ret;
00064     
00065     do {
00066         ret = write(fd, buf, len);
00067     } while (ret==-1 && errno==EINTR);
00068     return ret;
00069 }
00070 
00071 
00072 static int 
00073 rsend( int sock, const char *buf, int len, int flags )
00074 {
00075     int ret;
00076     
00077     do {
00078         ret = send(sock, buf, len, flags);
00079     } while (ret==-1 && errno==EINTR);
00080     return ret;
00081 }
00082 
00083 
00084 static int  
00085 rrecv( int sock, char *buf, int len, int flags )
00086 {
00087     int ret;
00088     
00089     do {
00090         ret = recv(sock, buf, len, flags);
00091     } while (ret==-1 && errno==EINTR);
00092     return ret;
00093 }
00094 
00095 
00096 
00097 static bool
00098 socket_nonblock( int sock )
00099 {
00100     int file_flags; 
00101     int old_errno = errno;
00102 
00103     /* next call sets errno 38 ENOSYS on unix sockets */
00104     if ( (file_flags = fcntl(sock, F_GETFL, 0)) == -1 ) {
00105         return false;
00106     }
00107     if ( fcntl(sock, F_SETFL, (file_flags | O_NONBLOCK)) ) {
00108         return false;
00109     }
00110 
00111     errno = old_errno;
00112     return true;
00113 }
00114 
00115 
00116 static bool
00117 socket_block( int sock )
00118 {
00119     int file_flags; 
00120     
00121     if ( (file_flags = fcntl(sock, F_GETFL, 0)) == -1 ) {
00122         return false;
00123     }
00124     if ( fcntl(sock, F_SETFL, (file_flags & ~O_NONBLOCK)) ) {
00125         return false;
00126     }
00127     
00128     return true;
00129 }
00130 
00131 
00132 static int
00133 raccept( int s, struct sockaddr *addr, socklen_t *addrlen )
00134 {
00135     int rc;
00136     
00137     do {
00138         rc = accept( s, addr, addrlen );
00139     } while ( rc == -1 && errno == EINTR );
00140     
00141     return rc;
00142 }
00143 
00144 
00145 static int
00146 rconnect( int sock, const struct sockaddr *saddr, socklen_t slen )
00147 {
00148     int rc;
00149     
00150     do {
00151         rc = connect( sock, saddr, slen );
00152     } while ( rc == -1 && errno == EINTR );
00153     
00154     return rc;
00155 }
00156 
00157 
00158 in_addr_t 
00159 host_to_ip( const char* name )
00160 {
00161     struct hostent* hostStruct = NULL;
00162     struct in_addr* hostNode   = NULL;
00163     in_addr_t       ret = (in_addr_t)0;
00164 
00165     /* FIXME: inet_aton */
00166     ret = inet_addr( name );
00167     if ( ret != (in_addr_t)-1 ) 
00168         return ret;
00169 
00170     hostStruct = gethostbyname( name );
00171     if ( NULL == hostStruct )
00172         return( (in_addr_t)0 ); //not found
00173 
00174     /* extract IP */
00175     hostNode = (struct in_addr*) hostStruct->h_addr;
00176     return( hostNode->s_addr );
00177 }
00178 
00179 
00180 int 
00181 fullsend( int sock, const char *buf, int buflen, int flags )
00182 {
00183     int sent = 0, lastsend = 0;
00184 
00185     while (sent < buflen) {
00186         errno = 0;
00187         lastsend  = rsend(sock, buf+sent*sizeof(char), buflen-sent, flags);
00188         if (lastsend < 0) {
00189             /* no need for timeout check, when remote side closes the 
00190                socket, an error will be returned */
00191             if (errno==EAGAIN || errno==EINTR) {
00192                 continue;
00193             } else {
00194                 return sent; 
00195             }
00196         } else if (lastsend == 0) {
00197             return sent;
00198         } else {
00199             sent += lastsend;
00200         }
00201     }
00202 
00203     return sent;
00204 }
00205 
00206 
00207 int 
00208 fullrecv( int sock, char *buf, int buflen, int flags )
00209 {
00210     int recvsofar = 0, lastrecv = 0;
00211 
00212     while (recvsofar < buflen) {
00213         errno = 0;
00214         lastrecv  = rrecv(sock, buf+recvsofar*sizeof(char), buflen-recvsofar, flags);
00215         
00216         /*socket closed*/
00217         if ( lastrecv == 0 )  {
00218             return recvsofar;
00219         } else if (lastrecv < 0) {
00220             return lastrecv; /*<0*/
00221         } else {
00222             recvsofar += lastrecv;
00223         }
00224     }
00225 
00226     return recvsofar;
00227 }
00228 
00229 
00230 /*
00231  * Restart select if call interrupted. 
00232  */
00233 static int 
00234 rselect( int n, fd_set *rds, fd_set *wds, fd_set *eds, struct timeval *tout )
00235 {
00236     int rc;
00237     
00238     do {
00239         rc = select( n, rds, wds, eds, tout );
00240     } while ( rc == -1 && errno == EINTR );
00241     
00242     return rc;
00243 }
00244 
00245 
00246 /* IP specific.  Do not allow options */
00247 static bool
00248 check_ip_options( int sock )
00249 {
00250     int             ipproto;
00251     struct protoent *ip;
00252     unsigned char   optbuf[BUFSIZ/3];
00253     int             optsz = sizeof(optbuf);
00254 
00255     if ( (ip=getprotobyname("ip")) != NULL )
00256         ipproto = ip->p_proto;
00257     else
00258         ipproto = IPPROTO_IP;
00259     
00260     if (  getsockopt(sock, ipproto, IP_OPTIONS, (char*)optbuf, (socklen_t*)&optsz) == 0 
00261        && optsz != 0 ) {
00262        /* turn off options */
00263        if ( setsockopt(sock, ipproto, IP_OPTIONS, NULL, optsz ) != 0 )
00264            return false;
00265     }
00266     
00267     return true;
00268 }
00269 
00270 
00271 static int 
00272 muse_connect( const char* host, int port )
00273 {
00274     int                rsrv = INVALID_SOCKET;
00275     struct sockaddr_in rsrvINETAddress;
00276     struct sockaddr    *rsrvSockAddrPtr = NULL;
00277     int                selret, conret;
00278     struct timeval     tval = {5, 0}; 
00279     fd_set             rset, wset;
00280 
00281     rsrvSockAddrPtr = (struct sockaddr*)&rsrvINETAddress;
00282     memset( (char*)&rsrvINETAddress, 0, sizeof(rsrvINETAddress) );
00283     rsrvINETAddress.sin_family      = AF_INET;
00284     rsrvINETAddress.sin_addr.s_addr = host_to_ip( host );
00285     rsrvINETAddress.sin_port        = htons( port );
00286     
00287     rsrv = socket( AF_INET, SOCK_STREAM, 0 );
00288     if ( !socket_nonblock(rsrv) ) {
00289         close( rsrv );
00290         return INVALID_SOCKET;
00291     }
00292 
00293     conret = rconnect( rsrv, (struct sockaddr*)rsrvSockAddrPtr, sizeof(rsrvINETAddress) ); 
00294     if ( conret == 0 )
00295         goto ok;
00296     
00297     if ( conret < 0 && errno != EINPROGRESS ) {
00298         close( rsrv ); 
00299         return INVALID_SOCKET;
00300     }
00301     errno = 0;
00302     
00303     /* 
00304      * Else wait for connection.  Cannot use cfgst_microsleep 
00305      */
00306      
00307     FD_ZERO( &rset );
00308     FD_SET( rsrv, &rset );
00309     wset = rset; 
00310     selret = rselect( rsrv+1, &rset, &wset, NULL, &tval ); 
00311     if ( selret == 0 ) {
00312         close( rsrv );        /* timeout */
00313         errno = ETIMEDOUT;
00314         return INVALID_SOCKET;
00315     }
00316 
00317     if ( FD_ISSET(rsrv, &rset) || FD_ISSET(rsrv, &wset) ) {
00318         int error=0, optret, len=sizeof(error);
00319         optret = getsockopt( rsrv, SOL_SOCKET, SO_ERROR, &error, (socklen_t*)&len ); 
00320         if ( optret < 0 || error != 0 ) {
00321             /* Solaris pending error */ 
00322             errno = error; 
00323             warning("  SOL_SOCKET SO_ERROR=%d", error); 
00324             /*perror( "cfgst_connect" );*/
00325             close( rsrv );
00326             return INVALID_SOCKET;    
00327         }
00328     } else {
00329         warning("  select error: sockfd not set"); 
00330         close( rsrv );
00331         return INVALID_SOCKET;
00332     }
00333         
00334     if ( !socket_block(rsrv) ) {
00335         close( rsrv );
00336         return INVALID_SOCKET;
00337     }
00338 ok:
00339     notice("Connected to %s:%d", host, port); 
00340     return rsrv;
00341 }
00342 
00343 
00344 static int  
00345 disconnect( int sock )
00346 {
00347     /* 2: send and receives disallowed */
00348     return shutdown(sock, 2);
00349 }
00350 
00351 
00352 #define HTTP_PREFIX "http://"
00353 FILE *
00354 http_open( const char *url )
00355 {
00356     int sock = INVALID_SOCKET;
00357     FILE *fd = NULL;
00358     char *host=NULL, *file=NULL, *p;
00359         int port = 0;
00360         
00361         host = (char*)calloc( 1, strlen(url)+1 ); 
00362         file = (char*)calloc( 1, strlen(url)+1 );
00363         if (!host || !file) goto out;
00364         
00365         p = strstr(url, HTTP_PREFIX); if (!p) goto out;
00366         p += 7;
00367         strcpy(host, p);
00368         
00369         p = strchr(host, '/');
00370         if (p) {
00371             strcpy(file, p);
00372                 *p = '\0';
00373         } else {
00374             *file = '/';
00375         }
00376         
00377         p = strchr(host, ':');
00378         if (p) {
00379             *p++ = '\0';
00380                 port = atoi(p);
00381         } else {
00382             port = 80;
00383         }
00384                 
00385         notice("http_open host='%s', port=%d, file='%s'", host, port, file);
00386         sock = muse_connect(host, port);
00387         if (sock > 0 ) { 
00388                 fd = fdopen(sock, "rb");
00389                 fullsend(sock, "GET ", 4, 0);
00390                 fullsend(sock, file, strlen(file), 0);
00391                 fullsend(sock, " HTTP/1.0\r\n\r\n", 13, 0);
00392         }
00393         
00394 out:
00395     if (host) free(host);
00396         if (file) free(file);
00397     return fd;
00398 }
00399 
00400 
00401 static void
00402 strip_header( FILE *fd )
00403 {
00404     int crlf=0;
00405         while (crlf<2 && !feof(fd)) {
00406           int ch = fgetc(fd);
00407           if (ch == '\n') crlf++;
00408           else if (ch == '\r') /*nothing*/;
00409           else crlf = 0;
00410         }
00411 }
00412 
00413 
00414 FILE *
00415 hopen( const char *url, const char *mode )
00416 {
00417     FILE *fd = NULL;
00418     if (!url) return NULL;
00419         notice("hopen: '%s'", url);
00420     if (strstr(url, HTTP_PREFIX)) {
00421             fd = http_open(url);
00422                 if (fd) {
00423                         strip_header(fd);
00424                         /*if server replies with err message and closes*/
00425                         if (feof(fd)) {fclose(fd); fd=NULL;}
00426                 }
00427                 return fd;
00428     } else {
00429         return fopen(url, mode);
00430         }
00431 }
00432 
00433 
00434 /* return \0 terminated string */
00435 static char *
00436 read_header( FILE *fd )
00437 {
00438     int crlf=0;
00439         int len=128, used=0;
00440         char *header = (char*)calloc(1, 128);
00441         
00442         if (!header) return NULL;
00443         
00444         while (crlf<2 && !feof(fd)) {
00445                 int ch = fgetc(fd);
00446 
00447                 if (ch == '\n') crlf++;
00448                 else if (ch == '\r') /*nothing*/;
00449                 else crlf = 0;
00450             //printf("%c", ch);
00451 
00452                 header[used++] = ch;
00453 
00454                 if (used == len) {
00455                     len *= 2;
00456                         header = (char*)realloc(header, len);
00457                         if (!header) return NULL;
00458                 }
00459         }
00460 
00461     header[used] = '\0';        
00462     return header;
00463 }
00464 
00465 
00466 hstream 
00467 stream_detect( const char *url )
00468 {
00469     FILE *fd;
00470         char *hdr = NULL;
00471         hstream ret = HS_NONE;
00472         
00473         notice("stream_detect: '%d'", ret);
00474     if (!url) return HS_NONE;
00475     if (!strstr(url, HTTP_PREFIX))  return HS_NONE;
00476         if ((fd=http_open(url)) == NULL) return HS_NONE;
00477         
00478         hdr = read_header(fd);
00479         fclose(fd);
00480         if (!hdr) return HS_NONE;
00481         
00482         //FIXME: analyse header
00483         if (strstr(hdr,"application/ogg")) 
00484                 ret = HS_OGG;
00485         else if (strstr(hdr,"SHOUTcast") || strstr(hdr,"ICY 200")) 
00486                 ret = HS_MP3;
00487         notice("stream_detect: '%d'", ret);
00488 
00489         free(hdr);
00490     return ret;
00491 }
00492 

Generated on Thu Dec 16 12:28:21 2004 for MuSE by doxygen1.3