Search in sources :

Example 41 with HttpField

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;
}
Also used : PrefaceFrame(org.eclipse.jetty.http2.frames.PrefaceFrame) SettingsFrame(org.eclipse.jetty.http2.frames.SettingsFrame) HttpField(org.eclipse.jetty.http.HttpField) BadMessageException(org.eclipse.jetty.http.BadMessageException) Request(org.eclipse.jetty.http.MetaData.Request) HeadersFrame(org.eclipse.jetty.http2.frames.HeadersFrame)

Example 42 with HttpField

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();
}
Also used : BadMessageException(org.eclipse.jetty.http.BadMessageException) Entry(org.eclipse.jetty.http2.hpack.HpackContext.Entry) HttpHeader(org.eclipse.jetty.http.HttpHeader) HttpField(org.eclipse.jetty.http.HttpField)

Example 43 with HttpField

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));
    }
}
Also used : StaticEntry(org.eclipse.jetty.http2.hpack.HpackContext.StaticEntry) Entry(org.eclipse.jetty.http2.hpack.HpackContext.Entry) HttpHeader(org.eclipse.jetty.http.HttpHeader) HttpField(org.eclipse.jetty.http.HttpField) PreEncodedHttpField(org.eclipse.jetty.http.PreEncodedHttpField) PreEncodedHttpField(org.eclipse.jetty.http.PreEncodedHttpField)

Example 44 with HttpField

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));
}
Also used : MetaData(org.eclipse.jetty.http.MetaData) HttpField(org.eclipse.jetty.http.HttpField) PreEncodedHttpField(org.eclipse.jetty.http.PreEncodedHttpField)

Example 45 with HttpField

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());
    }
}
Also used : ByteBufferPool(org.eclipse.jetty.io.ByteBufferPool) MappedByteBufferPool(org.eclipse.jetty.io.MappedByteBufferPool) ArrayList(java.util.ArrayList) HeadersGenerator(org.eclipse.jetty.http2.generator.HeadersGenerator) HpackEncoder(org.eclipse.jetty.http2.hpack.HpackEncoder) ByteBuffer(java.nio.ByteBuffer) Parser(org.eclipse.jetty.http2.parser.Parser) MetaData(org.eclipse.jetty.http.MetaData) HostPortHttpField(org.eclipse.jetty.http.HostPortHttpField) HttpField(org.eclipse.jetty.http.HttpField) HttpFields(org.eclipse.jetty.http.HttpFields) HeaderGenerator(org.eclipse.jetty.http2.generator.HeaderGenerator) HostPortHttpField(org.eclipse.jetty.http.HostPortHttpField) Test(org.junit.Test)

Aggregations

HttpField (org.eclipse.jetty.http.HttpField)57 Test (org.junit.Test)29 HttpFields (org.eclipse.jetty.http.HttpFields)19 ByteBuffer (java.nio.ByteBuffer)17 MetaData (org.eclipse.jetty.http.MetaData)16 HostPortHttpField (org.eclipse.jetty.http.HostPortHttpField)12 ArrayList (java.util.ArrayList)8 PreEncodedHttpField (org.eclipse.jetty.http.PreEncodedHttpField)7 Entry (org.eclipse.jetty.http2.hpack.HpackContext.Entry)7 ByteBufferPool (org.eclipse.jetty.io.ByteBufferPool)7 MappedByteBufferPool (org.eclipse.jetty.io.MappedByteBufferPool)7 HttpHeader (org.eclipse.jetty.http.HttpHeader)6 HttpServletResponse (javax.servlet.http.HttpServletResponse)5 ContentResponse (org.eclipse.jetty.client.api.ContentResponse)5 HeaderGenerator (org.eclipse.jetty.http2.generator.HeaderGenerator)5 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)4 HpackEncoder (org.eclipse.jetty.http2.hpack.HpackEncoder)4 Parser (org.eclipse.jetty.http2.parser.Parser)4 CountDownLatch (java.util.concurrent.CountDownLatch)3 HttpServletRequest (javax.servlet.http.HttpServletRequest)3