2016-10-04 68 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的一般問題?!

相關問題