<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>