[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2\njobs:\n  latest:\n    docker:\n      - image: thinca/vim:latest\n      - image: citizenstig/httpbin\n    steps: &steps\n      - run:\n          name: install git and curl\n          command: apk add --no-cache git curl\n      - checkout\n      - run:\n          name: test requirements\n          command: |\n            git clone https://github.com/thinca/vim-themis\n            git clone https://github.com/syngan/vim-vimlint /tmp/vim-vimlint\n            git clone https://github.com/ynkdir/vim-vimlparser /tmp/vim-vimlparser\n      - run:\n          name: test\n          command: |\n            vim-themis/bin/themis --reporter dot test\n      - run:\n          name: lint\n          command: |\n            sh /tmp/vim-vimlint/bin/vimlint.sh -l /tmp/vim-vimlint -p /tmp/vim-vimlparser -e EVL102.l:_=1 -c func_abort=1 autoload ftdetect plugin syntax\n  oldest:\n    docker:\n      - image: thinca/vim:v7.4.566\n      - image: citizenstig/httpbin\n    steps: *steps\n      \n\nworkflows:\n  version: 2\n  test:\n    jobs:\n      - latest\n      - oldest\n"
  },
  {
    "path": ".gitignore",
    "content": "\n# Created by https://www.gitignore.io/api/vim\n\n### Vim ###\n# swap\n[._]*.s[a-w][a-z]\n[._]s[a-w][a-z]\n# session\nSession.vim\n# temporary\n.netrwhist\n*~\n# auto-generated tag files\ntags\n\nvim-themis\nvim-vimlint\nvim-vimlparser\n.vagrant\nVagrantfile\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)\nand this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n### Added\n- `g:vim_http_tempbuffer` that makes response buffers temporary\n  [#5](https://github.com/nicwest/vim-http/pull/5).\n\n\n## [1.0.0]\n### Added\n- support for `PATCH` http method\n- pin minimum vim version at 7.4.566, if you are using a version prior to\n  this you can use the version of the code with the\n  [pre-7.4.566](https://github.com/nicwest/vim-http/tree/pre-7.4.566) tag\n\n## [0.0.1] - 2017-08-21\n### Added\n- This CHANGELOG file!\n- g:vim_http_split_vertically to controll split direction\n\n\n[Unreleased]: https://github.com/nicwest/vim-http/compare/1.0.0...HEAD\n[1.0.0]: https://github.com/nicwest/vim-http/compare/0.0.1...1.0.0\n[0.0.1]: https://github.com/nicwest/vim-http/compare/TAIL...0.0.1\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Contributing\n============\n\nAll issues/pull requests are welcome.\n\nThat said, things that I'm unlikely to merge:\n\n* Deviations from the various HTTP specs\n  [rfc1945](https://tools.ietf.org/html/rfc1945)\n  [rfc7230-7235](https://tools.ietf.org/html/rfc7230)\n  [rfc7540](https://tools.ietf.org/html/rfc7540).\n  The idea is that a request should be able to be piped into a connection to a\n  web server (for example via telnet or openssl) and for the server to\n  recognise the request.\n\n* Binding things to keyboard shortcuts by default or with some kind of global\n  flag. People to should be making their own decisions about where they bind\n  things and/or if they need to bind stuff at all. I'm happy to merge\n  suggestions into the README.\n\n* New functionality without tests.\n\nSupport\n-------\n\nI aim to support versions of vim from 7.4.566 \n(see https://github.com/nicwest/vim-http/pull/5#issuecomment-472483160) to\nwhatever the latest version is.\n\nAny changes to keep this plug-in up to date are welcome. Reasonable changes\nthat allow us to support older versions are also welcome but are not required.\n\nGenerally speaking I'm looking to not break things for older versions of vim\nor curl, however sometimes compromises are needed. In a case where the oldest\nsupported version increases I'll tag the previous commit for people with older\nsetups.\n\n* [<7.4.566](https://github.com/nicwest/vim-http/tree/pre-7.4.566) [#5](https://github.com/nicwest/vim-http/pull/5)\n\n\nTests\n-----\n\nThe integration test use [httpbin](https://httpbin.org/), and assumes it\nrunning locally. I use a \n[docker container](https://github.com/citizen-stig/dockerhttpbin):\n\n```\ndocker pull citizenstig/httpbin\ndocker run -d=true -p 8000:8000 citizenstig/httpbin\n```\n\nTo run the tests pull the \n[themis test suite](https://github.com/thinca/vim-themis) \n(you don't have to install it but you can if you want). I normally just dump it\nin the plugin directory.\n\n```\ngit clone git@github.com:thinca/vim-themis.git\n./vim-themis/bin/themis --reporter dot test\n```\n\nThe test responses use a VERY hacked together parsing to match against what\ncan be a somewhat dynamic set of headers etc.\n\nBasically it will match anything inside a pair of `%%`:\n\n\n```\n\"User-Agent\": \"curl/%% '[123.]\\+' %%\"\n```\n\nWould match \n\n```\n\"User-Agent\": \"curl/1.2.3\"\n```\n\nBut not\n\n```\n\"User-Agent\": \"curl/4.5.6\"\n```\n\nOr\n\n```\n\"User-Agent\": \"curl/latest\"\n```\n\nStyle\n-----\n\nThis project attempts to follow the \n[Google Vimscript Style Guide](https://google.github.io/styleguide/vimscriptguide.xml)\n"
  },
  {
    "path": "LICENSE.md",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org>\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: test setup httpbin\n\ntest:\n\tvim-themis/bin/themis --reporter dot test\n\nsetup:\n\tdocker pull citizenstig/httpbin\n\tgit clone git@github.com:thinca/vim-themis.git\n\tgit clone git@github.com:syngan/vim-vimlint\n\tgit clone git@github.com:ynkdir/vim-vimlparser\n\nhttpbin:\n\tdocker run -d=true -p 8000:8000 citizenstig/httpbin\n\nlint:\n\t./vim-vimlint/bin/vimlint.sh -l ./vim-vimlint -p ./vim-vimlparser -e EVL102.l:_=1 -c func_abort=1 autoload ftdetect plugin syntax\n"
  },
  {
    "path": "README.md",
    "content": "[![CircleCI](https://circleci.com/gh/nicwest/vim-http/tree/master.svg?style=svg)](https://circleci.com/gh/nicwest/vim-http/tree/master)\n[![Powered by vital.vim](https://img.shields.io/badge/powered%20by-vital.vim-80273f.svg)](https://github.com/vim-jp/vital.vim)\n\nvim-http\n========\n\nSimple wrapper over curl and http syntax highlighting.\n\n[![asciicast](https://asciinema.org/a/120707.png)](https://asciinema.org/a/120707)\n\n\nUsage\n------\n\nWrite a raw http request\n\n```http\nGET http://httpbin.org/get HTTP/1.1\nHost: httpbin.org\nAccept: application/json\n```\n\n`:Http` will execute the request and display the response in a new buffer.\n\n`:Http!` will execute the request as above and follow any redirects.\n\n`:HttpShowCurl` displays the curl request that the plugin executes under the \nhood\n\n`:HttpShowRequest` displays the internal object representing the request\n\n`:HttpClean` will add Host and Content-Length headers\n\n`:HttpAuth` will prompt for authorization credentials\n\n\nConfiguration\n-------------\n\n`g:vim_http_clean_before_do` if set to `1` (default) will clean a request before\nsending it to curl. Disable this by setting this global to `0`\n\n`g:vim_http_additional_curl_args` can be used to provide additional arguments\nto curl.\n\n`g:vim_http_split_vertically` when set to `1` will split the window vertically\non response rather than horizontally (the default).\n\n`g:vim_http_right_below` when set to `1` split window will be open on the\nright (for vertical) or below (for horizontal).\n\n`g:vim_http_tempbuffer` when set to `1` response buffers will overwrite each\nother instead of persisting forever.\n\nHelper Methods\n--------------\n\n`http#remove_header(header)` removes all occurances of the given header in the\ncurrent buffer.\n\n`http#set_header(header, value)` sets the header to the given value in the\ncurrent buffer, removes duplicates\n\nExamples for your vimrc:\n\n```viml\nfunction! s:set_json_header() abort\n  call http#set_header('Content-Type', 'application/json')\nendfunction\n\nfunction! s:clean_personal_stuff() abort\n  call http#remove_header('Cookie')\n  call http#remove_header('Accept')\n  call http#remove_header('User-Agent')\n  call http#remove_header('Accept-Language')\nendfunction \n\nfunction! s:add_compression() abort\n  call http#set_header('Accept-Encoding', 'deflate, gzip')\n  let g:vim_http_additional_curl_args = '--compressed'\nendfunction\n\ncommand! JSON call s:set_json_header()\ncommand! Anon call s:clean_personal_stuff()\ncommand! Compression call s:add_compression()\n```\n"
  },
  {
    "path": "autoload/http.vim",
    "content": "let s:save_cpo = &cpo\nset cpo&vim\n\nlet s:V = vital#vim_http#new()\nlet s:Base64 = s:V.import('Data.Base64')\n\nfunction! s:new_request() abort\n    let l:response = {\n                \\ 'method': 'GET',\n                \\ 'headers': {},\n                \\ 'uri': '',\n                \\ 'content': '',\n                \\ 'version': '1.1',\n                \\ 'meta': {\n                \\   'follow': 0,\n                \\   }\n                \\ }\n    return l:response\nendfunction\n\nlet s:pre_clean_uri_line_pattern = '^\\(\\a*\\) \\(.*\\) HTTP/\\([0-9.]\\+\\)\\|$'\nlet s:uri_line_pattern = '^\\(OPTIONS\\|GET\\|HEAD\\|POST\\|PUT\\|DELETE\\|TRACE\\|CONNECT\\|PATCH\\) \\(.*\\) HTTP/\\([0-9.]\\+\\)$'\nlet s:header_line_pattern = '^\\([^:]\\+\\): \\(.*\\)$'\n\nfunction! s:get_lines(buffer, count, line1, line2) abort\n    if a:count == -1\n      return getbufline(a:buffer, 0, '$')\n    else\n      return getbufline(a:buffer, a:line1, a:line2)\n    endif\nendfunction\n\nfunction! s:parse_request(lines, pattern, follow) abort\n    let l:request = s:new_request()\n    if a:follow == 1\n        let l:request.meta.follow = 1\n    end\n\n    if len(a:lines) < 0\n        throw 'No lines in buffer :('\n    endif\n\n    let l:uri_line = a:lines[0]\n    let l:uri_line_matches = matchlist(l:uri_line, a:pattern)\n\n    if len(l:uri_line_matches) == 0\n        throw 'Unable to parse first line of request'\n    end\n\n    let l:request.method = l:uri_line_matches[1]\n    let l:request.uri = l:uri_line_matches[2]\n    let l:request.version = l:uri_line_matches[3]\n\n    if l:request.version =~ '2.*'\n      let l:request.version = '2'\n    end\n\n    let l:in_content = 0\n    let l:first_content_line = 1\n    for l:line in a:lines[0:]\n        if l:in_content == 0 && l:line =~ '^\\s*$'\n            let l:in_content = 1\n        end\n        if l:in_content == 0\n            let l:header_matches = matchlist(l:line, s:header_line_pattern)\n\n            if len(l:header_matches) > 0\n                let l:header_key = l:header_matches[1]\n                let l:header_value = l:header_matches[2]\n                if has_key(l:request.headers, l:header_key) == 0\n                    let l:request.headers[l:header_key] = []\n                endif\n                let l:request.headers[l:header_key] += [l:header_value]\n            endif\n        else\n            if l:first_content_line && l:line =~ '^\\s*$'\n                continue\n            endif\n            if l:first_content_line\n                let l:first_content_line = 0\n            else\n                let l:request.content .= \"\\r\\n\"\n            endif\n            let l:request.content .= l:line\n        endif\n    endfor\n\n    return l:request\nendfunction\n\nfunction! s:in_curl_format(request) abort\n    let l:curl_fmt = 'curl%s%s%s%s %s'\n\n    let l:flags = ' -si'\n\n    if a:request.meta.follow == 1\n        let l:flags = l:flags . 'L'\n    endif\n\n    let l:flags = l:flags.' '.g:vim_http_additional_curl_args\n\n    let l:flags = l:flags.' --http'.a:request.version\n\n    let l:method = printf(' -X %s', a:request.method)\n\n    let l:url = shellescape(a:request.uri)\n\n    let l:headers = ''\n\n    for [l:header_key, l:header_value_list] in items(a:request.headers)\n        for l:header_value in l:header_value_list\n            let l:headers = l:headers .\n                        \\ ' -H \"' .\n                        \\ printf('%s: %s', l:header_key, l:header_value) .\n                        \\ '\"'\n        endfor\n\n    endfor\n\n    let l:data = ''\n    if len(a:request.content)\n        let l:data = ' -d ' . substitute(shellescape(a:request.content), '\\\\\\n', \"\\n\", 'g')\n    endif\n\n    let l:curl_request = printf(l:curl_fmt, l:flags, l:method, l:headers, l:data, l:url)\n\n    return l:curl_request\n\nendfunction\n\nfunction! s:lines_with_header(header) abort\n  let l:linenr = 1\n  let l:lines = []\n  while l:linenr < line('$')\n    let l:line = getline(l:linenr)\n    if l:line =~ '^\\s*$'\n      break\n    endif\n    if l:line =~ \"^\".a:header.\":\"\n      call add(l:lines, l:linenr)\n    end\n    let l:linenr = l:linenr + 1\n  endwhile\n  return l:lines\nendfunction\n\nfunction! s:remove_header(header) abort\n  let l:offset = 0\n  for l:linenr in s:lines_with_header(a:header)\n    let l:target = l:linenr - l:offset\n    exe l:target.\"delete _\"\n    let l:offset = l:offset + 1\n  endfor\nendfunction\n\nfunction! s:new_response_buffer(request_buffer, response) abort\n    let l:request_buffer_name  = bufname(a:request_buffer)\n    let l:buffer_name = fnamemodify(l:request_buffer_name, \":r\") . '.response.' . localtime() . '.http'\n    if g:vim_http_tempbuffer\n      for l:win in range(1, winnr('$'))\n        if getwinvar(l:win, 'vim_http_tempbuffer')\n          execute l:win . 'windo close'\n        endif\n      endfor\n    endif\n    let l:rightbelow = g:vim_http_right_below ? 'rightbelow ' : ''\n    let l:keepalt = g:vim_http_tempbuffer ? 'keepalt ' : ''\n    let l:vert = g:vim_http_split_vertically ? 'vert ' : ''\n    execute l:keepalt . l:rightbelow . l:vert . 'new ' . l:buffer_name\n    set ft=http\n    if g:vim_http_tempbuffer\n      setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nonumber\n      let w:vim_http_tempbuffer = 1\n    endif\n\n    let l:response_lines = split(a:response, \"\\\\(\\r\\n\\\\|\\n\\\\)\")\n\n    call append(0, l:response_lines)\n    norm! G\"_ddgg\nendfunction\n\nfunction! http#do_buffer(follow) abort\n    let l:bang =  a:follow ? '!' : ''\n    call http#do(l:bang, -1, 0, 0)\nendfunction\n\nfunction! http#do(bang, count, line1, line2) abort\n    let l:follow =  a:bang == '!' ? 1 : 0\n    if g:vim_http_clean_before_do && a:count == -1\n      call http#clean()\n    end\n    let l:buffer = bufnr('')\n    let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)\n    let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)\n    let l:curl = s:in_curl_format(l:request)\n    let l:response = system(l:curl)\n    call s:new_response_buffer(l:buffer, l:response)\nendfunction\n\nfunction! http#show_curl(bang, count, line1, line2) abort\n    let l:follow =  a:bang == '!' ? 1 : 0\n    let l:buffer = bufnr('')\n    let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)\n    let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)\n    let l:curl = s:in_curl_format(l:request)\n    echo l:curl\nendfunction\n\nfunction! http#show_request(bang, count, line1, line2) abort\n    let l:follow =  a:bang == '!' ? 1 : 0\n    let l:buffer = bufnr('')\n    let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)\n    let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)\n    echo l:request\nendfunction\n\nfunction! http#clean() abort\n  let l:buffer = bufnr('')\n  let l:lines = s:get_lines(l:buffer, -1, 0, 0)\n  let l:request = s:parse_request(l:lines, s:pre_clean_uri_line_pattern, 0)\n\n  \" when the http proto is > 1.0 make sure we are adding a host header\n  if index(['1.1', '2'], l:request.version) != -1 && !has_key(l:request.headers, 'Host')\n    let l:matches = matchlist(l:request.uri, '^\\([^:]\\+://\\)\\?\\([^/]\\+\\)')\n    let l:host = l:matches[2]\n    if len(l:host)\n      call append(1, 'Host: ' . l:host)\n    endif\n  endif\n\n  if l:request.version == ''\n    call setline('.', getline('.') . ' HTTP/1.1')\n  endif\n\n  let l:content_length = len(l:request.content)\n\n  \" when we have a Content-Length header and it doesn't match the actual\n  \" content length\n  if l:content_length && has_key(l:request.headers, 'Content-Length')\n    if string(l:content_length) != l:request.headers['Content-Length'][-1]\n      let l:correct = input(\"correct Content-Length header? [Y]/N:\")\n      if len(l:correct) == 0 || tolower(l:correct) != \"n\"\n        call remove(l:request.headers, 'Content-Length')\n        call s:remove_header('Content-Length')\n      endif\n    endif\n  endif\n\n  \" when we are sending content we should add a header for the content length\n  \" curl is going to do this for us anyway, but it's good to be explicit\n  if  l:content_length && !has_key(l:request.headers, 'Content-Length')\n    call append(1 + len(l:request.headers), 'Content-Length: ' . l:content_length)\n  endif\nendfunction\n\nfunction! http#auth() abort\n  let l:buffer = bufnr('')\n  let l:lines = s:get_lines(l:buffer, -1, 0, 0)\n  let l:request = s:parse_request(l:lines, s:uri_line_pattern, 0)\n\n  let l:method = input('method [Basic]: ')\n  if len(l:method) == 0\n    let l:method = 'Basic'\n  end\n  let l:user = input('user: ')\n  let l:password = input('password: ')\n  let l:encoded = s:Base64.encode(l:user . ':' . l:password)\n  let l:header = 'Authorization: ' . l:method . ' ' . l:encoded\n  call append(1 + len(l:request.headers), l:header)\nendfunction\n\nfunction! http#remove_header(header) abort\n  call s:remove_header(a:header)\nendfunction\n\nfunction! http#set_header(header, value) abort\n  call s:remove_header(a:header)\n  let l:buffer = bufnr('')\n  let l:lines = s:get_lines(l:buffer, -1, 0, 0)\n  let l:request = s:parse_request(l:lines, s:uri_line_pattern, 0)\n  call append(1 + len(l:request.headers), a:header.': '.a:value)\nendfunction\n\n\" Teardown:{{{1\nlet &cpo = s:save_cpo\n"
  },
  {
    "path": "autoload/vital/_vim_http/Data/Base64.vim",
    "content": "\" ___vital___\n\" NOTE: lines between '\" ___vital___' is generated by :Vitalize.\n\" Do not mofidify the code nor insert new lines before '\" ___vital___'\nif v:version > 703 || v:version == 703 && has('patch1170')\n  function! vital#_vim_http#Data#Base64#import() abort\n    return map({'decode': '', 'encodebin': '', 'encode': ''},  'function(\"s:\" . v:key)')\n  endfunction\nelse\n  function! s:_SID() abort\n    return matchstr(expand('<sfile>'), '<SNR>\\zs\\d\\+\\ze__SID$')\n  endfunction\n  execute join(['function! vital#_vim_http#Data#Base64#import() abort', printf(\"return map({'decode': '', 'encodebin': '', 'encode': ''}, \\\"function('<SNR>%s_' . v:key)\\\")\", s:_SID()), 'endfunction'], \"\\n\")\n  delfunction s:_SID\nendif\n\" ___vital___\n\" Utilities for Base64.\n\nlet s:save_cpo = &cpo\nset cpo&vim\n\nfunction! s:encode(data) abort\n  let b64 = s:_b64encode(s:_str2bytes(a:data), s:standard_table, '=')\n  return join(b64, '')\nendfunction\n\nfunction! s:encodebin(data) abort\n  let b64 = s:_b64encode(s:_binstr2bytes(a:data), s:standard_table, '=')\n  return join(b64, '')\nendfunction\n\nfunction! s:decode(data) abort\n  let bytes = s:_b64decode(split(a:data, '\\zs'), s:standard_table, '=')\n  return s:_bytes2str(bytes)\nendfunction\n\nlet s:standard_table = [\n      \\ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',\n      \\ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',\n      \\ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',\n      \\ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/']\n\nlet s:urlsafe_table = [\n      \\ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',\n      \\ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',\n      \\ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',\n      \\ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','-','_']\n\nfunction! s:_b64encode(bytes, table, pad) abort\n  let b64 = []\n  for i in range(0, len(a:bytes) - 1, 3)\n    let n = a:bytes[i] * 0x10000\n          \\ + get(a:bytes, i + 1, 0) * 0x100\n          \\ + get(a:bytes, i + 2, 0)\n    call add(b64, a:table[n / 0x40000])\n    call add(b64, a:table[n / 0x1000 % 0x40])\n    call add(b64, a:table[n / 0x40 % 0x40])\n    call add(b64, a:table[n % 0x40])\n  endfor\n  if len(a:bytes) % 3 == 1\n    let b64[-1] = a:pad\n    let b64[-2] = a:pad\n  endif\n  if len(a:bytes) % 3 == 2\n    let b64[-1] = a:pad\n  endif\n  return b64\nendfunction\n\nfunction! s:_b64decode(b64, table, pad) abort\n  let a2i = {}\n  for i in range(len(a:table))\n    let a2i[a:table[i]] = i\n  endfor\n  let bytes = []\n  for i in range(0, len(a:b64) - 1, 4)\n    let n = a2i[a:b64[i]] * 0x40000\n          \\ + a2i[a:b64[i + 1]] * 0x1000\n          \\ + (a:b64[i + 2] == a:pad ? 0 : a2i[a:b64[i + 2]]) * 0x40\n          \\ + (a:b64[i + 3] == a:pad ? 0 : a2i[a:b64[i + 3]])\n    call add(bytes, n / 0x10000)\n    call add(bytes, n / 0x100 % 0x100)\n    call add(bytes, n % 0x100)\n  endfor\n  if a:b64[-1] == a:pad\n    unlet a:b64[-1]\n  endif\n  if a:b64[-2] == a:pad\n    unlet a:b64[-1]\n  endif\n  return bytes\nendfunction\n\nfunction! s:_binstr2bytes(str) abort\n  return map(range(len(a:str)/2), 'eval(\"0x\".a:str[v:val*2 : v:val*2+1])')\nendfunction\n\nfunction! s:_str2bytes(str) abort\n  return map(range(len(a:str)), 'char2nr(a:str[v:val])')\nendfunction\n\nfunction! s:_bytes2str(bytes) abort\n  return eval('\"' . join(map(copy(a:bytes), 'printf(''\\x%02x'', v:val)'), '') . '\"')\nendfunction\n\nlet &cpo = s:save_cpo\nunlet s:save_cpo\n\n\" vim:set et ts=2 sts=2 sw=2 tw=0:\n"
  },
  {
    "path": "autoload/vital/_vim_http.vim",
    "content": "let s:_plugin_name = expand('<sfile>:t:r')\n\nfunction! vital#{s:_plugin_name}#new() abort\n  return vital#{s:_plugin_name[1:]}#new()\nendfunction\n"
  },
  {
    "path": "autoload/vital/vim_http.vim",
    "content": "let s:plugin_name = expand('<sfile>:t:r')\nlet s:vital_base_dir = expand('<sfile>:h')\nlet s:project_root = expand('<sfile>:h:h:h')\nlet s:is_vital_vim = s:plugin_name is# 'vital'\n\nlet s:loaded = {}\nlet s:cache_sid = {}\n\n\" function() wrapper\nif v:version > 703 || v:version == 703 && has('patch1170')\n  function! s:_function(fstr) abort\n    return function(a:fstr)\n  endfunction\nelse\n  function! s:_SID() abort\n    return matchstr(expand('<sfile>'), '<SNR>\\zs\\d\\+\\ze__SID$')\n  endfunction\n  let s:_s = '<SNR>' . s:_SID() . '_'\n  function! s:_function(fstr) abort\n    return function(substitute(a:fstr, 's:', s:_s, 'g'))\n  endfunction\nendif\n\nfunction! vital#{s:plugin_name}#new() abort\n  return s:new(s:plugin_name)\nendfunction\n\nfunction! vital#{s:plugin_name}#import(...) abort\n  if !exists('s:V')\n    let s:V = s:new(s:plugin_name)\n  endif\n  return call(s:V.import, a:000, s:V)\nendfunction\n\nlet s:Vital = {}\n\nfunction! s:new(plugin_name) abort\n  let base = deepcopy(s:Vital)\n  let base._plugin_name = a:plugin_name\n  return base\nendfunction\n\nfunction! s:vital_files() abort\n  if !exists('s:vital_files')\n    let s:vital_files = map(\n    \\   s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(),\n    \\   'fnamemodify(v:val, \":p:gs?[\\\\\\\\/]?/?\")')\n  endif\n  return copy(s:vital_files)\nendfunction\nlet s:Vital.vital_files = s:_function('s:vital_files')\n\nfunction! s:import(name, ...) abort dict\n  let target = {}\n  let functions = []\n  for a in a:000\n    if type(a) == type({})\n      let target = a\n    elseif type(a) == type([])\n      let functions = a\n    endif\n    unlet a\n  endfor\n  let module = self._import(a:name)\n  if empty(functions)\n    call extend(target, module, 'keep')\n  else\n    for f in functions\n      if has_key(module, f) && !has_key(target, f)\n        let target[f] = module[f]\n      endif\n    endfor\n  endif\n  return target\nendfunction\nlet s:Vital.import = s:_function('s:import')\n\nfunction! s:load(...) abort dict\n  for arg in a:000\n    let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg]\n    let target = split(join(as, ''), '\\W\\+')\n    let dict = self\n    let dict_type = type({})\n    while !empty(target)\n      let ns = remove(target, 0)\n      if !has_key(dict, ns)\n        let dict[ns] = {}\n      endif\n      if type(dict[ns]) == dict_type\n        let dict = dict[ns]\n      else\n        unlet dict\n        break\n      endif\n    endwhile\n    if exists('dict')\n      call extend(dict, self._import(name))\n    endif\n    unlet arg\n  endfor\n  return self\nendfunction\nlet s:Vital.load = s:_function('s:load')\n\nfunction! s:unload() abort dict\n  let s:loaded = {}\n  let s:cache_sid = {}\n  unlet! s:vital_files\nendfunction\nlet s:Vital.unload = s:_function('s:unload')\n\nfunction! s:exists(name) abort dict\n  if a:name !~# '\\v^\\u\\w*%(\\.\\u\\w*)*$'\n    throw 'vital: Invalid module name: ' . a:name\n  endif\n  return s:_module_path(a:name) isnot# ''\nendfunction\nlet s:Vital.exists = s:_function('s:exists')\n\nfunction! s:search(pattern) abort dict\n  let paths = s:_extract_files(a:pattern, self.vital_files())\n  let modules = sort(map(paths, 's:_file2module(v:val)'))\n  return s:_uniq(modules)\nendfunction\nlet s:Vital.search = s:_function('s:search')\n\nfunction! s:plugin_name() abort dict\n  return self._plugin_name\nendfunction\nlet s:Vital.plugin_name = s:_function('s:plugin_name')\n\nfunction! s:_self_vital_files() abort\n  let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name)\n  let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name)\n  let base = builtin . ',' . installed\n  return split(globpath(base, '**/*.vim', 1), \"\\n\")\nendfunction\n\nfunction! s:_global_vital_files() abort\n  let pattern = 'autoload/vital/__*__/**/*.vim'\n  return split(globpath(&runtimepath, pattern, 1), \"\\n\")\nendfunction\n\nfunction! s:_extract_files(pattern, files) abort\n  let tr = {'.': '/', '*': '[^/]*', '**': '.*'}\n  let target = substitute(a:pattern, '\\.\\|\\*\\*\\?', '\\=tr[submatch(0)]', 'g')\n  let regexp = printf('autoload/vital/[^/]\\+/%s.vim$', target)\n  return filter(a:files, 'v:val =~# regexp')\nendfunction\n\nfunction! s:_file2module(file) abort\n  let filename = fnamemodify(a:file, ':p:gs?[\\\\/]?/?')\n  let tail = matchstr(filename, 'autoload/vital/_\\w\\+/\\zs.*\\ze\\.vim$')\n  return join(split(tail, '[\\\\/]\\+'), '.')\nendfunction\n\n\" @param {string} name e.g. Data.List\nfunction! s:_import(name) abort dict\n  if has_key(s:loaded, a:name)\n    return copy(s:loaded[a:name])\n  endif\n  let module = self._get_module(a:name)\n  if has_key(module, '_vital_created')\n    call module._vital_created(module)\n  endif\n  let export_module = filter(copy(module), 'v:key =~# \"^\\\\a\"')\n  \" Cache module before calling module.vital_loaded() to avoid cyclic\n  \" dependences but remove the cache if module._vital_loaded() fails.\n  \" let s:loaded[a:name] = export_module\n  let s:loaded[a:name] = export_module\n  if has_key(module, '_vital_loaded')\n    try\n      call module._vital_loaded(vital#{s:plugin_name}#new())\n    catch\n      unlet s:loaded[a:name]\n      throw 'vital: fail to call ._vital_loaded(): ' . v:exception\n    endtry\n  endif\n  return copy(s:loaded[a:name])\nendfunction\nlet s:Vital._import = s:_function('s:_import')\n\n\" s:_get_module() returns module object wihch has all script local functions.\nfunction! s:_get_module(name) abort dict\n  let funcname = s:_import_func_name(self.plugin_name(), a:name)\n  if s:_exists_autoload_func_with_source(funcname)\n    return call(funcname, [])\n  else\n    return s:_get_builtin_module(a:name)\n  endif\nendfunction\n\nfunction! s:_get_builtin_module(name) abort\n return s:sid2sfuncs(s:_module_sid(a:name))\nendfunction\n\nif s:is_vital_vim\n  \" For vital.vim, we can use s:_get_builtin_module directly\n  let s:Vital._get_module = s:_function('s:_get_builtin_module')\nelse\n  let s:Vital._get_module = s:_function('s:_get_module')\nendif\n\nfunction! s:_import_func_name(plugin_name, module_name) abort\n  return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name))\nendfunction\n\nfunction! s:_module_sid(name) abort\n  let path = s:_module_path(a:name)\n  if !filereadable(path)\n    throw 'vital: module not found: ' . a:name\n  endif\n  let vital_dir = s:is_vital_vim ? '__\\w\\+__' : printf('_\\{1,2}%s\\%%(__\\)\\?', s:plugin_name)\n  let base = join([vital_dir, ''], '[/\\\\]\\+')\n  let p = base . substitute('' . a:name, '\\.', '[/\\\\\\\\]\\\\+', 'g')\n  let sid = s:_sid(path, p)\n  if !sid\n    call s:_source(path)\n    let sid = s:_sid(path, p)\n    if !sid\n      throw printf('vital: cannot get <SID> from path: %s', path)\n    endif\n  endif\n  return sid\nendfunction\n\nfunction! s:_module_path(name) abort\n  return get(s:_extract_files(a:name, s:vital_files()), 0, '')\nendfunction\n\nfunction! s:_module_sid_base_dir() abort\n  return s:is_vital_vim ? &rtp : s:project_root\nendfunction\n\nfunction! s:_dot_to_sharp(name) abort\n  return substitute(a:name, '\\.', '#', 'g')\nendfunction\n\n\" It will sources autoload file if a given func is not already defined.\nfunction! s:_exists_autoload_func_with_source(funcname) abort\n  if exists('*' . a:funcname)\n    \" Return true if a given func is already defined\n    return 1\n  endif\n  \" source a file which may include a given func definition and try again.\n  let path = 'autoload/' . substitute(substitute(a:funcname, '#[^#]*$', '.vim', ''), '#', '/', 'g')\n  call s:_runtime(path)\n  return exists('*' . a:funcname)\nendfunction\n\nfunction! s:_runtime(path) abort\n  execute 'runtime' fnameescape(a:path)\nendfunction\n\nfunction! s:_source(path) abort\n  execute 'source' fnameescape(a:path)\nendfunction\n\n\" @vimlint(EVL102, 1, l:_)\n\" @vimlint(EVL102, 1, l:__)\nfunction! s:_sid(path, filter_pattern) abort\n  let unified_path = s:_unify_path(a:path)\n  if has_key(s:cache_sid, unified_path)\n    return s:cache_sid[unified_path]\n  endif\n  for line in filter(split(s:_redir(':scriptnames'), \"\\n\"), 'v:val =~# a:filter_pattern')\n    let [_, sid, path; __] = matchlist(line, '^\\s*\\(\\d\\+\\):\\s\\+\\(.\\+\\)\\s*$')\n    if s:_unify_path(path) is# unified_path\n      let s:cache_sid[unified_path] = sid\n      return s:cache_sid[unified_path]\n    endif\n  endfor\n  return 0\nendfunction\n\nfunction! s:_redir(cmd) abort\n  let [save_verbose, save_verbosefile] = [&verbose, &verbosefile]\n  set verbose=0 verbosefile=\n  redir => res\n    silent! execute a:cmd\n  redir END\n  let [&verbose, &verbosefile] = [save_verbose, save_verbosefile]\n  return res\nendfunction\n\nif filereadable(expand('<sfile>:r') . '.VIM') \" is case-insensitive or not\n  let s:_unify_path_cache = {}\n  \" resolve() is slow, so we cache results.\n  \" Note: On windows, vim can't expand path names from 8.3 formats.\n  \" So if getting full path via <sfile> and $HOME was set as 8.3 format,\n  \" vital load duplicated scripts. Below's :~ avoid this issue.\n  function! s:_unify_path(path) abort\n    if has_key(s:_unify_path_cache, a:path)\n      return s:_unify_path_cache[a:path]\n    endif\n    let value = tolower(fnamemodify(resolve(fnamemodify(\n    \\                   a:path, ':p')), ':~:gs?[\\\\/]?/?'))\n    let s:_unify_path_cache[a:path] = value\n    return value\n  endfunction\nelse\n  function! s:_unify_path(path) abort\n    return resolve(fnamemodify(a:path, ':p:gs?[\\\\/]?/?'))\n  endfunction\nendif\n\n\" copied and modified from Vim.ScriptLocal\nlet s:SNR = join(map(range(len(\"\\<SNR>\")), '\"[\\\\x\" . printf(\"%0x\", char2nr(\"\\<SNR>\"[v:val])) . \"]\"'), '')\nfunction! s:sid2sfuncs(sid) abort\n  let fs = split(s:_redir(printf(':function /^%s%s_', s:SNR, a:sid)), \"\\n\")\n  let r = {}\n  let pattern = printf('\\m^function\\s<SNR>%d_\\zs\\w\\{-}\\ze(', a:sid)\n  for fname in map(fs, 'matchstr(v:val, pattern)')\n    let r[fname] = function(s:_sfuncname(a:sid, fname))\n  endfor\n  return r\nendfunction\n\n\"\" Return funcname of script local functions with SID\nfunction! s:_sfuncname(sid, funcname) abort\n  return printf('<SNR>%s_%s', a:sid, a:funcname)\nendfunction\n\nif exists('*uniq')\n  function! s:_uniq(list) abort\n    return uniq(a:list)\n  endfunction\nelse\n  function! s:_uniq(list) abort\n    let i = len(a:list) - 1\n    while 0 < i\n      if a:list[i] ==# a:list[i - 1]\n        call remove(a:list, i)\n      endif\n      let i -= 1\n    endwhile\n    return a:list\n  endfunction\nendif\n"
  },
  {
    "path": "autoload/vital/vim_http.vital",
    "content": "vim_http\nf07284453d294e2159424aa76b9b902a5b25d5ae\n\nData.Base64\n"
  },
  {
    "path": "autoload/vital.vim",
    "content": "function! vital#of(name) abort\n  let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital', 1)\n  let file = split(files, \"\\n\")\n  if empty(file)\n    throw 'vital: version file not found: ' . a:name\n  endif\n  let ver = readfile(file[0], 'b')\n  if empty(ver)\n    throw 'vital: invalid version file: ' . a:name\n  endif\n  return vital#_{substitute(ver[0], '\\W', '', 'g')}#new()\nendfunction\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3.4\"\nservices:\n  httpbin:\n    image: citizenstig/httpbin\n  oldest:\n    image: thinca/vim:v7.4.566\n    volumes:\n      - .:/root/.vim\n  latest:\n    image: thinca/vim:latest\n    volumes:\n      - .:/root/.vim\n"
  },
  {
    "path": "ftdetect/http.vim",
    "content": "augroup filetypedetect\n    au! BufRead,BufNewFile *.http   setlocal filetype=http fileformat=dos\naugroup END\n"
  },
  {
    "path": "plugin/http.vim",
    "content": "if !exists('g:vim_http_clean_before_do')\n  let g:vim_http_clean_before_do = 1\nendif\nif !exists('g:vim_http_additional_curl_args')\n  let g:vim_http_additional_curl_args = ''\nendif\nif !exists('g:vim_http_split_vertically')\n  let g:vim_http_split_vertically = 0\nendif\nif !exists('g:vim_http_right_below')\n  let g:vim_http_right_below = 0\nendif\nif !exists('g:vim_http_tempbuffer')\n  let g:vim_http_tempbuffer = 0\nendif\n\ncommand! -bang -range Http call http#do('<bang>', '<count>', '<line1>', '<line2>')\ncommand! -bang -range HttpShowCurl call http#show_curl('<bang>', '<count>', '<line1>', '<line2>')\ncommand! -bang -range HttpShowRequest call http#show_request('<bang>', '<count>', '<line1>', '<line2>')\ncommand! HttpClean call http#clean()\ncommand! HttpAuth call http#auth()\n"
  },
  {
    "path": "syntax/http.vim",
    "content": "if version < 600\n  syntax clear\nelseif exists(\"b:current_syntax\")\n  finish\nendif\n\nlet s:cpo_save = &cpo\nset cpo&vim\n\nsyn keyword httpMethod  OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT PATCH contained\n\nsyn match httpProto         'HTTP/[0-9.]\\+' contained\nsyn match httpStatusCode    '[0-9]\\{3\\}' contained\nsyn match httpStatus        '[0-9]\\{3\\} .*$' contained contains=httpStatusCode\nsyn match httpHeaderKey     '^[A-Z][A-Za-z0-9\\-]*:' contained\nsyn match httpURILine       '^\\(OPTIONS\\|GET\\|HEAD\\|POST\\|PUT\\|DELETE\\|TRACE\\|CONNECT\\|PATCH\\)\\( .*\\)\\?\\(HTTP/[0-9.]\\+\\)\\?$'  contains=httpMethod,httpProto contained\nsyn match httpResponseLine  '^HTTP/[0-9.]\\+ [0-9]\\{3\\}.*$' contains=httpProto,httpStatus contained\nsyn match httpHeaderLine    '^[A-Z][A-Za-z0-9\\-]*: .*$' contains=httpHeaderKey contained\n\nsyn region httpHeader       start='^\\(OPTIONS\\|GET\\|HEAD\\|POST\\|PUT\\|DELETE\\|TRACE\\|CONNECT\\|PATCH\\)\\( .*\\)\\?\\(HTTP/[0-9.]\\+\\)\\?$' end='\\n\\s*\\n' contains=httpURILine,httpHeaderLine\nsyn region httpHeader       start='^HTTP/[0-9.]\\+ [0-9]\\{3\\}.*$' end='\\n\\s*\\n' contains=httpResponseLine,httpHeaderLine\n\nhi link httpMethod      Type\nhi link httpProto       Statement\nhi link httpHeaderKey   Identifier\nhi link httpStatus      String\nhi link httpStatusCode      Number\n\nlet b:current_syntax = 'http'\n\nlet &cpo = s:cpo_save\nunlet s:cpo_save\n"
  },
  {
    "path": "test/.themisrc",
    "content": "\nlet g:http_test_files = expand('<sfile>:p:h') . '/test-http-files/'\n\ncall themis#log('test_files_location: ' . g:http_test_files)\n"
  },
  {
    "path": "test/integration.vim",
    "content": "let s:suite = themis#suite('integration')\nlet s:assert = themis#helper('assert')\n\n\" Setup: {{{1\nlet s:original_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)')\n\nfunction! s:suite.before_each()\n    let l:current_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)')\n    let l:buffers_to_wipe = filter(copy(l:current_buffers), 'index(s:original_buffers, v:val) == -1')\n    for l:buffer in l:buffers_to_wipe\n        execute 'bw!' . l:buffer\n    endfor\nendfunction\n\nfunction! s:load_request_expected(name) abort\n    let l:request_path = g:http_test_files . a:name . '.http'\n    execute 'edit! ' . l:request_path\nendfunction\n\nfunction! s:load_request_expected_explicit(name) abort\n    let l:request_path = g:http_test_files . a:name\n    execute 'edit! ' . l:request_path\nendfunction\n\nfunction s:assert_response_explicit(filepath, name) abort\n    let l:expected_path = g:http_test_files . a:filepath\n    let l:expected = readfile(l:expected_path)\n    let l:lines = getbufline(a:name, 0, '$')\n\n    let l:bad = 0\n    let l:lnr = 0\n    for l:expect in l:expected\n        let l:line = l:lines[l:lnr]\n        if l:expect =~ '.*%%[^%]\\+%%.*'\n            let l:patterns = split(l:expect, '%%')\n            let l:in_pattern = 0\n            let l:line_index = 0 \n            let l:pattern_nr = 0\n            for l:pattern in l:patterns\n                if l:in_pattern\n                    let l:p = '\\(' . substitute(l:pattern, '^\\s*\\([''\"]\\)\\(.*\\)\\1\\s*$', '\\2', '') . '\\)'\n                    let l:pp = l:p\n                    if len(l:patterns) > l:pattern_nr+1\n                        let l:pp = l:p . l:patterns[l:pattern_nr+1]\n                    endif\n                    if l:line[l:line_index :] !~ l:pp\n                        let l:bad = 1\n                        break\n                    endif\n                    let l:line_index += len(substitute(l:line[l:line_index  :], l:pp, '\\1', ''))\n                    let l:in_pattern = 0\n                else\n                    if l:pattern != l:line[l:line_index : l:line_index + len(l:pattern)-1]\n                        let l:bad = 1\n                        break\n                    end\n                    let l:line_index += len(l:pattern)\n                    let l:in_pattern = 1\n                endif\n\n                let l:pattern_nr += 1\n            endfor\n        else\n            if l:line != l:expect\n                let l:bad = 1\n            endif\n        endif\n        if l:bad == 1\n            break\n        end\n\n        let l:lnr += 1\n\n    endfor\n\n    if l:bad\n        let l:msg = [printf('Got response for %s.http:', a:name)]\n        let l:msg = l:msg + [''] + l:lines + ['', 'Expected:'] + l:expected\n        throw themis#failure(msg)\n    endif\nendfunction\n\nfunction s:assert_response(name) abort\n  call s:assert_response_explicit(a:name . '.expected.http', a:name . '.response.*.http')\nendfunction\n\nfunction! s:command_with_input(cmd, input)\n  exe 'normal :' . a:cmd .'\r'.join(a:input, '\r').'\r'\nendfunction\n\" }}}\n\" GET: {{{1\nfunction! s:suite.simple_get()\n    call s:load_request_expected('simple_get')\n    Http\n    call s:assert_response('simple_get')\nendfunction\n\nfunction! s:suite.get_with_url_params()\n    call s:load_request_expected('get_url_params')\n    Http\n    call s:assert_response('get_url_params')\nendfunction\n\nfunction! s:suite.get_with_body_params()\n    call s:load_request_expected('get_body_params')\n    Http\n    call s:assert_response('get_body_params')\nendfunction\n\nfunction! s:suite.http_1_0()\n    call s:load_request_expected('http_1_0')\n    Http!\n    call s:assert_response('http_1_0')\nendfunction\n\nfunction! s:suite.http_1_1()\n    call s:load_request_expected('http_1_1')\n    Http!\n    call s:assert_response('http_1_1')\nendfunction\n\nfunction! s:suite.redirect()\n    call s:load_request_expected('redirect')\n    Http\n    call s:assert_response('redirect')\nendfunction\n\nfunction! s:suite.redirect_with_follow()\n    call s:load_request_expected('redirect_follow')\n    Http!\n    call s:assert_response('redirect_follow')\nendfunction\n\n\" }}}\n\" POST :{{{1\nfunction! s:suite.post_json()\n    call s:load_request_expected('post_json')\n    Http!\n    call s:assert_response('post_json')\nendfunction\n\n\" }}}\n\" PATCH :{{{1\nfunction! s:suite.patch_json()\n    call s:load_request_expected('patch_json')\n    Http!\n    call s:assert_response('patch_json')\nendfunction\n\n\" }}}\n\" Clean :{{{1\nfunction! s:suite.clean()\n    call s:load_request_expected('post_incomplete')\n    HttpClean\n    let l:contents = getline(0, '$')\n    let l:expected = ['POST http://localhost:8000/post HTTP/1.1', \n          \\ 'Host: localhost:8000',\n          \\ 'Content-Length: 43',\n          \\ 'Content-Type: application/json',\n          \\ '',\n          \\ '',\n          \\ '{',\n          \\ '    \"foo\": \"bar\",',\n          \\ '    \"lol\": \"beans\"',\n          \\ '}',\n          \\ ]\n    call s:assert.equal(l:contents, l:expected)\nendfunction\n\nfunction! s:suite.clean_adds_host_on_http2_requests()\n    call s:load_request_expected('http_2')\n    HttpClean\n    let l:contents = getline(0, '$')\n    let l:expected = ['GET http://localhost:8000/get HTTP/2',\n          \\ 'Host: localhost:8000',\n          \\ ]\n    call s:assert.equal(l:contents, l:expected)\nendfunction\n\nfunction! s:suite.clean_replaces_invalid_content_length_header()\n  call s:load_request_expected('post_invalid_content_length')\n  call s:command_with_input('HttpClean', ['Y'])\n  let l:contents = getline(0, '$')\n  let l:expected = ['POST http://localhost:8000/post HTTP/1.1', \n        \\ 'Host: localhost:8000',\n        \\ 'Content-Type: application/json',\n        \\ 'Content-Length: 43',\n        \\ '',\n        \\ '',\n        \\ '{',\n        \\ '    \"foo\": \"bar\",',\n        \\ '    \"lol\": \"beans\"',\n        \\ '}',\n        \\ ]\n  call s:assert.equal(l:contents, l:expected)\nendfunction\n\nfunction! s:suite.clean_appends_protocol()\n  call s:load_request_expected('simple_get_without_protocol')\n  call s:command_with_input('HttpClean', ['Y'])\n  let l:contents = getline(0, '$')\n  let l:expected = ['GET http://localhost:8000/get HTTP/1.1']\n  call s:assert.equal(l:contents, l:expected)\nendfunction\n\n\" }}}\n\" Auth: {{{1\nfunction! s:suite.auth()\n    call s:load_request_expected('simple_get')\n    call s:command_with_input('HttpAuth', ['Bearer', 'borisjohnson', 'ijustcantwaittobeking'])\n    let l:contents = getline(0, '$')\n    let l:expected = ['GET http://localhost:8000/get HTTP/1.1', \n          \\ 'Host: localhost:8000',\n          \\ 'Authorization: Bearer Ym9yaXNqb2huc29uOmlqdXN0Y2FudHdhaXR0b2Jla2luZw==',\n          \\ ]\n    call s:assert.equal(l:contents, l:expected)\nendfunction\n\" }}}\n\" Headers: {{{1\nfunction! s:suite.remove_header()\n  call s:load_request_expected('get_with_multiple_headers')\n  call http#remove_header('User-Agent')\n  let l:contents = getline(0, '$')\n  let l:expected = ['GET http://localhost:8000/get HTTP/1.1', \n        \\ 'Host: localhost:8000',\n        \\ 'Accept: text/html',\n        \\ 'Accept-Encoding: gzip, deflate',\n        \\ 'Accept-Language: en-US,en;q=0.5 ',\n        \\ 'Connection: close',\n        \\ 'Upgrade-Insecure-Requests: 1 ',\n        \\ ]\n  call s:assert.equal(l:contents, l:expected)\nendfunction\n\nfunction! s:suite.set_header()\n  call s:load_request_expected('get_with_multiple_headers')\n  call http#set_header('User-Agent', 'qutebrowser/1.0.0')\n  let l:contents = getline(0, '$')\n  let l:expected = ['GET http://localhost:8000/get HTTP/1.1', \n        \\ 'Host: localhost:8000',\n        \\ 'Accept: text/html',\n        \\ 'Accept-Encoding: gzip, deflate',\n        \\ 'Accept-Language: en-US,en;q=0.5 ',\n        \\ 'Connection: close',\n        \\ 'Upgrade-Insecure-Requests: 1 ',\n        \\ 'User-Agent: qutebrowser/1.0.0',\n        \\ ]\n  call s:assert.equal(l:contents, l:expected)\nendfunction\n\" }}}\n\" Misc: {{{1\nfunction! s:suite.tempbuffer()\n    let l:vim_http_tempbuffer = g:vim_http_tempbuffer\n    let g:vim_http_tempbuffer = 1\n\n    call s:load_request_expected('simple_get')\n    call s:load_request_expected('get_url_params')\n    let l:expected = filter(range(1, bufnr('$')), 'bufexists(v:val)')\n\n    Http     \" test get_url_params.http\n    wincmd p \" switch from response window back to get_url_params.http window\n    buffer # \" switch to simple_get.http buffer\n    Http     \" test simple_get.http\n    wincmd c \" close response window\n\n    let l:contents = filter(range(1, bufnr('$')), 'bufexists(v:val)')\n    let g:vim_http_tempbuffer = l:vim_http_tempbuffer\n    call s:assert.equal(l:contents, l:expected)\nendfunction\n\n\" Range: {{{1\n\nfunction! s:suite.do_with_range()\n    call s:load_request_expected_explicit('example_in_docs.md')\n    14,15Http!\n    call s:assert_response_explicit('simple_get.expected.http', 'example_in_docs.response.*.http')\nendfunction\n\n\" vim:fdm=marker\n"
  },
  {
    "path": "test/test-http-files/example_in_docs.md",
    "content": "Imagine this was some documentation and we had an HTTP request right there in\nthe file. Why do I have to create a new buffer for it? That's just dumb lol!\n\n```http\nPOST http://localhost:8000/post HTTP/1.1\nHost: \"localhost:8000\"\n\nI AM A READMEFILE HERE ME ROAR!\n```\n\nSo let's execute this and see what happens:\n\n```http\nGET http://localhost:8000/get HTTP/1.1\nHost: localhost:8000\n```\n\nDid it work?\n\n```http\nPATCH http://localhost:8000/patch HTTP/1.1\nHost: \"localhost:8000\"\n\nLOLOLOLOLOLOL\n```\n\nI hope so\n\nHere is a picture of a cat:\n\n![CAT!](https://i.imgur.com/DKUR9Tk.png)\n"
  },
  {
    "path": "test/test-http-files/get_body_params.expected.http",
    "content": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nContent-Length: %% '[0-9]\\+' %%\r\n\r\n{\r\n  \"args\": {}, \r\n  \"headers\": {\r\n    \"Accept\": \"*/*\", \r\n    \"Content-Length\": \"17\", \r\n    \"Content-Type\": \"application/x-www-form-urlencoded\", \r\n    \"Host\": \"localhost:8000\", \r\n    \"User-Agent\": \"curl/%% '.*' %%\"\r\n  }, \r\n  \"origin\": \"%% '[0-9.]\\+' %%\", \r\n  \"url\": \"http://localhost:8000/get\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/get_body_params.http",
    "content": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n\r\n\r\nfoo=bar&lol=beans\r\n"
  },
  {
    "path": "test/test-http-files/get_url_params.expected.http",
    "content": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nContent-Length: %% '[0-9]\\+' %%\r\n\r\n{\r\n  \"args\": {\r\n    \"foo\": \"bar\", \r\n    \"lol\": \"beans\"\r\n  }, \r\n  \"headers\": {\r\n    \"Accept\": \"*/*\", \r\n    \"Host\": \"localhost:8000\", \r\n    \"User-Agent\": \"curl/%% '.*' %%\"\r\n  }, \r\n  \"origin\": \"%% '[0-9.]\\+' %%\", \r\n  \"url\": \"http://localhost:8000/get?foo=bar&lol=beans\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/get_url_params.http",
    "content": "GET http://localhost:8000/get?foo=bar&lol=beans HTTP/1.1\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/get_with_multiple_headers.http",
    "content": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\nAccept: text/html\r\nAccept-Encoding: gzip, deflate\r\nUser-Agent: Mozilla/5.0\r\nAccept-Language: en-US,en;q=0.5 \r\nConnection: close\r\nUser-Agent: Mozilla/4.0\r\nUpgrade-Insecure-Requests: 1 \r\n"
  },
  {
    "path": "test/test-http-files/http_1_0.expected.http",
    "content": "HTTP/1.0 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nContent-Length: %% '[0-9]\\+' %%\r\n\r\n{\r\n  \"args\": {}, \r\n  \"headers\": {\r\n    \"Accept\": \"*/*\", \r\n    \"Host\": \"localhost:8000\", \r\n    \"User-Agent\": \"curl/%% '.*' %%\"\r\n  }, \r\n  \"origin\": \"%% '[0-9.]\\+' %%\", \r\n  \"url\": \"http://localhost:8000/get\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/http_1_0.http",
    "content": "GET http://localhost:8000/get HTTP/1.0\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/http_1_1.expected.http",
    "content": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nContent-Length: %% '[0-9]\\+' %%\r\n\r\n{\r\n  \"args\": {}, \r\n  \"headers\": {\r\n    \"Accept\": \"*/*\", \r\n    \"Host\": \"localhost:8000\", \r\n    \"User-Agent\": \"curl/%% '.*' %%\"\r\n  }, \r\n  \"origin\": \"%% '[0-9.]\\+' %%\", \r\n  \"url\": \"http://localhost:8000/get\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/http_1_1.http",
    "content": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/http_2.http",
    "content": "GET http://localhost:8000/get HTTP/2\r\n"
  },
  {
    "path": "test/test-http-files/patch_json.expected.http",
    "content": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nContent-Length: %% '[0-9]\\+' %%\r\n\r\n{\r\n  \"args\": {}, \r\n  \"data\": \"{\\r\\n    \\\"foo\\\": \\\"bar\\\",\\r\\n    \\\"lol\\\": \\\"beans\\\"\\r\\n}\", \r\n  \"files\": {}, \r\n  \"form\": {}, \r\n  \"headers\": {\r\n    \"Accept\": \"*/*\", \r\n    \"Content-Length\": \"43\", \r\n    \"Content-Type\": \"application/json\", \r\n    \"Host\": \"localhost:8000\", \r\n    \"User-Agent\": \"curl/%% '.*' %%\"\r\n  }, \r\n  \"json\": {\r\n    \"foo\": \"bar\", \r\n    \"lol\": \"beans\"\r\n  }, \r\n  \"origin\": \"%% '[0-9.]\\+' %%\", \r\n  \"url\": \"http://localhost:8000/patch\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/patch_json.http",
    "content": "PATCH http://localhost:8000/patch HTTP/1.1\r\nHost: localhost:8000\r\nContent-Type: application/json\r\nContent-Length: 43\r\n\r\n\r\n{\r\n    \"foo\": \"bar\",\r\n    \"lol\": \"beans\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/post_incomplete.http",
    "content": "POST http://localhost:8000/post HTTP/1.1\r\nContent-Type: application/json\r\n\r\n\r\n{\r\n    \"foo\": \"bar\",\r\n    \"lol\": \"beans\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/post_invalid_content_length.http",
    "content": "POST http://localhost:8000/post HTTP/1.1\r\nHost: localhost:8000\r\nContent-Length: 20\r\nContent-Type: application/json\r\n\r\n\r\n{\r\n    \"foo\": \"bar\",\r\n    \"lol\": \"beans\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/post_json.expected.http",
    "content": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nContent-Length: %% '[0-9]\\+' %%\r\n\r\n{\r\n  \"args\": {}, \r\n  \"data\": \"{\\r\\n    \\\"foo\\\": \\\"bar\\\",\\r\\n    \\\"lol\\\": \\\"beans\\\"\\r\\n}\", \r\n  \"files\": {}, \r\n  \"form\": {}, \r\n  \"headers\": {\r\n    \"Accept\": \"*/*\", \r\n    \"Content-Length\": \"43\", \r\n    \"Content-Type\": \"application/json\", \r\n    \"Host\": \"localhost:8000\", \r\n    \"User-Agent\": \"curl/%% '.*' %%\"\r\n  }, \r\n  \"json\": {\r\n    \"foo\": \"bar\", \r\n    \"lol\": \"beans\"\r\n  }, \r\n  \"origin\": \"%% '[0-9.]\\+' %%\", \r\n  \"url\": \"http://localhost:8000/post\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/post_json.http",
    "content": "POST http://localhost:8000/post HTTP/1.1\r\nHost: \"localhost:8000\"\r\nContent-Type: application/json\r\nContent-Length: 43\r\n\r\n\r\n{\r\n    \"foo\": \"bar\",\r\n    \"lol\": \"beans\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/redirect.expected.http",
    "content": "HTTP/1.1 302 FOUND\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 0\r\nLocation: http://localhost:8000/get\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\n\r\n"
  },
  {
    "path": "test/test-http-files/redirect.http",
    "content": "GET http://localhost:8000/redirect-to?url=http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/redirect_follow.expected.http",
    "content": "HTTP/1.1 302 FOUND\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 0\r\nLocation: http://localhost:8000/get\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\n\r\nHTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nContent-Length: %% '[0-9]\\+' %%\r\n\r\n{\r\n  \"args\": {}, \r\n  \"headers\": {\r\n    \"Accept\": \"*/*\", \r\n    \"Host\": \"localhost:8000\", \r\n    \"User-Agent\": \"curl/%% '.*' %%\"\r\n  }, \r\n  \"origin\": \"%% '[0-9.]\\+' %%\", \r\n  \"url\": \"http://localhost:8000/get\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/redirect_follow.http",
    "content": "GET http://localhost:8000/redirect-to?url=http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/simple_get.expected.http",
    "content": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Credentials: true\r\nContent-Length: %% '[0-9]\\+' %%\r\n\r\n{\r\n  \"args\": {}, \r\n  \"headers\": {\r\n    \"Accept\": \"*/*\", \r\n    \"Host\": \"localhost:8000\", \r\n    \"User-Agent\": \"curl/%% '.*' %%\"\r\n  }, \r\n  \"origin\": \"%% '[0-9.]\\+' %%\", \r\n  \"url\": \"http://localhost:8000/get\"\r\n}\r\n"
  },
  {
    "path": "test/test-http-files/simple_get.http",
    "content": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/simple_get_without_protocol.http",
    "content": "GET http://localhost:8000/get\r\n"
  }
]