<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body style="overflow-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;">Hi,<div><br></div><div>Although, having the JDK specify an API for an http server has been awesome, but I think an unfortunate design decision was to make Headers a concrete class instead of an interface. I don’t think this can be easily changed now without serious backwards compatibility issues.</div><div><br></div><div>With a high performance http server, often the most expensive element is the encoding/decoding/processing of the “headers” when accessing cached resources, etc. Garbage generation can be a real problem for low-latency high volume servers. Since the headers survive for the duration of the request, the are subject to not being quickly cleaned in the new generation.</div><div><br></div><div>A couple of proposals:</div><div><br></div><div>1. The first is to change the implementation of ’normalize’ in Headers. Currently, the cost is paid on every put and every get - even if the developer is aware of the ’normalized’ format and uses that, e.g. getFirst(“Content-length”)</div><div><br></div><div>By changing the code to:</div><div><br></div><div><div style="color: rgb(64, 64, 64); background-color: rgb(255, 255, 255); font-family: Menlo, Monaco, "Courier New", monospace; font-size: 12px; line-height: 18px; white-space: pre;"><div>    <span style="color: rgb(63, 151, 223);">public</span> <span style="color: rgb(63, 151, 223);">static</span> <span style="color: rgb(70, 224, 192);">String</span> <span style="color: rgb(99, 99, 36);">normalizeOption8</span>(<span style="color: rgb(70, 224, 192);">String</span> <span style="color: rgb(9, 89, 132);">key</span>) {</div><div>        <span style="color: rgb(70, 224, 192);">int</span> <span style="color: rgb(9, 89, 132);">len</span> <span style="color: rgb(54, 54, 54);">=</span> <span style="color: rgb(9, 89, 132);">key</span>.<span style="color: rgb(99, 99, 36);">length</span>();</div><div>        <span style="color: rgb(157, 78, 150);">if</span> (<span style="color: rgb(9, 89, 132);">len</span> <span style="color: rgb(54, 54, 54);">==</span> <span style="color: rgb(73, 104, 57);">0</span>) {</div><div>            <span style="color: rgb(157, 78, 150);">return</span> <span style="color: rgb(9, 89, 132);">key</span>;</div><div>        }</div><div>        <span style="color: rgb(70, 224, 192);">int</span> <span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);">=</span><span style="color: rgb(73, 104, 57);">0</span>;</div><div>        <span style="color: rgb(70, 224, 192);">char</span> <span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">=</span> <span style="color: rgb(9, 89, 132);">key</span>.<span style="color: rgb(99, 99, 36);">charAt</span>(<span style="color: rgb(73, 104, 57);">0</span>);</div><div>        <span style="color: rgb(157, 78, 150);">if</span> (<span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">==</span> <span style="color: rgb(162, 86, 55);">'</span><span style="color: rgb(152, 116, 40);">\r</span><span style="color: rgb(162, 86, 55);">'</span> <span style="color: rgb(54, 54, 54);">||</span> <span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">==</span> <span style="color: rgb(162, 86, 55);">'</span><span style="color: rgb(152, 116, 40);">\n</span><span style="color: rgb(162, 86, 55);">'</span>)</div><div>            <span style="color: rgb(157, 78, 150);">throw</span> <span style="color: rgb(157, 78, 150);">new</span> <span style="color: rgb(99, 99, 36);">IllegalArgumentException</span>(<span style="color: rgb(162, 86, 55);">"illegal character in key"</span>);</div><div>        <span style="color: rgb(157, 78, 150);">if</span>(<span style="color: rgb(9, 89, 132);">c</span><span style="color: rgb(54, 54, 54);">>=</span><span style="color: rgb(162, 86, 55);">'a'</span> <span style="color: rgb(54, 54, 54);">&&</span> <span style="color: rgb(9, 89, 132);">c</span><span style="color: rgb(54, 54, 54);"><=</span><span style="color: rgb(162, 86, 55);">'z'</span>) {</div><div>            <span style="color: rgb(146, 205, 120);">// start with lowercase</span></div><div>        } <span style="color: rgb(157, 78, 150);">else</span> {</div><div>            <span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);">++</span>;</div><div>            <span style="color: rgb(157, 78, 150);">for</span>(;<span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);"><</span><span style="color: rgb(9, 89, 132);">len</span>;<span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);">++</span>) {</div><div>                <span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">=</span> <span style="color: rgb(9, 89, 132);">key</span>.<span style="color: rgb(99, 99, 36);">charAt</span>(<span style="color: rgb(9, 89, 132);">i</span>);</div><div>                <span style="color: rgb(157, 78, 150);">if</span> (<span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">==</span> <span style="color: rgb(162, 86, 55);">'</span><span style="color: rgb(152, 116, 40);">\r</span><span style="color: rgb(162, 86, 55);">'</span> <span style="color: rgb(54, 54, 54);">||</span> <span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">==</span> <span style="color: rgb(162, 86, 55);">'</span><span style="color: rgb(152, 116, 40);">\n</span><span style="color: rgb(162, 86, 55);">'</span>)</div><div>                    <span style="color: rgb(157, 78, 150);">throw</span> <span style="color: rgb(157, 78, 150);">new</span> <span style="color: rgb(99, 99, 36);">IllegalArgumentException</span>(<span style="color: rgb(162, 86, 55);">"illegal character in key"</span>);</div><div>                <span style="color: rgb(157, 78, 150);">else</span> <span style="color: rgb(157, 78, 150);">if</span>(<span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">>=</span> <span style="color: rgb(162, 86, 55);">'A'</span> <span style="color: rgb(54, 54, 54);">&&</span> <span style="color: rgb(9, 89, 132);">c</span><span style="color: rgb(54, 54, 54);"><=</span><span style="color: rgb(162, 86, 55);">'Z'</span>) {</div><div>                    <span style="color: rgb(157, 78, 150);">break</span>;</div><div>                }</div><div>            }</div><div>        }</div><div>        <span style="color: rgb(157, 78, 150);">if</span>(<span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);">==</span><span style="color: rgb(9, 89, 132);">len</span>) <span style="color: rgb(157, 78, 150);">return</span> <span style="color: rgb(9, 89, 132);">key</span>;</div><br><div>        <span style="color: rgb(70, 224, 192);">char</span>[] <span style="color: rgb(9, 89, 132);">b</span> <span style="color: rgb(54, 54, 54);">=</span> <span style="color: rgb(9, 89, 132);">key</span>.<span style="color: rgb(99, 99, 36);">toCharArray</span>();</div><div>        <span style="color: rgb(157, 78, 150);">if</span>(<span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);">==</span><span style="color: rgb(73, 104, 57);">0</span>) {</div><div>            <span style="color: rgb(9, 89, 132);">b</span>[<span style="color: rgb(73, 104, 57);">0</span>] <span style="color: rgb(54, 54, 54);">=</span> (<span style="color: rgb(70, 224, 192);">char</span>)(<span style="color: rgb(9, 89, 132);">b</span>[<span style="color: rgb(73, 104, 57);">0</span>] <span style="color: rgb(54, 54, 54);">-</span> (<span style="color: rgb(162, 86, 55);">'a'</span> <span style="color: rgb(54, 54, 54);">-</span> <span style="color: rgb(162, 86, 55);">'A'</span>));</div><div>            <span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);">++</span>;</div><div>        }</div><div>        <span style="color: rgb(157, 78, 150);">for</span> (;<span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);"><</span><span style="color: rgb(9, 89, 132);">len</span>; <span style="color: rgb(9, 89, 132);">i</span><span style="color: rgb(54, 54, 54);">++</span>) {</div><div>            <span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">=</span> <span style="color: rgb(9, 89, 132);">b</span>[<span style="color: rgb(9, 89, 132);">i</span>];</div><div>            <span style="color: rgb(157, 78, 150);">if</span>(<span style="color: rgb(9, 89, 132);">c</span><span style="color: rgb(54, 54, 54);">>=</span><span style="color: rgb(162, 86, 55);">'A'</span> <span style="color: rgb(54, 54, 54);">&&</span> <span style="color: rgb(9, 89, 132);">c</span><span style="color: rgb(54, 54, 54);"><=</span><span style="color: rgb(162, 86, 55);">'Z'</span>) {</div><div>                <span style="color: rgb(9, 89, 132);">b</span>[<span style="color: rgb(9, 89, 132);">i</span>] <span style="color: rgb(54, 54, 54);">=</span> (<span style="color: rgb(70, 224, 192);">char</span>) (<span style="color: rgb(9, 89, 132);">c</span><span style="color: rgb(54, 54, 54);">+</span>(<span style="color: rgb(162, 86, 55);">'a'</span><span style="color: rgb(54, 54, 54);">-</span><span style="color: rgb(162, 86, 55);">'A'</span>));</div><div>            } <span style="color: rgb(157, 78, 150);">else</span> <span style="color: rgb(157, 78, 150);">if</span> (<span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">==</span> <span style="color: rgb(162, 86, 55);">'</span><span style="color: rgb(152, 116, 40);">\r</span><span style="color: rgb(162, 86, 55);">'</span> <span style="color: rgb(54, 54, 54);">||</span> <span style="color: rgb(9, 89, 132);">c</span> <span style="color: rgb(54, 54, 54);">==</span> <span style="color: rgb(162, 86, 55);">'</span><span style="color: rgb(152, 116, 40);">\n</span><span style="color: rgb(162, 86, 55);">'</span>)</div><div>                <span style="color: rgb(157, 78, 150);">throw</span> <span style="color: rgb(157, 78, 150);">new</span> <span style="color: rgb(99, 99, 36);">IllegalArgumentException</span>(<span style="color: rgb(162, 86, 55);">"illegal character in key"</span>);</div><div>        }</div><div>        <span style="color: rgb(157, 78, 150);">return</span> <span style="color: rgb(157, 78, 150);">new</span> <span style="color: rgb(99, 99, 36);">String</span>(<span style="color: rgb(9, 89, 132);">b</span>);</div><div>    }</div></div></div><div><br></div><div>You can avoid the overhead in the common case (which is creating a new char[] twice - once to access the characters and a second time when the String is created).</div><div><br></div><div>The jmh performance tests:</div><div><br></div><div><div><font face="Courier New">Benchmark                                                     (testString)  Mode  Cnt     Score      Error   Units</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK                         CONTENT_LENGTH  avgt    3     0.021 ±    0.002   us/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.alloc.rate           CONTENT_LENGTH  avgt    3  4724.968 ±  380.497  MB/sec</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.alloc.rate.norm      CONTENT_LENGTH  avgt    3   104.000 ±    0.001    B/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.count                CONTENT_LENGTH  avgt    3   118.000             counts</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.time                 CONTENT_LENGTH  avgt    3   109.000                 ms</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK                         Content-length  avgt    3     0.022 ±    0.012   us/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.alloc.rate           Content-length  avgt    3  4562.486 ± 2509.142  MB/sec</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.alloc.rate.norm      Content-length  avgt    3   104.000 ±    0.001    B/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.count                Content-length  avgt    3   135.000             counts</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.time                 Content-length  avgt    3   122.000                 ms</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK                         Content-Length  avgt    3     0.022 ±    0.001   us/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.alloc.rate           Content-Length  avgt    3  4482.320 ±  279.049  MB/sec</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.alloc.rate.norm      Content-Length  avgt    3   104.000 ±    0.001    B/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.count                Content-Length  avgt    3   149.000             counts</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeJDK:gc.time                 Content-Length  avgt    3   118.000                 ms</font></div><div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8                     CONTENT_LENGTH  avgt    5     0.022 ±   0.005   us/op</font></div><div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.alloc.rate       CONTENT_LENGTH  avgt    5  4453.114 ± 984.009  MB/sec</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.alloc.rate.norm  CONTENT_LENGTH  avgt    5   104.000 ±   0.001    B/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.count            CONTENT_LENGTH  avgt    5   214.000            counts</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.time             CONTENT_LENGTH  avgt    5   188.000                ms</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8                     Content-length  avgt    5     0.010 ±   0.001   us/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.alloc.rate       Content-length  avgt    5     0.001 ±   0.001  MB/sec</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.alloc.rate.norm  Content-length  avgt    5    ≈ 10⁻⁵              B/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.count            Content-length  avgt    5       ≈ 0            counts</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8                     Content-Length  avgt    5     0.025 ±   0.002   us/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.alloc.rate       Content-Length  avgt    5  3927.226 ± 241.881  MB/sec</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.alloc.rate.norm  Content-Length  avgt    5   104.000 ±   0.001    B/op</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.count            Content-Length  avgt    5   190.000            counts</font></div><div><font face="Courier New">NormalizerJMH.benchmarkNormalizeOption8:gc.time             Content-Length  avgt    5   159.000                ms</font></div></div><div><br></div></div></div><div>You can review the project and the various options attempted here <a href="https://github.com/robaho/normalize_test/tree/main">https://github.com/robaho/normalize_test/tree/main</a></div><div><br></div><div>2. Another proposed change to Headers would be to add a protected constructor that allowed you to pass in the Map implementation (or null), allowing specialized map data structures that are better suited to http headers storage, especially when being converted to/from http2 headers.</div><div><br></div><div><div style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">Although it is possible to subclass Headers to provide your own map, you still pay a penalty because the base class will instantiate a map, and the code is quite ugly as you must override every method in the Headers class. You can see a sample implementation here <a href="https://github.com/robaho/httpserver/blob/main/src/main/java/robaho/net/httpserver/OptimizedHeaders.java">https://github.com/robaho/httpserver/blob/main/src/main/java/robaho/net/httpserver/OptimizedHeaders.java</a></div><div style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);"><br></div></div><div style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">The subclass does offer the ability to bypass the normalization completely with a package level method to be used internally by the server when it has already guaranteed the key is normalized.</div><div style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);"><br></div><div style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);">3. The public static of() methods in Headers should be changed/removed, as they create a dependency on a sun internal class (sun.net.httpserver.UnmodifiableHeaders) from a public “api” class. These could be changed to instead wrap the map with an unmodifiable map.</div><div><br></div></body></html>