From ef7f4871b82e6148062f00023bbad05dddeea795 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Mon, 14 Dec 2020 14:30:39 -0500 Subject: [PATCH] Remove dependency on the java.desktop module The Favicon.create(BufferedImage) method has been removed. In its place we have Favicon.create(Path) (carried over from the 1.x.x series) and Favicon.create(byte[]) (brand new). --- .../com/velocitypowered/api/util/Favicon.java | 32 +++++------------ .../api/util/FaviconChecker.java | 34 ++++++++++++++++++ .../api/util/FaviconCheckerTest.java | 33 +++++++++++++++++ .../api/util/test_icon_dimensions_correct.png | Bin 0 -> 1805 bytes .../api/util/test_icon_dimensions_wrong.png | Bin 0 -> 4655 bytes .../test_icon_dimensions_wrong_format.jpg | Bin 0 -> 2949 bytes .../com/velocitypowered/proxy/Velocity.java | 4 --- 7 files changed, 75 insertions(+), 28 deletions(-) create mode 100644 api/src/main/java/com/velocitypowered/api/util/FaviconChecker.java create mode 100644 api/src/test/java/com/velocitypowered/api/util/FaviconCheckerTest.java create mode 100644 api/src/test/resources/com/velocitypowered/api/util/test_icon_dimensions_correct.png create mode 100644 api/src/test/resources/com/velocitypowered/api/util/test_icon_dimensions_wrong.png create mode 100644 api/src/test/resources/com/velocitypowered/api/util/test_icon_dimensions_wrong_format.jpg diff --git a/api/src/main/java/com/velocitypowered/api/util/Favicon.java b/api/src/main/java/com/velocitypowered/api/util/Favicon.java index a3ce28164..9ee159991 100644 --- a/api/src/main/java/com/velocitypowered/api/util/Favicon.java +++ b/api/src/main/java/com/velocitypowered/api/util/Favicon.java @@ -1,15 +1,11 @@ package com.velocitypowered.api.util; import com.google.common.base.Preconditions; -import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Base64; import java.util.Objects; -import javax.imageio.ImageIO; import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -65,23 +61,17 @@ public final class Favicon { } /** - * Creates a new {@code Favicon} from the specified {@code image}. + * Creates a new {@code Favicon} from the specified {@code buffer}. * - * @param image the image to use for the favicon + * @param buffer the buffer to use for the favicon * @return the created {@link Favicon} instance + * @throws IOException if the file could not be read from the path */ - public static Favicon create(BufferedImage image) { - Preconditions.checkNotNull(image, "image"); - Preconditions.checkArgument(image.getWidth() == 64 && image.getHeight() == 64, - "Image is not 64x64 (found %sx%s)", image.getWidth(),image.getHeight()); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - try { - ImageIO.write(image, "PNG", os); - } catch (IOException e) { - throw new AssertionError(e); + public static Favicon create(byte[] buffer) throws IOException { + if (!FaviconChecker.check(buffer)) { + throw new IllegalArgumentException("Image is not a PNG file or does not have 64x64 dimensions"); } - return new Favicon( - "data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray())); + return new Favicon("data:image/png;base64," + Base64.getEncoder().encodeToString(buffer)); } /** @@ -92,12 +82,6 @@ public final class Favicon { * @throws IOException if the file could not be read from the path */ public static Favicon create(Path path) throws IOException { - try (InputStream stream = Files.newInputStream(path)) { - BufferedImage image = ImageIO.read(stream); - if (image == null) { - throw new IOException("Unable to read the image."); - } - return create(image); - } + return create(Files.readAllBytes(path)); } } diff --git a/api/src/main/java/com/velocitypowered/api/util/FaviconChecker.java b/api/src/main/java/com/velocitypowered/api/util/FaviconChecker.java new file mode 100644 index 000000000..4dcd5a374 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/util/FaviconChecker.java @@ -0,0 +1,34 @@ +package com.velocitypowered.api.util; + +import java.nio.ByteBuffer; + +class FaviconChecker { + private static final byte[] PNG_MAGIC = new byte[] { + (byte) 137, 80, 78, 71, 13, 10, 26, 10 + }; + private static final byte[] IHDR_NAME = new byte[] { + 73, 72, 68, 82 + }; + + public static boolean check(byte[] data) { + ByteBuffer buf = ByteBuffer.wrap(data); + + for (byte value : PNG_MAGIC) { + if (buf.get() != value) { + return false; + } + } + + buf.position(buf.position() + 4); + for (byte value : IHDR_NAME) { + if (buf.get() != value) { + return false; + } + } + + int width = buf.getInt(); + int height = buf.getInt(); + + return width == 64 && height == 64; + } +} diff --git a/api/src/test/java/com/velocitypowered/api/util/FaviconCheckerTest.java b/api/src/test/java/com/velocitypowered/api/util/FaviconCheckerTest.java new file mode 100644 index 000000000..b61b19323 --- /dev/null +++ b/api/src/test/java/com/velocitypowered/api/util/FaviconCheckerTest.java @@ -0,0 +1,33 @@ +package com.velocitypowered.api.util; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.common.io.Resources; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +public class FaviconCheckerTest { + + @Test + void handlesProperlyFormattedFavicon() throws IOException { + assertTrue(FaviconChecker.check(readFile("test_icon_dimensions_correct.png")), + "Correctly formatted favicon is not valid"); + } + + @Test + void handlesBadDimensionFavicon() throws IOException { + assertFalse(FaviconChecker.check(readFile("test_icon_dimensions_wrong.png")), + "Incorrect dimension favicon is valid?"); + } + + @Test + void handlesBadFormatFavicon() throws IOException { + assertFalse(FaviconChecker.check(readFile("test_icon_dimensions_wrong_format.jpg")), + "Incorrect format favicon is valid?"); + } + + private static byte[] readFile(String path) throws IOException { + return Resources.toByteArray(FaviconCheckerTest.class.getResource(path)); + } +} diff --git a/api/src/test/resources/com/velocitypowered/api/util/test_icon_dimensions_correct.png b/api/src/test/resources/com/velocitypowered/api/util/test_icon_dimensions_correct.png new file mode 100644 index 0000000000000000000000000000000000000000..b20eabf2f38b1adfcda25a7425d9136f1e12e37d GIT binary patch literal 1805 zcmV+o2lDudP)EX>4Tx04R}tkv&MmKpe$iQ$>-gpdCaUGE^rEej$!pg(6f4wL+^7CYOFelZGV4 z#ZhoAIQX$xb#QUk)xlK|1V2EW9Gw(hq{ROvg%&X$9QWhhy~o`h82gwTUNL@_Qi%b1g-1boNWJpz2ai}Ec0bAOI*HES^-AQI0q!?cMvh^IGg zgY!Odm=$D|_?&puqze*1a$WKGjdR{*foF!zRC1m;Of2SGSZQHaFg4;S;)ts0lrN+` zRyl8R)=CxDxF>&MD66k5bDic8;#kBIB#2N@K@la`)K?KYDHf8nAM^1Kx_*gV3b~44 zb3)HKQ^L^|%^%EfY3|#3=f4K(Ce3D*m zYLO$LcN@64ZfeRNaJd5vJQ=bnyHb#*kjnw@XY@@Op#K)=TJw5q?BnzSNKsdb8{ps& z7|B!iy3e~iTYLNWOryUa!q;-g99X*T00006VoOIv0RI600RN!9r;`8x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00iYpL_t(|+U;6PNHS3r{;~;)tW8#nsD&_rZgSB+;*UtE zAZgJHS`-pOs}=>d3fe^rC0tcxLC^yUTD1v+AgM5@D6CBnkkq0aO~vB;tA-v^<7={V z4hVYhopbN`&Uel|_l`M4M95-cUjL}7s^Y)9-R`UaAm@bvuZ@2#@c{9gfm3{CyjY)5 zzu5R62|ywqPNn_V0`MC>n-BoW#O+@JM3GGL`b%t3q>}MJlX&&Ke11g^h*y5)Hu&At z|4AXF$Y$>g0+6f`-~F5P;_6#G%0+OX|Di^FTb0o^f zxGzNh>U#pw(_lO_k^&r|bx6Joq+*cN43X+GhWSpq#S7ARh8vUV7!=DpUwK}vKc2aB zvf=IUSMNO)$a2ZarvC>B9geb|0WtvDNPn3DKdBDLM8){J_K3J-J*iGMmj*TU$#Oi-kg=5Qzzd8XXP?O;1n1X`7gsprxfH3WvjV zcXvl4BO`Qtd`y17pX%%D$>Z@*G#VwB%SDfmkDu!K3IL16LRPDl0)YT=97mg*o4k=z zQ&VKK*@)vf3IqZ)IXOvoyPXGsWmy^>9i`jbTY7war19}_T3K014*(vIhwAF;cyAC9 z9UdN%&1R$X^K)uyYT}I+1M)Kfd_EtsEK3g$4gtN`-`(A%;o)IX0OHyPyQ}ZdWwyW4Kz15!{KnCtgI~YqiIe~LSu7tbET;S-!G)Jv=lo#I|v4YZ@j=4 z6AA@%IvsR69b&N<+-^6@%E}TlsHCI>E|&`&$Dy#W@WcEE2M2HF_xt?_1Oia2)#)#U z3JVKyd3lL&I1Hc9hvMR5m`o@TZl%ZxWB*0>FFuT z%gY7zhyl>s+l%@6c?5$&ghC;#t*wFMIH=WX3=9llad8o$PzW5yVPRnbLqkJ?4oMk+ znwlDf!(p_yx5MRf0RVb?dr?wSf{u<3w6?b5@bC~CjRq=}3MVHgFc=IN92`VXPY*O2 z&8K?460_M1hGAeZ7+^M=p;RhSP*4B>FquqHsZ?ldYeQ#eCn_o`(ACu?-w4U~0WttG v05SkF05SkF05SkF05SkF05SkF07=My1I9JHsm}v)00000NkvXXu0mjf1C}z{ literal 0 HcmV?d00001 diff --git a/api/src/test/resources/com/velocitypowered/api/util/test_icon_dimensions_wrong.png b/api/src/test/resources/com/velocitypowered/api/util/test_icon_dimensions_wrong.png new file mode 100644 index 0000000000000000000000000000000000000000..c3f0357b51fa6297aee948a0abbeaf29e8b6d2b1 GIT binary patch literal 4655 zcmX9?1yqyo8{Gh@5d&!_Al)e`DKRG@T~fl3kj{yOlyr9qKT?E=fH*=z5D)=L86ko+ z(v5=he>wlNv+bPseBZa{eeQkkz3*c6bdi+gEaVUfgc5a6-2i;6UcE_)!DrHGzXk*X zjdWI1(?hALv3q)ZJa%?pKTGua;SlEdV#vt*rL4Bl>O(WhCbq}t`+hrT!k;1eWK-x(bH3<;hvf>rIF`nNPEY2 zxKG^@5^G7`9A~?1rozmE##8?z>1b{APQPw#z(@S9L-uQ**hn30qB`j?cuV&9I{O)g z<%UjZGx1xB7K080vF706J!h822%v!bqEW~zNNOj2N)vLIzG99dt zdEPVkfj~q!uij8hxw1c4B=JS*Xpk&ZP|=EWvb}ej0!z%k8fLy~9&T<9?!FK;Z-+;| z4rulOXJ03FBuYomG>nQ70)dmD)K!fzGuwI3Tn!yAJC)boS)_kEttXFFm_%$!Sspe2)EVYMY>#@z}aFq9^~&MauZ zJ(KvxN_6dSH*U?LW)uE*BN?xVx^y}(SUZrLKR+a`7aD-n3oZG#{vI~D)&1aLMa|OGYcK}e+bp9ljf!3C>+7Anr-a3$PI^^Frwoyh=gG?da@=O$ zk4{c%nwc>P3k$1jY95@-dR%8@#0!%q_jQ}v1Z!w$Tr1&|@c5xXOohNaK&8%X`#1P* z7`%6z?H$UOp&^2T>ry$9BTlz53asShQc}>7$;sZup7>pU zc!|)!1Rg&=IXSsOtqR`@Y`fU&rWUeng2(riOvOJZcbj?}_O04rX% z$yz@`@!5%06wGBPq2PS2TdJHY|- zWW(7n+{J#`qC=@6=2`qAB0T*3WY6bX0~+jl$OQxi={YyOo0|fU1!~()EEwUqVfwQW z-gplWkF|zjrEa^ry`Rx+9Haz8Lqk`OJq{Gl)gH`~s_u*2p+-uT@TKMC#F$h&obJrg zF)(-o-0ke_0DX=&#-pdE%uKKr8c&`G^6D1YF7+nn46TaT7b=IoPY0g*RsHmSSXDCn46bZ&`vb2gjRX|I1sc#?&$%SW@+7QoreB$Wl@M-PaM7L z_ome4WprA4x_L*4qUjq;GPw@jN=_-M%!vsjT3XtFh~0&K%+x#Q>ubYByQyIAg7$kD zKlSwy>gnsNf|(v29gV95UDqrfE&k@xx*lk6&sknxp1>p?zSNg&Xl6F0Ax{vzxw$#r z;#XZ?@%&bStk+`EF2~;xu!7Z?rC~fmS#vuJAp3N1YE*wQfrMZyEb0=fVxH0@2zbWFaI7!p{%kp zfE;wiNaD-MiN69XHA8$W9`=oZePI3{qfyH2)en=$)#xYxE~OlIvta?}cY zDwQ~6bMyZH)S^?k$g&4CG&P9{NvO6MmJ@Ht@bU9M`kZnFlkBZ-J9S%^BzwX*-U(llD^8{jnSrSmC4-9Q{ME19O4lWE6Xu}1{0 z{@YzE-q)z})vhtzym|9#=4tTPd3E1D>Lwl=8_NiL_Jv(iLL!BWA8pG+8oua-pdta9 z&7rKU>|_IaZ)kX3zA)+4E4~lbqUk(3i{DIb%CXo)+>byl{QzJqc~VdKL`A80ZU!mY z;L`SLL;u21X)PCT465j4Ug0;V+k@{oek}k9T!bjw_a$xHow?5k85q!>pKN#Ailr13 zP;`b7lDw*_0z&s<6JKjDksHpUmp#yvz$}UR_j}FR#bt3wX1=VvJPNe<1=Y>CIrm-K zyiH$uZd=270bQT9uRSh}-X~k;6E0HqLiTlLG4b)GuU^H7+xHS-WzK?>w)|VuG(-13 zl9*vlh*W{S`fN?rrA)H1D<~95J(&bPb%pVUFHrs>IFLfnaFJ|SEq)do$-~Vp?z^rh z#1Nmhe$6_3ewUixYZ2?=vp(`49UYw*`ZL_c#YM0B8xZh1uccmhC`7wZKC{_p&A`x5 z&CLy4GTy;ULS|IXh3ax?JovL&+Ys_M&`5yZzqRnn!=4^27x$Xg;;&|hid7+Ic&(L_ zzV+K#fLChw3rhR_#h$?;Wj1{}W(o8?9KUa|0~#&%?8No)P~NwmhR^ji6HM+{?iUv8 zfHj{#e`Z%yyqQ0w>FO#(6&(SFK=8$FcsR*X&_I?jKs~C)ku)DmOP*agsion(gby2oh*Uu>ho^qC#`y^vAPZal22XLJTl>+>MBjonYvO z>Kz7J5~eF3uz@ic**t#r?N`c<6`bgGb@fxAUSOhBvsUH_x;yH_0hD67+JzxEfSF`w zW`0?8Fz-c6q-JD9npBrJHRUi`V{WZu0Dukvkih~u!+m(Y&^X{ZFk9AS-d8l#HMax> z<4a0PIvDQ0&C19qo8WNwsH>|}__JP$e*Bo&xB*ZC$m?`RsB*o~b$a?JE>z7!9F=)i zY)lL(^WWayUI{R@9c*_W2m^Kjgrc?%N7ShO{wVoxO^F0sJbYMHUCk*hObK$%@1Q12 z5kS(@N&QhP={Do4M-UZ1&EH2LHwiC}u#5eDOb<*_!c< z32I!BP%w3lKTyV^2QPi#KyM!?9@BxiVU=KSXUCyblnln5M<=fg!4Nyg zoY)zapga@!W#NOhN9C+Xs0l3NjF1h8h?|=*PkJn~51JNq@<)CN$0CE zSnjG*3p2pdxcD{3W}(K$#`-ok!)2)S^;z6*5eFI22y$}cEC()>-NAByVI1<8E$oSt z(?VAi3iX!Zw~v;Wm-#Y_Z0)bT+&}*d zA}MHlID4^4b!IaGgALY1FgD|U`Q6gdBTUYO#!dg7@BP4>>db@y=H8coPbD3`(1C;q zwgE(vAoIS4ySoUdW&%5ib2Bp*AQ)(?sSyGqYUhYSqq{#&Vjs%+uEPOT6gQ+3_zf$3 z<{obNKE$!PjOy|h^2@pp> zRpC>0_9Va{9A7>qZnV$=whr8$7lZ(i#!~Y02i`f4zqP=E5Ya<`L?R{qx6H=I$0J^w zAdw_89zWEx97Jr|wVytfo1UH?%n>Jy?hc#X)`Sy*nY3Y>zg-Jg}Qw)G@LPUaa~}R zb8}w6n;@WTg%B!pb}_y(vxEiH{n#vK9D=%-JgJm-T?LDI#S zu5FC|>aW)Z#t!0F5U{Gq=xEM?dV}u>aP5`H)zl;aTS&{!j_ybQI-QiTd4_+rVzi(lQd4TE}pI0jpG9^X7xS6BDko{<9Ofu7x8F)JMfkrW6# zCfW3eS2L}fr8Ud05ak_Hu4d1a^n+}m|6`(4B zrCgGd>1SscF4W@ZRL+d-Z2N}kdsy^`4 zLb~L>2>hl7T~Be}zpzfey+Jn0EP`KwPZsEZWx3BtfeA(c#9TMri7iOzJehu#1n1p(Z^FpL2J zfkEMXHp7PE;<}B3dk%;|6375OV94Qyh0s_m2k=dK_XxmD0Ceh!a(&b7zZ)dHeZsf^ zpeWF6%?;s)L+lCh>OJ8hBAf#;nd`^#hPWJJT|V3(#9bo2*C#wK!aEl+0wEytx3Fzt zZDfckJO6^c{(`xF{6Oeq0)437fk7}o;_?Z5i*S?(2L|khds`Gyg+!kq=gsg_9LG1f0cqAUXr+ z1LZ5XVZO#jiz*b+1_11UQ21sA0JsbQ7E*=6KMRGzg_8iF`v7PR`Whcx0D$RRs89W> zqviu3l>oq{8((!^*#Oie0if6&!r^lkzk`Oqh_5dI)1?4NxB?)19{@48PrN~!$PS7g z1F!{hVewcj27?vD;g;Zu zVnm|2n7Fuvqzp+yQd&}6oFq?@mLdPb2S}N!TUxr3p?paj4239Z{oy5*7GeDhVJ60!v;zA(0-t1dTxLQS^7{SP zBwt$7nRO?k@o06HO~vj&WAkNO`vT*`y49|&O}?B})P7Y}`Sgt zC`ny&T>W%UmIKqj?ajnqwpmfRIqi?Sx?X{L)rsTJRqAAe=}{rmHh zI<)y15!=%L$+qWWm1q>o3Z?!x4#*b=Q^*?%jl$qymwaLqg`fdOS_UhtYDiX7H`>Gr zCCO1}O~!F)>4nr));4w-4?3nkbGgI{$+)=7FJnC8gHlw~sAH)qDQIsB;fb4ywwqhR zW^B_Ba*XcD_Q9#1zR~8%CXYd z6}ysn|K{uCOq1IU0*UK!V0O5o;$CXvka^r1)(fXh_kC?v5q)k)_&V{(d1Bc4fk%UR zC)Z$yvMkvx?aFcw#)tYDx%tN|PnBNXFxG13JJi3tv0{ICO+afksnp-7<2jf5u%rLB zVkPhSaR>gTqV&*LQKP%oy~8ni#dovy!kPrS9j;7H)_y;ob0;h|r?^?lUDC^Ru46N# zsqgodj(Bv$L<+(A=9)FriYI;V)qu7(ee{c?;= zKkdP{sdRB4v$W%v9c-npMDvcMj=p_!+M(@&;ST@Wxei)TRrB7qn->`S9u=o7@9I0P ze|nD~wx~WumK3Ksk-gPoazWed*#Y$d%39}^N-bXpQd4%$stVt9qpn8^%l8gg2sGDf z_hqlDmuxG_I-jZW+Es0L`}WXWl}vw`+e$hm7xO0Hl=|EmO|};R#7>J7=9> z1FDnYm4i1E4ztg#3TKI`;U*d();F|e-saxJY?vukbj6WbK#n{^+C*jk2P7ZfLM z)wc=$Fv{p~imptS#q=Mz8hdU1x*b|aX7wMt{&?`G2gYe-PPtdzlCKABds5VRIUsY# zf_}RNzFn3z|8%PI4E=gr=ZyI?g-teUakQ>MC9!{477vmuE7x0%_8V#_f?nm5y_cnA+tKar$|Hgcm)7>qNDt$4un>XrB72-zj2qqamIO`LA zNiIS01fLn>!w=)*$CowqJ)ynFDR|C4Bjw&(`e)gy3!&0>5@l}^jaT|+-`Z$4phSG& z{QkFzl#%;~SNuA!j>5#!sOe16 z%Kw|0AsQ;A|20$=9aQ0=e>(QBhpq|?X`48tP>OZhm+1l1W!q`G*ql6DS4~wk+#vbg zmRt5Z!#lL-O(h+yKc?3mZ#4~`O7GRnk8z!(54w9JCL0A+2cyhy9_)2xpF+-hm$>iS zK)I0lWJ^IV{=zc4F8%ct=YX)g;vHzU`c6l#(`J<*YQXzxs{{0EvVQVu3VGdwS-N@&CywCzw&Y6iH=X9t)0 zEgVZ6aw|02RCp^Q&{Ko78g(wWwOLiliJ%vSseTfLXAaMum=OXc>B+(CIdzD2 z;nc-r?ejkCZ0*HUwB8PGo`1T({dtqYaCUV4PrSpHs)=^hoQ9Dmu?BT*wWM1L@$QN@ zA_*RK(X~UK@~AlaIVpj=a&DGfg5g+K8#}Xd;-G<)nR^hY>^9a;;qixAYb@(5o-uQ% zA>)ZI-nkg5LlvQ?S?tcQgF!Z_r literal 0 HcmV?d00001 diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java b/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java index ed8094cbe..85811abfd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java @@ -11,10 +11,6 @@ public class Velocity { private static final Logger logger = LogManager.getLogger(Velocity.class); static { - // We use BufferedImage for favicons, and on macOS this puts the Java application in the dock. - // How inconvenient. Force AWT to work with its head chopped off. - System.setProperty("java.awt.headless", "true"); - // By default, Netty allocates 16MiB arenas for the PooledByteBufAllocator. This is too much // memory for Minecraft, which imposes a maximum packet size of 2MiB! We'll use 4MiB as a more // sane default.