<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ocaml on Noetic Nought</title>
    <link>https://punchagan.muse-amuse.in/tags/ocaml/</link>
    <description>Recent content in Ocaml on Noetic Nought</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <copyright>© 2006-2026 CC-BY-SA-4.0</copyright>
    <lastBuildDate>Fri, 16 Jan 2026 21:37:00 +0530</lastBuildDate>
    <atom:link href="https://punchagan.muse-amuse.in/tags/ocaml/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Safari and invalid HTTP/2 headers</title>
      <link>https://punchagan.muse-amuse.in/blog/safari-and-invalid-http-2-headers/</link>
      <pubDate>Fri, 16 Jan 2026 21:37:00 +0530</pubDate>
      <guid>https://punchagan.muse-amuse.in/blog/safari-and-invalid-http-2-headers/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/punchagan/elfeed-offline/&#34;&gt;Elfeed-offline&lt;/a&gt; currently has a &lt;a href=&#34;https://camlworks.github.io/dream/&#34;&gt;Dream web server&lt;/a&gt; which acts as a proxy server in&#xA;front of Elfeed&amp;rsquo;s Emacs &lt;a href=&#34;https://github.com/skeeto/emacs-web-server/tree/master&#34;&gt;simple-httpd&lt;/a&gt; server.&lt;/p&gt;&#xA;&lt;p&gt;simple-httpd supports HTTP/1.1 protocol, while Dream provides transparent&#xA;upgrading of connections to HTTP/2 — if the client can handle HTTP/2 and the&#xA;connection is using HTTPS, it is transparently upgraded to HTTP/2.&lt;/p&gt;&#xA;&lt;p&gt;My proxying code was too simplistic in forwarding the headers too along with&#xA;the content received from the simple-httpd server. Some of the HTTP/1 headers&#xA;are no longer valid in HTTP/2. And, Safari (and curl) strictly adhere to the&#xA;protocol and fail if there are invalid headers. Curl, for instance, fails with&#xA;the following error:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt; HTTP/2 200&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt; server: simple-httpd (Emacs 30.1)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;lt; date: Fri, 16 Jan 2026 10:57:25 GMT&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* Invalid HTTP header field was received: frame type: 1, stream: 1, name: [connection], value: [keep-alive]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* [HTTP2] [1] received invalid frame: FRAME[HEADERS, len=77, hend=1, eos=0], error -531: Invalid HTTP header field was received&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* HTTP/2 stream 1 was not closed cleanly: unknown (err 4294966765)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;* Connection #0 to host 192.168.1.5 left intact&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl: (92) Invalid HTTP header field was received: frame type: 1, stream: 1, name: [connection], value: [keep-alive]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This was causing issues for &lt;a href=&#34;https://github.com/Feyorsh&#34;&gt;@Feyorsh&lt;/a&gt; who was trying out elfeed-offline with&#xA;Safari. Thanks for taking the time to debug the problem and for suggesting a&#xA;fix!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>An offline-friendly Elfeed web UI</title>
      <link>https://punchagan.muse-amuse.in/blog/offline-friendly-elfeed-web-ui/</link>
      <pubDate>Wed, 07 Jan 2026 02:31:00 +0530</pubDate>
      <guid>https://punchagan.muse-amuse.in/blog/offline-friendly-elfeed-web-ui/</guid>
      <description>&lt;p&gt;I want to read articles from my RSS subscriptions on my phone without signing&#xA;up to a hosted service or running a public server.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/skeeto/elfeed&#34;&gt;Elfeed&lt;/a&gt;, the Emacs feed reader, already lets me manage my subscriptions and read&#xA;from within Emacs. It comes with an experimental web UI, but needs the server&#xA;(running on my laptop) to be accessible whenever I want to read. My phone&#xA;becomes useless the moment my laptop sleeps.&lt;/p&gt;&#xA;&lt;p&gt;So, I built an alternate web UI with a service worker that caches content on&#xA;the client side for a smooth offline reading experience. It&amp;rsquo;s written in &lt;a href=&#34;https://ocaml.org/&#34;&gt;OCaml&lt;/a&gt;&#xA;and compiled to JavaScript using &lt;a href=&#34;https://ocsigen.org/js_of_ocaml/latest/manual/overview&#34;&gt;&lt;code&gt;js_of_ocaml&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-bother-with-rss-feeds&#34;&gt;Why bother with RSS feeds?&lt;/h2&gt;&#xA;&lt;p&gt;It is annoyingly easy to fall into the trap of short form videos or other&#xA;algorithmically &amp;ldquo;curated&amp;rdquo; feeds and get sucked into scrolling mindlessly. I&#xA;fear this is going to get even worse with all the generative AI stuff.&lt;/p&gt;&#xA;&lt;p&gt;I want to be more deliberate about what I consume. RSS feels calmer and gives&#xA;me a greater sense of control. Or it may just be nostalgia from good old Google&#xA;Reader days.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-emacs-and-elfeed&#34;&gt;Why Emacs and Elfeed?&lt;/h2&gt;&#xA;&lt;p&gt;Since Google Reader was killed, I&amp;rsquo;ve bounced between many readers but none of&#xA;them has really stuck with me. I&amp;rsquo;ve had &lt;a href=&#34;https://punchagan.muse-amuse.in/blog/elfeed-hook-to-fetch-full-content/&#34;&gt;different phases&lt;/a&gt; of using Elfeed, over&#xA;the years, though.&lt;/p&gt;&#xA;&lt;p&gt;I like the fact that everything is local with Elfeed &amp;ndash; no hosted services, no&#xA;public servers. And since it lives inside Emacs, everything from tagging to how&#xA;entries are displayed can be tweaked.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-elfeed-offline&#34;&gt;Why Elfeed offline?&lt;/h2&gt;&#xA;&lt;p&gt;Elfeed also ships a basic web UI that lets me read my feeds from a different&#xA;device, but only when my laptop is online and reachable. I&amp;rsquo;d like to be able to&#xA;read these posts even when I&amp;rsquo;m &amp;ldquo;on the road&amp;rdquo;, say, while waiting for a train or&#xA;while taking a cab ride.&lt;/p&gt;&#xA;&lt;p&gt;I knew it should be possible to do this with some client side caching. And, I&#xA;considered improving the web UI of Elfeed itself but it doesn&amp;rsquo;t seem to be&#xA;&lt;a href=&#34;https://github.com/skeeto/elfeed/pulls&#34;&gt;actively maintained&lt;/a&gt; in the last couple of years. Creating a separate project&#xA;would also give me more freedom to experiment.&lt;/p&gt;&#xA;&lt;h2 id=&#34;how-does-it-work&#34;&gt;How does it work?&lt;/h2&gt;&#xA;&lt;p&gt;Elfeed offline comes with a tiny &lt;a href=&#34;https://camlworks.github.io/dream/&#34;&gt;Dream&lt;/a&gt; based webserver that acts as a proxy in&#xA;front of the Emacs Elfeed webserver for the API requests. The Dream web server&#xA;also serves the static assets like the HTML, JavaScript, stylesheets and the&#xA;Service Worker for the web app. The diagram below shows how data flows between the different components.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-artist&#34; data-lang=&#34;artist&#34;&gt;+---------------+     +-----------------+&#xA;|  Emacs Elfeed |&amp;lt;---&amp;gt;|     Dream       |&#xA;|     Server    |     |   Web-server    |&#xA;+---------------+     +-----------------+&#xA;                               ^&#xA;                               |&#xA;                               v&#xA; +------------+        +----------------+       +---------+&#xA; |    Web     |        |    Service     |       | Browser |&#xA; |  Client    |&amp;lt;------&amp;gt;|     Worker     |&amp;lt;-----&amp;gt;|  Cache  |&#xA; +------------+        +----------------+       +---------+&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The Service Worker intercepts all the &lt;code&gt;GET&lt;/code&gt; requests from the client and&#xA;responds using a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/Caching#cache_first_with_cache_refresh&#34;&gt;cache-first-with-cache-refresh&lt;/a&gt; strategy. This makes the&#xA;reading experience feel very responsive. The web client also implements a &amp;ldquo;good&#xA;enough&amp;rdquo; clone of the original search functionality of Elfeed to allow searching&#xA;and filtering content while offline.&lt;/p&gt;&#xA;&lt;p&gt;The web app also lets me mark entries as read or star them when I&amp;rsquo;m offline.&#xA;The Service Worker caches these operations and updates the server when the&#xA;server becomes reachable. There&amp;rsquo;s no smart conflict resolution here, and the&#xA;API requests just overwrite and update the state on the server.&lt;/p&gt;&#xA;&lt;p&gt;I have been using this for a couple of weeks now, and am quite happy with how&#xA;responsive the UI feels and the number of posts I managed to read so far.&lt;/p&gt;&#xA;&lt;p&gt;But, there are definitely some rough edges to smoothen out.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;It&amp;rsquo;s a pain to make sure that both the servers are running when I&amp;rsquo;m online&#xA;and want to sync new updates to my phone. I&amp;rsquo;m considering adding a tiny Emacs&#xA;helper to manage this.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I need to remember to open the app on my phone to trigger a cache update&#xA;before stepping away from my laptop. I want to explore using the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/Background_Synchronization_API&#34;&gt;Background&#xA;Sync API&lt;/a&gt; (not available on Firefox) to possibly make this easier.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The UI could use some improvements to indicate pending syncs when offline and&#xA;also indicate the last sync with the server to help ensure everything is&#xA;synced before stepping away from my laptop.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Service workers need the server to be accessible via https. I have a wrapper&#xA;script around mkcert to make this easy, but some apps on my phone &lt;a href=&#34;https://www.reddit.com/r/UPI/comments/1p4le9x/what_happened_to_my_bhim_app/&#34;&gt;refuse to&#xA;work&lt;/a&gt; correctly when there are user installed certificates.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;why-ocaml&#34;&gt;Why OCaml?&lt;/h2&gt;&#xA;&lt;p&gt;At work, I write OCaml every day and enjoy it. This project seemed like a good&#xA;one to try out &lt;code&gt;js_of_ocaml&lt;/code&gt; since everything is local and I don&amp;rsquo;t need to&#xA;worry about bundle sizes, etc.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;js_of_ocaml&lt;/code&gt; makes it quite convenient to share code between the server and&#xA;the client side. And given there&amp;rsquo;s also a &amp;ldquo;third&amp;rdquo; Service Worker component in&#xA;the mix, I thought it would make life easier. For instance, the message types&#xA;and the serialization code for the messages between the client and the service&#xA;worker are shared.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m also using this project to experiment with Dune&amp;rsquo;s &amp;ldquo;soon to be released&amp;rdquo;™&#xA;&lt;a href=&#34;https://dune.readthedocs.io/en/stable/tutorials/dune-package-management/index.html&#34;&gt;package management&lt;/a&gt; that our team at Tarides is working on building.&lt;/p&gt;&#xA;&lt;h2 id=&#34;how-can-you-use-it&#34;&gt;How can you use it?&lt;/h2&gt;&#xA;&lt;p&gt;You can try out Elfeed offline&amp;rsquo;s UI &lt;a href=&#34;https://elfeed-offline.muse-amuse.in/&#34;&gt;here&lt;/a&gt; as a static site (with some posts from&#xA;Planet Emacslife and The OCaml Planet). You should be able to read posts,&#xA;search and filter for posts, mark them as read or star them. Any changes you&#xA;make should be preserved between reloads.&lt;/p&gt;&#xA;&lt;p&gt;If you are an Emacs user but don&amp;rsquo;t use Elfeed, you can checkout the &lt;a href=&#34;https://github.com/skeeto/elfeed&#34;&gt;Elfeed&amp;rsquo;s&#xA;README&lt;/a&gt; for instructions to set it up. It also has links to a bunch of blog&#xA;posts and videos that show off the features of Elfeed.&lt;/p&gt;&#xA;&lt;p&gt;If you are already using Elfeed, you should be able to set it up quite easily&#xA;if you&amp;rsquo;re happy installing some OCaml tooling. The installations instructions&#xA;are available in the &lt;a href=&#34;https://github.com/punchagan/elfeed-offline/blob/main/README.org&#34;&gt;README&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;outro&#34;&gt;Outro&lt;/h2&gt;&#xA;&lt;p&gt;I have been enjoying using Elfeed offline for the past couple of weeks. And I&amp;rsquo;m&#xA;hoping I&amp;rsquo;ll end up spending much more time reading in it, than fixing and&#xA;building it. It currently definitely feels like something I built for myself,&#xA;but I&amp;rsquo;d love to hear from others who try it out.&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
