2016-10-04 69 views
0

我们试图完成的目标是将本地视频资源(存储在磁盘上)加载到WKWebView实例中,以用作WebGL中的纹理。到目前为止,我们已经使用绑定到localhost的服务器(GCDWebServer)完成此操作,并将本地源代码作为HTML字符串(本例中为baseURL:“http://localhost:8989/”)加载,然后使用以下内容播放视频行代码:OpenSSL服务器没有收到来自WKWebView的媒体(音频/视频)请求

<video src="test.mp4" width="320" height="240" preload="auto" playsinline autoplay muted></video> 

然而,随着即将到来的变化,以苹果的ATS政策,我们现在需要这个通过HTTPS发生。我们的新的服务器的实现是基于OpenSSL和下面包括:

#import "SSLServer.h" 
#import "Logging.h" 
#import "Util.h" 

#import "SSLServerResponse.h" 

#include <errno.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <arpa/inet.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <resolv.h> 
#include "openssl/ssl.h" 
#include "openssl/err.h" 

#define FAIL -1 
#define SSL_SERVER_UPDATE_INTERVAL 0.0f 

@interface SSLServer() 
{ 
    BOOL _keepAlive; 
    NSUInteger _port; 

    NSString* _directoryPath; 
    NSString* _certFilepath; 

    SSLServerStartCompletionHandler _startCompletionHandler; 
} 
@end 

@implementation SSLServer 

-(instancetype)initWithPort:(NSUInteger)port directoryPath:(NSString*)directoryPath andCertFilepath:(NSString*)certFilepath { 

    self = [super init]; 

    if (self) { 
     _keepAlive = YES; 
     _port = 8989; //port; 
     _certFilepath = certFilepath ? certFilepath : @""; 
     _directoryPath = directoryPath; 
    } 

    return self; 
} 

-(void)startWithCompletionHandler:(SSLServerStartCompletionHandler)handler { 
    _startCompletionHandler = handler; 

    SSL_CTX *ctx; 
    int server; 

    SSL_library_init(); 

    //Initialize SSL 
    ctx = [self initServerCTX]; 
    if (ctx == NULL) { 
     LogInfoPrivate(@"[SSLServer] : Failed to create SSL context for some reason"); 
     return; 
    } 

    //Load certs 
    [self loadCertificates:ctx certFile:_certFilepath keyFile:_certFilepath]; 

    //Create server socket 
    server = [self openListener:_port]; 
    if (server == -1) { 
     _startCompletionHandler(NO, (NSUInteger)_port); 
    } else { 
     _startCompletionHandler(YES, (NSUInteger)_port); 
    } 
    _startCompletionHandler = nil; 

    while (_keepAlive) { 
     struct sockaddr_in addr; 
     socklen_t len = sizeof(addr); 
     SSL *ssl; 

     LogInfoPrivate(@"[SSLServer] : Listening on port: %lu", (unsigned long)_port); 

     //Accept connection as usual 
     int client = accept(server, (struct sockaddr*)&addr, &len); 
     LogInfoPrivate(@"[SSLServer] : Connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); 
     LogInfoPrivate(@"[SSLServer] : Port:%lu\n", (unsigned long)_port); 

     //Get new SSL state with context 
     ssl = SSL_new(ctx); 

     //Set connection socket to SSL state 
     SSL_set_fd(ssl, client); 

     //Service connection    

     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
      [self servlet:ssl directoryPath:_directoryPath]; 
     }); 

     [NSThread sleepForTimeInterval:SSL_SERVER_UPDATE_INTERVAL]; 
    } 

    //Close server socket 
    close(server); 

    //Release context 
    SSL_CTX_free(ctx); 
} 

-(int)openListener:(NSUInteger)port { 
    int sock; 
    struct sockaddr_in addr; 

    sock = socket(PF_INET, SOCK_STREAM, 0); 
    bzero(&addr, sizeof(addr)); 
    addr.sin_family = AF_INET; 
    addr.sin_port = htons((unsigned long)port); 
    addr.sin_addr.s_addr = INADDR_ANY; 

    socklen_t len = sizeof(addr); 

    if (bind(sock, (struct sockaddr*)&addr, len) != 0) { 
     LogErrorPrivate(@"[SSLServer] : Can't bind port"); 
     return -1; 
    } 

    if (listen(sock, 10) != 0) { 
     LogErrorPrivate(@"[SSLServer] : Can't configure listening port"); 
     return -1; 
    } 

    // when sin_port is init'd as '0', socket lib will randomize the port, so, we grab the bound port here. 
    if (port == 0) { 
     if (getsockname(sock, (struct sockaddr *)&addr, &len) == -1) { 
      LogErrorPrivate(@"[SSLServer] : Could not retrieve random port number"); 
      return -1; 
     } else { 
      _port = ntohs(addr.sin_port); 
     } 
    } 

    return sock; 
} 

-(SSL_CTX*)initServerCTX { 
    const SSL_METHOD *method; 
    SSL_CTX *ctx; 

    //Load & register all cryptos, etc. 
    OpenSSL_add_all_algorithms(); 

    //Load all error 4messages 
    SSL_load_error_strings(); 

    //Create new server-method instance 
    method = TLSv1_2_server_method(); 

    //Create new context from method 
    ctx = SSL_CTX_new(method); 
    if (ctx == NULL) { 
     ERR_print_errors_fp(stderr); 
     return NULL; 
    } 

    return ctx; 
} 

-(void)loadCertificates:(SSL_CTX*)ctx certFile:(NSString*)certFile keyFile:(NSString*)keyFile { 
    //Set the local certificate from CertFile 
    if (SSL_CTX_use_certificate_file(ctx, [certFile UTF8String], SSL_FILETYPE_PEM) <= 0) { 
     ERR_print_errors_fp(stderr); 
     return; 
    } 

    //Set the private key from KeyFile (may be the same as CertFile) 
    if (SSL_CTX_use_PrivateKey_file(ctx, [keyFile UTF8String], SSL_FILETYPE_PEM) <= 0) { 
     ERR_print_errors_fp(stderr); 
     return; 
    } 

    //Verify private key 
    if (!SSL_CTX_check_private_key(ctx)) { 
     LogErrorPrivate(@"[SSLServer] : Private key does not match the public certificate"); 
     return; 
    } 
} 

-(void)showCerts:(SSL*)ssl { 
    X509 *cert; 
    char *line; 

    //Get certificates (if available) 
    cert = SSL_get_peer_certificate(ssl); 
    if (cert != NULL) { 
     LogInfoPrivate(@"[SSLServer] : Server certificates:"); 
     line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); 
     LogInfoPrivate(@"[SSLServer] : Subject: %s", line); 
     free(line); 
     line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); 
     LogInfoPrivate(@"[SSLServer] : Issuer: %s", line); 
     free(line); 
     X509_free(cert); 
    } else { 
     LogInfoPrivate(@"[SSLServer] : No certificates."); 
    } 
} 

//Serve the connection 
-(void)servlet:(SSL*)ssl directoryPath:(NSString*)directoryPath { 
    char buf[1024]; 
    int sd, bytes; 

    //Do SSL-protocol accept 
    if (SSL_accept(ssl) == FAIL) { 
     ERR_print_errors_fp(stderr); 
    } else { 
     //Get any certificates 
     [self showCerts:ssl]; 

     //Get request 
     bytes = SSL_read(ssl, buf, sizeof(buf)); 
     if (bytes > 0) { 
      buf[bytes] = 0; 
      LogInfoPrivate(@"[SSLServer] Client msg: \"%s\"", buf); 

      NSArray* requestElements = [[NSString stringWithUTF8String:buf] componentsSeparatedByString:@" "]; 
      NSString* resource = [requestElements[1] lastPathComponent]; 
      NSString* resourcePath = [directoryPath stringByAppendingPathComponent:resource]; 
      LogInfoPrivate(@"[SSLServer] Resource path: %@", resource); 

      //Configure the response 
      SSLServerResponse* response = [[SSLServerResponse alloc] initWithResourcePath:resourcePath chunked:YES]; 
      [response configure]; 

      //Write the response 
      for (NSData* data in response.payload) { 
       SSL_write(ssl, (const char*)[data bytes], (int)[data length]); 
      } 
     } else { 
      LogInfoPrivate(@"[SSLServer] : Nothing to send back"); 
      ERR_print_errors_fp(stderr); 
     } 
    } 

    //Get socket connection 
    sd = SSL_get_fd(ssl); 

    //Release SSL state 
    SSL_free(ssl); 

    //Close connection 
    close(sd); 
} 

-(void)finish { 
    _keepAlive = NO; 
} 
@end 

我们遇到的问题是图像文件(.png)和文本送达正确;但是,音频和视频不是。我们一直在监控Charles的网络流量,甚至没有任何请求出现在Web视图中。更具体地说,除了对图像和javascript的GET请求之外,在套接字上不发生通信。对于图片和文字,我们看到Charles中的请求,并且Web视图正在获取身份验证挑战回调。我们通过将'allowInlineMediaPlayback'设置为'YES'和'mediaPlaybackRequiresUserAction'为'NO'来配置我们的WKWebViewConfiguration。有人能解释为什么只有一些请求被从网络视图中解雇?

+0

原谅我的无知...... *“苹果的ATS政策即将发生变化......”*您是否有链接指出新的要求? – jww

+0

*“有人可以解释为什么只有一些请求正在从网络视图中被解雇......”* - 它几乎听起来像一个混合内容问题,不信任你的本地主机。我猜测问题是HTTPS证书。另请参阅[如何使用您的证书颁发机构签署证书签名请求](http://stackoverflow.com/a/21340898/608639)和[如何使用openssl创建自签名证书?](http:// stackoverflow .com/q/10175812/608639)它提供了许多有关X.509服务器证书的背景信息,如何显示名称以及各种规则的来源。 – jww

回答

1

我们在“http://+:13333”上有一个httpListener,但是当我们在WKWebView中加载我们的htmlString并使用这个内容“http://localhost:13333”时,会发生nothings。

我们的解决方案是用“127.0.0.1”取代“localhost”。

也许这是WKWebView的一般问题?!

相关问题