<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ram Damera</title>
    <description>The latest articles on DEV Community by Ram Damera (@r0mflip).</description>
    <link>https://dev.to/r0mflip</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F101817%2Ff06f38c1-3664-47bf-bbfa-34e3e2417fbd.jpg</url>
      <title>DEV Community: Ram Damera</title>
      <link>https://dev.to/r0mflip</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/r0mflip"/>
    <language>en</language>
    <item>
      <title>A rant on failing to build</title>
      <dc:creator>Ram Damera</dc:creator>
      <pubDate>Sun, 16 Sep 2018 22:17:18 +0000</pubDate>
      <link>https://dev.to/r0mflip/a-rant-on-failing-to-build-197g</link>
      <guid>https://dev.to/r0mflip/a-rant-on-failing-to-build-197g</guid>
      <description>&lt;h2&gt;
  
  
  A humble beginning
&lt;/h2&gt;

&lt;p&gt;I like node servers, I like writing bare servers and I also like Express.js. I consider myself a beginner in Node.JS but I just wanted to write an express middleware that generates &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag"&gt;ETag headers&lt;/a&gt;. ETags are a shiny/new and neat way to invalidate cache.&lt;/p&gt;

&lt;p&gt;At first, when I read about ETag headers I thought that having 'strong' and 'weak' variants of it is useless and I came to know that I was wrong. 'strong' ETags represent that the header value is generated based on the bytes of the response body. Which means when I generate an ETag for any given content it needs to be unique for that content, in fancy words it means a strong ETag is a hash of the content which is generated by a collission resistent algorithm.&lt;/p&gt;

&lt;h2&gt;
  
  
  The flashy code
&lt;/h2&gt;

&lt;p&gt;Capturing the response body is pretty easy from a middleware. It goes something like...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taggart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// standard express style&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// save methods&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// sha1 ain't that bad&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// keep track, for content-length&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// sometimes chunk can be 'undefined'&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// convert chunk to buffer&lt;/span&gt;
      &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// update hash using chunk data&lt;/span&gt;
      &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;byteLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;onData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// generate tag&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// weak or strong? use length and hash as ETag&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weak&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`W/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ETag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// override the default methods&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;onData&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;onEnd&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;taggart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The vision
&lt;/h2&gt;

&lt;p&gt;What we are doing is that we are hijacking the &lt;code&gt;res.write&lt;/code&gt; and &lt;code&gt;res.end&lt;/code&gt; methods of &lt;code&gt;res&lt;/code&gt; object, which is an instance of &lt;a href="https://nodejs.org/api/http.html#http_class_http_serverresponse"&gt;&lt;code&gt;http.ServerResponse&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;write&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; methods of the &lt;code&gt;res&lt;/code&gt; object are used to write data to the response that is sent to the client and they are inherited from &lt;a href="https://nodejs.org/api/stream.html"&gt;&lt;code&gt;Stream&lt;/code&gt;&lt;/a&gt; class.&lt;/p&gt;

&lt;p&gt;In the beginning we are creating a hash and in the &lt;code&gt;onData&lt;/code&gt; method we are updating the hash using the chunks and getting rid of them(&lt;strong&gt;do not store the chunks&lt;/strong&gt;, they can get pretty huge). We are also keeping track of the size of the response.&lt;/p&gt;

&lt;p&gt;A call to the &lt;code&gt;res.end&lt;/code&gt; indicates that the response has ended and now we can finalize the hash in &lt;code&gt;onEnd&lt;/code&gt; function and set it as a header. But there is catch. In HTTP the requests and responses are streamed. For every call to &lt;code&gt;res.write&lt;/code&gt; the partial response is sent to the client when it can be, and the headers are sent in the first chunk. The headers are sent in the first chunk, which happens during the &lt;a href="https://nodejs.org/api/http.html#http_response_write_chunk_encoding_callback"&gt;first call to &lt;code&gt;res.write&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dead end
&lt;/h2&gt;

&lt;p&gt;If you try to run the above code, you will get a fatal error. If your response is small enough(less than 65535 bytes or so ¯\_(ツ)_/¯), then it can fit in the first chunk. You get the whole data in a single chunk, you update the hash, and set the header in the call to &lt;code&gt;res.end&lt;/code&gt;. Totally works, but only if your responses are less than 64Kb or so.&lt;/p&gt;

&lt;p&gt;But I want to send ETags for images and videos which are I'am pretty sure are not less than 64Kb. It would really help to send back a &lt;code&gt;302&lt;/code&gt; http code for a 2Mb image, right? Due to the streaming nature of the responses we are unable to do this. The hash can be generated only when &lt;code&gt;res.end&lt;/code&gt; is called but by then the headers might have already been sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The only way is up
&lt;/h2&gt;

&lt;p&gt;So, now for the compromises:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can settle with ETags only for small responses.&lt;/li&gt;
&lt;li&gt;We can generate hashes for static content before hand, maybe in a build process or something, save it in a dictionary and retrieve it later. Hassle.&lt;/li&gt;
&lt;li&gt;We can generate hashe on every request - first prepare the response, generate the hash and then stream the response with the proper header. Clean and practical.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.39"&gt;&lt;code&gt;Trailers&lt;/code&gt;&lt;/a&gt; can be used, I am working on it, it doesn't look promissing, I might be wrong. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The conclusion
&lt;/h2&gt;

&lt;p&gt;We can use modules like &lt;a href="https://github.com/jshttp/etag/"&gt;&lt;code&gt;etag&lt;/code&gt;&lt;/a&gt; which is kind of coupled with &lt;a href="https://github.com/pillarjs/send/"&gt;&lt;code&gt;send&lt;/code&gt;&lt;/a&gt; and which looks like it is intended to be used with &lt;a href="https://github.com/expressjs/serve-static"&gt;&lt;code&gt;server-static&lt;/code&gt;&lt;/a&gt;. They are written by the same guy anyway. &lt;code&gt;send&lt;/code&gt; generates 'weak' ETags and it does so based on &lt;a href="https://nodejs.org/api/fs.html#fs_class_fs_stats"&gt;file stats&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've come to realize that generating ETags through a module is hard. I has to be hooked into your server, its a low level component. Checking if the content is stale or not is &lt;a href="https://github.com/pillarjs/send/blob/ea926ba28b9e058fb67d24f883a8ce1680f7b883/index.js#L328-L349"&gt;pretty easy&lt;/a&gt; anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pillarjs/send/pull/105#issuecomment-198766116"&gt;https://github.com/pillarjs/send/pull/105#issuecomment-198766116&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3"&gt;https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is my first public blog post ever. Thanks for reading all the way through, hope you like it. I'am a non-native English speaker, I'am working on my skills there.&lt;br&gt;
My so called failed middleware is &lt;a href="https://github.com/ramlmn/taggart/"&gt;on GitHub&lt;/a&gt; and Iam working on the &lt;a href="https://github.com/ramlmn/taggart/tree/fix-tagging"&gt;&lt;code&gt;fix-tagging&lt;/code&gt; branch&lt;/a&gt; do not checkout the master branch, its dirty.&lt;br&gt;
Please leave suggestions, it would help me much. Thanks.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>http</category>
      <category>cache</category>
      <category>etag</category>
      <category>headers</category>
    </item>
  </channel>
</rss>
