shithub: opusfile

Download patch

ref: 9c097eeeeac3f38d28d10f7d8c2c778d7e41c554
parent: 218518dcbd25df4dd97445431ec84ccc7606e952
author: Ralph Giles <giles@mozilla.com>
date: Tue Nov 27 04:36:00 EST 2012

Initial winsock support patch from nu774.

Some tweak might be still needed to take care of OPENSSL_AppLink
to get https support working. In win32, user application of openssl
is required to include openssl/applink.c or something, when openssl
is compiled with OPENSSL_USE_APPLINK.

I don't know how it should be taken care of, from the library point of
view (it must be done by user of libopusfile, since openssl always
searches that function in executable module).

Posted to the hydrogenaudio format 2012 November 19.
http://www.hydrogenaudio.org/forums/index.php?s=&showtopic=97856&view=findpost&p=814582

--- a/Makefile.am
+++ b/Makefile.am
@@ -12,7 +12,13 @@
 	src/http.c src/info.c \
 	src/internal.c src/internal.h \
 	src/opusfile.c src/stream.c
+if OS_WIN32
+libopusfile_la_SOURCES += src/win32/wsockwrapper.c
+endif
 libopusfile_la_LIBADD = $(DEPS_LIBS)
+if OS_WIN32
+libopusfile_la_LIBADD += -lws2_32
+endif
 libopusfile_la_LDFLAGS = -no-undefined \
  -version-info @OP_LT_CURRENT@:@OP_LT_REVISION@:@OP_LT_AGE@
 
--- a/configure.ac
+++ b/configure.ac
@@ -41,12 +41,12 @@
   AS_HELP_STRING([--disable-http], [Disable HTTP support]),,
   enable_http=yes)
 
-AS_IF([test "x$enable_http" != "xno"],
-  AC_CHECK_HEADER([sys/socket.h],,
-    AC_MSG_WARN([HTTP support requires a posix socket library.])
-    enable_http=no
-  )
-)
+# AS_IF([test "x$enable_http" != "xno"],
+#   AC_CHECK_HEADER([sys/socket.h],,
+#     AC_MSG_WARN([HTTP support requires a posix socket library.])
+#     enable_http=no
+#   )
+# )
 
 AS_IF([test "x$enable_http" != "xno"], [
    openssl="openssl"
@@ -92,8 +92,10 @@
   *-mingw*)
     # -std=c89 causes some warnings under mingw.
     CC_CHECK_CFLAGS_APPEND([-U__STRICT_ANSI__])
+    host_mingw=true
     ;;
 esac
+AM_CONDITIONAL(OS_WIN32, test "x$host_mingw" = xtrue)
 
 dnl Check for doxygen
 AC_ARG_ENABLE([doc],
--- a/src/http.c
+++ b/src/http.c
@@ -205,6 +205,7 @@
 }
 
 #if defined(OP_ENABLE_HTTP)
+#ifndef _WIN32
 # include <sys/ioctl.h>
 # include <sys/types.h>
 # include <sys/socket.h>
@@ -216,6 +217,26 @@
 # include <netdb.h>
 # include <poll.h>
 # include <unistd.h>
+#define ERRNO() errno
+#define CLOSE(fd) close(fd)
+#define IOCTL(fd,req,...) ioctl(fd,req,__VA_ARGS__)
+#define GETSOCKOPT(fd,lvl,name,val,len) getsockopt(fd,lvl,name,val,len)
+#define SETSOCKOPT(fd,lvl,name,val,len) getsockopt(fd,lvl,name,val,len)
+#define FTIME(x) ftime(x)
+#else /* _WIN32 */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include "win32/winerrno.h"
+#include "win32/wsockwrapper.h"
+#define ERRNO() (WSAGetLastError() - WSABASEERR)
+#define CLOSE(x) closesocket(x)
+#define IOCTL(fd,req,arg) ioctlsocket(fd,req,(u_long*)(arg))
+#define GETSOCKOPT(fd,lvl,name,val,len) \
+    getsockopt(fd,lvl,name,(char*)(val),len)
+#define SETSOCKOPT(fd,lvl,name,val,len) \
+    setsockopt(fd,lvl,name,(const char*)(val),len)
+#define FTIME(x) win32_ftime(x)
+#endif /* _WIN32 */
 # include <openssl/ssl.h>
 # include <openssl/x509v3.h>
 
@@ -581,7 +602,9 @@
   char             service[6];
   memset(&hints,0,sizeof(hints));
   hints.ai_socktype=SOCK_STREAM;
+#ifndef _WIN32
   hints.ai_flags=AI_NUMERICSERV;
+#endif
   OP_ASSERT(_port<=65535U);
   sprintf(service,"%u",_port);
   if(OP_LIKELY(!getaddrinfo(_host,service,&hints,&addrs)))return addrs;
@@ -589,6 +612,7 @@
 }
 
 static int op_sock_set_nonblocking(int _fd,int _nonblocking){
+#ifndef _WIN32
   int flags;
   flags=fcntl(_fd,F_GETFL);
   if(OP_UNLIKELY(flags<0))return flags;
@@ -595,6 +619,9 @@
   if(_nonblocking)flags|=O_NONBLOCK;
   else flags&=~O_NONBLOCK;
   return fcntl(_fd,F_SETFL,flags);
+#else
+  return IOCTL(_fd, FIONBIO, &_nonblocking);
+#endif
 }
 
 /*Disable/enable write coalescing if we can.
@@ -610,11 +637,21 @@
 #  endif
   /*It doesn't really matter if this call fails, but it would be interesting
      to hit a case where it does.*/
-  OP_ALWAYS_TRUE(!setsockopt(_fd,OP_SO_LEVEL,TCP_NODELAY,
+  OP_ALWAYS_TRUE(!SETSOCKOPT(_fd,OP_SO_LEVEL,TCP_NODELAY,
    &_nodelay,sizeof(_nodelay)));
 # endif
 }
 
+#ifdef _WIN32
+static void op_init_winsock(){
+  static LONG count = 0;
+  static WSADATA wsadata;
+  if (InterlockedIncrement(&count) == 1) {
+    WSAStartup(0x0202, &wsadata);
+  }
+}
+#endif
+
 /*A single physical connection to an HTTP server.
   We may have several of these open at once.*/
 struct OpusHTTPConn{
@@ -657,7 +694,7 @@
 static void op_http_conn_clear(OpusHTTPConn *_conn){
   if(_conn->ssl_conn!=NULL)SSL_free(_conn->ssl_conn);
   /*SSL frees the BIO for us.*/
-  if(_conn->fd>=0)close(_conn->fd);
+  if(_conn->fd!=-1)CLOSE(_conn->fd);
 }
 
 /*The global stream state.*/
@@ -803,13 +840,13 @@
     else{
       ssize_t ret;
       errno=0;
-      ret=write(fd.fd,_buf,_buf_size);
+      ret=send(fd.fd,_buf,_buf_size,0);
       if(ret>0){
         _buf+=ret;
         _buf_size-=ret;
         continue;
       }
-      err=errno;
+      err=ERRNO();
       if(err!=EAGAIN&&err!=EWOULDBLOCK)return OP_FALSE;
       fd.events=POLLOUT;
     }
@@ -821,7 +858,7 @@
 static int op_http_conn_estimate_available(OpusHTTPConn *_conn){
   int available;
   int ret;
-  ret=ioctl(_conn->fd,FIONREAD,&available);
+  ret=IOCTL(_conn->fd,FIONREAD,&available);
   if(ret<0)available=0;
   /*This requires the SSL read_ahead flag to be unset to work.
     We ignore partial records as well as the protocol overhead for any pending
@@ -855,7 +892,7 @@
   opus_int64   read_rate;
   read_delta_bytes=_conn->read_bytes;
   if(read_delta_bytes<=0)return;
-  OP_ALWAYS_TRUE(!ftime(&read_time));
+  OP_ALWAYS_TRUE(!FTIME(&read_time));
   read_delta_ms=op_time_diff_ms(&read_time,&_conn->read_time);
   read_rate=_conn->read_rate;
   read_delta_ms=OP_MAX(read_delta_ms,1);
@@ -913,7 +950,7 @@
     else{
       ssize_t ret;
       errno=0;
-      ret=read(fd.fd,_buf+nread,_buf_size-nread);
+      ret=recv(fd.fd,_buf+nread,_buf_size-nread,0);
       OP_ASSERT(ret<=_buf_size-nread);
       if(ret>0){
         /*Read some data.
@@ -925,7 +962,7 @@
       /*If we already read some data or the connection was closed, return
          right now.*/
       if(ret==0||nread>0)break;
-      err=errno;
+      err=ERRNO();
       if(err!=EAGAIN&&err!=EWOULDBLOCK)return OP_EREAD;
       fd.events=POLLIN;
     }
@@ -968,7 +1005,7 @@
       ret=(int)recv(fd.fd,_buf,_buf_size,MSG_PEEK);
       /*Either saw some data or the connection was closed.*/
       if(ret>=0)return ret;
-      err=errno;
+      err=ERRNO();
       if(err!=EAGAIN&&err!=EWOULDBLOCK)return 0;
       fd.events=POLLIN;
     }
@@ -1727,6 +1764,7 @@
 static int op_sock_connect_next(int _fd,
  struct addrinfo **_addr,int _ai_family){
   struct addrinfo *addr;
+  int err;
   addr=*_addr;
   for(;;){
     /*Move to the next address of the requested type.*/
@@ -1735,7 +1773,9 @@
     /*No more: failure.*/
     if(addr==NULL)return OP_FALSE;
     if(connect(_fd,addr->ai_addr,addr->ai_addrlen)>=0)return 1;
-    if(OP_LIKELY(errno==EINPROGRESS))return 0;
+    err=ERRNO();
+    /* winsock will set WSAEWOULDBLOCK */
+    if(OP_LIKELY(err==EINPROGRESS||err==EWOULDBLOCK))return 0;
     addr=addr->ai_next;
   }
 }
@@ -1780,7 +1820,7 @@
   _stream->free_head=_conn->next;
   _conn->next=_stream->lru_head;
   _stream->lru_head=_conn;
-  OP_ALWAYS_TRUE(!ftime(_start_time));
+  OP_ALWAYS_TRUE(!FTIME(_start_time));
   *&_conn->read_time=*_start_time;
   _conn->read_bytes=0;
   _conn->read_rate=0;
@@ -1789,7 +1829,7 @@
     ai_family=addrs[pi]->ai_family;
     fds[pi].fd=socket(ai_family,SOCK_STREAM,addrs[pi]->ai_protocol);
     fds[pi].events=POLLOUT;
-    if(OP_LIKELY(fds[pi].fd>=0)){
+    if(OP_LIKELY(fds[pi].fd!= -1)){
       if(OP_LIKELY(op_sock_set_nonblocking(fds[pi].fd,1)>=0)){
         ret=op_sock_connect_next(fds[pi].fd,addrs+pi,ai_family);
         if(OP_UNLIKELY(ret>0)){
@@ -1802,7 +1842,7 @@
         /*Tried all the addresses for this protocol.*/
       }
       /*Clean up the socket.*/
-      close(fds[pi].fd);
+      CLOSE(fds[pi].fd);
     }
     /*Remove this protocol from the list.*/
     memmove(addrs+pi,addrs+pi+1,sizeof(*addrs)*(nprotos-pi-1));
@@ -1819,8 +1859,8 @@
       errlen=sizeof(err);
       /*Some platforms will return the pending error in &err and return 0.
         Others will put it in errno and return -1.*/
-      ret=getsockopt(fds[pi].fd,SOL_SOCKET,SO_ERROR,&err,&errlen);
-      if(ret<0)err=errno;
+      ret=GETSOCKOPT(fds[pi].fd,SOL_SOCKET,SO_ERROR,&err,&errlen);
+      if(ret<0)err=ERRNO();
       /*Success!*/
       if(err==0||err==EISCONN)break;
       /*Move on to the next address for this protocol.*/
@@ -1833,7 +1873,7 @@
       else if(ret==0)continue;
       /*Tried all the addresses for this protocol.
         Remove it from the list.*/
-      close(fds[pi].fd);
+      CLOSE(fds[pi].fd);
       memmove(fds+pi,fds+pi+1,sizeof(*fds)*(nprotos-pi-1));
       memmove(addrs+pi,addrs+pi+1,sizeof(*addrs)*(nprotos-pi-1));
       nprotos--;
@@ -1841,7 +1881,7 @@
     }
   }
   /*Close all the other sockets.*/
-  for(pj=0;pj<nprotos;pj++)if(pi!=pj)close(fds[pj].fd);
+  for(pj=0;pj<nprotos;pj++)if(pi!=pj)CLOSE(fds[pj].fd);
   /*If none of them succeeded, we're done.*/
   if(pi>=nprotos)return OP_FALSE;
   /*Save this address for future connection attempts.*/
@@ -1861,7 +1901,7 @@
       if(OP_LIKELY(ret>=0))return ret;
       SSL_free(ssl_conn);
     }
-    close(fds[pi].fd);
+    CLOSE(fds[pi].fd);
     _conn->fd=-1;
     return OP_FALSE;
   }
@@ -2002,6 +2042,9 @@
      out that last_host!=NULL implies we've already taken one trip through the
      loop.*/
   last_port=0;
+#ifdef _WIN32
+  op_init_winsock();
+#endif
   ret=op_parse_url(&_stream->url,_url);
   if(OP_UNLIKELY(ret<0))return ret;
   for(nredirs=0;nredirs<OP_REDIRECT_LIMIT;nredirs++){
@@ -2162,7 +2205,7 @@
     if(OP_UNLIKELY(ret<0))return ret;
     ret=op_http_conn_read_response(_stream->conns+0,&_stream->response);
     if(OP_UNLIKELY(ret<0))return ret;
-    OP_ALWAYS_TRUE(!ftime(&end_time));
+    OP_ALWAYS_TRUE(!FTIME(&end_time));
     next=op_http_parse_status_line(&v1_1_compat,&status_code,
      _stream->response.buf);
     if(OP_UNLIKELY(next==NULL))return OP_FALSE;
@@ -2520,7 +2563,7 @@
   if(OP_UNLIKELY(ret<0))return ret;
   ret=op_http_conn_handle_response(_stream,_conn);
   if(OP_UNLIKELY(ret!=0))return OP_FALSE;
-  OP_ALWAYS_TRUE(!ftime(&end_time));
+  OP_ALWAYS_TRUE(!FTIME(&end_time));
   _stream->cur_conni=_conn-_stream->conns;
   OP_ASSERT(_stream->cur_conni>=0&&_stream->cur_conni<OP_NCONNS_MAX);
   /*The connection has been successfully opened.
@@ -2813,7 +2856,7 @@
     op_http_conn_read_rate_update(stream->conns+ci);
     *&seek_time=*&stream->conns[ci].read_time;
   }
-  else OP_ALWAYS_TRUE(!ftime(&seek_time));
+  else OP_ALWAYS_TRUE(!FTIME(&seek_time));
   /*If we seeked past the end of the stream, just disable the active
      connection.*/
   if(pos>=content_length){
--- /dev/null
+++ b/src/win32/winerrno.h
@@ -1,0 +1,52 @@
+#ifndef WINERRNO_H
+#define WINERRNO_H
+
+#include <errno.h>
+
+/* XXX: conflicts with MSVC errno definition */
+#ifdef ENAMETOOLONG
+#undef ENAMETOOLONG
+#endif
+#ifdef ENOTEMPTY
+#undef ENOTEMPTY
+#endif
+
+#define EWOULDBLOCK          35
+#define EINPROGRESS          36
+#define EALREADY             37
+#define ENOTSOCK             38
+#define EDESTADDRREQ         39
+#define EMSGSIZE             40
+#define EPROTOTYPE           41
+#define ENOPROTOOPT          42
+#define EPROTONOSUPPORT      43
+#define ESOCKTNOSUPPORT      44
+#define EOPNOTSUPP           45
+#define EPFNOSUPPORT         46
+#define EAFNOSUPPORT         47
+#define EADDRINUSE           48
+#define EADDRNOTAVAIL        49
+#define ENETDOWN             50
+#define ENETUNREACH          51
+#define ENETRESET            52
+#define ECONNABORTED         53
+#define ECONNRESET           54
+#define ENOBUFS              55
+#define EISCONN              56
+#define ENOTCONN             57
+#define ESHUTDOWN            58
+#define ETOOMANYREFS         59
+#define ETIMEDOUT            60
+#define ECONNREFUSED         61
+#define ELOOP                62
+#define ENAMETOOLONG         63
+#define EHOSTDOWN            64
+#define EHOSTUNREACH         65
+#define ENOTEMPTY            66
+#define EPROCLIM             67
+#define EUSERS               68
+#define EDQUOT               69
+#define ESTALE               70
+#define EREMOTE              71
+
+#endif
--- /dev/null
+++ b/src/win32/wsockwrapper.c
@@ -1,0 +1,44 @@
+#include <stdio.h>
+#include "wsockwrapper.h"
+
+int win32_poll(struct pollfd *fds, unsigned nfds, int timeout)
+{
+    fd_set ifds, ofds, efds;
+    struct timeval tv;
+    unsigned i;
+    int rc;
+
+    FD_ZERO(&ifds);
+    FD_ZERO(&ofds);
+    FD_ZERO(&efds);
+    for (i = 0; i < nfds; ++i) {
+	fds[i].revents = 0;
+	if (fds[i].events & POLLIN)
+	    FD_SET(fds[i].fd, &ifds);
+	if (fds[i].events & POLLOUT)
+	    FD_SET(fds[i].fd, &ofds);
+	FD_SET(fds[i].fd, &efds);
+    }
+    if (timeout >= 0) {
+	tv.tv_sec = timeout / 1000;
+	tv.tv_usec = (timeout - tv.tv_sec * 1000) * 1000;
+    }
+    rc = select(255, &ifds, &ofds, &efds, timeout < 0 ? 0 : &tv);
+    if (rc > 0) {
+	for (i = 0; i < nfds; ++i) {
+	    if (FD_ISSET(fds[i].fd, &ifds))
+		fds[i].revents |= POLLIN;
+	    if (FD_ISSET(fds[i].fd, &ofds))
+		fds[i].revents |= POLLOUT;
+	    if (FD_ISSET(fds[i].fd, &efds))
+		fds[i].revents |= POLLHUP;
+	}
+    }
+    return rc;
+}
+
+int win32_ftime(struct timeb *timer)
+{
+    ftime(timer);
+    return 0;
+}
--- /dev/null
+++ b/src/win32/wsockwrapper.h
@@ -1,0 +1,25 @@
+#ifndef WSOCKWRAPPER_H
+#define WSOCKWRAPPER_H
+
+#include <winsock2.h>
+#include <sys/timeb.h>
+
+#define POLLIN      0x0001    /* There is data to read */
+#define POLLPRI     0x0002    /* There is urgent data to read */
+#define POLLOUT     0x0004    /* Writing now will not block */
+#define POLLERR     0x0008    /* Error condition */
+#define POLLHUP     0x0010    /* Hung up */
+#define POLLNVAL    0x0020    /* Invalid request: fd not open */
+
+struct pollfd {
+    SOCKET fd;        /* file descriptor */
+    short events;     /* requested events */
+    short revents;    /* returned events */
+};
+
+#define poll(x, y, z) win32_poll(x, y, z)
+int win32_poll(struct pollfd *, unsigned int, int);
+
+int win32_ftime(struct timeb *timer);
+
+#endif