use of org.eclipse.jetty.http.HttpField in project jetty.project by eclipse.
the class HTTP2ServerConnection method upgrade.
public boolean upgrade(Request request) {
if (HttpMethod.PRI.is(request.getMethod())) {
getParser().directUpgrade();
} else {
HttpField settingsField = request.getFields().getField(HttpHeader.HTTP2_SETTINGS);
if (settingsField == null)
throw new BadMessageException("Missing " + HttpHeader.HTTP2_SETTINGS + " header");
String value = settingsField.getValue();
final byte[] settings = B64Code.decodeRFC4648URL(value == null ? "" : value);
if (LOG.isDebugEnabled())
LOG.debug("{} settings {}", this, TypeUtil.toHexString(settings));
SettingsFrame settingsFrame = SettingsBodyParser.parseBody(BufferUtil.toBuffer(settings));
if (settingsFrame == null) {
LOG.warn("Invalid {} header value: {}", HttpHeader.HTTP2_SETTINGS, value);
throw new BadMessageException();
}
getParser().standardUpgrade();
upgradeFrames.add(new PrefaceFrame());
upgradeFrames.add(settingsFrame);
// Remember the request to send a response from onOpen().
upgradeFrames.add(new HeadersFrame(1, new Request(request), null, true));
}
return true;
}
use of org.eclipse.jetty.http.HttpField in project jetty.project by eclipse.
the class HpackDecoder method decode.
public MetaData decode(ByteBuffer buffer) {
if (LOG.isDebugEnabled())
LOG.debug(String.format("CtxTbl[%x] decoding %d octets", _context.hashCode(), buffer.remaining()));
// If the buffer is big, don't even think about decoding it
if (buffer.remaining() > _builder.getMaxSize())
throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431, "Header frame size " + buffer.remaining() + ">" + _builder.getMaxSize());
while (buffer.hasRemaining()) {
if (LOG.isDebugEnabled() && buffer.hasArray()) {
int l = Math.min(buffer.remaining(), 32);
LOG.debug("decode {}{}", TypeUtil.toHexString(buffer.array(), buffer.arrayOffset() + buffer.position(), l), l < buffer.remaining() ? "..." : "");
}
byte b = buffer.get();
if (b < 0) {
// 7.1 indexed if the high bit is set
int index = NBitInteger.decode(buffer, 7);
Entry entry = _context.get(index);
if (entry == null) {
throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Unknown index " + index);
} else if (entry.isStatic()) {
if (LOG.isDebugEnabled())
LOG.debug("decode IdxStatic {}", entry);
// emit field
_builder.emit(entry.getHttpField());
// TODO copy and add to reference set if there is room
// _context.add(entry.getHttpField());
} else {
if (LOG.isDebugEnabled())
LOG.debug("decode Idx {}", entry);
// emit
_builder.emit(entry.getHttpField());
}
} else {
// look at the first nibble in detail
byte f = (byte) ((b & 0xF0) >> 4);
String name;
HttpHeader header;
String value;
boolean indexed;
int name_index;
switch(f) {
// 7.3
case 2:
case // 7.3
3:
// change table size
int size = NBitInteger.decode(buffer, 5);
if (LOG.isDebugEnabled())
LOG.debug("decode resize=" + size);
if (size > _localMaxDynamicTableSize)
throw new IllegalArgumentException();
_context.resize(size);
continue;
// 7.2.2
case 0:
case // 7.2.3
1:
indexed = false;
name_index = NBitInteger.decode(buffer, 4);
break;
// 7.2.1
case 4:
// 7.2.1
case 5:
// 7.2.1
case 6:
case // 7.2.1
7:
indexed = true;
name_index = NBitInteger.decode(buffer, 6);
break;
default:
throw new IllegalStateException();
}
boolean huffmanName = false;
// decode the name
if (name_index > 0) {
Entry name_entry = _context.get(name_index);
name = name_entry.getHttpField().getName();
header = name_entry.getHttpField().getHeader();
} else {
huffmanName = (buffer.get() & 0x80) == 0x80;
int length = NBitInteger.decode(buffer, 7);
_builder.checkSize(length, huffmanName);
if (huffmanName)
name = Huffman.decode(buffer, length);
else
name = toASCIIString(buffer, length);
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (c >= 'A' && c <= 'Z') {
throw new BadMessageException(400, "Uppercase header name");
}
}
header = HttpHeader.CACHE.get(name);
}
// decode the value
boolean huffmanValue = (buffer.get() & 0x80) == 0x80;
int length = NBitInteger.decode(buffer, 7);
_builder.checkSize(length, huffmanValue);
if (huffmanValue)
value = Huffman.decode(buffer, length);
else
value = toASCIIString(buffer, length);
// Make the new field
HttpField field;
if (header == null) {
// just make a normal field and bypass header name lookup
field = new HttpField(null, name, value);
} else {
// and/or of a type that may be looked up multiple times.
switch(header) {
case C_STATUS:
if (indexed)
field = new HttpField.IntValueHttpField(header, name, value);
else
field = new HttpField(header, name, value);
break;
case C_AUTHORITY:
field = new AuthorityHttpField(value);
break;
case CONTENT_LENGTH:
if ("0".equals(value))
field = CONTENT_LENGTH_0;
else
field = new HttpField.LongValueHttpField(header, name, value);
break;
default:
field = new HttpField(header, name, value);
break;
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("decoded '{}' by {}/{}/{}", field, name_index > 0 ? "IdxName" : (huffmanName ? "HuffName" : "LitName"), huffmanValue ? "HuffVal" : "LitVal", indexed ? "Idx" : "");
}
// emit the field
_builder.emit(field);
// if indexed
if (indexed) {
// add to dynamic table
if (_context.add(field) == null)
throw new BadMessageException(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431, "Indexed field value too large");
}
}
}
return _builder.build();
}
use of org.eclipse.jetty.http.HttpField in project jetty.project by eclipse.
the class HpackEncoder method encode.
public void encode(ByteBuffer buffer, HttpField field) {
if (field.getValue() == null)
field = new HttpField(field.getHeader(), field.getName(), "");
int field_size = field.getName().length() + field.getValue().length();
_headerListSize += field_size + 32;
final int p = _debug ? buffer.position() : -1;
String encoding = null;
// Is there an entry for the field?
Entry entry = _context.get(field);
if (entry != null) {
// Known field entry, so encode it as indexed
if (entry.isStatic()) {
buffer.put(((StaticEntry) entry).getEncodedField());
if (_debug)
encoding = "IdxFieldS1";
} else {
int index = _context.index(entry);
buffer.put((byte) 0x80);
NBitInteger.encode(buffer, 7, index);
if (_debug)
encoding = "IdxField" + (entry.isStatic() ? "S" : "") + (1 + NBitInteger.octectsNeeded(7, index));
}
} else {
// Unknown field entry, so we will have to send literally.
final boolean indexed;
// But do we know it's name?
HttpHeader header = field.getHeader();
// Select encoding strategy
if (header == null) {
// Select encoding strategy for unknown header names
Entry name = _context.get(field.getName());
if (field instanceof PreEncodedHttpField) {
int i = buffer.position();
((PreEncodedHttpField) field).putTo(buffer, HttpVersion.HTTP_2);
byte b = buffer.get(i);
indexed = b < 0 || b >= 0x40;
if (_debug)
encoding = indexed ? "PreEncodedIdx" : "PreEncoded";
} else // has the custom header name been seen before?
if (name == null) {
// unknown name and value, so let's index this just in case it is
// the first time we have seen a custom name or a custom field.
// unless the name is changing, this is worthwhile
indexed = true;
encodeName(buffer, (byte) 0x40, 6, field.getName(), null);
encodeValue(buffer, true, field.getValue());
if (_debug)
encoding = "LitHuffNHuffVIdx";
} else {
// known custom name, but unknown value.
// This is probably a custom field with changing value, so don't index.
indexed = false;
encodeName(buffer, (byte) 0x00, 4, field.getName(), null);
encodeValue(buffer, true, field.getValue());
if (_debug)
encoding = "LitHuffNHuffV!Idx";
}
} else {
// Select encoding strategy for known header names
Entry name = _context.get(header);
if (field instanceof PreEncodedHttpField) {
// Preencoded field
int i = buffer.position();
((PreEncodedHttpField) field).putTo(buffer, HttpVersion.HTTP_2);
byte b = buffer.get(i);
indexed = b < 0 || b >= 0x40;
if (_debug)
encoding = indexed ? "PreEncodedIdx" : "PreEncoded";
} else if (__DO_NOT_INDEX.contains(header)) {
// Non indexed field
indexed = false;
boolean never_index = __NEVER_INDEX.contains(header);
boolean huffman = !__DO_NOT_HUFFMAN.contains(header);
encodeName(buffer, never_index ? (byte) 0x10 : (byte) 0x00, 4, header.asString(), name);
encodeValue(buffer, huffman, field.getValue());
if (_debug)
encoding = "Lit" + ((name == null) ? "HuffN" : ("IdxN" + (name.isStatic() ? "S" : "") + (1 + NBitInteger.octectsNeeded(4, _context.index(name))))) + (huffman ? "HuffV" : "LitV") + (indexed ? "Idx" : (never_index ? "!!Idx" : "!Idx"));
} else if (field_size >= _context.getMaxDynamicTableSize() || header == HttpHeader.CONTENT_LENGTH && field.getValue().length() > 2) {
// Non indexed if field too large or a content length for 3 digits or more
indexed = false;
encodeName(buffer, (byte) 0x00, 4, header.asString(), name);
encodeValue(buffer, true, field.getValue());
if (_debug)
encoding = "LitIdxNS" + (1 + NBitInteger.octectsNeeded(4, _context.index(name))) + "HuffV!Idx";
} else {
// indexed
indexed = true;
boolean huffman = !__DO_NOT_HUFFMAN.contains(header);
encodeName(buffer, (byte) 0x40, 6, header.asString(), name);
encodeValue(buffer, huffman, field.getValue());
if (_debug)
encoding = ((name == null) ? "LitHuffN" : ("LitIdxN" + (name.isStatic() ? "S" : "") + (1 + NBitInteger.octectsNeeded(6, _context.index(name))))) + (huffman ? "HuffVIdx" : "LitVIdx");
}
}
// table and reference set.
if (indexed)
if (_context.add(field) == null)
throw new IllegalStateException();
}
if (_debug) {
int e = buffer.position();
if (LOG.isDebugEnabled())
LOG.debug("encode {}:'{}' to '{}'", encoding, field, TypeUtil.toHexString(buffer.array(), buffer.arrayOffset() + p, e - p));
}
}
use of org.eclipse.jetty.http.HttpField in project jetty.project by eclipse.
the class HpackEncoder method encode.
public void encode(ByteBuffer buffer, MetaData metadata) {
if (LOG.isDebugEnabled())
LOG.debug(String.format("CtxTbl[%x] encoding", _context.hashCode()));
_headerListSize = 0;
int pos = buffer.position();
// Check the dynamic table sizes!
int maxDynamicTableSize = Math.min(_remoteMaxDynamicTableSize, _localMaxDynamicTableSize);
if (maxDynamicTableSize != _context.getMaxDynamicTableSize())
encodeMaxDynamicTableSize(buffer, maxDynamicTableSize);
// Add Request/response meta fields
if (metadata.isRequest()) {
MetaData.Request request = (MetaData.Request) metadata;
// TODO optimise these to avoid HttpField creation
String scheme = request.getURI().getScheme();
encode(buffer, new HttpField(HttpHeader.C_SCHEME, scheme == null ? HttpScheme.HTTP.asString() : scheme));
encode(buffer, new HttpField(HttpHeader.C_METHOD, request.getMethod()));
encode(buffer, new HttpField(HttpHeader.C_AUTHORITY, request.getURI().getAuthority()));
encode(buffer, new HttpField(HttpHeader.C_PATH, request.getURI().getPathQuery()));
} else if (metadata.isResponse()) {
MetaData.Response response = (MetaData.Response) metadata;
int code = response.getStatus();
HttpField status = code < __status.length ? __status[code] : null;
if (status == null)
status = new HttpField.IntValueHttpField(HttpHeader.C_STATUS, code);
encode(buffer, status);
}
// Add all the other fields
for (HttpField field : metadata) encode(buffer, field);
// Check size
if (_maxHeaderListSize > 0 && _headerListSize > _maxHeaderListSize) {
LOG.warn("Header list size too large {} > {} for {}", _headerListSize, _maxHeaderListSize);
if (LOG.isDebugEnabled())
LOG.debug("metadata={}", metadata);
}
if (LOG.isDebugEnabled())
LOG.debug(String.format("CtxTbl[%x] encoded %d octets", _context.hashCode(), buffer.position() - pos));
}
use of org.eclipse.jetty.http.HttpField in project jetty.project by eclipse.
the class HeadersGenerateParseTest method testGenerateParse.
@Test
public void testGenerateParse() throws Exception {
HeadersGenerator generator = new HeadersGenerator(new HeaderGenerator(), new HpackEncoder());
int streamId = 13;
HttpFields fields = new HttpFields();
fields.put("Accept", "text/html");
fields.put("User-Agent", "Jetty");
MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
final List<HeadersFrame> frames = new ArrayList<>();
Parser parser = new Parser(byteBufferPool, new Parser.Listener.Adapter() {
@Override
public void onHeaders(HeadersFrame frame) {
frames.add(frame);
}
}, 4096, 8192);
// Iterate a few times to be sure generator and parser are properly reset.
for (int i = 0; i < 2; ++i) {
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
PriorityFrame priorityFrame = new PriorityFrame(streamId, 3 * streamId, 200, true);
generator.generateHeaders(lease, streamId, metaData, priorityFrame, true);
frames.clear();
for (ByteBuffer buffer : lease.getByteBuffers()) {
while (buffer.hasRemaining()) {
parser.parse(buffer);
}
}
Assert.assertEquals(1, frames.size());
HeadersFrame frame = frames.get(0);
Assert.assertEquals(streamId, frame.getStreamId());
Assert.assertTrue(frame.isEndStream());
MetaData.Request request = (MetaData.Request) frame.getMetaData();
Assert.assertEquals(metaData.getMethod(), request.getMethod());
Assert.assertEquals(metaData.getURI(), request.getURI());
for (int j = 0; j < fields.size(); ++j) {
HttpField field = fields.getField(j);
Assert.assertTrue(request.getFields().contains(field));
}
PriorityFrame priority = frame.getPriority();
Assert.assertNotNull(priority);
Assert.assertEquals(priorityFrame.getStreamId(), priority.getStreamId());
Assert.assertEquals(priorityFrame.getParentStreamId(), priority.getParentStreamId());
Assert.assertEquals(priorityFrame.getWeight(), priority.getWeight());
Assert.assertEquals(priorityFrame.isExclusive(), priority.isExclusive());
}
}
Aggregations