/*
* Copyright (c) 2011-2013 The original author or authors
* ------------------------------------------------------
* 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 io.vertx.core.http.impl;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2Stream;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.impl.ContextImpl;
import io.vertx.core.impl.VertxInternal;
import java.util.ArrayDeque;
/**
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
*/
abstract class VertxHttp2Stream<C extends Http2ConnectionBase> {
private static final Object END = new Object(); // Marker
protected final C conn;
protected final VertxInternal vertx;
protected final ContextImpl context;
protected final ChannelHandlerContext handlerContext;
protected final Http2Stream stream;
private final ArrayDeque<Object> pending = new ArrayDeque<>(8);
private boolean paused;
private boolean writable = true;
VertxHttp2Stream(C conn, Http2Stream stream, boolean writable) {
this.conn = conn;
this.vertx = conn.vertx();
this.handlerContext = conn.handlerContext;
this.stream = stream;
this.context = conn.getContext();
this.writable = writable;
}
void onResetRead(long code) {
synchronized (conn) {
paused = false;
pending.clear();
handleReset(code);
}
}
boolean onDataRead(Buffer data) {
synchronized (conn) {
if (!paused) {
if (pending.isEmpty()) {
handleData(data);
return true;
} else {
pending.add(data);
checkNextTick(null);
}
} else {
pending.add(data);
}
return false;
}
}
void onWritabilityChanged() {
synchronized (conn) {
writable = !writable;
handleInterestedOpsChanged();
}
}
void onEnd() {
onEnd(null);
}
void onEnd(MultiMap trailers) {
synchronized (conn) {
if (paused || pending.size() > 0) {
if (trailers != null) {
pending.add(trailers);
} else {
pending.add(END);
}
} else {
handleEnd(trailers);
}
}
}
/**
* Check if paused buffers must be handled to the reader, this must be called from event loop.
*/
private void checkNextTick(Void v) {
synchronized (conn) {
if (!paused) {
Object msg = pending.poll();
if (msg instanceof Buffer) {
Buffer buf = (Buffer) msg;
conn.handler.consume(stream, buf.length());
handleData(buf);
if (pending.size() > 0) {
vertx.runOnContext(this::checkNextTick);
}
} else if (msg == END) {
handleEnd(null);
} else if (msg instanceof MultiMap) {
handleEnd((MultiMap) msg);
}
}
}
}
int id() {
return stream.id();
}
public void doPause() {
paused = true;
}
public void doResume() {
paused = false;
context.runOnContext(this::checkNextTick);
}
boolean isNotWritable() {
synchronized (conn) {
return !writable;
}
}
void writeFrame(int type, int flags, ByteBuf payload) {
conn.handler.writeFrame(stream, (byte) type, (short) flags, payload);
}
void writeHeaders(Http2Headers headers, boolean end) {
conn.handler.writeHeaders(stream, headers, end);
}
void writeData(ByteBuf chunk, boolean end) {
conn.handler.writeData(stream, chunk, end);
}
void writeReset(long code) {
conn.handler.writeReset(stream.id(), code);
}
void handleInterestedOpsChanged() {
}
void handleData(Buffer buf) {
}
void handleCustomFrame(int type, int flags, Buffer buff) {
}
void handleEnd(MultiMap trailers) {
}
void handleReset(long errorCode) {
}
void handleException(Throwable cause) {
}
void handleClose() {
}
}