Full Code of nicwest/vim-http for AI

master 7eb78e9753c1 cached
43 files
48.8 KB
16.5k tokens
1 requests
Download .txt
Repository: nicwest/vim-http
Branch: master
Commit: 7eb78e9753c1
Files: 43
Total size: 48.8 KB

Directory structure:
gitextract_413bwcyb/

├── .circleci/
│   └── config.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── autoload/
│   ├── http.vim
│   ├── vital/
│   │   ├── _vim_http/
│   │   │   └── Data/
│   │   │       └── Base64.vim
│   │   ├── _vim_http.vim
│   │   ├── vim_http.vim
│   │   └── vim_http.vital
│   └── vital.vim
├── docker-compose.yml
├── ftdetect/
│   └── http.vim
├── plugin/
│   └── http.vim
├── syntax/
│   └── http.vim
└── test/
    ├── .themisrc
    ├── integration.vim
    └── test-http-files/
        ├── example_in_docs.md
        ├── get_body_params.expected.http
        ├── get_body_params.http
        ├── get_url_params.expected.http
        ├── get_url_params.http
        ├── get_with_multiple_headers.http
        ├── http_1_0.expected.http
        ├── http_1_0.http
        ├── http_1_1.expected.http
        ├── http_1_1.http
        ├── http_2.http
        ├── patch_json.expected.http
        ├── patch_json.http
        ├── post_incomplete.http
        ├── post_invalid_content_length.http
        ├── post_json.expected.http
        ├── post_json.http
        ├── redirect.expected.http
        ├── redirect.http
        ├── redirect_follow.expected.http
        ├── redirect_follow.http
        ├── simple_get.expected.http
        ├── simple_get.http
        └── simple_get_without_protocol.http

================================================
FILE CONTENTS
================================================

================================================
FILE: .circleci/config.yml
================================================
version: 2
jobs:
  latest:
    docker:
      - image: thinca/vim:latest
      - image: citizenstig/httpbin
    steps: &steps
      - run:
          name: install git and curl
          command: apk add --no-cache git curl
      - checkout
      - run:
          name: test requirements
          command: |
            git clone https://github.com/thinca/vim-themis
            git clone https://github.com/syngan/vim-vimlint /tmp/vim-vimlint
            git clone https://github.com/ynkdir/vim-vimlparser /tmp/vim-vimlparser
      - run:
          name: test
          command: |
            vim-themis/bin/themis --reporter dot test
      - run:
          name: lint
          command: |
            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
  oldest:
    docker:
      - image: thinca/vim:v7.4.566
      - image: citizenstig/httpbin
    steps: *steps
      

workflows:
  version: 2
  test:
    jobs:
      - latest
      - oldest


================================================
FILE: .gitignore
================================================

# Created by https://www.gitignore.io/api/vim

### Vim ###
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

vim-themis
vim-vimlint
vim-vimlparser
.vagrant
Vagrantfile


================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- `g:vim_http_tempbuffer` that makes response buffers temporary
  [#5](https://github.com/nicwest/vim-http/pull/5).


## [1.0.0]
### Added
- support for `PATCH` http method
- pin minimum vim version at 7.4.566, if you are using a version prior to
  this you can use the version of the code with the
  [pre-7.4.566](https://github.com/nicwest/vim-http/tree/pre-7.4.566) tag

## [0.0.1] - 2017-08-21
### Added
- This CHANGELOG file!
- g:vim_http_split_vertically to controll split direction


[Unreleased]: https://github.com/nicwest/vim-http/compare/1.0.0...HEAD
[1.0.0]: https://github.com/nicwest/vim-http/compare/0.0.1...1.0.0
[0.0.1]: https://github.com/nicwest/vim-http/compare/TAIL...0.0.1


================================================
FILE: CONTRIBUTING.md
================================================
Contributing
============

All issues/pull requests are welcome.

That said, things that I'm unlikely to merge:

* Deviations from the various HTTP specs
  [rfc1945](https://tools.ietf.org/html/rfc1945)
  [rfc7230-7235](https://tools.ietf.org/html/rfc7230)
  [rfc7540](https://tools.ietf.org/html/rfc7540).
  The idea is that a request should be able to be piped into a connection to a
  web server (for example via telnet or openssl) and for the server to
  recognise the request.

* Binding things to keyboard shortcuts by default or with some kind of global
  flag. People to should be making their own decisions about where they bind
  things and/or if they need to bind stuff at all. I'm happy to merge
  suggestions into the README.

* New functionality without tests.

Support
-------

I aim to support versions of vim from 7.4.566 
(see https://github.com/nicwest/vim-http/pull/5#issuecomment-472483160) to
whatever the latest version is.

Any changes to keep this plug-in up to date are welcome. Reasonable changes
that allow us to support older versions are also welcome but are not required.

Generally speaking I'm looking to not break things for older versions of vim
or curl, however sometimes compromises are needed. In a case where the oldest
supported version increases I'll tag the previous commit for people with older
setups.

* [<7.4.566](https://github.com/nicwest/vim-http/tree/pre-7.4.566) [#5](https://github.com/nicwest/vim-http/pull/5)


Tests
-----

The integration test use [httpbin](https://httpbin.org/), and assumes it
running locally. I use a 
[docker container](https://github.com/citizen-stig/dockerhttpbin):

```
docker pull citizenstig/httpbin
docker run -d=true -p 8000:8000 citizenstig/httpbin
```

To run the tests pull the 
[themis test suite](https://github.com/thinca/vim-themis) 
(you don't have to install it but you can if you want). I normally just dump it
in the plugin directory.

```
git clone git@github.com:thinca/vim-themis.git
./vim-themis/bin/themis --reporter dot test
```

The test responses use a VERY hacked together parsing to match against what
can be a somewhat dynamic set of headers etc.

Basically it will match anything inside a pair of `%%`:


```
"User-Agent": "curl/%% '[123.]\+' %%"
```

Would match 

```
"User-Agent": "curl/1.2.3"
```

But not

```
"User-Agent": "curl/4.5.6"
```

Or

```
"User-Agent": "curl/latest"
```

Style
-----

This project attempts to follow the 
[Google Vimscript Style Guide](https://google.github.io/styleguide/vimscriptguide.xml)


================================================
FILE: LICENSE.md
================================================
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org>


================================================
FILE: Makefile
================================================
.PHONY: test setup httpbin

test:
	vim-themis/bin/themis --reporter dot test

setup:
	docker pull citizenstig/httpbin
	git clone git@github.com:thinca/vim-themis.git
	git clone git@github.com:syngan/vim-vimlint
	git clone git@github.com:ynkdir/vim-vimlparser

httpbin:
	docker run -d=true -p 8000:8000 citizenstig/httpbin

lint:
	./vim-vimlint/bin/vimlint.sh -l ./vim-vimlint -p ./vim-vimlparser -e EVL102.l:_=1 -c func_abort=1 autoload ftdetect plugin syntax


================================================
FILE: README.md
================================================
[![CircleCI](https://circleci.com/gh/nicwest/vim-http/tree/master.svg?style=svg)](https://circleci.com/gh/nicwest/vim-http/tree/master)
[![Powered by vital.vim](https://img.shields.io/badge/powered%20by-vital.vim-80273f.svg)](https://github.com/vim-jp/vital.vim)

vim-http
========

Simple wrapper over curl and http syntax highlighting.

[![asciicast](https://asciinema.org/a/120707.png)](https://asciinema.org/a/120707)


Usage
------

Write a raw http request

```http
GET http://httpbin.org/get HTTP/1.1
Host: httpbin.org
Accept: application/json
```

`:Http` will execute the request and display the response in a new buffer.

`:Http!` will execute the request as above and follow any redirects.

`:HttpShowCurl` displays the curl request that the plugin executes under the 
hood

`:HttpShowRequest` displays the internal object representing the request

`:HttpClean` will add Host and Content-Length headers

`:HttpAuth` will prompt for authorization credentials


Configuration
-------------

`g:vim_http_clean_before_do` if set to `1` (default) will clean a request before
sending it to curl. Disable this by setting this global to `0`

`g:vim_http_additional_curl_args` can be used to provide additional arguments
to curl.

`g:vim_http_split_vertically` when set to `1` will split the window vertically
on response rather than horizontally (the default).

`g:vim_http_right_below` when set to `1` split window will be open on the
right (for vertical) or below (for horizontal).

`g:vim_http_tempbuffer` when set to `1` response buffers will overwrite each
other instead of persisting forever.

Helper Methods
--------------

`http#remove_header(header)` removes all occurances of the given header in the
current buffer.

`http#set_header(header, value)` sets the header to the given value in the
current buffer, removes duplicates

Examples for your vimrc:

```viml
function! s:set_json_header() abort
  call http#set_header('Content-Type', 'application/json')
endfunction

function! s:clean_personal_stuff() abort
  call http#remove_header('Cookie')
  call http#remove_header('Accept')
  call http#remove_header('User-Agent')
  call http#remove_header('Accept-Language')
endfunction 

function! s:add_compression() abort
  call http#set_header('Accept-Encoding', 'deflate, gzip')
  let g:vim_http_additional_curl_args = '--compressed'
endfunction

command! JSON call s:set_json_header()
command! Anon call s:clean_personal_stuff()
command! Compression call s:add_compression()
```


================================================
FILE: autoload/http.vim
================================================
let s:save_cpo = &cpo
set cpo&vim

let s:V = vital#vim_http#new()
let s:Base64 = s:V.import('Data.Base64')

function! s:new_request() abort
    let l:response = {
                \ 'method': 'GET',
                \ 'headers': {},
                \ 'uri': '',
                \ 'content': '',
                \ 'version': '1.1',
                \ 'meta': {
                \   'follow': 0,
                \   }
                \ }
    return l:response
endfunction

let s:pre_clean_uri_line_pattern = '^\(\a*\) \(.*\) HTTP/\([0-9.]\+\)\|$'
let s:uri_line_pattern = '^\(OPTIONS\|GET\|HEAD\|POST\|PUT\|DELETE\|TRACE\|CONNECT\|PATCH\) \(.*\) HTTP/\([0-9.]\+\)$'
let s:header_line_pattern = '^\([^:]\+\): \(.*\)$'

function! s:get_lines(buffer, count, line1, line2) abort
    if a:count == -1
      return getbufline(a:buffer, 0, '$')
    else
      return getbufline(a:buffer, a:line1, a:line2)
    endif
endfunction

function! s:parse_request(lines, pattern, follow) abort
    let l:request = s:new_request()
    if a:follow == 1
        let l:request.meta.follow = 1
    end

    if len(a:lines) < 0
        throw 'No lines in buffer :('
    endif

    let l:uri_line = a:lines[0]
    let l:uri_line_matches = matchlist(l:uri_line, a:pattern)

    if len(l:uri_line_matches) == 0
        throw 'Unable to parse first line of request'
    end

    let l:request.method = l:uri_line_matches[1]
    let l:request.uri = l:uri_line_matches[2]
    let l:request.version = l:uri_line_matches[3]

    if l:request.version =~ '2.*'
      let l:request.version = '2'
    end

    let l:in_content = 0
    let l:first_content_line = 1
    for l:line in a:lines[0:]
        if l:in_content == 0 && l:line =~ '^\s*$'
            let l:in_content = 1
        end
        if l:in_content == 0
            let l:header_matches = matchlist(l:line, s:header_line_pattern)

            if len(l:header_matches) > 0
                let l:header_key = l:header_matches[1]
                let l:header_value = l:header_matches[2]
                if has_key(l:request.headers, l:header_key) == 0
                    let l:request.headers[l:header_key] = []
                endif
                let l:request.headers[l:header_key] += [l:header_value]
            endif
        else
            if l:first_content_line && l:line =~ '^\s*$'
                continue
            endif
            if l:first_content_line
                let l:first_content_line = 0
            else
                let l:request.content .= "\r\n"
            endif
            let l:request.content .= l:line
        endif
    endfor

    return l:request
endfunction

function! s:in_curl_format(request) abort
    let l:curl_fmt = 'curl%s%s%s%s %s'

    let l:flags = ' -si'

    if a:request.meta.follow == 1
        let l:flags = l:flags . 'L'
    endif

    let l:flags = l:flags.' '.g:vim_http_additional_curl_args

    let l:flags = l:flags.' --http'.a:request.version

    let l:method = printf(' -X %s', a:request.method)

    let l:url = shellescape(a:request.uri)

    let l:headers = ''

    for [l:header_key, l:header_value_list] in items(a:request.headers)
        for l:header_value in l:header_value_list
            let l:headers = l:headers .
                        \ ' -H "' .
                        \ printf('%s: %s', l:header_key, l:header_value) .
                        \ '"'
        endfor

    endfor

    let l:data = ''
    if len(a:request.content)
        let l:data = ' -d ' . substitute(shellescape(a:request.content), '\\\n', "\n", 'g')
    endif

    let l:curl_request = printf(l:curl_fmt, l:flags, l:method, l:headers, l:data, l:url)

    return l:curl_request

endfunction

function! s:lines_with_header(header) abort
  let l:linenr = 1
  let l:lines = []
  while l:linenr < line('$')
    let l:line = getline(l:linenr)
    if l:line =~ '^\s*$'
      break
    endif
    if l:line =~ "^".a:header.":"
      call add(l:lines, l:linenr)
    end
    let l:linenr = l:linenr + 1
  endwhile
  return l:lines
endfunction

function! s:remove_header(header) abort
  let l:offset = 0
  for l:linenr in s:lines_with_header(a:header)
    let l:target = l:linenr - l:offset
    exe l:target."delete _"
    let l:offset = l:offset + 1
  endfor
endfunction

function! s:new_response_buffer(request_buffer, response) abort
    let l:request_buffer_name  = bufname(a:request_buffer)
    let l:buffer_name = fnamemodify(l:request_buffer_name, ":r") . '.response.' . localtime() . '.http'
    if g:vim_http_tempbuffer
      for l:win in range(1, winnr('$'))
        if getwinvar(l:win, 'vim_http_tempbuffer')
          execute l:win . 'windo close'
        endif
      endfor
    endif
    let l:rightbelow = g:vim_http_right_below ? 'rightbelow ' : ''
    let l:keepalt = g:vim_http_tempbuffer ? 'keepalt ' : ''
    let l:vert = g:vim_http_split_vertically ? 'vert ' : ''
    execute l:keepalt . l:rightbelow . l:vert . 'new ' . l:buffer_name
    set ft=http
    if g:vim_http_tempbuffer
      setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nonumber
      let w:vim_http_tempbuffer = 1
    endif

    let l:response_lines = split(a:response, "\\(\r\n\\|\n\\)")

    call append(0, l:response_lines)
    norm! G"_ddgg
endfunction

function! http#do_buffer(follow) abort
    let l:bang =  a:follow ? '!' : ''
    call http#do(l:bang, -1, 0, 0)
endfunction

function! http#do(bang, count, line1, line2) abort
    let l:follow =  a:bang == '!' ? 1 : 0
    if g:vim_http_clean_before_do && a:count == -1
      call http#clean()
    end
    let l:buffer = bufnr('')
    let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)
    let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)
    let l:curl = s:in_curl_format(l:request)
    let l:response = system(l:curl)
    call s:new_response_buffer(l:buffer, l:response)
endfunction

function! http#show_curl(bang, count, line1, line2) abort
    let l:follow =  a:bang == '!' ? 1 : 0
    let l:buffer = bufnr('')
    let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)
    let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)
    let l:curl = s:in_curl_format(l:request)
    echo l:curl
endfunction

function! http#show_request(bang, count, line1, line2) abort
    let l:follow =  a:bang == '!' ? 1 : 0
    let l:buffer = bufnr('')
    let l:lines = s:get_lines(l:buffer, a:count, a:line1, a:line2)
    let l:request = s:parse_request(l:lines, s:uri_line_pattern, l:follow)
    echo l:request
endfunction

function! http#clean() abort
  let l:buffer = bufnr('')
  let l:lines = s:get_lines(l:buffer, -1, 0, 0)
  let l:request = s:parse_request(l:lines, s:pre_clean_uri_line_pattern, 0)

  " when the http proto is > 1.0 make sure we are adding a host header
  if index(['1.1', '2'], l:request.version) != -1 && !has_key(l:request.headers, 'Host')
    let l:matches = matchlist(l:request.uri, '^\([^:]\+://\)\?\([^/]\+\)')
    let l:host = l:matches[2]
    if len(l:host)
      call append(1, 'Host: ' . l:host)
    endif
  endif

  if l:request.version == ''
    call setline('.', getline('.') . ' HTTP/1.1')
  endif

  let l:content_length = len(l:request.content)

  " when we have a Content-Length header and it doesn't match the actual
  " content length
  if l:content_length && has_key(l:request.headers, 'Content-Length')
    if string(l:content_length) != l:request.headers['Content-Length'][-1]
      let l:correct = input("correct Content-Length header? [Y]/N:")
      if len(l:correct) == 0 || tolower(l:correct) != "n"
        call remove(l:request.headers, 'Content-Length')
        call s:remove_header('Content-Length')
      endif
    endif
  endif

  " when we are sending content we should add a header for the content length
  " curl is going to do this for us anyway, but it's good to be explicit
  if  l:content_length && !has_key(l:request.headers, 'Content-Length')
    call append(1 + len(l:request.headers), 'Content-Length: ' . l:content_length)
  endif
endfunction

function! http#auth() abort
  let l:buffer = bufnr('')
  let l:lines = s:get_lines(l:buffer, -1, 0, 0)
  let l:request = s:parse_request(l:lines, s:uri_line_pattern, 0)

  let l:method = input('method [Basic]: ')
  if len(l:method) == 0
    let l:method = 'Basic'
  end
  let l:user = input('user: ')
  let l:password = input('password: ')
  let l:encoded = s:Base64.encode(l:user . ':' . l:password)
  let l:header = 'Authorization: ' . l:method . ' ' . l:encoded
  call append(1 + len(l:request.headers), l:header)
endfunction

function! http#remove_header(header) abort
  call s:remove_header(a:header)
endfunction

function! http#set_header(header, value) abort
  call s:remove_header(a:header)
  let l:buffer = bufnr('')
  let l:lines = s:get_lines(l:buffer, -1, 0, 0)
  let l:request = s:parse_request(l:lines, s:uri_line_pattern, 0)
  call append(1 + len(l:request.headers), a:header.': '.a:value)
endfunction

" Teardown:{{{1
let &cpo = s:save_cpo


================================================
FILE: autoload/vital/_vim_http/Data/Base64.vim
================================================
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not mofidify the code nor insert new lines before '" ___vital___'
if v:version > 703 || v:version == 703 && has('patch1170')
  function! vital#_vim_http#Data#Base64#import() abort
    return map({'decode': '', 'encodebin': '', 'encode': ''},  'function("s:" . v:key)')
  endfunction
else
  function! s:_SID() abort
    return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
  endfunction
  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")
  delfunction s:_SID
endif
" ___vital___
" Utilities for Base64.

let s:save_cpo = &cpo
set cpo&vim

function! s:encode(data) abort
  let b64 = s:_b64encode(s:_str2bytes(a:data), s:standard_table, '=')
  return join(b64, '')
endfunction

function! s:encodebin(data) abort
  let b64 = s:_b64encode(s:_binstr2bytes(a:data), s:standard_table, '=')
  return join(b64, '')
endfunction

function! s:decode(data) abort
  let bytes = s:_b64decode(split(a:data, '\zs'), s:standard_table, '=')
  return s:_bytes2str(bytes)
endfunction

let s:standard_table = [
      \ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
      \ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
      \ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
      \ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/']

let s:urlsafe_table = [
      \ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
      \ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
      \ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
      \ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','-','_']

function! s:_b64encode(bytes, table, pad) abort
  let b64 = []
  for i in range(0, len(a:bytes) - 1, 3)
    let n = a:bytes[i] * 0x10000
          \ + get(a:bytes, i + 1, 0) * 0x100
          \ + get(a:bytes, i + 2, 0)
    call add(b64, a:table[n / 0x40000])
    call add(b64, a:table[n / 0x1000 % 0x40])
    call add(b64, a:table[n / 0x40 % 0x40])
    call add(b64, a:table[n % 0x40])
  endfor
  if len(a:bytes) % 3 == 1
    let b64[-1] = a:pad
    let b64[-2] = a:pad
  endif
  if len(a:bytes) % 3 == 2
    let b64[-1] = a:pad
  endif
  return b64
endfunction

function! s:_b64decode(b64, table, pad) abort
  let a2i = {}
  for i in range(len(a:table))
    let a2i[a:table[i]] = i
  endfor
  let bytes = []
  for i in range(0, len(a:b64) - 1, 4)
    let n = a2i[a:b64[i]] * 0x40000
          \ + a2i[a:b64[i + 1]] * 0x1000
          \ + (a:b64[i + 2] == a:pad ? 0 : a2i[a:b64[i + 2]]) * 0x40
          \ + (a:b64[i + 3] == a:pad ? 0 : a2i[a:b64[i + 3]])
    call add(bytes, n / 0x10000)
    call add(bytes, n / 0x100 % 0x100)
    call add(bytes, n % 0x100)
  endfor
  if a:b64[-1] == a:pad
    unlet a:b64[-1]
  endif
  if a:b64[-2] == a:pad
    unlet a:b64[-1]
  endif
  return bytes
endfunction

function! s:_binstr2bytes(str) abort
  return map(range(len(a:str)/2), 'eval("0x".a:str[v:val*2 : v:val*2+1])')
endfunction

function! s:_str2bytes(str) abort
  return map(range(len(a:str)), 'char2nr(a:str[v:val])')
endfunction

function! s:_bytes2str(bytes) abort
  return eval('"' . join(map(copy(a:bytes), 'printf(''\x%02x'', v:val)'), '') . '"')
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_vim_http.vim
================================================
let s:_plugin_name = expand('<sfile>:t:r')

function! vital#{s:_plugin_name}#new() abort
  return vital#{s:_plugin_name[1:]}#new()
endfunction


================================================
FILE: autoload/vital/vim_http.vim
================================================
let s:plugin_name = expand('<sfile>:t:r')
let s:vital_base_dir = expand('<sfile>:h')
let s:project_root = expand('<sfile>:h:h:h')
let s:is_vital_vim = s:plugin_name is# 'vital'

let s:loaded = {}
let s:cache_sid = {}

" function() wrapper
if v:version > 703 || v:version == 703 && has('patch1170')
  function! s:_function(fstr) abort
    return function(a:fstr)
  endfunction
else
  function! s:_SID() abort
    return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
  endfunction
  let s:_s = '<SNR>' . s:_SID() . '_'
  function! s:_function(fstr) abort
    return function(substitute(a:fstr, 's:', s:_s, 'g'))
  endfunction
endif

function! vital#{s:plugin_name}#new() abort
  return s:new(s:plugin_name)
endfunction

function! vital#{s:plugin_name}#import(...) abort
  if !exists('s:V')
    let s:V = s:new(s:plugin_name)
  endif
  return call(s:V.import, a:000, s:V)
endfunction

let s:Vital = {}

function! s:new(plugin_name) abort
  let base = deepcopy(s:Vital)
  let base._plugin_name = a:plugin_name
  return base
endfunction

function! s:vital_files() abort
  if !exists('s:vital_files')
    let s:vital_files = map(
    \   s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(),
    \   'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")')
  endif
  return copy(s:vital_files)
endfunction
let s:Vital.vital_files = s:_function('s:vital_files')

function! s:import(name, ...) abort dict
  let target = {}
  let functions = []
  for a in a:000
    if type(a) == type({})
      let target = a
    elseif type(a) == type([])
      let functions = a
    endif
    unlet a
  endfor
  let module = self._import(a:name)
  if empty(functions)
    call extend(target, module, 'keep')
  else
    for f in functions
      if has_key(module, f) && !has_key(target, f)
        let target[f] = module[f]
      endif
    endfor
  endif
  return target
endfunction
let s:Vital.import = s:_function('s:import')

function! s:load(...) abort dict
  for arg in a:000
    let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg]
    let target = split(join(as, ''), '\W\+')
    let dict = self
    let dict_type = type({})
    while !empty(target)
      let ns = remove(target, 0)
      if !has_key(dict, ns)
        let dict[ns] = {}
      endif
      if type(dict[ns]) == dict_type
        let dict = dict[ns]
      else
        unlet dict
        break
      endif
    endwhile
    if exists('dict')
      call extend(dict, self._import(name))
    endif
    unlet arg
  endfor
  return self
endfunction
let s:Vital.load = s:_function('s:load')

function! s:unload() abort dict
  let s:loaded = {}
  let s:cache_sid = {}
  unlet! s:vital_files
endfunction
let s:Vital.unload = s:_function('s:unload')

function! s:exists(name) abort dict
  if a:name !~# '\v^\u\w*%(\.\u\w*)*$'
    throw 'vital: Invalid module name: ' . a:name
  endif
  return s:_module_path(a:name) isnot# ''
endfunction
let s:Vital.exists = s:_function('s:exists')

function! s:search(pattern) abort dict
  let paths = s:_extract_files(a:pattern, self.vital_files())
  let modules = sort(map(paths, 's:_file2module(v:val)'))
  return s:_uniq(modules)
endfunction
let s:Vital.search = s:_function('s:search')

function! s:plugin_name() abort dict
  return self._plugin_name
endfunction
let s:Vital.plugin_name = s:_function('s:plugin_name')

function! s:_self_vital_files() abort
  let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name)
  let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name)
  let base = builtin . ',' . installed
  return split(globpath(base, '**/*.vim', 1), "\n")
endfunction

function! s:_global_vital_files() abort
  let pattern = 'autoload/vital/__*__/**/*.vim'
  return split(globpath(&runtimepath, pattern, 1), "\n")
endfunction

function! s:_extract_files(pattern, files) abort
  let tr = {'.': '/', '*': '[^/]*', '**': '.*'}
  let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g')
  let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target)
  return filter(a:files, 'v:val =~# regexp')
endfunction

function! s:_file2module(file) abort
  let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?')
  let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$')
  return join(split(tail, '[\\/]\+'), '.')
endfunction

" @param {string} name e.g. Data.List
function! s:_import(name) abort dict
  if has_key(s:loaded, a:name)
    return copy(s:loaded[a:name])
  endif
  let module = self._get_module(a:name)
  if has_key(module, '_vital_created')
    call module._vital_created(module)
  endif
  let export_module = filter(copy(module), 'v:key =~# "^\\a"')
  " Cache module before calling module.vital_loaded() to avoid cyclic
  " dependences but remove the cache if module._vital_loaded() fails.
  " let s:loaded[a:name] = export_module
  let s:loaded[a:name] = export_module
  if has_key(module, '_vital_loaded')
    try
      call module._vital_loaded(vital#{s:plugin_name}#new())
    catch
      unlet s:loaded[a:name]
      throw 'vital: fail to call ._vital_loaded(): ' . v:exception
    endtry
  endif
  return copy(s:loaded[a:name])
endfunction
let s:Vital._import = s:_function('s:_import')

" s:_get_module() returns module object wihch has all script local functions.
function! s:_get_module(name) abort dict
  let funcname = s:_import_func_name(self.plugin_name(), a:name)
  if s:_exists_autoload_func_with_source(funcname)
    return call(funcname, [])
  else
    return s:_get_builtin_module(a:name)
  endif
endfunction

function! s:_get_builtin_module(name) abort
 return s:sid2sfuncs(s:_module_sid(a:name))
endfunction

if s:is_vital_vim
  " For vital.vim, we can use s:_get_builtin_module directly
  let s:Vital._get_module = s:_function('s:_get_builtin_module')
else
  let s:Vital._get_module = s:_function('s:_get_module')
endif

function! s:_import_func_name(plugin_name, module_name) abort
  return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name))
endfunction

function! s:_module_sid(name) abort
  let path = s:_module_path(a:name)
  if !filereadable(path)
    throw 'vital: module not found: ' . a:name
  endif
  let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name)
  let base = join([vital_dir, ''], '[/\\]\+')
  let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g')
  let sid = s:_sid(path, p)
  if !sid
    call s:_source(path)
    let sid = s:_sid(path, p)
    if !sid
      throw printf('vital: cannot get <SID> from path: %s', path)
    endif
  endif
  return sid
endfunction

function! s:_module_path(name) abort
  return get(s:_extract_files(a:name, s:vital_files()), 0, '')
endfunction

function! s:_module_sid_base_dir() abort
  return s:is_vital_vim ? &rtp : s:project_root
endfunction

function! s:_dot_to_sharp(name) abort
  return substitute(a:name, '\.', '#', 'g')
endfunction

" It will sources autoload file if a given func is not already defined.
function! s:_exists_autoload_func_with_source(funcname) abort
  if exists('*' . a:funcname)
    " Return true if a given func is already defined
    return 1
  endif
  " source a file which may include a given func definition and try again.
  let path = 'autoload/' . substitute(substitute(a:funcname, '#[^#]*$', '.vim', ''), '#', '/', 'g')
  call s:_runtime(path)
  return exists('*' . a:funcname)
endfunction

function! s:_runtime(path) abort
  execute 'runtime' fnameescape(a:path)
endfunction

function! s:_source(path) abort
  execute 'source' fnameescape(a:path)
endfunction

" @vimlint(EVL102, 1, l:_)
" @vimlint(EVL102, 1, l:__)
function! s:_sid(path, filter_pattern) abort
  let unified_path = s:_unify_path(a:path)
  if has_key(s:cache_sid, unified_path)
    return s:cache_sid[unified_path]
  endif
  for line in filter(split(s:_redir(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern')
    let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$')
    if s:_unify_path(path) is# unified_path
      let s:cache_sid[unified_path] = sid
      return s:cache_sid[unified_path]
    endif
  endfor
  return 0
endfunction

function! s:_redir(cmd) abort
  let [save_verbose, save_verbosefile] = [&verbose, &verbosefile]
  set verbose=0 verbosefile=
  redir => res
    silent! execute a:cmd
  redir END
  let [&verbose, &verbosefile] = [save_verbose, save_verbosefile]
  return res
endfunction

if filereadable(expand('<sfile>:r') . '.VIM') " is case-insensitive or not
  let s:_unify_path_cache = {}
  " resolve() is slow, so we cache results.
  " Note: On windows, vim can't expand path names from 8.3 formats.
  " So if getting full path via <sfile> and $HOME was set as 8.3 format,
  " vital load duplicated scripts. Below's :~ avoid this issue.
  function! s:_unify_path(path) abort
    if has_key(s:_unify_path_cache, a:path)
      return s:_unify_path_cache[a:path]
    endif
    let value = tolower(fnamemodify(resolve(fnamemodify(
    \                   a:path, ':p')), ':~:gs?[\\/]?/?'))
    let s:_unify_path_cache[a:path] = value
    return value
  endfunction
else
  function! s:_unify_path(path) abort
    return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?'))
  endfunction
endif

" copied and modified from Vim.ScriptLocal
let s:SNR = join(map(range(len("\<SNR>")), '"[\\x" . printf("%0x", char2nr("\<SNR>"[v:val])) . "]"'), '')
function! s:sid2sfuncs(sid) abort
  let fs = split(s:_redir(printf(':function /^%s%s_', s:SNR, a:sid)), "\n")
  let r = {}
  let pattern = printf('\m^function\s<SNR>%d_\zs\w\{-}\ze(', a:sid)
  for fname in map(fs, 'matchstr(v:val, pattern)')
    let r[fname] = function(s:_sfuncname(a:sid, fname))
  endfor
  return r
endfunction

"" Return funcname of script local functions with SID
function! s:_sfuncname(sid, funcname) abort
  return printf('<SNR>%s_%s', a:sid, a:funcname)
endfunction

if exists('*uniq')
  function! s:_uniq(list) abort
    return uniq(a:list)
  endfunction
else
  function! s:_uniq(list) abort
    let i = len(a:list) - 1
    while 0 < i
      if a:list[i] ==# a:list[i - 1]
        call remove(a:list, i)
      endif
      let i -= 1
    endwhile
    return a:list
  endfunction
endif


================================================
FILE: autoload/vital/vim_http.vital
================================================
vim_http
f07284453d294e2159424aa76b9b902a5b25d5ae

Data.Base64


================================================
FILE: autoload/vital.vim
================================================
function! vital#of(name) abort
  let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital', 1)
  let file = split(files, "\n")
  if empty(file)
    throw 'vital: version file not found: ' . a:name
  endif
  let ver = readfile(file[0], 'b')
  if empty(ver)
    throw 'vital: invalid version file: ' . a:name
  endif
  return vital#_{substitute(ver[0], '\W', '', 'g')}#new()
endfunction


================================================
FILE: docker-compose.yml
================================================
version: "3.4"
services:
  httpbin:
    image: citizenstig/httpbin
  oldest:
    image: thinca/vim:v7.4.566
    volumes:
      - .:/root/.vim
  latest:
    image: thinca/vim:latest
    volumes:
      - .:/root/.vim


================================================
FILE: ftdetect/http.vim
================================================
augroup filetypedetect
    au! BufRead,BufNewFile *.http   setlocal filetype=http fileformat=dos
augroup END


================================================
FILE: plugin/http.vim
================================================
if !exists('g:vim_http_clean_before_do')
  let g:vim_http_clean_before_do = 1
endif
if !exists('g:vim_http_additional_curl_args')
  let g:vim_http_additional_curl_args = ''
endif
if !exists('g:vim_http_split_vertically')
  let g:vim_http_split_vertically = 0
endif
if !exists('g:vim_http_right_below')
  let g:vim_http_right_below = 0
endif
if !exists('g:vim_http_tempbuffer')
  let g:vim_http_tempbuffer = 0
endif

command! -bang -range Http call http#do('<bang>', '<count>', '<line1>', '<line2>')
command! -bang -range HttpShowCurl call http#show_curl('<bang>', '<count>', '<line1>', '<line2>')
command! -bang -range HttpShowRequest call http#show_request('<bang>', '<count>', '<line1>', '<line2>')
command! HttpClean call http#clean()
command! HttpAuth call http#auth()


================================================
FILE: syntax/http.vim
================================================
if version < 600
  syntax clear
elseif exists("b:current_syntax")
  finish
endif

let s:cpo_save = &cpo
set cpo&vim

syn keyword httpMethod  OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT PATCH contained

syn match httpProto         'HTTP/[0-9.]\+' contained
syn match httpStatusCode    '[0-9]\{3\}' contained
syn match httpStatus        '[0-9]\{3\} .*$' contained contains=httpStatusCode
syn match httpHeaderKey     '^[A-Z][A-Za-z0-9\-]*:' contained
syn match httpURILine       '^\(OPTIONS\|GET\|HEAD\|POST\|PUT\|DELETE\|TRACE\|CONNECT\|PATCH\)\( .*\)\?\(HTTP/[0-9.]\+\)\?$'  contains=httpMethod,httpProto contained
syn match httpResponseLine  '^HTTP/[0-9.]\+ [0-9]\{3\}.*$' contains=httpProto,httpStatus contained
syn match httpHeaderLine    '^[A-Z][A-Za-z0-9\-]*: .*$' contains=httpHeaderKey contained

syn region httpHeader       start='^\(OPTIONS\|GET\|HEAD\|POST\|PUT\|DELETE\|TRACE\|CONNECT\|PATCH\)\( .*\)\?\(HTTP/[0-9.]\+\)\?$' end='\n\s*\n' contains=httpURILine,httpHeaderLine
syn region httpHeader       start='^HTTP/[0-9.]\+ [0-9]\{3\}.*$' end='\n\s*\n' contains=httpResponseLine,httpHeaderLine

hi link httpMethod      Type
hi link httpProto       Statement
hi link httpHeaderKey   Identifier
hi link httpStatus      String
hi link httpStatusCode      Number

let b:current_syntax = 'http'

let &cpo = s:cpo_save
unlet s:cpo_save


================================================
FILE: test/.themisrc
================================================

let g:http_test_files = expand('<sfile>:p:h') . '/test-http-files/'

call themis#log('test_files_location: ' . g:http_test_files)


================================================
FILE: test/integration.vim
================================================
let s:suite = themis#suite('integration')
let s:assert = themis#helper('assert')

" Setup: {{{1
let s:original_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)')

function! s:suite.before_each()
    let l:current_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)')
    let l:buffers_to_wipe = filter(copy(l:current_buffers), 'index(s:original_buffers, v:val) == -1')
    for l:buffer in l:buffers_to_wipe
        execute 'bw!' . l:buffer
    endfor
endfunction

function! s:load_request_expected(name) abort
    let l:request_path = g:http_test_files . a:name . '.http'
    execute 'edit! ' . l:request_path
endfunction

function! s:load_request_expected_explicit(name) abort
    let l:request_path = g:http_test_files . a:name
    execute 'edit! ' . l:request_path
endfunction

function s:assert_response_explicit(filepath, name) abort
    let l:expected_path = g:http_test_files . a:filepath
    let l:expected = readfile(l:expected_path)
    let l:lines = getbufline(a:name, 0, '$')

    let l:bad = 0
    let l:lnr = 0
    for l:expect in l:expected
        let l:line = l:lines[l:lnr]
        if l:expect =~ '.*%%[^%]\+%%.*'
            let l:patterns = split(l:expect, '%%')
            let l:in_pattern = 0
            let l:line_index = 0 
            let l:pattern_nr = 0
            for l:pattern in l:patterns
                if l:in_pattern
                    let l:p = '\(' . substitute(l:pattern, '^\s*\([''"]\)\(.*\)\1\s*$', '\2', '') . '\)'
                    let l:pp = l:p
                    if len(l:patterns) > l:pattern_nr+1
                        let l:pp = l:p . l:patterns[l:pattern_nr+1]
                    endif
                    if l:line[l:line_index :] !~ l:pp
                        let l:bad = 1
                        break
                    endif
                    let l:line_index += len(substitute(l:line[l:line_index  :], l:pp, '\1', ''))
                    let l:in_pattern = 0
                else
                    if l:pattern != l:line[l:line_index : l:line_index + len(l:pattern)-1]
                        let l:bad = 1
                        break
                    end
                    let l:line_index += len(l:pattern)
                    let l:in_pattern = 1
                endif

                let l:pattern_nr += 1
            endfor
        else
            if l:line != l:expect
                let l:bad = 1
            endif
        endif
        if l:bad == 1
            break
        end

        let l:lnr += 1

    endfor

    if l:bad
        let l:msg = [printf('Got response for %s.http:', a:name)]
        let l:msg = l:msg + [''] + l:lines + ['', 'Expected:'] + l:expected
        throw themis#failure(msg)
    endif
endfunction

function s:assert_response(name) abort
  call s:assert_response_explicit(a:name . '.expected.http', a:name . '.response.*.http')
endfunction

function! s:command_with_input(cmd, input)
  exe 'normal :' . a:cmd .'
'.join(a:input, '
').'
'
endfunction
" }}}
" GET: {{{1
function! s:suite.simple_get()
    call s:load_request_expected('simple_get')
    Http
    call s:assert_response('simple_get')
endfunction

function! s:suite.get_with_url_params()
    call s:load_request_expected('get_url_params')
    Http
    call s:assert_response('get_url_params')
endfunction

function! s:suite.get_with_body_params()
    call s:load_request_expected('get_body_params')
    Http
    call s:assert_response('get_body_params')
endfunction

function! s:suite.http_1_0()
    call s:load_request_expected('http_1_0')
    Http!
    call s:assert_response('http_1_0')
endfunction

function! s:suite.http_1_1()
    call s:load_request_expected('http_1_1')
    Http!
    call s:assert_response('http_1_1')
endfunction

function! s:suite.redirect()
    call s:load_request_expected('redirect')
    Http
    call s:assert_response('redirect')
endfunction

function! s:suite.redirect_with_follow()
    call s:load_request_expected('redirect_follow')
    Http!
    call s:assert_response('redirect_follow')
endfunction

" }}}
" POST :{{{1
function! s:suite.post_json()
    call s:load_request_expected('post_json')
    Http!
    call s:assert_response('post_json')
endfunction

" }}}
" PATCH :{{{1
function! s:suite.patch_json()
    call s:load_request_expected('patch_json')
    Http!
    call s:assert_response('patch_json')
endfunction

" }}}
" Clean :{{{1
function! s:suite.clean()
    call s:load_request_expected('post_incomplete')
    HttpClean
    let l:contents = getline(0, '$')
    let l:expected = ['POST http://localhost:8000/post HTTP/1.1', 
          \ 'Host: localhost:8000',
          \ 'Content-Length: 43',
          \ 'Content-Type: application/json',
          \ '',
          \ '',
          \ '{',
          \ '    "foo": "bar",',
          \ '    "lol": "beans"',
          \ '}',
          \ ]
    call s:assert.equal(l:contents, l:expected)
endfunction

function! s:suite.clean_adds_host_on_http2_requests()
    call s:load_request_expected('http_2')
    HttpClean
    let l:contents = getline(0, '$')
    let l:expected = ['GET http://localhost:8000/get HTTP/2',
          \ 'Host: localhost:8000',
          \ ]
    call s:assert.equal(l:contents, l:expected)
endfunction

function! s:suite.clean_replaces_invalid_content_length_header()
  call s:load_request_expected('post_invalid_content_length')
  call s:command_with_input('HttpClean', ['Y'])
  let l:contents = getline(0, '$')
  let l:expected = ['POST http://localhost:8000/post HTTP/1.1', 
        \ 'Host: localhost:8000',
        \ 'Content-Type: application/json',
        \ 'Content-Length: 43',
        \ '',
        \ '',
        \ '{',
        \ '    "foo": "bar",',
        \ '    "lol": "beans"',
        \ '}',
        \ ]
  call s:assert.equal(l:contents, l:expected)
endfunction

function! s:suite.clean_appends_protocol()
  call s:load_request_expected('simple_get_without_protocol')
  call s:command_with_input('HttpClean', ['Y'])
  let l:contents = getline(0, '$')
  let l:expected = ['GET http://localhost:8000/get HTTP/1.1']
  call s:assert.equal(l:contents, l:expected)
endfunction

" }}}
" Auth: {{{1
function! s:suite.auth()
    call s:load_request_expected('simple_get')
    call s:command_with_input('HttpAuth', ['Bearer', 'borisjohnson', 'ijustcantwaittobeking'])
    let l:contents = getline(0, '$')
    let l:expected = ['GET http://localhost:8000/get HTTP/1.1', 
          \ 'Host: localhost:8000',
          \ 'Authorization: Bearer Ym9yaXNqb2huc29uOmlqdXN0Y2FudHdhaXR0b2Jla2luZw==',
          \ ]
    call s:assert.equal(l:contents, l:expected)
endfunction
" }}}
" Headers: {{{1
function! s:suite.remove_header()
  call s:load_request_expected('get_with_multiple_headers')
  call http#remove_header('User-Agent')
  let l:contents = getline(0, '$')
  let l:expected = ['GET http://localhost:8000/get HTTP/1.1', 
        \ 'Host: localhost:8000',
        \ 'Accept: text/html',
        \ 'Accept-Encoding: gzip, deflate',
        \ 'Accept-Language: en-US,en;q=0.5 ',
        \ 'Connection: close',
        \ 'Upgrade-Insecure-Requests: 1 ',
        \ ]
  call s:assert.equal(l:contents, l:expected)
endfunction

function! s:suite.set_header()
  call s:load_request_expected('get_with_multiple_headers')
  call http#set_header('User-Agent', 'qutebrowser/1.0.0')
  let l:contents = getline(0, '$')
  let l:expected = ['GET http://localhost:8000/get HTTP/1.1', 
        \ 'Host: localhost:8000',
        \ 'Accept: text/html',
        \ 'Accept-Encoding: gzip, deflate',
        \ 'Accept-Language: en-US,en;q=0.5 ',
        \ 'Connection: close',
        \ 'Upgrade-Insecure-Requests: 1 ',
        \ 'User-Agent: qutebrowser/1.0.0',
        \ ]
  call s:assert.equal(l:contents, l:expected)
endfunction
" }}}
" Misc: {{{1
function! s:suite.tempbuffer()
    let l:vim_http_tempbuffer = g:vim_http_tempbuffer
    let g:vim_http_tempbuffer = 1

    call s:load_request_expected('simple_get')
    call s:load_request_expected('get_url_params')
    let l:expected = filter(range(1, bufnr('$')), 'bufexists(v:val)')

    Http     " test get_url_params.http
    wincmd p " switch from response window back to get_url_params.http window
    buffer # " switch to simple_get.http buffer
    Http     " test simple_get.http
    wincmd c " close response window

    let l:contents = filter(range(1, bufnr('$')), 'bufexists(v:val)')
    let g:vim_http_tempbuffer = l:vim_http_tempbuffer
    call s:assert.equal(l:contents, l:expected)
endfunction

" Range: {{{1

function! s:suite.do_with_range()
    call s:load_request_expected_explicit('example_in_docs.md')
    14,15Http!
    call s:assert_response_explicit('simple_get.expected.http', 'example_in_docs.response.*.http')
endfunction

" vim:fdm=marker


================================================
FILE: test/test-http-files/example_in_docs.md
================================================
Imagine this was some documentation and we had an HTTP request right there in
the file. Why do I have to create a new buffer for it? That's just dumb lol!

```http
POST http://localhost:8000/post HTTP/1.1
Host: "localhost:8000"

I AM A READMEFILE HERE ME ROAR!
```

So let's execute this and see what happens:

```http
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000
```

Did it work?

```http
PATCH http://localhost:8000/patch HTTP/1.1
Host: "localhost:8000"

LOLOLOLOLOLOL
```

I hope so

Here is a picture of a cat:

![CAT!](https://i.imgur.com/DKUR9Tk.png)


================================================
FILE: test/test-http-files/get_body_params.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "17", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "localhost:8000", 
    "User-Agent": "curl/%% '.*' %%"
  }, 
  "origin": "%% '[0-9.]\+' %%", 
  "url": "http://localhost:8000/get"
}


================================================
FILE: test/test-http-files/get_body_params.http
================================================
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000


foo=bar&lol=beans


================================================
FILE: test/test-http-files/get_url_params.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%

{
  "args": {
    "foo": "bar", 
    "lol": "beans"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Host": "localhost:8000", 
    "User-Agent": "curl/%% '.*' %%"
  }, 
  "origin": "%% '[0-9.]\+' %%", 
  "url": "http://localhost:8000/get?foo=bar&lol=beans"
}


================================================
FILE: test/test-http-files/get_url_params.http
================================================
GET http://localhost:8000/get?foo=bar&lol=beans HTTP/1.1
Host: localhost:8000


================================================
FILE: test/test-http-files/get_with_multiple_headers.http
================================================
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000
Accept: text/html
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0
Accept-Language: en-US,en;q=0.5 
Connection: close
User-Agent: Mozilla/4.0
Upgrade-Insecure-Requests: 1 


================================================
FILE: test/test-http-files/http_1_0.expected.http
================================================
HTTP/1.0 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "localhost:8000", 
    "User-Agent": "curl/%% '.*' %%"
  }, 
  "origin": "%% '[0-9.]\+' %%", 
  "url": "http://localhost:8000/get"
}


================================================
FILE: test/test-http-files/http_1_0.http
================================================
GET http://localhost:8000/get HTTP/1.0
Host: localhost:8000


================================================
FILE: test/test-http-files/http_1_1.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "localhost:8000", 
    "User-Agent": "curl/%% '.*' %%"
  }, 
  "origin": "%% '[0-9.]\+' %%", 
  "url": "http://localhost:8000/get"
}


================================================
FILE: test/test-http-files/http_1_1.http
================================================
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000


================================================
FILE: test/test-http-files/http_2.http
================================================
GET http://localhost:8000/get HTTP/2


================================================
FILE: test/test-http-files/patch_json.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%

{
  "args": {}, 
  "data": "{\r\n    \"foo\": \"bar\",\r\n    \"lol\": \"beans\"\r\n}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "43", 
    "Content-Type": "application/json", 
    "Host": "localhost:8000", 
    "User-Agent": "curl/%% '.*' %%"
  }, 
  "json": {
    "foo": "bar", 
    "lol": "beans"
  }, 
  "origin": "%% '[0-9.]\+' %%", 
  "url": "http://localhost:8000/patch"
}


================================================
FILE: test/test-http-files/patch_json.http
================================================
PATCH http://localhost:8000/patch HTTP/1.1
Host: localhost:8000
Content-Type: application/json
Content-Length: 43


{
    "foo": "bar",
    "lol": "beans"
}


================================================
FILE: test/test-http-files/post_incomplete.http
================================================
POST http://localhost:8000/post HTTP/1.1
Content-Type: application/json


{
    "foo": "bar",
    "lol": "beans"
}


================================================
FILE: test/test-http-files/post_invalid_content_length.http
================================================
POST http://localhost:8000/post HTTP/1.1
Host: localhost:8000
Content-Length: 20
Content-Type: application/json


{
    "foo": "bar",
    "lol": "beans"
}


================================================
FILE: test/test-http-files/post_json.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%

{
  "args": {}, 
  "data": "{\r\n    \"foo\": \"bar\",\r\n    \"lol\": \"beans\"\r\n}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "43", 
    "Content-Type": "application/json", 
    "Host": "localhost:8000", 
    "User-Agent": "curl/%% '.*' %%"
  }, 
  "json": {
    "foo": "bar", 
    "lol": "beans"
  }, 
  "origin": "%% '[0-9.]\+' %%", 
  "url": "http://localhost:8000/post"
}


================================================
FILE: test/test-http-files/post_json.http
================================================
POST http://localhost:8000/post HTTP/1.1
Host: "localhost:8000"
Content-Type: application/json
Content-Length: 43


{
    "foo": "bar",
    "lol": "beans"
}


================================================
FILE: test/test-http-files/redirect.expected.http
================================================
HTTP/1.1 302 FOUND
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: http://localhost:8000/get
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true



================================================
FILE: test/test-http-files/redirect.http
================================================
GET http://localhost:8000/redirect-to?url=http://localhost:8000/get HTTP/1.1
Host: localhost:8000


================================================
FILE: test/test-http-files/redirect_follow.expected.http
================================================
HTTP/1.1 302 FOUND
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 0
Location: http://localhost:8000/get
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "localhost:8000", 
    "User-Agent": "curl/%% '.*' %%"
  }, 
  "origin": "%% '[0-9.]\+' %%", 
  "url": "http://localhost:8000/get"
}


================================================
FILE: test/test-http-files/redirect_follow.http
================================================
GET http://localhost:8000/redirect-to?url=http://localhost:8000/get HTTP/1.1
Host: localhost:8000


================================================
FILE: test/test-http-files/simple_get.expected.http
================================================
HTTP/1.1 200 OK
Server: gunicorn/%% '.*' %%
Date: %% '.*' %%
Connection: close
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Content-Length: %% '[0-9]\+' %%

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "localhost:8000", 
    "User-Agent": "curl/%% '.*' %%"
  }, 
  "origin": "%% '[0-9.]\+' %%", 
  "url": "http://localhost:8000/get"
}


================================================
FILE: test/test-http-files/simple_get.http
================================================
GET http://localhost:8000/get HTTP/1.1
Host: localhost:8000


================================================
FILE: test/test-http-files/simple_get_without_protocol.http
================================================
GET http://localhost:8000/get
Download .txt
gitextract_413bwcyb/

├── .circleci/
│   └── config.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── autoload/
│   ├── http.vim
│   ├── vital/
│   │   ├── _vim_http/
│   │   │   └── Data/
│   │   │       └── Base64.vim
│   │   ├── _vim_http.vim
│   │   ├── vim_http.vim
│   │   └── vim_http.vital
│   └── vital.vim
├── docker-compose.yml
├── ftdetect/
│   └── http.vim
├── plugin/
│   └── http.vim
├── syntax/
│   └── http.vim
└── test/
    ├── .themisrc
    ├── integration.vim
    └── test-http-files/
        ├── example_in_docs.md
        ├── get_body_params.expected.http
        ├── get_body_params.http
        ├── get_url_params.expected.http
        ├── get_url_params.http
        ├── get_with_multiple_headers.http
        ├── http_1_0.expected.http
        ├── http_1_0.http
        ├── http_1_1.expected.http
        ├── http_1_1.http
        ├── http_2.http
        ├── patch_json.expected.http
        ├── patch_json.http
        ├── post_incomplete.http
        ├── post_invalid_content_length.http
        ├── post_json.expected.http
        ├── post_json.http
        ├── redirect.expected.http
        ├── redirect.http
        ├── redirect_follow.expected.http
        ├── redirect_follow.http
        ├── simple_get.expected.http
        ├── simple_get.http
        └── simple_get_without_protocol.http
Condensed preview — 43 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (55K chars).
[
  {
    "path": ".circleci/config.yml",
    "chars": 1034,
    "preview": "version: 2\njobs:\n  latest:\n    docker:\n      - image: thinca/vim:latest\n      - image: citizenstig/httpbin\n    steps: &s"
  },
  {
    "path": ".gitignore",
    "chars": 241,
    "preview": "\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.v"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 970,
    "preview": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changel"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2531,
    "preview": "Contributing\n============\n\nAll issues/pull requests are welcome.\n\nThat said, things that I'm unlikely to merge:\n\n* Devia"
  },
  {
    "path": "LICENSE.md",
    "chars": 1210,
    "preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
  },
  {
    "path": "Makefile",
    "chars": 460,
    "preview": ".PHONY: test setup httpbin\n\ntest:\n\tvim-themis/bin/themis --reporter dot test\n\nsetup:\n\tdocker pull citizenstig/httpbin\n\tg"
  },
  {
    "path": "README.md",
    "chars": 2491,
    "preview": "[![CircleCI](https://circleci.com/gh/nicwest/vim-http/tree/master.svg?style=svg)](https://circleci.com/gh/nicwest/vim-ht"
  },
  {
    "path": "autoload/http.vim",
    "chars": 8938,
    "preview": "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:"
  },
  {
    "path": "autoload/vital/_vim_http/Data/Base64.vim",
    "chars": 3483,
    "preview": "\" ___vital___\n\" NOTE: lines between '\" ___vital___' is generated by :Vitalize.\n\" Do not mofidify the code nor insert new"
  },
  {
    "path": "autoload/vital/_vim_http.vim",
    "chars": 143,
    "preview": "let s:_plugin_name = expand('<sfile>:t:r')\n\nfunction! vital#{s:_plugin_name}#new() abort\n  return vital#{s:_plugin_name["
  },
  {
    "path": "autoload/vital/vim_http.vim",
    "chars": 10082,
    "preview": "let s:plugin_name = expand('<sfile>:t:r')\nlet s:vital_base_dir = expand('<sfile>:h')\nlet s:project_root = expand('<sfile"
  },
  {
    "path": "autoload/vital/vim_http.vital",
    "chars": 63,
    "preview": "vim_http\nf07284453d294e2159424aa76b9b902a5b25d5ae\n\nData.Base64\n"
  },
  {
    "path": "autoload/vital.vim",
    "chars": 400,
    "preview": "function! vital#of(name) abort\n  let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital', 1)\n  let file"
  },
  {
    "path": "docker-compose.yml",
    "chars": 215,
    "preview": "version: \"3.4\"\nservices:\n  httpbin:\n    image: citizenstig/httpbin\n  oldest:\n    image: thinca/vim:v7.4.566\n    volumes:"
  },
  {
    "path": "ftdetect/http.vim",
    "chars": 109,
    "preview": "augroup filetypedetect\n    au! BufRead,BufNewFile *.http   setlocal filetype=http fileformat=dos\naugroup END\n"
  },
  {
    "path": "plugin/http.vim",
    "chars": 773,
    "preview": "if !exists('g:vim_http_clean_before_do')\n  let g:vim_http_clean_before_do = 1\nendif\nif !exists('g:vim_http_additional_cu"
  },
  {
    "path": "syntax/http.vim",
    "chars": 1343,
    "preview": "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"
  },
  {
    "path": "test/.themisrc",
    "chars": 131,
    "preview": "\nlet g:http_test_files = expand('<sfile>:p:h') . '/test-http-files/'\n\ncall themis#log('test_files_location: ' . g:http_t"
  },
  {
    "path": "test/integration.vim",
    "chars": 8715,
    "preview": "let s:suite = themis#suite('integration')\nlet s:assert = themis#helper('assert')\n\n\" Setup: {{{1\nlet s:original_buffers ="
  },
  {
    "path": "test/test-http-files/example_in_docs.md",
    "chars": 573,
    "preview": "Imagine this was some documentation and we had an HTTP request right there in\nthe file. Why do I have to create a new bu"
  },
  {
    "path": "test/test-http-files/get_body_params.expected.http",
    "chars": 520,
    "preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
  },
  {
    "path": "test/test-http-files/get_body_params.http",
    "chars": 85,
    "preview": "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",
    "chars": 493,
    "preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
  },
  {
    "path": "test/test-http-files/get_url_params.http",
    "chars": 80,
    "preview": "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",
    "chars": 247,
    "preview": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\nAccept: text/html\r\nAccept-Encoding: gzip, deflate\r\nUser-Ag"
  },
  {
    "path": "test/test-http-files/http_1_0.expected.http",
    "chars": 431,
    "preview": "HTTP/1.0 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
  },
  {
    "path": "test/test-http-files/http_1_0.http",
    "chars": 62,
    "preview": "GET http://localhost:8000/get HTTP/1.0\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/http_1_1.expected.http",
    "chars": 431,
    "preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
  },
  {
    "path": "test/test-http-files/http_1_1.http",
    "chars": 62,
    "preview": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/http_2.http",
    "chars": 38,
    "preview": "GET http://localhost:8000/get HTTP/2\r\n"
  },
  {
    "path": "test/test-http-files/patch_json.expected.http",
    "chars": 671,
    "preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
  },
  {
    "path": "test/test-http-files/patch_json.http",
    "chars": 167,
    "preview": "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"
  },
  {
    "path": "test/test-http-files/post_incomplete.http",
    "chars": 123,
    "preview": "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"
  },
  {
    "path": "test/test-http-files/post_invalid_content_length.http",
    "chars": 165,
    "preview": "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"
  },
  {
    "path": "test/test-http-files/post_json.expected.http",
    "chars": 670,
    "preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
  },
  {
    "path": "test/test-http-files/post_json.http",
    "chars": 167,
    "preview": "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"
  },
  {
    "path": "test/test-http-files/redirect.expected.http",
    "chars": 256,
    "preview": "HTTP/1.1 302 FOUND\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: text/html; charset=u"
  },
  {
    "path": "test/test-http-files/redirect.http",
    "chars": 100,
    "preview": "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",
    "chars": 687,
    "preview": "HTTP/1.1 302 FOUND\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: text/html; charset=u"
  },
  {
    "path": "test/test-http-files/redirect_follow.http",
    "chars": 100,
    "preview": "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",
    "chars": 431,
    "preview": "HTTP/1.1 200 OK\r\nServer: gunicorn/%% '.*' %%\r\nDate: %% '.*' %%\r\nConnection: close\r\nContent-Type: application/json\r\nAcces"
  },
  {
    "path": "test/test-http-files/simple_get.http",
    "chars": 62,
    "preview": "GET http://localhost:8000/get HTTP/1.1\r\nHost: localhost:8000\r\n"
  },
  {
    "path": "test/test-http-files/simple_get_without_protocol.http",
    "chars": 31,
    "preview": "GET http://localhost:8000/get\r\n"
  }
]

About this extraction

This page contains the full source code of the nicwest/vim-http GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 43 files (48.8 KB), approximately 16.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!