2017-12-20 2 views
0

나는 지난 며칠 동안 작업하고있는 정말 이상한 문제가 있습니다.Java nio socketchannel은 TLS1.2를 사용하여 사파리와 IOS에서 초기 에오를 읽습니다.

나는 내 서버 측에 프록시 애플리케이션을 작성했습니다. 모든 프록시는 TLS/nonTLS 요청과 다른 애플리케이션 (WebApplications, IOSApps, Android Apps 등)의 응답을 관리하고 트래픽을 서버의 애플리케이션으로 프록시합니다.

내가 겪는 문제점은 Safari를 통해 https (TLSv1.2)를 통해 내 프록시를 사용하는 WebApplication에 액세스 할 때 무작위로 내 프록시에서 응답을 얻지 못합니다. 디버깅을 많이하고이 문제는 연결의 읽기 기능 때문에 발생합니다. 시간 초과는 매우 무작위입니다. 저는 10 회의 요청 중 평균 8 회의 타임 아웃을 받았습니다.

public int read(ByteBuffer dst) throws IOException { 

    if (!dst.hasRemaining()) { 
     return 0; 
    } 
    if (peerAppData.hasRemaining()) { 
     peerAppData.flip(); 
     return ByteBufferUtils.transferByteBuffer(peerAppData, dst); 
    } 
    peerNetData.compact(); 

    int bytesRead = socketChannel.read(peerNetData); 

    if (bytesRead > 0) { 
     peerNetData.flip(); 
     while (peerNetData.hasRemaining()) { 
      peerAppData.compact(); 
      SSLEngineResult result = null; 
      try { 
       result = sslInfo.sslEngine.unwrap(peerNetData, peerAppData); 
      } catch (SSLException e) { 
       System.out.println(e.getMessage()); 
       e.printStackTrace(); 
       // throw e; 
      } 
      switch (result.getStatus()) { 
      case OK: 
       peerAppData.flip(); 
       return ByteBufferUtils.transferByteBuffer(peerAppData, dst); 
      case BUFFER_UNDERFLOW: 
       peerAppData.flip(); 
       return ByteBufferUtils.transferByteBuffer(peerAppData, dst); 
      case BUFFER_OVERFLOW: 
       peerAppData = enlargeApplicationBuffer(peerAppData); 
       break; 
      case CLOSED: 
       closeConnection(); 
       dst.clear(); 
       return -1; 
      default: 
       System.out.println("Invalid SSL status: " + result.getStatus()); 
       throw new IllegalStateException("Invalid SSL status: " + result.getStatus()); 
      } 
     } 
    } else if (bytesRead < 0) { 
     handleEndOfStream(); 
    } 
    ByteBufferUtils.transferByteBuffer(peerAppData, dst); 
    return bytesRead; 
} 

이 코드는 크롬, 오페라 및 안드로이드에 대한 잘 작동 :

는 코드입니다. 하지만 사파리와 IOS에서는 무작위로 SocketChannel의 두 번째 읽기가 -1을 반환합니다. 그 때문에 연결이 끊어집니다. 그래서 사파리/IOS 쪽에서 시간 제한을받는 이유가 설명됩니다.

코드가 작동하지만 파일 업로드와 같은 데이터 스트림을 프록시 할 수 없기 때문에 이것을 사용할 수 없습니다. SSLEngineResult를 올바르게 처리하지 못합니다.

public int read(ByteBuffer dst) throws IOException { 

    int bytesRead = 1; 
    int totalBytesRead = 0; 

    peerNetData.clear(); 

    while (bytesRead > 0) { 
     bytesRead = socketChannel.read(peerNetData); 

     if (bytesRead > 0) { 
      totalBytesRead = totalBytesRead + bytesRead; 
     } 
    } 

    peerNetData.flip(); 

    if (totalBytesRead < 0) { 
     return bytesRead; 
    } 

    while (peerNetData.hasRemaining()) { 
     SSLEngineResult result = sslInfo.sslEngine.unwrap(peerNetData, dst); 
     if (result.getStatus() != SSLEngineResult.Status.OK) { 
      return -1; 
     } 
    } 

    peerNetData.compact(); 

    return totalBytesRead; 
} 

나는이 코드가 사파리 브라우저 나 IOS 장치의 요청에 의해서만 실패하는 이유를 알지 못합니다.

혹시 같은 문제가 발생 했습니까?

미리 감사드립니다. 내가 놓친 것이 있으면 알려주세요!

답변

0

프록시의 업스트림 read()이 -1을 반환하면 업스트림 피어가 연결을 종료 했으므로 타임 아웃시키는 대신 다운 스트림 피어와 동일한 작업을 수행해야합니다.

+0

안녕하세요 EJP, 답변 해 주셔서 감사합니다. 하지만 왜 업스트림은 Safari와 IOS에서만 닫힙니다. 그리고 왜 다른 코드 닛으로 시간 초과되지 않습니다. 거기에서 똑같은 행동을해야하지 않습니까? – R3tty

+0

물론 저는 잘 모릅니다. 그러나 업스트림 피어가 무엇이든 관계없이 다운 스트림 피어에게 충실히 반영되어야합니다. – EJP

+0

헤이 EJP, 내 늦은 응답을 위해 부르짖으며, 나는 떠나고 있었다. 그래서 나는 이상한 행동을 고쳤다. 문제는 어떤 이유로 든 safari/ios 데이터의 일부 unwrap이 760 바이트가 풀려 있지만 peerNetData에 남아있는 데이터가 남아있을 때 OK를 반환한다는 것입니다. 내 코드에서 대상 버퍼 및 반환 760 바이트 전송합니다. 그건 틀린 행동입니다, 나는 보통 peerNetData에 남아있는 데이터가 없을 때까지 계속해야합니다. 나를 위해, 내가 그것을 할 것이기 때문에, 스트림 데이터가 OutOfMemory 예외를 일으킬 것이기 때문에 해결 방법을 써야했다. 원하는 경우 내 코드를 게시 할 수 있습니다. – R3tty