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 ================================================ 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(''), '\zs\d\+\ze__SID$') endfunction execute join(['function! vital#_vim_http#Data#Base64#import() abort', printf("return map({'decode': '', 'encodebin': '', 'encode': ''}, \"function('%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(':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(':t:r') let s:vital_base_dir = expand(':h') let s:project_root = expand(':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(''), '\zs\d\+\ze__SID$') endfunction let s:_s = '' . 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 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(':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 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("\")), '"[\\x" . printf("%0x", char2nr("\"[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%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('%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('', '', '', '') command! -bang -range HttpShowCurl call http#show_curl('', '', '', '') command! -bang -range HttpShowRequest call http#show_request('', '', '', '') 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(':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