/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.http; import io.datakernel.bytebuf.ByteBuf; import io.datakernel.bytebuf.ByteBufStrings; import io.datakernel.exception.ParseException; import java.util.ArrayList; import java.util.List; import static io.datakernel.bytebuf.ByteBufStrings.equalsLowerCaseAscii; import static io.datakernel.http.HttpUtils.parseQ; import static io.datakernel.http.HttpUtils.skipSpaces; public final class AcceptMediaType { public static final int DEFAULT_Q = 100; private static final byte[] Q_KEY = new byte[]{'q'}; private MediaType mime; private int q; private AcceptMediaType(MediaType mime, int q) { this.mime = mime; this.q = q; } private AcceptMediaType(MediaType mime) { this(mime, DEFAULT_Q); } public static AcceptMediaType of(MediaType mime) { return new AcceptMediaType(mime); } public static AcceptMediaType of(MediaType mime, int q) { return new AcceptMediaType(mime, q); } static List<AcceptMediaType> parse(byte[] bytes, int pos, int length) throws ParseException { List<AcceptMediaType> cts = new ArrayList<>(); parse(bytes, pos, length, cts); return cts; } static void parse(byte[] bytes, int pos, int length, List<AcceptMediaType> list) throws ParseException { int end = pos + length; while (pos < end) { // parsing media type pos = skipSpaces(bytes, pos, end); int start = pos; int lowerCaseHashCode = 1; while (pos < end && !(bytes[pos] == ';' || bytes[pos] == ',')) { byte b = bytes[pos]; if (b >= 'A' && b <= 'Z') { b += 'a' - 'A'; } lowerCaseHashCode = lowerCaseHashCode * 31 + b; pos++; } MediaType mime = MediaTypes.of(bytes, start, pos - start, lowerCaseHashCode); if (pos < end) { if (bytes[pos++] == ',') { list.add(AcceptMediaType.of(mime)); } else { int q = DEFAULT_Q; pos = skipSpaces(bytes, pos, end); start = pos; while (pos < end && bytes[pos] != ',') { if (bytes[pos] == '=' && equalsLowerCaseAscii(Q_KEY, bytes, start, pos - start)) { start = ++pos; while (pos < end && !(bytes[pos] == ';' || bytes[pos] == ',')) { pos++; } q = parseQ(bytes, start, pos - start); pos--; } else if (bytes[pos] == ';') { pos = skipSpaces(bytes, pos + 1, end); start = pos; } pos++; } list.add(AcceptMediaType.of(mime, q)); pos++; } } else { list.add(AcceptMediaType.of(mime)); } } } static void render(List<AcceptMediaType> types, ByteBuf buf) { int pos = render(types, buf.array(), buf.writePosition()); buf.writePosition(pos); } static int render(List<AcceptMediaType> types, byte[] container, int pos) { for (int i = 0; i < types.size(); i++) { AcceptMediaType type = types.get(i); pos += MediaTypes.render(type.mime, container, pos); if (type.q != DEFAULT_Q) { container[pos++] = ';'; container[pos++] = ' '; container[pos++] = 'q'; container[pos++] = '='; container[pos++] = '0'; container[pos++] = '.'; int q = type.q; if (q % 10 == 0) q /= 10; pos += ByteBufStrings.encodeDecimal(container, pos, q); } if (i < types.size() - 1) { container[pos++] = ','; container[pos++] = ' '; } } return pos; } int estimateSize() { return mime.size() + 10; } public MediaType getMediaType() { return mime; } public int getQ() { return q; } @Override public String toString() { return "AcceptContentType{" + "mime=" + mime + ", q=" + q + '}'; } }