Basic implementation of CREDENTIAL frame, parser and generator.
This commit is contained in:
parent
18141b68a3
commit
395d49ba71
|
@ -20,7 +20,6 @@ import org.eclipse.jetty.spdy.api.SessionStatus;
|
|||
|
||||
public class SessionException extends RuntimeException
|
||||
{
|
||||
|
||||
private final SessionStatus sessionStatus;
|
||||
|
||||
public SessionException(SessionStatus sessionStatus)
|
||||
|
|
|
@ -29,7 +29,8 @@ public enum ControlFrameType
|
|||
PING((short)6),
|
||||
GO_AWAY((short)7),
|
||||
HEADERS((short)8),
|
||||
WINDOW_UPDATE((short)9);
|
||||
WINDOW_UPDATE((short)9),
|
||||
CREDENTIAL((short)10);
|
||||
|
||||
public static ControlFrameType from(short code)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed 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.eclipse.jetty.spdy.frames;
|
||||
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
public class CredentialFrame extends ControlFrame
|
||||
{
|
||||
private final short slot;
|
||||
private final byte[] proof;
|
||||
private final Certificate[] certificateChain;
|
||||
|
||||
public CredentialFrame(short version, short slot, byte[] proof, Certificate[] certificateChain)
|
||||
{
|
||||
super(version, ControlFrameType.CREDENTIAL, (byte)0);
|
||||
this.slot = slot;
|
||||
this.proof = proof;
|
||||
this.certificateChain = certificateChain;
|
||||
}
|
||||
|
||||
public short getSlot()
|
||||
{
|
||||
return slot;
|
||||
}
|
||||
|
||||
public byte[] getProof()
|
||||
{
|
||||
return proof;
|
||||
}
|
||||
|
||||
public Certificate[] getCertificateChain()
|
||||
{
|
||||
return certificateChain;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed 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.eclipse.jetty.spdy.generator;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.spdy.ByteBufferPool;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrame;
|
||||
import org.eclipse.jetty.spdy.frames.CredentialFrame;
|
||||
|
||||
public class CredentialGenerator extends ControlFrameGenerator
|
||||
{
|
||||
public CredentialGenerator(ByteBufferPool bufferPool)
|
||||
{
|
||||
super(bufferPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer generate(ControlFrame frame)
|
||||
{
|
||||
CredentialFrame credential = (CredentialFrame)frame;
|
||||
|
||||
byte[] proof = credential.getProof();
|
||||
|
||||
List<byte[]> certificates = serializeCertificates(credential.getCertificateChain());
|
||||
int certificatesLength = 0;
|
||||
for (byte[] certificate : certificates)
|
||||
certificatesLength += certificate.length;
|
||||
|
||||
int frameBodyLength = 2 + 4 + proof.length + certificates.size() * 4 + certificatesLength;
|
||||
|
||||
int totalLength = ControlFrame.HEADER_LENGTH + frameBodyLength;
|
||||
ByteBuffer buffer = getByteBufferPool().acquire(totalLength, true);
|
||||
generateControlFrameHeader(credential, frameBodyLength, buffer);
|
||||
|
||||
buffer.putShort(credential.getSlot());
|
||||
buffer.putInt(proof.length);
|
||||
buffer.put(proof);
|
||||
for (byte[] certificate : certificates)
|
||||
{
|
||||
buffer.putInt(certificate.length);
|
||||
buffer.put(certificate);
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private List<byte[]> serializeCertificates(Certificate[] certificates)
|
||||
{
|
||||
// TODO
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ public class Generator
|
|||
generators.put(ControlFrameType.GO_AWAY, new GoAwayGenerator(bufferPool));
|
||||
generators.put(ControlFrameType.HEADERS, new HeadersGenerator(bufferPool, headersBlockGenerator));
|
||||
generators.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateGenerator(bufferPool));
|
||||
generators.put(ControlFrameType.CREDENTIAL, new CredentialGenerator(bufferPool));
|
||||
|
||||
dataFrameGenerator = new DataFrameGenerator(bufferPool);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public abstract class ControlFrameParser
|
|||
parsers.put(ControlFrameType.GO_AWAY, new GoAwayBodyParser(this));
|
||||
parsers.put(ControlFrameType.HEADERS, new HeadersBodyParser(decompressor, this));
|
||||
parsers.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateBodyParser(this));
|
||||
parsers.put(ControlFrameType.CREDENTIAL, new CredentialBodyParser(this));
|
||||
}
|
||||
|
||||
public short getVersion()
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed 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.eclipse.jetty.spdy.parser;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.spdy.SessionException;
|
||||
import org.eclipse.jetty.spdy.api.SessionStatus;
|
||||
import org.eclipse.jetty.spdy.frames.ControlFrameType;
|
||||
import org.eclipse.jetty.spdy.frames.CredentialFrame;
|
||||
|
||||
public class CredentialBodyParser extends ControlFrameBodyParser
|
||||
{
|
||||
private final List<Certificate> certificates = new ArrayList<>();
|
||||
private final ControlFrameParser controlFrameParser;
|
||||
private State state = State.SLOT;
|
||||
private int totalLength;
|
||||
private int cursor;
|
||||
private short slot;
|
||||
private int proofLength;
|
||||
private byte[] proof;
|
||||
private int certificateLength;
|
||||
private byte[] certificate;
|
||||
|
||||
public CredentialBodyParser(ControlFrameParser controlFrameParser)
|
||||
{
|
||||
this.controlFrameParser = controlFrameParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse(ByteBuffer buffer)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case SLOT:
|
||||
{
|
||||
if (buffer.remaining() >= 2)
|
||||
{
|
||||
slot = buffer.getShort();
|
||||
checkSlotValid();
|
||||
state = State.PROOF_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.SLOT_BYTES;
|
||||
cursor = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SLOT_BYTES:
|
||||
{
|
||||
byte currByte = buffer.get();
|
||||
--cursor;
|
||||
slot += (currByte & 0xFF) << 8 * cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
checkSlotValid();
|
||||
state = State.PROOF_LENGTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROOF_LENGTH:
|
||||
{
|
||||
if (buffer.remaining() >= 4)
|
||||
{
|
||||
proofLength = buffer.getInt() & 0x7F_FF_FF_FF;
|
||||
state = State.PROOF;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.PROOF_LENGTH_BYTES;
|
||||
cursor = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROOF_LENGTH_BYTES:
|
||||
{
|
||||
byte currByte = buffer.get();
|
||||
--cursor;
|
||||
proofLength += (currByte & 0xFF) << 8 * cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
proofLength &= 0x7F_FF_FF_FF;
|
||||
state = State.PROOF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROOF:
|
||||
{
|
||||
totalLength = controlFrameParser.getLength() - 2 - 4 - proofLength;
|
||||
proof = new byte[proofLength];
|
||||
if (buffer.remaining() >= proofLength)
|
||||
{
|
||||
buffer.get(proof);
|
||||
state = State.CERTIFICATE_LENGTH;
|
||||
if (totalLength == 0)
|
||||
{
|
||||
onCredential();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.PROOF_BYTES;
|
||||
cursor = proofLength;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PROOF_BYTES:
|
||||
{
|
||||
proof[proofLength - cursor] = buffer.get();
|
||||
--cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
state = State.CERTIFICATE_LENGTH;
|
||||
if (totalLength == 0)
|
||||
{
|
||||
onCredential();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CERTIFICATE_LENGTH:
|
||||
{
|
||||
if (buffer.remaining() >= 4)
|
||||
{
|
||||
certificateLength = buffer.getInt() & 0x7F_FF_FF_FF;
|
||||
state = State.CERTIFICATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.CERTIFICATE_LENGTH_BYTES;
|
||||
cursor = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CERTIFICATE_LENGTH_BYTES:
|
||||
{
|
||||
byte currByte = buffer.get();
|
||||
--cursor;
|
||||
certificateLength += (currByte & 0xFF) << 8 * cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
certificateLength &= 0x7F_FF_FF_FF;
|
||||
state = State.CERTIFICATE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CERTIFICATE:
|
||||
{
|
||||
totalLength -= 4 + certificateLength;
|
||||
certificate = new byte[certificateLength];
|
||||
if (buffer.remaining() >= certificateLength)
|
||||
{
|
||||
buffer.get(certificate);
|
||||
if (onCertificate())
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = State.CERTIFICATE_BYTES;
|
||||
cursor = certificateLength;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CERTIFICATE_BYTES:
|
||||
{
|
||||
certificate[certificateLength - cursor] = buffer.get();
|
||||
--cursor;
|
||||
if (cursor == 0)
|
||||
{
|
||||
if (onCertificate())
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkSlotValid()
|
||||
{
|
||||
if (slot <= 0)
|
||||
throw new SessionException(SessionStatus.PROTOCOL_ERROR,
|
||||
"Invalid slot " + slot + " for " + ControlFrameType.CREDENTIAL + " frame");
|
||||
}
|
||||
|
||||
private boolean onCertificate()
|
||||
{
|
||||
certificates.add(deserializeCertificate(certificate));
|
||||
if (totalLength == 0)
|
||||
{
|
||||
onCredential();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
certificateLength = 0;
|
||||
state = State.CERTIFICATE_LENGTH;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Certificate deserializeCertificate(byte[] bytes)
|
||||
{
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
private void onCredential()
|
||||
{
|
||||
CredentialFrame frame = new CredentialFrame(controlFrameParser.getVersion(), slot,
|
||||
Arrays.copyOf(proof, proof.length), certificates.toArray(new Certificate[certificates.size()]));
|
||||
controlFrameParser.onControlFrame(frame);
|
||||
reset();
|
||||
}
|
||||
|
||||
private void reset()
|
||||
{
|
||||
state = State.SLOT;
|
||||
totalLength = 0;
|
||||
cursor = 0;
|
||||
slot = 0;
|
||||
proofLength = 0;
|
||||
proof = null;
|
||||
certificateLength = 0;
|
||||
certificate = null;
|
||||
certificates.clear();
|
||||
}
|
||||
|
||||
public enum State
|
||||
{
|
||||
SLOT, SLOT_BYTES, PROOF_LENGTH, PROOF_LENGTH_BYTES, PROOF, PROOF_BYTES,
|
||||
CERTIFICATE_LENGTH, CERTIFICATE_LENGTH_BYTES, CERTIFICATE, CERTIFICATE_BYTES
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed 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.eclipse.jetty.spdy.frames;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.cert.Certificate;
|
||||
|
||||
import org.eclipse.jetty.spdy.StandardByteBufferPool;
|
||||
import org.eclipse.jetty.spdy.StandardCompressionFactory;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.generator.Generator;
|
||||
import org.eclipse.jetty.spdy.parser.Parser;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class CredentialGenerateParseTest
|
||||
{
|
||||
@Test
|
||||
public void testGenerateParse() throws Exception
|
||||
{
|
||||
short slot = 1;
|
||||
byte[] proof = new byte[]{0, 1, 2};
|
||||
Certificate[] certificates = new Certificate[0]; // TODO
|
||||
CredentialFrame frame1 = new CredentialFrame(SPDY.V3, slot, proof, certificates);
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
|
||||
ByteBuffer buffer = generator.control(frame1);
|
||||
|
||||
Assert.assertNotNull(buffer);
|
||||
|
||||
TestSPDYParserListener listener = new TestSPDYParserListener();
|
||||
Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
|
||||
parser.addListener(listener);
|
||||
parser.parse(buffer);
|
||||
ControlFrame frame2 = listener.getControlFrame();
|
||||
|
||||
Assert.assertNotNull(frame2);
|
||||
Assert.assertEquals(ControlFrameType.CREDENTIAL, frame2.getType());
|
||||
CredentialFrame credential = (CredentialFrame)frame2;
|
||||
Assert.assertEquals(SPDY.V3, credential.getVersion());
|
||||
Assert.assertEquals(0, credential.getFlags());
|
||||
Assert.assertEquals(slot, credential.getSlot());
|
||||
Assert.assertArrayEquals(proof, credential.getProof());
|
||||
Assert.assertArrayEquals(certificates, credential.getCertificateChain());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateParseOneByteAtATime() throws Exception
|
||||
{
|
||||
short slot = 1;
|
||||
byte[] proof = new byte[]{0, 1, 2};
|
||||
Certificate[] certificates = new Certificate[0]; // TODO
|
||||
CredentialFrame frame1 = new CredentialFrame(SPDY.V3, slot, proof, certificates);
|
||||
Generator generator = new Generator(new StandardByteBufferPool(), new StandardCompressionFactory().newCompressor());
|
||||
ByteBuffer buffer = generator.control(frame1);
|
||||
|
||||
Assert.assertNotNull(buffer);
|
||||
|
||||
TestSPDYParserListener listener = new TestSPDYParserListener();
|
||||
Parser parser = new Parser(new StandardCompressionFactory().newDecompressor());
|
||||
parser.addListener(listener);
|
||||
while (buffer.hasRemaining())
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
ControlFrame frame2 = listener.getControlFrame();
|
||||
|
||||
Assert.assertNotNull(frame2);
|
||||
Assert.assertEquals(ControlFrameType.CREDENTIAL, frame2.getType());
|
||||
CredentialFrame credential = (CredentialFrame)frame2;
|
||||
Assert.assertEquals(SPDY.V3, credential.getVersion());
|
||||
Assert.assertEquals(0, credential.getFlags());
|
||||
Assert.assertEquals(slot, credential.getSlot());
|
||||
Assert.assertArrayEquals(proof, credential.getProof());
|
||||
Assert.assertArrayEquals(certificates, credential.getCertificateChain());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue