| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.tomcat.websocket; |
| |
| import java.io.EOFException; |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.nio.channels.CompletionHandler; |
| |
| import javax.websocket.CloseReason; |
| import javax.websocket.CloseReason.CloseCodes; |
| |
| import org.apache.juli.logging.Log; |
| import org.apache.juli.logging.LogFactory; |
| import org.apache.tomcat.util.res.StringManager; |
| |
| public class WsFrameClient extends WsFrameBase { |
| |
| private final Log log = LogFactory.getLog(WsFrameClient.class); // must not be static |
| private static final StringManager sm = |
| StringManager.getManager(Constants.PACKAGE_NAME); |
| |
| private final AsyncChannelWrapper channel; |
| private final CompletionHandler<Integer,Void> handler; |
| // Not final as it may need to be re-sized |
| private volatile ByteBuffer response; |
| |
| public WsFrameClient(ByteBuffer response, AsyncChannelWrapper channel, |
| WsSession wsSession, Transformation transformation) { |
| super(wsSession, transformation); |
| this.response = response; |
| this.channel = channel; |
| this.handler = new WsFrameClientCompletionHandler(); |
| } |
| |
| |
| void startInputProcessing() { |
| try { |
| processSocketRead(); |
| } catch (IOException e) { |
| close(e); |
| } |
| } |
| |
| |
| private void processSocketRead() throws IOException { |
| |
| while (response.hasRemaining()) { |
| int remaining = response.remaining(); |
| |
| int toCopy = Math.min(remaining, inputBuffer.length - writePos); |
| |
| // Copy remaining bytes read in HTTP phase to input buffer used by |
| // frame processing |
| response.get(inputBuffer, writePos, toCopy); |
| writePos += toCopy; |
| |
| // Process the data we have |
| processInputBuffer(); |
| } |
| response.clear(); |
| |
| // Get some more data |
| if (isOpen()) { |
| channel.read(response, null, handler); |
| } |
| } |
| |
| |
| private final void close(Throwable t) { |
| CloseReason cr; |
| if (t instanceof WsIOException) { |
| cr = ((WsIOException) t).getCloseReason(); |
| } else { |
| cr = new CloseReason( |
| CloseCodes.CLOSED_ABNORMALLY, t.getMessage()); |
| } |
| |
| try { |
| wsSession.close(cr); |
| } catch (IOException ignore) { |
| // Ignore |
| } |
| } |
| |
| |
| @Override |
| protected boolean isMasked() { |
| // Data is from the server so it is not masked |
| return false; |
| } |
| |
| |
| @Override |
| protected Log getLog() { |
| return log; |
| } |
| |
| |
| private class WsFrameClientCompletionHandler |
| implements CompletionHandler<Integer,Void> { |
| |
| @Override |
| public void completed(Integer result, Void attachment) { |
| if (result.intValue() == -1) { |
| // BZ 57762. A dropped connection will get reported as EOF |
| // rather than as an error so handle it here. |
| if (isOpen()) { |
| // No close frame was received |
| close(new EOFException()); |
| } |
| // No data to process |
| return; |
| } |
| response.flip(); |
| try { |
| processSocketRead(); |
| } catch (IOException e) { |
| // Only send a close message on an IOException if the client |
| // has not yet received a close control message from the server |
| // as the IOException may be in response to the client |
| // continuing to send a message after the server sent a close |
| // control message. |
| if (isOpen()) { |
| log.debug(sm.getString("wsFrameClient.ioe", e)); |
| close(e); |
| } |
| } |
| } |
| |
| @Override |
| public void failed(Throwable exc, Void attachment) { |
| if (exc instanceof ReadBufferOverflowException) { |
| // response will be empty if this exception is thrown |
| response = ByteBuffer.allocate( |
| ((ReadBufferOverflowException) exc).getMinBufferSize()); |
| response.flip(); |
| try { |
| processSocketRead(); |
| } catch (IOException e) { |
| close(e); |
| } |
| } else { |
| close(exc); |
| } |
| } |
| } |
| } |