{"id":18644,"date":"2022-03-22T09:00:22","date_gmt":"2022-03-22T08:00:22","guid":{"rendered":"https:\/\/daniel.haxx.se\/blog\/?p=18644"},"modified":"2022-03-22T09:00:22","modified_gmt":"2022-03-22T08:00:22","slug":"a-headers-api-for-libcurl","status":"publish","type":"post","link":"https:\/\/daniel.haxx.se\/blog\/2022\/03\/22\/a-headers-api-for-libcurl\/","title":{"rendered":"A headers API for libcurl"},"content":{"rendered":"\n<p>For many years we&#8217;ve had this outstanding idea to add a new API to libcurl that would offer applications easy access to HTTP response headers.<\/p>\n\n\n\n<p>Applications could already retrieve the headers using existing methods but that requires them to write a callback and to a certain amount of parsing and &#8220;understanding&#8221; HTTP that we always felt was a little unfortunate, a bit error-prone on the behalf of the applications and perhaps also a thing that forced a lot of applications out there having to write the same kind of extra function logic.<\/p>\n\n\n\n<p>If libcurl provides this functionality, it would remove a lot of (duplicated) code from a lot of applications.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Designing the API<\/h2>\n\n\n\n<p>We started this process a while ago when I first wrote down a basic approach to an API for this and sent it off to the <a href=\"https:\/\/curl.se\/mail\/list.cgi?list=curl-library\">curl-library mailing list<\/a> for feedback and critique.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/* first take *\/\nchar *curl_easy_header(CURL *easy,\n                       const char *name);<\/pre>\n\n\n\n<p>The conversation that followed that first plea for help, made me realize that my first proposal had been far too basic and it wouldn&#8217;t at all work to satisfy the needs and use cases we could think of for this API.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Try again<\/h2>\n\n\n\n<p>I went back to mull over what I&#8217;ve learned and update my design proposal, trying to take the feedback into account in the best possible way. A few weeks later, I returned with a &#8220;proposal v2&#8221; and again I asked for comments and opinions on what I had put together.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\/* second shot *\/\nCURLHcode curl_easy_header(CURL *easy,\n                           const char *name,\n                           size_t index,\n                           struct curl_header **h);\n<\/pre>\n\n\n\n<p>As I had already adjusted the API from feedback the first time around, the feedback this time was perhaps not calling for as big changes or radical differences as they did the first time around. I could adapt my proposal to what people asked and suggested. We arrived at something that seemed like a pretty solid API for offering HTTP headers to applications.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Let&#8217;s do this<\/h2>\n\n\n\n<p>As the API proposal feedback settled down and the interface felt good and sensible, I decided it was time for me to write up a first implementation so that we can offer code to people to give everyone a chance to try out the API in real life as well. There&#8217;s one thing to give feedback on a &#8220;paper product&#8221;, actually being able to use it and try it in an application is way better. I dove in.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The final take<\/h2>\n\n\n\n<p>When the code worked to the level that I started to be able to extract the first headers with the API, it proved to that we needed to adjust the API a little more, so I did. I then ran into more questions and thoughts about specifics that we hadn&#8217;t yet dealt with or nailed proper in the discussions up to that point and I took some questions back to the curl community. This became an iterative process and we smoothed out questions about how access different header &#8220;sources&#8221; as well as how to deal with multiple headers and &#8220;request sequences&#8221;. All supported now.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* final version *\/\nCURLHcode curl_easy_header(CURL *easy,\n                           const char *name,\n                           size_t index,\n                           unsigned int origin,\n                           int request,\n                           struct curl_header **h);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Multiple headers<\/h3>\n\n\n\n<p>This API allows applications to extract <em>all<\/em> headers from a previous transfer. It can get one or many headers when there are duplicated ones, like <code>Set-Cookie:<\/code> commonly arrive as.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Sources<\/h3>\n\n\n\n<p>The application can ask for &#8220;normal&#8221; headers, for trailers (that arrive after the body), headers associated with the CONNECT request (if such a one was performed), pseudo headers (that might arrive when HTTP\/2 and HTTP\/3 is used) or headers associated with a HTTP 1xx &#8220;intermediate&#8221; response.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Multiple responses<\/h3>\n\n\n\n<p>The libcurl APIs typically work on <em>transfers<\/em>, which means that a single transfer may end up doing multiple transfers, multiple HTTP requests. Primarily when redirects are followed but it can also be due to other reasons. This header API therefore allows the caller to extract headers from the entire &#8220;chain&#8221; of requests a previous transfer was made with.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">EXPERIMENTAL<\/h2>\n\n\n\n<p>This API is initially merged (in <a href=\"https:\/\/github.com\/curl\/curl\/commit\/d1e4a677340c6a1f0ebbc13f1021808c23ad1138\">this commit<\/a>) labeled &#8220;experimental&#8221; to be included in the upcoming 7.83.0 release. The <em>experimental<\/em> label means a few different things to us:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The API is <strong>disabled by default<\/strong> in the build and you need to explicitly ask for it with <code>--enable-headers-api<\/code> when you run configure<\/li><li>There are no ABI and API promises for these functions yet. <strong>We might change the functions<\/strong> based on feedback before we remove the label.<\/li><li><strong>We strongly discourage anyone from shipping experimentally labeled functions in production.<\/strong><\/li><li>We rely on people to enable and test this and <strong>provide feedback<\/strong>, to give us confidence enough to remove the experimental label as soon as possible.<\/li><\/ul>\n\n\n\n<p>We use the experimental &#8220;route&#8221; to lower the bar for merging new stuff, so that we get some extra chances to fix up mistakes before the rules and API are carved in stone and we are set to support that for a life time.<\/p>\n\n\n\n<p>This setup relies on users actually trying out the experimental stuff as otherwise it isn&#8217;t method for improving the API, it will only delay the introduction of it to the general public. And it risks becoming be less good.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Documentation<\/h2>\n\n\n\n<p>The two new functions have detailed man pages: <a href=\"https:\/\/curl.se\/libcurl\/c\/curl_easy_header.html\">curl_easy_header<\/a> and <a href=\"https:\/\/curl.se\/libcurl\/c\/curl_easy_nextheader.html\">curl_easy_nextheader<\/a>. If there is anything missing on unclear in there, let us know!<\/p>\n\n\n\n<p>I have also created an initial example source snippet showing header API use. See <a href=\"https:\/\/curl.se\/libcurl\/c\/headerapi.html\">headerapi.c<\/a>.<\/p>\n\n\n\n<p>This API deserves its own little section in the <a href=\"https:\/\/everything.curl.dev\/\">everything curl<\/a> book, but I think I will wait for it to get landed &#8220;for real&#8221; before I work on adding that.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For many years we&#8217;ve had this outstanding idea to add a new API to libcurl that would offer applications easy access to HTTP response headers. Applications could already retrieve the headers using existing methods but that requires them to write a callback and to a certain amount of parsing and &#8220;understanding&#8221; HTTP that we always &hellip; <a href=\"https:\/\/daniel.haxx.se\/blog\/2022\/03\/22\/a-headers-api-for-libcurl\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">A headers API for libcurl<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":5,"featured_media":18794,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[371,33,230],"class_list":["post-18644","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-curl","tag-api","tag-curl-and-libcurl","tag-http"],"_links":{"self":[{"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/posts\/18644","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/comments?post=18644"}],"version-history":[{"count":24,"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/posts\/18644\/revisions"}],"predecessor-version":[{"id":18793,"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/posts\/18644\/revisions\/18793"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/media\/18794"}],"wp:attachment":[{"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/media?parent=18644"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/categories?post=18644"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/daniel.haxx.se\/blog\/wp-json\/wp\/v2\/tags?post=18644"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}