blob: 44fc076417a2187c9aec6db8bcc523e4c09e2e96 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library http2.src.frames.frame_defragmenter;
import '../sync_errors.dart';
import 'frames.dart';
/// Class used for defragmenting [HeadersFrame]s and [PushPromiseFrame]s.
// TODO: Somehow emit an error if too many continuation frames have been sent
// (since we're buffering all of them).
class FrameDefragmenter {
/// The current incomplete [HeadersFrame] fragment.
HeadersFrame _headersFrame;
/// The current incomplete [PushPromiseFrame] fragment.
PushPromiseFrame _pushPromiseFrame;
/// Tries to defragment [frame].
///
/// If the given [frame] is a [HeadersFrame] or a [PushPromiseFrame] which
/// needs de-fragmentation, it will be saved and `null` will be returned.
///
/// If there is currently an incomplete [HeadersFrame] or [PushPromiseFrame]
/// saved, [frame] needs to be a [ContinuationFrame]. It will be added to the
/// saved frame. In case the defragmentation is complete, the defragmented
/// [HeadersFrame] or [PushPromiseFrame] will be returned.
///
/// All other [Frame] types will be returned.
// TODO: Consider handling continuation frames without preceding
// headers/push-promise frame here instead of the call site?
Frame tryDefragmentFrame(Frame frame) {
if (_headersFrame != null) {
if (frame is ContinuationFrame) {
if (_headersFrame.header.streamId != frame.header.streamId) {
throw new ProtocolException(
'Defragmentation: frames have different stream ids.');
}
_headersFrame = _headersFrame.addBlockContinuation(frame);
if (frame.hasEndHeadersFlag) {
var frame = _headersFrame;
_headersFrame = null;
return frame;
} else {
return null;
}
} else {
throw new ProtocolException(
'Defragmentation: Incomplete frame must be followed by '
'continuation frame.');
}
} else if (_pushPromiseFrame != null) {
if (frame is ContinuationFrame) {
if (_pushPromiseFrame.header.streamId != frame.header.streamId) {
throw new ProtocolException(
'Defragmentation: frames have different stream ids.');
}
_pushPromiseFrame = _pushPromiseFrame.addBlockContinuation(frame);
if (frame.hasEndHeadersFlag) {
var frame = _pushPromiseFrame;
_pushPromiseFrame = null;
return frame;
} else {
return null;
}
} else {
throw new ProtocolException(
'Defragmentation: Incomplete frame must be followed by '
'continuation frame.');
}
} else {
if (frame is HeadersFrame) {
if (!frame.hasEndHeadersFlag) {
_headersFrame = frame;
return null;
}
} else if (frame is PushPromiseFrame) {
if (!frame.hasEndHeadersFlag) {
_pushPromiseFrame = frame;
return null;
}
}
}
// If this frame is not relevant for header defragmentation, we pass it to
// the next stage.
return frame;
}
}