com.webpieces.http2engine.impl.client.Level5ClientStateMachine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http2-engine Show documentation
Show all versions of http2-engine Show documentation
A re-usable asynchronous HTTP/2 parser
package com.webpieces.http2engine.impl.client;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.RECV_DATA;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.RECV_HEADERS;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.RECV_HEADERS_EOS;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.RECV_PUSH;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.RECV_RST;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.SENT_DATA;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.SENT_DATA_EOS;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.SENT_HEADERS;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.SENT_HEADERS_EOS;
import static com.webpieces.http2engine.impl.shared.data.Http2Event.SENT_RST;
import org.webpieces.util.futures.XFuture;
import org.webpieces.javasm.api.Memento;
import org.webpieces.javasm.api.State;
import org.webpieces.util.locking.PermitQueue;
import com.webpieces.http2.api.dto.error.CancelReasonCode;
import com.webpieces.http2.api.dto.error.ConnectionException;
import com.webpieces.http2.api.dto.highlevel.Http2Push;
import com.webpieces.http2.api.dto.highlevel.Http2Request;
import com.webpieces.http2.api.dto.highlevel.Http2Response;
import com.webpieces.http2.api.dto.highlevel.Http2Trailers;
import com.webpieces.http2.api.dto.lowlevel.DataFrame;
import com.webpieces.http2.api.streaming.ResponseStreamHandle;
import com.webpieces.http2engine.api.client.Http2Config;
import com.webpieces.http2engine.impl.shared.Level5CStateMachine;
import com.webpieces.http2engine.impl.shared.Level6RemoteFlowControl;
import com.webpieces.http2engine.impl.shared.StreamState;
import com.webpieces.http2engine.impl.shared.data.HeaderSettings;
import com.webpieces.http2engine.impl.shared.data.Stream;
public class Level5ClientStateMachine extends Level5CStateMachine {
private Level6ClntLocalFlowControl local;
private HeaderSettings localSettings;
private HeaderSettings remoteSettings;
private int afterResetExpireSeconds;
public Level5ClientStateMachine(
String key,
StreamState streamState,
Level6RemoteFlowControl remoteFlowControl,
Level6ClntLocalFlowControl localFlowControl,
Http2Config config,
HeaderSettings remoteSettings,
PermitQueue maxConcurrentQueue
) {
super(key, streamState, remoteFlowControl, localFlowControl, maxConcurrentQueue);
this.local = localFlowControl;
this.localSettings = config.getLocalSettings();
this.remoteSettings = remoteSettings;
afterResetExpireSeconds = config.getAfterResetExpireSeconds();
State reservedRemote = stateMachine.createState("Reserved(remote)");
NoTransitionImpl failIfNoTransition = new NoTransitionImpl(true);
idleState.setNoTransitionListener(failIfNoTransition);
openState.setNoTransitionListener(failIfNoTransition);
reservedRemote.setNoTransitionListener(failIfNoTransition);
halfClosedLocal.setNoTransitionListener(failIfNoTransition);
closed.setNoTransitionListener(failIfNoTransition);
stateMachine.createTransition(idleState, openState, SENT_HEADERS);
stateMachine.createTransition(idleState, halfClosedLocal, SENT_HEADERS_EOS); //jump to half closed as is send H AND send ES
stateMachine.createTransition(idleState, reservedRemote, RECV_PUSH);
stateMachine.createTransition(openState, openState, SENT_DATA);
stateMachine.createTransition(openState, halfClosedLocal, SENT_DATA_EOS, SENT_HEADERS_EOS); //headers here is trailing headers
stateMachine.createTransition(openState, closed, SENT_RST, RECV_RST);
stateMachine.createTransition(reservedRemote, halfClosedLocal, RECV_HEADERS);
stateMachine.createTransition(reservedRemote, closed, RECV_HEADERS_EOS, SENT_RST, RECV_RST);
stateMachine.createTransition(halfClosedRemote, halfClosedRemote, SENT_DATA); //only trailing headers allowed (ie. must have EOS)
stateMachine.createTransition(halfClosedLocal, halfClosedLocal, RECV_HEADERS, RECV_DATA);
}
public XFuture sendResponse(Http2Response frame) {
Stream stream = streamState.getStream(frame, true);
XFuture future = fireToClient(stream, frame);
return future;
}
public XFuture fireToClient(Stream stream, Http2Response payload) { //, Supplier possiblyClose
return fireRecvToSM(stream, payload)
.thenCompose(v -> {
return local.fireResponseToApp(stream, payload);
});
}
public XFuture firePushToClient(ClientPushStream stream, Http2Push fullPromise) {
return fireRecvToSM(stream, fullPromise)
.thenCompose(v -> {
return local.firePushToApp(stream, fullPromise);
});
}
public XFuture createStreamAndSend(Http2Request frame, ResponseStreamHandle responseListener) {
Stream stream = createStream(frame.getStreamId(), responseListener);
return fireToSocket(stream, frame).thenApply(v -> stream);
}
private ClientStream createStream(int streamId, ResponseStreamHandle responseListener) {
Memento initialState = createStateMachine("stream" + streamId);
long localWindowSize = localSettings.getInitialWindowSize();
long remoteWindowSize = remoteSettings.getInitialWindowSize();
ClientStream stream = new ClientStream(logId, streamId, initialState, responseListener, localWindowSize, remoteWindowSize);
streamState.create(stream);
return stream;
}
public XFuture sendPushToApp(Http2Push fullPromise) {
int newStreamId = fullPromise.getPromisedStreamId();
if(newStreamId % 2 == 1)
throw new ConnectionException(CancelReasonCode.INVALID_STREAM_ID, logId, newStreamId,
"Server sent bad push promise="+fullPromise+" as new stream id is incorrect and is an odd number");
ClientStream causalStream = (ClientStream) streamState.getStream(fullPromise, true);
ClientPushStream stream = createPushStream(newStreamId, causalStream.getResponseListener());
return firePushToClient(stream, fullPromise);
}
private ClientPushStream createPushStream(int streamId, ResponseStreamHandle responseListener) {
Memento initialState = createStateMachine("stream" + streamId);
long localWindowSize = localSettings.getInitialWindowSize();
long remoteWindowSize = remoteSettings.getInitialWindowSize();
ClientPushStream stream = new ClientPushStream(logId, streamId, initialState, responseListener, localWindowSize, remoteWindowSize);
streamState.create(stream);
return stream;
}
public XFuture sendDataToApp(DataFrame frame) {
return sendDataToAppImpl(frame, true);
}
@Override
protected XFuture sendTrailersToApp(Http2Trailers frame) {
return sendTrailersToAppImpl(frame, true);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy