All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.signalfx.shaded.jetty.websocket.common.message.MessageWriter Maven / Gradle / Ivy

//
//  ========================================================================
//  Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package com.signalfx.shaded.jetty.websocket.common.message;

import java.io.IOException;
import java.io.Writer;
import java.nio.ByteBuffer;

import com.signalfx.shaded.jetty.io.ByteBufferPool;
import com.signalfx.shaded.jetty.util.BufferUtil;
import com.signalfx.shaded.jetty.util.log.Log;
import com.signalfx.shaded.jetty.util.log.Logger;
import com.signalfx.shaded.jetty.websocket.api.BatchMode;
import com.signalfx.shaded.jetty.websocket.api.WriteCallback;
import com.signalfx.shaded.jetty.websocket.api.extensions.OutgoingFrames;
import com.signalfx.shaded.jetty.websocket.common.BlockingWriteCallback;
import com.signalfx.shaded.jetty.websocket.common.BlockingWriteCallback.WriteBlocker;
import com.signalfx.shaded.jetty.websocket.common.WebSocketSession;
import com.signalfx.shaded.jetty.websocket.common.frames.TextFrame;

/**
 * Support for writing a single WebSocket TEXT message via a {@link Writer}
 * 

* Note: Per WebSocket spec, all WebSocket TEXT messages must be encoded in UTF-8 */ public class MessageWriter extends Writer { private static final Logger LOG = Log.getLogger(MessageWriter.class); private final OutgoingFrames outgoing; private final ByteBufferPool bufferPool; private final BlockingWriteCallback blocker; private long frameCount; private TextFrame frame; private ByteBuffer buffer; private Utf8CharBuffer utf; private WriteCallback callback; private boolean closed; public MessageWriter(WebSocketSession session) { this(session.getOutgoingHandler(), session.getPolicy().getMaxTextMessageBufferSize(), session.getBufferPool()); } public MessageWriter(OutgoingFrames outgoing, int bufferSize, ByteBufferPool bufferPool) { this.outgoing = outgoing; this.bufferPool = bufferPool; this.blocker = new BlockingWriteCallback(); this.buffer = bufferPool.acquire(bufferSize, true); BufferUtil.flipToFill(buffer); this.frame = new TextFrame(); this.utf = Utf8CharBuffer.wrap(buffer); } @Override public void write(char[] chars, int off, int len) throws IOException { try { send(chars, off, len); } catch (Throwable x) { // Notify without holding locks. notifyFailure(x); throw x; } } @Override public void write(int c) throws IOException { try { send(new char[]{(char)c}, 0, 1); } catch (Throwable x) { // Notify without holding locks. notifyFailure(x); throw x; } } @Override public void flush() throws IOException { try { flush(false); } catch (Throwable x) { // Notify without holding locks. notifyFailure(x); throw x; } } @Override public void close() throws IOException { try { flush(true); bufferPool.release(buffer); if (LOG.isDebugEnabled()) LOG.debug("Stream closed, {} frames sent", frameCount); // Notify without holding locks. notifySuccess(); } catch (Throwable x) { // Notify without holding locks. notifyFailure(x); throw x; } } private void flush(boolean fin) throws IOException { synchronized (this) { if (closed) throw new IOException("Stream is closed"); closed = fin; ByteBuffer data = utf.getByteBuffer(); if (LOG.isDebugEnabled()) LOG.debug("flush({}): {}", fin, BufferUtil.toDetailString(buffer)); frame.setPayload(data); frame.setFin(fin); try (WriteBlocker b = blocker.acquireWriteBlocker()) { outgoing.outgoingFrame(frame, b, BatchMode.OFF); b.block(); } ++frameCount; // Any flush after the first will be a CONTINUATION frame. frame.setIsContinuation(); utf.clear(); } } private void send(char[] chars, int offset, int length) throws IOException { synchronized (this) { if (closed) throw new IOException("Stream is closed"); while (length > 0) { // There may be no space available, we want // to handle correctly when space == 0. int space = utf.remaining(); int size = Math.min(space, length); utf.append(chars, offset, size); offset += size; length -= size; if (length > 0) { // If we could not write everything, it means // that the buffer was full, so flush it. flush(false); } } } } public void setCallback(WriteCallback callback) { synchronized (this) { this.callback = callback; } } private void notifySuccess() { WriteCallback callback; synchronized (this) { callback = this.callback; } if (callback != null) { callback.writeSuccess(); } } private void notifyFailure(Throwable failure) { WriteCallback callback; synchronized (this) { callback = this.callback; } if (callback != null) { callback.writeFailed(failure); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy