├── logo.png ├── .gitignore ├── src └── Ws │ └── Http │ ├── Exception.php │ ├── Watcher │ └── Exception.php │ ├── Automated │ ├── Exception.php │ ├── CookieStore.php │ ├── README.md │ ├── Task.php │ ├── Task │ │ ├── Body.php │ │ └── Request.php │ ├── Bootstrap.php │ ├── example.json │ └── postmanv1.json │ ├── Method.php │ ├── Request │ └── Body.php │ ├── Response.php │ ├── Watcher.php │ ├── ARequest.php │ └── Request.php ├── tests ├── Ws │ └── Http │ │ ├── _json │ │ ├── freegeoip.net.json │ │ └── a.automated.json │ │ ├── Automated.php │ │ ├── Autohome.php │ │ └── ATest.php └── bootstrap.php ├── composer.json ├── LICENSE └── README.md /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toohamster/ws-http/HEAD/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /tests/data 3 | composer.phar 4 | composer.lock 5 | .DS_Store 6 | Thumbs.db 7 | -------------------------------------------------------------------------------- /src/Ws/Http/Exception.php: -------------------------------------------------------------------------------- 1 | test(); 12 | } 13 | 14 | public function test() 15 | { 16 | output(__METHOD__); 17 | $file = __DIR__ . "/_json/a.automated.json"; 18 | 19 | $json = file_get_contents($file); 20 | 21 | $task = Task::parse($json); 22 | // output($task); 23 | 24 | Bootstrap::runTask($task); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toohamster/ws-http", 3 | "description": "ws-http: Simplified, lightweight HTTP client library", 4 | "keywords": ["http", "ws-http"], 5 | "license": "MIT", 6 | "homepage": "https://github.com/toohamster/ws-http", 7 | "support": { 8 | "issues": "https://github.com/toohamster/ws-http/issues", 9 | "source": "https://github.com/toohamster/ws-http" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "toohamster", 14 | "email": "449211678@qq.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.4" 19 | }, 20 | "require-dev": { 21 | 22 | }, 23 | "autoload": { 24 | "classmap": [], 25 | "files": [], 26 | "psr-4": {"Ws\\Http\\": "src/Ws/Http"} 27 | }, 28 | "minimum-stability": "dev" 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/Ws/Http/Automated/Task.php: -------------------------------------------------------------------------------- 1 | id = $params['id']; 51 | $obj->name = $params['name']; 52 | $obj->type = (int) $params['type']; 53 | $obj->body = TaskBody::parse($params['body']); 54 | 55 | return $obj; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 仓鼠 Xu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Ws/Http/Automated/Task/Body.php: -------------------------------------------------------------------------------- 1 | setVar($item['name'], $item['value']); 36 | } 37 | 38 | foreach( (array) $params['requests'] as $item ) 39 | { 40 | $obj->setRequest($item); 41 | } 42 | 43 | return $obj; 44 | } 45 | 46 | public function setVar($name, $value) 47 | { 48 | $this->vars[$name] = $value; 49 | } 50 | 51 | public function setRequest($request) 52 | { 53 | $this->requests[] = Request::parse($request);; 54 | } 55 | 56 | /** 57 | * 替换查询参数并返回 58 | * 59 | * @param string $value 待替换的参数值 60 | * 61 | * @return string 62 | */ 63 | public function varReplace($value) 64 | { 65 | if (is_string($value) && !empty($this->vars)) { 66 | foreach ($this->vars as $key => $val) { 67 | $key = '${' . trim($key) . '}'; 68 | 69 | if (empty($val)) { 70 | $val = ''; 71 | } 72 | $value = str_ireplace($key, $val, $value); 73 | } 74 | } 75 | return $value; 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | run(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/Ws/Http/Method.php: -------------------------------------------------------------------------------- 1 | $file) { 58 | $data[$name] = call_user_func([__CLASS__, 'File'], $file); 59 | } 60 | } 61 | 62 | return $data; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Ws/Http/Response.php: -------------------------------------------------------------------------------- 1 | code = intval($curl_info['http_code']); 20 | $this->headers = $this->parseHeaders($headers); 21 | $this->raw_body = $raw_body; 22 | $this->curl_info = $curl_info; 23 | 24 | if (!empty($this->headers['Content-Type'])) 25 | { 26 | $ct = trim($this->headers['Content-Type']); 27 | if ( false !== stripos($ct, 'application/json') ) 28 | { 29 | // make sure raw_body is the first argument 30 | array_unshift($json_args, $raw_body); 31 | 32 | $json = call_user_func_array('json_decode', $json_args); 33 | 34 | if (json_last_error() === JSON_ERROR_NONE) { 35 | $this->body = $json; 36 | } 37 | } 38 | } 39 | } 40 | 41 | /** 42 | * if PECL_HTTP is not available use a fall back function 43 | * 44 | * thanks to ricardovermeltfoort@gmail.com 45 | * http://php.net/manual/en/function.http-parse-headers.php#112986 46 | * @param string $raw_headers raw headers 47 | * @return array 48 | */ 49 | private function parseHeaders($raw_headers) 50 | { 51 | if (function_exists('http_parse_headers')) { 52 | return http_parse_headers($raw_headers); 53 | } else { 54 | $key = ''; 55 | $headers = []; 56 | 57 | foreach (explode("\n", $raw_headers) as $i => $h) { 58 | $h = explode(':', $h, 2); 59 | 60 | if (isset($h[1])) { 61 | if (!isset($headers[$h[0]])) { 62 | $headers[$h[0]] = trim($h[1]); 63 | } elseif (is_array($headers[$h[0]])) { 64 | $headers[$h[0]] = array_merge($headers[$h[0]], [trim($h[1])]); 65 | } else { 66 | $headers[$h[0]] = array_merge([$headers[$h[0]]], [trim($h[1])]); 67 | } 68 | 69 | $key = $h[0]; 70 | } else { 71 | if (substr($h[0], 0, 1) == "\t") { 72 | $headers[$key] .= "\r\n\t".trim($h[0]); 73 | } elseif (!$key) { 74 | $headers[0] = trim($h[0]); 75 | } 76 | } 77 | } 78 | 79 | return $headers; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Ws/Http/Automated/Bootstrap.php: -------------------------------------------------------------------------------- 1 | name, $task->id); 25 | 26 | $data = []; 27 | 28 | $requests = $task->body->requests; 29 | foreach ($requests as $request) 30 | { 31 | if ($request->isDelay()) 32 | { 33 | if ($request->delay > 0) 34 | { 35 | sleep($request->delay); 36 | } 37 | } 38 | else 39 | { 40 | // 41 | try 42 | { 43 | $httpResponse = self::doRequest($request, $task); 44 | 45 | output($httpResponse); 46 | } 47 | catch(GlobalException $ex) 48 | { 49 | output($ex->getMessage()); 50 | } 51 | 52 | break; 53 | } 54 | } 55 | 56 | } 57 | 58 | public static function doRequest(TaskRequest $request, Task $task) 59 | { 60 | if ($request->isDelay()) return null; 61 | 62 | output($request->name, $request->id); 63 | 64 | $httpRequest = HttpRequest::create(); 65 | if ( $request->authorization ) 66 | { 67 | $httpRequest->auth( $task->body->varReplace($request->authorization['type']), 68 | $task->body->varReplace($request->authorization['user']), 69 | $task->body->varReplace($request->authorization['password'])); 70 | } 71 | 72 | switch ($request->timeout[0]) 73 | { 74 | case 's': 75 | $httpRequest->timeout($task->body->varReplace($request->timeout[1])); 76 | break; 77 | case 'ms': 78 | $httpRequest->timeoutMs($task->body->varReplace($request->timeout[1])); 79 | break; 80 | } 81 | 82 | if ($request->proxy) 83 | { 84 | $httpRequest->proxy($task->body->varReplace($request->proxy['address']), 85 | $task->body->varReplace($request->proxy['port']), 86 | $task->body->varReplace($request->proxy['type']), 87 | $task->body->varReplace($request->proxy['tunnel'])); 88 | 89 | if (!empty($request->proxy['auth'])) 90 | { 91 | $httpRequest->proxyAuth($task->body->varReplace($request->proxy['auth']['user']), 92 | $task->body->varReplace($request->proxy['auth']['password']), 93 | $task->body->varReplace($request->proxy['auth']['method'])); 94 | } 95 | } 96 | 97 | $httpResponse = $httpRequest->send($request->method, 98 | $task->body->varReplace($request->url), 99 | $task->body->varReplace($request->data), 100 | $task->body->varReplace($request->headers)); 101 | 102 | return $httpResponse; 103 | } 104 | 105 | } -------------------------------------------------------------------------------- /tests/Ws/Http/Autohome.php: -------------------------------------------------------------------------------- 1 | dir = __DIR__.'/../../data/autohome'; 17 | $this->crawl(); 18 | } 19 | 20 | public function crawl() 21 | { 22 | output(__METHOD__); 23 | $httpRequest = HttpRequest::create(); 24 | // 1 的车型 有164条, 11的话 可以查出 242条 25 | $url = "http://www.autohome.com.cn/ashx/AjaxIndexCarFind.ashx?type=1"; 26 | $httpResponse = $httpRequest->get($url); 27 | $filename = $this->dir . '/branditems.json'; 28 | 29 | $raw_body = self::gbk2utf8($httpResponse->raw_body); 30 | file_put_contents($filename, $raw_body); 31 | 32 | $data = json_decode($raw_body, true); 33 | if ( !empty($data) && $data['returncode'] == 0 ) 34 | { 35 | foreach ( $data['result']['branditems'] as $branditem ) 36 | { 37 | if ( !empty($branditem['id']) ) 38 | { 39 | $branditemId = $branditem['id']; 40 | $url = "http://www.autohome.com.cn/ashx/AjaxIndexCarFind.ashx?type=3&value={$branditemId}"; 41 | $httpResponse = $httpRequest->get($url); 42 | 43 | $filename = $this->dir . "/branditem-{$branditemId}.json"; 44 | $raw_body = self::gbk2utf8($httpResponse->raw_body); 45 | file_put_contents($filename, $raw_body); 46 | sleep(1); 47 | 48 | $data001 = json_decode($raw_body, true); 49 | if ( !empty($data001) && $data001['returncode'] == 0 ) 50 | { 51 | foreach ( $data001['result']['factoryitems'] as $factoryitem ) 52 | { 53 | if ( !empty($factoryitem['id']) ) 54 | { 55 | $factoryitemId = $factoryitem['id']; 56 | if ( !empty($factoryitem['seriesitems']) ) 57 | { 58 | foreach ( $factoryitem['seriesitems'] as $seriesitem ) 59 | { 60 | if ( !empty($seriesitem['id']) ) 61 | { 62 | $seriesitemId = $seriesitem['id']; 63 | 64 | $url = "http://www.autohome.com.cn/ashx/AjaxIndexCarFind.ashx?type=5&value={$seriesitemId}"; 65 | $httpResponse = $httpRequest->get($url); 66 | 67 | $filename = $this->dir . "/branditem-{$branditemId}-factoryitem-{$factoryitemId}-seriesitem-{$seriesitemId}.json"; 68 | $raw_body = self::gbk2utf8($httpResponse->raw_body); 69 | file_put_contents($filename, $raw_body); 70 | sleep(1); 71 | 72 | $data002 = json_decode($raw_body, true); 73 | 74 | $this->oilAndCars($data002,$httpRequest); 75 | 76 | } 77 | } 78 | 79 | } 80 | } 81 | } 82 | } 83 | 84 | } 85 | } 86 | } 87 | 88 | } 89 | 90 | public function oilAndCars($data, $httpRequest) 91 | { 92 | 93 | if ( !empty($data) && $data['returncode'] == 0 ) 94 | { 95 | foreach ( $data['result']['yearitems'] as $yearitem ) 96 | { 97 | if ( !empty($yearitem['id']) ) 98 | { 99 | $yearitemId = $yearitem['id']; 100 | foreach ( $yearitem['specitems'] as $specitem ) 101 | { 102 | if ( !empty($specitem['id']) ) 103 | { 104 | $specitemId = $specitem['id']; 105 | // 处理油价 106 | $url = "http://www.autohome.com.cn/ashx/ajaxoil.ashx?type=spec&specId={$specitemId}"; 107 | 108 | $httpResponse = $httpRequest->get($url); 109 | 110 | $filename = $this->dir . "/oil-{$specitemId}.json"; 111 | $raw_body = self::gbk2utf8($httpResponse->raw_body); 112 | file_put_contents($filename, $raw_body); 113 | sleep(1); 114 | 115 | // 找车 116 | // http://www.autohome.com.cn/spec/18658/#pvareaid=10006 117 | } 118 | } 119 | 120 | } 121 | } 122 | } 123 | } 124 | 125 | private static function gbk2utf8($content) 126 | { 127 | return iconv("GBK", "UTF-8", $content); 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /src/Ws/Http/Automated/Task/Request.php: -------------------------------------------------------------------------------- 1 | id = $params['id']; 38 | $obj->type = (int) $params['type']; 39 | 40 | switch ($obj->type) { 41 | case 1: 42 | $obj->name = $params['name']; 43 | $obj->url = $params['url']; 44 | $obj->method = strtoupper(trim($params['method'])); 45 | 46 | $obj->setAuthorization($params['authorization']); 47 | $obj->setProxy($params['proxy']); 48 | $obj->setTimeout(empty($params['timeout']) ? null : $params['timeout']); 49 | 50 | // 设置默认请求头 51 | // $obj->headers['Content-Type'] = 'text/plain'; 52 | 53 | // 处理请求数据 54 | switch ($obj->method) 55 | { 56 | // 这两种类型无需对参数做处理 57 | case Method::GET: 58 | case Method::HEAD: 59 | foreach( (array) $params['data'] as $item ) 60 | { 61 | if ( 'text' == $item['type'] ) 62 | { 63 | $obj->data[ $item['key'] ] = $item['value']; 64 | } 65 | else 66 | { 67 | // 暂未实现 68 | } 69 | } 70 | break; 71 | 72 | // 只有 PUT,POST,DELETE,OPTIONS,PATCH 支持内容体 73 | case Method::POST: 74 | case Method::PUT: 75 | case Method::DELETE: 76 | case Method::OPTIONS: 77 | $dataMode = strtolower( trim($params['dataMode']) ); 78 | switch ($dataMode) { 79 | case 'params': 80 | foreach( (array) $params['data'] as $item ) 81 | { 82 | if ( 'text' == $item['type'] ) 83 | { 84 | $obj->data[ $item['key'] ] = $item['value']; 85 | } 86 | else if ( 'file' == $item['type'] ) 87 | { 88 | $obj->headers['Content-Type'] = 'multipart/form-data'; 89 | // file 内容暂不支持 90 | } 91 | } 92 | break; 93 | case 'urlencoded': 94 | $obj->headers['Content-Type'] = 'application/x-www-form-urlencoded'; 95 | foreach( (array) $params['data'] as $item ) 96 | { 97 | if ( 'text' == $item['type'] ) 98 | { 99 | $obj->data[ $item['key'] ] = $item['value']; 100 | } 101 | } 102 | break; 103 | 104 | case 'raw-json': 105 | $obj->headers['Content-Type'] = 'application/json'; 106 | $obj->data = $params['data']['value']; 107 | break; 108 | case 'raw-xml': 109 | $obj->headers['Content-Type'] = 'application/xml'; 110 | $obj->data = $params['data']['value']; 111 | break; 112 | case 'raw-textxml': 113 | $obj->headers['Content-Type'] = 'text/xml'; 114 | $obj->data = $params['data']['value']; 115 | break; 116 | case 'raw-html': 117 | $obj->headers['Content-Type'] = 'text/html'; 118 | $obj->data = $params['data']['value']; 119 | break; 120 | case 'raw-text': 121 | $obj->headers['Content-Type'] = 'text/plain'; 122 | $obj->data = $params['data']['value']; 123 | break; 124 | case 'raw-binary': 125 | default: 126 | throw new Exception("Not supported request `dataMode`: {$dataMode}"); 127 | break; 128 | } 129 | 130 | break; 131 | default: 132 | throw new Exception("Not supported request `method`: {$obj->method}"); 133 | break; 134 | } 135 | 136 | // 设置请求头 137 | foreach( (array) $params['headers'] as $key => $val ) 138 | { 139 | $obj->headers[$key] = $val; 140 | } 141 | 142 | // 设置断言规则 143 | 144 | // 设置变量提取规则 145 | 146 | break; 147 | case 2: 148 | $obj->delay = (int) $params['delay']; 149 | break; 150 | default: 151 | throw new Exception("Unknown request `type`: {$obj->type}"); 152 | break; 153 | } 154 | 155 | return $obj; 156 | } 157 | 158 | /** 159 | * 类型是否为请求间隔 160 | * 161 | * @return boolean 162 | */ 163 | public function isDelay() 164 | { 165 | return 2 == $this->type; 166 | } 167 | 168 | public function setAuthorization($authorization=[]) 169 | { 170 | 171 | $this->authorization = false; 172 | 173 | if (!empty($authorization) && empty($authorization['type'])) 174 | { 175 | $type = trim($authorization['type']); 176 | if ( 'Basic' == $type ) 177 | { 178 | $this->authorization = [ 179 | 'type' => 'Basic', 180 | 'user' => trim($authorization['user']), 181 | 'password' => $authorization['password'], 182 | ]; 183 | } 184 | else 185 | { 186 | $this->headers['Authorization'] = trim($authorization['body']); 187 | } 188 | } 189 | } 190 | 191 | public function setTimeout($timeout) 192 | { 193 | // 默认是 30 秒 194 | $this->timeout = ['s', 30]; 195 | 196 | if ( empty($timeout) ) return; 197 | 198 | $timeout = trim($timeout); 199 | if (preg_match('/ms$/i',$timeout)) 200 | { 201 | $this->timeout[0] = 'ms'; 202 | $timeout = str_ireplace('ms', '', $timeout); 203 | } 204 | else if (preg_match('/s$/i',$timeout)) 205 | { 206 | $timeout = str_ireplace('s', '', $timeout); 207 | } 208 | else 209 | { 210 | $timeout = 30; 211 | } 212 | 213 | $timeout = intval(trim($timeout)); 214 | if ( $timeout < 1 ) 215 | { 216 | $this->timeout = ['s', 30]; 217 | } 218 | else 219 | { 220 | $this->timeout[1] = $timeout; 221 | } 222 | } 223 | 224 | public function setProxy($proxy=[]) 225 | { 226 | $this->proxy = false; 227 | 228 | if (!empty($proxy) && empty($proxy['type'])) 229 | { 230 | 231 | $this->proxy = [ 232 | 'address' => trim($proxy['address']), 233 | 'port' => trim($proxy['port']), 234 | 'tunnel' => trim($proxy['tunnel']), 235 | ]; 236 | 237 | $type = strtolower( trim($proxy['type']) ); 238 | 239 | switch ($type) { 240 | case 'http': 241 | $this->proxy['type'] = CURLPROXY_HTTP; 242 | break; 243 | case 'http1.0': 244 | if ( !defined('CURLPROXY_HTTP_1_0') ) 245 | { 246 | throw new Exception("Unknown proxy `type`: {$type}"); 247 | } 248 | $this->proxy['type'] = CURLPROXY_HTTP_1_0; 249 | break; 250 | case 'socks4': 251 | $this->proxy['type'] = CURLPROXY_SOCKS4; 252 | break; 253 | case 'socks4a': 254 | if ( !defined('CURLPROXY_SOCKS4A') ) 255 | { 256 | throw new Exception("Unknown proxy `type`: {$type}"); 257 | } 258 | $this->proxy['type'] = CURLPROXY_SOCKS4A; 259 | break; 260 | case 'socks5': 261 | $this->proxy['type'] = CURLPROXY_SOCKS5; 262 | break; 263 | case 'socks5.hostname': 264 | if ( !defined('CURLPROXY_SOCKS5_HOSTNAME') ) 265 | { 266 | throw new Exception("Unknown proxy `type`: {$type}"); 267 | } 268 | $this->proxy['type'] = CURLPROXY_SOCKS5_HOSTNAME; 269 | break; 270 | default: 271 | throw new Exception("Unknown proxy `type`: {$type}"); 272 | break; 273 | } 274 | } 275 | } 276 | 277 | public function toArray() 278 | { 279 | 280 | if ($this->isDelay()) 281 | { 282 | return ['id' => $this->id, 'type' => 2, 'delay' => $this->delay]; 283 | } 284 | 285 | return [ 286 | 'id' => $this->id, 'type' => 1, 287 | 'name' => $this->name, 288 | 'url' => $this->url, 289 | 'method' => $this->method, 290 | 'authorization' => $this->authorization, 291 | 'proxy' => $this->proxy, 292 | 'timeout' => $this->timeout, 293 | 'headers' => $this->headers, 294 | 'data' => $this->data, 295 | ]; 296 | } 297 | 298 | } -------------------------------------------------------------------------------- /src/Ws/Http/Watcher.php: -------------------------------------------------------------------------------- 1 | response = $response; 22 | } 23 | 24 | public function setResponse(Response $response) 25 | { 26 | $this->response = $response; 27 | 28 | return $this; 29 | } 30 | 31 | public function assertTotalTimeLessThan($assertedTime) 32 | { 33 | $totalTime = $this->response->curl_info['total_time']; 34 | 35 | if (floatval($assertedTime) < floatval($totalTime)) { 36 | throw new Exception("Asserted total time '$assertedTime' is less than '$totalTime'."); 37 | } 38 | 39 | return $this; 40 | } 41 | 42 | public function assertStatusCode($statusCode) 43 | { 44 | $httpCode = $this->response->code; 45 | if (intval($statusCode) !== $httpCode) { 46 | throw new Exception("Asserted status code '$statusCode' does not equal response status code '$httpCode'."); 47 | } 48 | 49 | return $this; 50 | } 51 | 52 | public function assertHeadersExist(array $assertedHeaders = []) 53 | { 54 | $headers = & $this->response->headers; 55 | foreach ($assertedHeaders as $header) { 56 | if (!isset($headers[$header])) { 57 | throw new Exception("Asserted header '$header' is not set."); 58 | } 59 | } 60 | 61 | return $this; 62 | } 63 | 64 | public function assertHeaders(array $assertedHeaders = []) 65 | { 66 | if (isAssoc($assertedHeaders)) 67 | { 68 | $headers = & $this->response->headers; 69 | 70 | foreach ($assertedHeaders as $k => $v) { 71 | if (!array_key_exists($k, $headers)) { 72 | throw new Exception("Asserted header '$k' is not set."); 73 | } 74 | 75 | if (is_array($headers[$k])) { 76 | if (!in_array($v, $headers[$k])) { 77 | throw new Exception("Asserted header '$k' exists, but the response header value '$v' is not equal."); 78 | } 79 | } else { 80 | if ($v !== $headers[$k]) { 81 | throw new Exception("Asserted header '$k=$v' does not equal response header '$k={$headers[$k]}'."); 82 | } 83 | } 84 | } 85 | } 86 | else { 87 | $this->assertHeadersExist($assertedHeaders); 88 | } 89 | 90 | return $this; 91 | } 92 | 93 | public function assertBody($assertedBody, $useRegularExpression = false) 94 | { 95 | $body = & $this->response->raw_body; 96 | 97 | if ($assertedBody === 'IS_EMPTY') { 98 | if ($body === false || $body === "") { 99 | return $this; 100 | } else { 101 | throw new Exception("Response body is not empty."); 102 | } 103 | } 104 | 105 | if ($assertedBody === 'IS_VALID_JSON') { 106 | if (json_decode($body) === null) { 107 | throw new Exception("Response body is invalid JSON."); 108 | } 109 | 110 | return $this; 111 | } 112 | 113 | if ($useRegularExpression) { 114 | if (!@preg_match($assertedBody, $body)) { 115 | throw new Exception("Asserted body '$assertedBody' does not match response body of '$body'."); 116 | } 117 | } else { 118 | if (strpos($assertedBody, $body)) { 119 | throw new Exception("Asserted body '$assertedBody' does not equal response body of '$body'."); 120 | } 121 | } 122 | 123 | return $this; 124 | } 125 | 126 | public function assertBodyJson($asserted, $onNotEqualVarExport = false) 127 | { 128 | $body = json_decode($this->response->raw_body); 129 | 130 | if ($body === null) { 131 | throw new Exception("Response body is invalid JSON."); 132 | } 133 | 134 | if ($asserted != $body) { 135 | $errorMessage = "Asserted body does not equal response body."; 136 | if ($onNotEqualVarExport) { 137 | $errorMessage .= "\n\n--------------- ASSERTED BODY ---------------\n" . var_export($asserted, true) . 138 | "\n\n--------------- RESPONSE BODY ---------------\n" . var_export($body, true) . "\n\n"; 139 | } 140 | throw new Exception($errorMessage); 141 | } 142 | 143 | return $this; 144 | } 145 | 146 | public function assertBodyJsonFile($assertedJsonFile, $onNotEqualPrintJson = false) 147 | { 148 | if (!file_exists($assertedJsonFile)) { 149 | throw new Exception("Asserted JSON file '$assertedJsonFile' does not exist."); 150 | } 151 | 152 | $asserted = file_get_contents($assertedJsonFile); 153 | if (json_decode($asserted) === null) { 154 | throw new Exception("Asserted JSON file is invalid JSON."); 155 | } 156 | 157 | $body = $this->response->raw_body; 158 | 159 | if (json_decode($body) === null) { 160 | throw new Exception("Response body is invalid JSON."); 161 | } 162 | 163 | $asserted = prettyPrintJson($asserted); 164 | $body = prettyPrintJson($body); 165 | 166 | if ($asserted != $body) { 167 | $errorMessage = "Asserted JSON file does not equal response body."; 168 | if ($onNotEqualPrintJson) { 169 | $errorMessage .= "\n\n--------------- ASSERTED JSON FILE ---------------\n" . $asserted . 170 | "\n\n--------------- RESPONSE BODY ---------------\n" . $body . "\n\n"; 171 | } 172 | throw new Exception($errorMessage); 173 | } 174 | 175 | return $this; 176 | } 177 | 178 | } 179 | 180 | function isAssoc($array) 181 | { 182 | return (bool) count(array_filter(array_keys($array), 'is_string')); 183 | } 184 | 185 | function prettyPrintJson($json) 186 | { 187 | $result = ''; 188 | $level = 0; 189 | $prev_char = ''; 190 | $in_quotes = false; 191 | $ends_line_level = null; 192 | $json_length = strlen($json); 193 | 194 | for ($i = 0; $i < $json_length; $i++) { 195 | $char = $json[$i]; 196 | $new_line_level = null; 197 | $post = ""; 198 | if ($ends_line_level !== null) { 199 | $new_line_level = $ends_line_level; 200 | $ends_line_level = null; 201 | } 202 | if ($char === '"' && $prev_char != '\\') { 203 | $in_quotes = !$in_quotes; 204 | } else { 205 | if (!$in_quotes) { 206 | switch ($char) { 207 | case '}': 208 | case ']': 209 | $level--; 210 | $ends_line_level = null; 211 | $new_line_level = $level; 212 | break; 213 | 214 | case '{': 215 | case '[': 216 | $level++; 217 | case ',': 218 | $ends_line_level = $level; 219 | break; 220 | 221 | case ':': 222 | $post = " "; 223 | break; 224 | 225 | case " ": 226 | case "\t": 227 | case "\n": 228 | case "\r": 229 | $char = ""; 230 | $ends_line_level = $new_line_level; 231 | $new_line_level = null; 232 | break; 233 | } 234 | } 235 | } 236 | if ($new_line_level !== null) { 237 | $result .= "\n" . str_repeat("\t", $new_line_level); 238 | } 239 | $result .= $char . $post; 240 | $prev_char = $char; 241 | } 242 | 243 | return $result; 244 | } -------------------------------------------------------------------------------- /tests/Ws/Http/ATest.php: -------------------------------------------------------------------------------- 1 | testAAA(); 19 | // $this->testAuth(); 20 | // $this->testGet(); 21 | // $this->testPost(); 22 | // $this->testJson(); 23 | // $this->testJsonFile(); 24 | } 25 | 26 | private function testAAA() 27 | { 28 | output(__METHOD__); 29 | try 30 | { 31 | $httpRequest = HttpRequest::create(); 32 | $httpRequest->timeout(10); 33 | // $httpRequest->proxy('127.0.0.1','8888'); 34 | 35 | $httpResponse = $httpRequest->get("http://117.121.26.105/admin/login",[ 36 | 'User-Agent' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0', 37 | ]); 38 | 39 | // $httpRequest->get("http://117.121.26.105/assets/application-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css"); 40 | // $httpRequest->get("http://117.121.26.105/assets/application-3be7df993ba49cb551697afa2cc2aa789f6b450516bdf1913f9d901b11cbb391.js"); 41 | // $httpRequest->get("http://117.121.26.105/favicon.ico"); 42 | 43 | output($httpResponse->headers); 44 | output($httpResponse->raw_body); 45 | 46 | $cookie = $httpResponse->headers['Set-Cookie']; 47 | $raw_body = $httpResponse->raw_body; 48 | // get token 49 | preg_match('', $raw_body, $tt); 50 | 51 | $authenticity_token = $tt[1]; 52 | 53 | // $cookie = str_ireplace('%3D', '=', $cookie); 54 | // $cookie = str_ireplace('; path=/; HttpOnly', '', $cookie); 55 | $httpRequest->cookie($cookie); 56 | $body = [ 57 | // 'utf8' => '✓', 58 | // 'authenticity_token' => $authenticity_token, 59 | 'login' => 'admin', 60 | 'password' => 'lottery', 61 | // '_method' => 'post', 62 | ]; 63 | // 64 | $httpResponse = $httpRequest->post("http://117.121.26.105/admin/login",[ 65 | 'Content-Type' => 'application/x-www-form-urlencoded', 66 | 'Host' => '117.121.26.105', 67 | 'Pragmal' => 'no-cache', 68 | 'Upgrade-Insecure-Requests' => 1, 69 | 'Cache-Control' => 'no-cache', 70 | 'Origin' => 'http://117.121.26.105', 71 | 'X-CSRF-Token' => $authenticity_token, 72 | 'User-Agent' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0', 73 | 'Connection' => 'keep-alive', 74 | 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 75 | 76 | 'X-Requested-With' => 'XMLHttpRequest', 77 | 'Referer' => 'http://117.121.26.105/admin/login', 78 | 'Accept-Encoding' => 'gzip, deflate', 79 | 'Accept-Language' => 'zh-CN,zh;q=0.8', 80 | // 'Cookie' => $cookie, 81 | 82 | ], $body); 83 | 84 | 85 | output($httpResponse->headers); 86 | output($httpResponse->raw_body); 87 | 88 | } 89 | catch(GlobalException $ex) 90 | { 91 | output( $ex->getMessage() , __METHOD__); 92 | } 93 | 94 | } 95 | 96 | private function testAuth() 97 | { 98 | output(__METHOD__); 99 | try 100 | { 101 | $httpRequest = HttpRequest::create(); 102 | $httpRequest->auth("foo", "bar"); 103 | $httpRequest->timeout(10); 104 | 105 | $httpResponse = $httpRequest->get("https://api.stripe.com"); 106 | // output($httpResponse); 107 | $watcher = HttpWatcher::create($httpResponse); 108 | 109 | $watcher->assertStatusCode(401) 110 | ->assertHeadersExist(array( 111 | "Www-Authenticate" 112 | )) 113 | ->assertHeaders(array( 114 | "Server" => "nginx", 115 | "Cache-Control" => "no-cache, no-store" 116 | )) 117 | ->assertBody('IS_VALID_JSON'); 118 | } 119 | catch(GlobalException $ex) 120 | { 121 | output( $ex->getMessage() , __METHOD__); 122 | } 123 | } 124 | 125 | private function testGet() 126 | { 127 | output(__METHOD__); 128 | 129 | $httpRequest = HttpRequest::create(); 130 | 131 | try 132 | { 133 | output(__LINE__); 134 | $httpResponse = $httpRequest->get("https://freegeoip.net/csv/8.8.8.8"); 135 | $watcher = HttpWatcher::create( $httpResponse ); 136 | 137 | $watcher 138 | ->assertStatusCode(200) 139 | ->assertHeaders(array( 140 | "Access-Control-Allow-Origin" => "*" 141 | )) 142 | ->assertBody('"8.8.8.8","US","United States","","","","","38.0000","-97.0000","",""'); 143 | 144 | } 145 | catch(GlobalException $ex) 146 | { 147 | output( $ex->getMessage() , __METHOD__ . ':freegeoip.net'); 148 | } 149 | 150 | try 151 | { 152 | output(__LINE__); 153 | $httpResponse = $httpRequest->get("https://www.google.com"); 154 | $watcher->setResponse( $httpResponse ) 155 | ->assertStatusCode(200) 156 | ->assertHeadersExist(array( 157 | "X-Frame-Options" 158 | )) 159 | ->assertHeaders(array( 160 | "Server" => "gws", 161 | "Transfer-Encoding" => "chunked" 162 | )) 163 | ->assertBody("/.*/", true); 164 | } 165 | catch(GlobalException $ex) 166 | { 167 | output( $ex->getMessage() , __METHOD__ . ':google.com'); 168 | } 169 | 170 | try 171 | { 172 | output(__LINE__); 173 | $httpResponse = $httpRequest->get("https://api.github.com"); 174 | $watcher->setResponse( $httpResponse ) 175 | ->assertStatusCode(200) 176 | ->assertHeadersExist(array( 177 | "X-GitHub-Request-Id", 178 | "ETag" 179 | )) 180 | ->assertHeaders(array( 181 | "Server" => "GitHub.com" 182 | )) 183 | ->assertBody('IS_VALID_JSON') 184 | ->assertTotalTimeLessThan(2); 185 | } 186 | catch(GlobalException $ex) 187 | { 188 | output( $ex->getMessage() , __METHOD__ . ':github.com'); 189 | } 190 | 191 | // 测试代理功能,本地使用Lantern (http://127.0.0.1:8787) 192 | try 193 | { 194 | output(__LINE__); 195 | $httpRequest->proxy('http://127.0.0.1', 8787); 196 | 197 | $httpResponse = $httpRequest->get("https://www.google.com"); 198 | $watcher->setResponse( $httpResponse ) 199 | ->assertStatusCode(200) 200 | ->assertHeadersExist(array( 201 | "X-Frame-Options" 202 | )) 203 | ->assertHeaders(array( 204 | "Server" => "gws", 205 | "Transfer-Encoding" => "chunked" 206 | )) 207 | ->assertBody("/.*/", true); 208 | } 209 | catch(GlobalException $ex) 210 | { 211 | output( $ex->getMessage() , __METHOD__ . ':google.com'); 212 | } 213 | 214 | } 215 | 216 | private function testPost() 217 | { 218 | output(__METHOD__); 219 | try 220 | { 221 | $httpRequest = HttpRequest::create(); 222 | $httpRequest->timeout(30); 223 | // 测试的域名在墙外 本地使用Lantern (http://127.0.0.1:8787) 224 | $httpRequest->proxy('http://127.0.0.1', 8787); 225 | 226 | $httpResponse = $httpRequest->post("https://api.balancedpayments.com/api_keys"); 227 | // output($httpResponse); 228 | $watcher = HttpWatcher::create($httpResponse); 229 | 230 | $watcher 231 | ->assertStatusCode(201) 232 | ->assertHeadersExist(array( 233 | "X-Balanced-Host", 234 | "X-Balanced-Guru" 235 | )) 236 | ->assertHeaders(array( 237 | "Content-Type" => "application/json" 238 | )) 239 | ->assertBody('IS_VALID_JSON'); 240 | } 241 | catch(GlobalException $ex) 242 | { 243 | output( $ex->getMessage() , __METHOD__); 244 | } 245 | } 246 | 247 | private function testJson() 248 | { 249 | output(__METHOD__); 250 | try 251 | { 252 | $expected = new stdClass(); 253 | $expected->ip = "8.8.8.8"; 254 | $expected->country_code = "US"; 255 | $expected->country_name = "United States"; 256 | $expected->region_code = "CA"; 257 | $expected->region_name = "California"; 258 | $expected->city = "Mountain View"; 259 | $expected->zip_code = "94040"; 260 | $expected->time_zone = "America/Los_Angeles"; 261 | $expected->latitude = 37.386000000000003; 262 | $expected->longitude = -122.0838; 263 | $expected->metro_code = 807; 264 | 265 | $httpRequest = HttpRequest::create(); 266 | 267 | $httpResponse = $httpRequest->get("https://freegeoip.net/json/8.8.8.8"); 268 | // output($httpResponse); 269 | $watcher = HttpWatcher::create($httpResponse); 270 | 271 | $watcher 272 | ->assertStatusCode(200) 273 | ->assertHeadersExist(array( 274 | "Date" 275 | )) 276 | ->assertHeaders(array( 277 | // "Access-Control-Allow-Origin" => "*" 278 | )) 279 | ->assertBodyJson($expected, true); 280 | } 281 | catch(GlobalException $ex) 282 | { 283 | output( $ex->getMessage() , __METHOD__); 284 | } 285 | } 286 | 287 | private function testJsonFile() 288 | { 289 | output(__METHOD__); 290 | try 291 | { 292 | $httpRequest = HttpRequest::create(); 293 | 294 | $httpResponse = $httpRequest->get("https://freegeoip.net/json/8.8.8.8"); 295 | // output($httpResponse); 296 | $watcher = HttpWatcher::create($httpResponse); 297 | 298 | $watcher 299 | ->assertStatusCode(200) 300 | ->assertHeadersExist(array( 301 | "Content-Length" 302 | )) 303 | ->assertHeaders(array( 304 | // "Access-Control-Allow-Origin" => "*" 305 | )) 306 | ->assertBodyJsonFile(__DIR__ . "/_json/freegeoip.net.json", true); 307 | } 308 | catch(GlobalException $ex) 309 | { 310 | output( $ex->getMessage() , __METHOD__); 311 | } 312 | } 313 | 314 | } -------------------------------------------------------------------------------- /src/Ws/Http/ARequest.php: -------------------------------------------------------------------------------- 1 | jsonOpts($assoc, $depth, $options); 17 | } 18 | 19 | /** 20 | * Verify SSL peer 21 | * 22 | * @param bool $enabled enable SSL verification, by default is true 23 | * @return bool 24 | */ 25 | public static function verifyPeer($enabled) 26 | { 27 | return Request::create('default')->verifyPeer($enabled); 28 | } 29 | 30 | /** 31 | * Verify SSL host 32 | * 33 | * @param bool $enabled enable SSL host verification, by default is true 34 | * @return bool 35 | */ 36 | public static function verifyHost($enabled) 37 | { 38 | return Request::create('default')->verifyPeer($enabled); 39 | } 40 | 41 | /** 42 | * Verify SSL File 43 | * 44 | * @param string $file SSL verification file 45 | * @return string 46 | */ 47 | public static function verifyFile($file) 48 | { 49 | return Request::create('default')->verifyFile($file); 50 | } 51 | 52 | /** 53 | * Get Verify SSL File 54 | * 55 | * @return string 56 | */ 57 | public static function getVerifyFile() 58 | { 59 | return Request::create('default')->getVerifyFile(); 60 | } 61 | 62 | /** 63 | * Set a timeout 64 | * 65 | * @param integer $seconds timeout value in seconds 66 | * @return integer 67 | */ 68 | public static function timeout($seconds) 69 | { 70 | return Request::create('default')->timeout($seconds); 71 | } 72 | 73 | /** 74 | * Set a timeout 75 | * 76 | * @param integer $ms timeout value in millisecond 77 | * @return integer 78 | */ 79 | public static function timeoutMs($ms) 80 | { 81 | return Request::create('default')->timeoutMs($ms); 82 | } 83 | 84 | /** 85 | * Set default headers to send on every request 86 | * 87 | * @param array $headers headers array 88 | * @return array 89 | */ 90 | public static function defaultHeaders($headers) 91 | { 92 | return Request::create('default')->defaultHeaders($headers); 93 | } 94 | 95 | /** 96 | * Set a new default header to send on every request 97 | * 98 | * @param string $name header name 99 | * @param string $value header value 100 | * @return string 101 | */ 102 | public static function defaultHeader($name, $value) 103 | { 104 | return Request::create('default')->defaultHeader($name, $value); 105 | } 106 | 107 | /** 108 | * Clear all the default headers 109 | */ 110 | public static function clearDefaultHeaders() 111 | { 112 | return Request::create('default')->clearDefaultHeaders(); 113 | } 114 | 115 | /** 116 | * Set curl options to send on every request 117 | * 118 | * @param array $options options array 119 | * @return array 120 | */ 121 | public static function curlOpts($options) 122 | { 123 | return Request::create('default')->curlOpts($options); 124 | } 125 | 126 | /** 127 | * Set a new default header to send on every request 128 | * 129 | * @param string $name header name 130 | * @param string $value header value 131 | * @return string 132 | */ 133 | public static function curlOpt($name, $value) 134 | { 135 | return Request::create('default')->curlOpt($name, $value); 136 | } 137 | 138 | /** 139 | * Clear all the default headers 140 | */ 141 | public static function clearCurlOpts() 142 | { 143 | return Request::create('default')->clearCurlOpts(); 144 | } 145 | 146 | /** 147 | * Set a cookie string for enabling cookie handling 148 | * 149 | * @param string $cookie 150 | */ 151 | public static function cookie($cookie) 152 | { 153 | Request::create('default')->cookie($cookie); 154 | } 155 | 156 | /** 157 | * Set a cookie file path for enabling cookie handling 158 | * 159 | * $cookieFile must be a correct path with write permission 160 | * 161 | * @param string $cookieFile - path to file for saving cookie 162 | */ 163 | public static function cookieFile($cookieFile) 164 | { 165 | Request::create('default')->cookieFile($cookieFile); 166 | } 167 | 168 | /** 169 | * Set authentication method to use 170 | * 171 | * @param string $username authentication username 172 | * @param string $password authentication password 173 | * @param integer $method authentication method 174 | */ 175 | public static function auth($username = '', $password = '', $method = CURLAUTH_BASIC) 176 | { 177 | Request::create('default')->auth($username, $password, $method); 178 | } 179 | 180 | /** 181 | * Set proxy to use 182 | * 183 | * @param string $address proxy address 184 | * @param integer $port proxy port 185 | * @param integer $type (Available options for this are CURLPROXY_HTTP, CURLPROXY_HTTP_1_0 CURLPROXY_SOCKS4, CURLPROXY_SOCKS5, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5_HOSTNAME) 186 | * @param bool $tunnel enable/disable tunneling 187 | */ 188 | public static function proxy($address, $port = 1080, $type = CURLPROXY_HTTP, $tunnel = false) 189 | { 190 | Request::create('default')->proxy($address, $port, $type, $tunnel); 191 | } 192 | 193 | /** 194 | * Set proxy authentication method to use 195 | * 196 | * @param string $username authentication username 197 | * @param string $password authentication password 198 | * @param integer $method authentication method 199 | */ 200 | public static function proxyAuth($username = '', $password = '', $method = CURLAUTH_BASIC) 201 | { 202 | Request::create('default')->proxyAuth($username, $password, $method); 203 | } 204 | 205 | /** 206 | * Send a GET request to a URL 207 | * 208 | * @param string $url URL to send the GET request to 209 | * @param array $headers additional headers to send 210 | * @param mixed $parameters parameters to send in the querystring 211 | * @return \Ws\Http\Response 212 | */ 213 | public static function get($url, $headers = [], $parameters = null) 214 | { 215 | return Request::create('default')->get($url, $headers, $parameters); 216 | } 217 | 218 | /** 219 | * Send a HEAD request to a URL 220 | * @param string $url URL to send the HEAD request to 221 | * @param array $headers additional headers to send 222 | * @param mixed $parameters parameters to send in the querystring 223 | * @return \Ws\Http\Response 224 | */ 225 | public static function head($url, $headers = [], $parameters = null) 226 | { 227 | return Request::create('default')->head($url, $headers, $parameters); 228 | } 229 | 230 | /** 231 | * Send a OPTIONS request to a URL 232 | * @param string $url URL to send the OPTIONS request to 233 | * @param array $headers additional headers to send 234 | * @param mixed $parameters parameters to send in the querystring 235 | * @return \Ws\Http\Response 236 | */ 237 | public static function options($url, $headers = [], $parameters = null) 238 | { 239 | return Request::create('default')->options($url, $headers, $parameters); 240 | } 241 | 242 | /** 243 | * Send a CONNECT request to a URL 244 | * @param string $url URL to send the CONNECT request to 245 | * @param array $headers additional headers to send 246 | * @param mixed $parameters parameters to send in the querystring 247 | * @return \Ws\Http\Response 248 | */ 249 | public static function connect($url, $headers = [], $parameters = null) 250 | { 251 | return Request::create('default')->connect($url, $headers, $parameters); 252 | } 253 | 254 | /** 255 | * Send POST request to a URL 256 | * @param string $url URL to send the POST request to 257 | * @param array $headers additional headers to send 258 | * @param mixed $body POST body data 259 | * @return \Ws\Http\Response 260 | */ 261 | public static function post($url, $headers = [], $body = null) 262 | { 263 | return Request::create('default')->post($url, $headers, $body); 264 | } 265 | 266 | /** 267 | * Send DELETE request to a URL 268 | * @param string $url URL to send the DELETE request to 269 | * @param array $headers additional headers to send 270 | * @param mixed $body DELETE body data 271 | * @return \Ws\Http\Response 272 | */ 273 | public static function delete($url, $headers = [], $body = null) 274 | { 275 | return Request::create('default')->delete($url, $headers, $body); 276 | } 277 | 278 | /** 279 | * Send PUT request to a URL 280 | * @param string $url URL to send the PUT request to 281 | * @param array $headers additional headers to send 282 | * @param mixed $body PUT body data 283 | * @return \Ws\Http\Response 284 | */ 285 | public static function put($url, $headers = [], $body = null) 286 | { 287 | return Request::create('default')->put($url, $headers, $body); 288 | } 289 | 290 | /** 291 | * Send PATCH request to a URL 292 | * @param string $url URL to send the PATCH request to 293 | * @param array $headers additional headers to send 294 | * @param mixed $body PATCH body data 295 | * @return \Ws\Http\Response 296 | */ 297 | public static function patch($url, $headers = [], $body = null) 298 | { 299 | return Request::create('default')->patch($url, $headers, $body); 300 | } 301 | 302 | /** 303 | * Send TRACE request to a URL 304 | * @param string $url URL to send the TRACE request to 305 | * @param array $headers additional headers to send 306 | * @param mixed $body TRACE body data 307 | * @return \Ws\Http\Response 308 | */ 309 | public static function trace($url, $headers = [], $body = null) 310 | { 311 | return Request::create('default')->trace($url, $headers, $body); 312 | } 313 | 314 | } -------------------------------------------------------------------------------- /tests/Ws/Http/_json/a.automated.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "EtSAqRw4Sx5YFD3CYyq", 3 | "name": "夜点娱乐", 4 | "type": 1, 5 | "body": { 6 | "vars": [ 7 | { 8 | "name": "token", 9 | "value": "1" 10 | }, 11 | { 12 | "name": "openid", 13 | "value": "" 14 | }, 15 | { 16 | "name": "userid", 17 | "value": "0" 18 | }, 19 | { 20 | "name": "city", 21 | "value": "440100" 22 | }, 23 | { 24 | "name": "searchKtvName", 25 | "value": "金柜" 26 | }, 27 | { 28 | "name": "bookingKtvId", 29 | "value": "" 30 | }, 31 | { 32 | "name": "bookingRoomtype", 33 | "value": "" 34 | }, 35 | { 36 | "name": "bookingStart", 37 | "value": "" 38 | }, 39 | { 40 | "name": "bookingEnd", 41 | "value": "" 42 | }, 43 | { 44 | "name": "bookingDay", 45 | "value": "" 46 | }, 47 | { 48 | "name": "loginCookie", 49 | "value": "" 50 | } 51 | ], 52 | "requests": [ 53 | { 54 | "id": "1471348763956", 55 | "type": 1, 56 | "name": "微信用户登录token获取", 57 | "url": "http://production-yedian.chinacloudapp.cn/user/oauthlogin", 58 | 59 | "method": "POST", 60 | "timeout": "15 s", 61 | "header": { 62 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 63 | "X-KTV-Application-Platform": "1", 64 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a" 65 | }, 66 | "authorization": {}, 67 | "dataMode": "raw-json", 68 | "data": { 69 | "value": "{\"type\":\"wechat\",\"openid\":\"dddddddokwyOwsZZhu7_3zIQNtfB9r3CKhE\",\"display_name\":\"\\u7275\\u732a\\u7684\\u4ed3\\u9f20\",\"avatar_url\":\"http://wx.qlogo.cn/mmopen/JQpUg1oh5aelkvzXvcicURj0n4WzKJqBPr4RPjG6L9wyrKhGGKV1iaB4NJLod8Jn5y8ZCTCqIPED6l3av2iaEX3C88s1TaRLsA6/0\"}" 70 | }, 71 | "postProcessors": [ 72 | { 73 | "type": "setVar", 74 | "setVar": [ 75 | { 76 | "match": "json", 77 | "attr": "token", 78 | "var": "token" 79 | }, 80 | { 81 | "match": "header", 82 | "attr": "Set-Cookie", 83 | "var": "loginCookie" 84 | } 85 | ] 86 | }, 87 | { 88 | "type": "asserts", 89 | "asserts": [] 90 | } 91 | ] 92 | }, 93 | { 94 | "id": "1471348763962", 95 | "type": 1, 96 | "name": "根据token获取用户信息", 97 | "url": "http://production-yedian.chinacloudapp.cn/user/info", 98 | 99 | "method": "GET", 100 | "timeout": "15s", 101 | "header": { 102 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 103 | "X-KTV-Application-Platform": "1", 104 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a", 105 | "X-KTV-User-Token": "$token" 106 | }, 107 | "authorization": {}, 108 | "dataMode": "params", 109 | "data": [], 110 | "postProcessors": [ 111 | { 112 | "type": "setVar", 113 | "setVar": [ 114 | { 115 | "match": "json", 116 | "attr": "openid", 117 | "var": "openid" 118 | }, 119 | { 120 | "match": "json", 121 | "attr": "userid", 122 | "var": "userid" 123 | } 124 | ] 125 | }, 126 | { 127 | "type": "asserts", 128 | "asserts": [ 129 | { 130 | "match": "status", 131 | "attr": "", 132 | "method": "eq", 133 | "expect": "200" 134 | }, 135 | { 136 | "match": "json", 137 | "attr": "result", 138 | "method": "eq", 139 | "expect": "0" 140 | } 141 | ] 142 | } 143 | ] 144 | }, 145 | { 146 | "id": "1471354174497", 147 | "type": 1, 148 | "name": "按名称搜索KTV", 149 | "url": "http://production-yedian.chinacloudapp.cn/booking/xktvsearchlist", 150 | 151 | "method": "GET", 152 | "timeout": "15s", 153 | "header": { 154 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 155 | "X-KTV-Application-Platform": "1", 156 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a", 157 | "X-KTV-User-Token": "$token" 158 | }, 159 | "authorization": {}, 160 | "dataMode": "params", 161 | "data": [ 162 | {"key": "offset", "value": "0", "type": "text"}, 163 | {"key": "city", "value": "${city}", "type": "text"}, 164 | {"key": "limit", "value": "27", "type": "text"}, 165 | {"key": "name", "value": "${searchKtvName}", "type": "text"} 166 | ], 167 | "postProcessors": [ 168 | { 169 | "type": "setVar", 170 | "setVar": [ 171 | { 172 | "match": "json", 173 | "attr": "list[0].xktvid", 174 | "var": "bookingKtvId" 175 | } 176 | ] 177 | }, 178 | { 179 | "type": "asserts", 180 | "asserts": [ 181 | { 182 | "match": "status", 183 | "attr": "", 184 | "method": "eq", 185 | "expect": "200" 186 | }, 187 | { 188 | "match": "json", 189 | "attr": "result", 190 | "method": "eq", 191 | "expect": "0" 192 | }, 193 | { 194 | "match": "json", 195 | "attr": "total", 196 | "method": "gt", 197 | "expect": "0" 198 | } 199 | ] 200 | } 201 | ] 202 | }, 203 | { 204 | "id": "1471356911011", 205 | "type": 1, 206 | "name": "选择具体门店", 207 | "url": "http://production-yedian.chinacloudapp.cn/booking/xktv", 208 | 209 | "method": "GET", 210 | "timeout": "15s", 211 | "header": { 212 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 213 | "X-KTV-Application-Platform": "1", 214 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a", 215 | "X-KTV-User-Token": "${token}" 216 | }, 217 | "authorization": {}, 218 | "dataMode": "params", 219 | "data": [ 220 | {"key": "xktvid", "value": "${bookingKtvId}", "type": "text"} 221 | ], 222 | "postProcessors": [ 223 | { 224 | "type": "setVar", 225 | "setVar": [ 226 | { 227 | "match": "json", 228 | "attr": "data.taocaninfo.roomtype[0].id", 229 | "var": "bookingRoomtype" 230 | }, 231 | { 232 | "match": "json", 233 | "attr": "data.taocaninfo.course[0].starttime.time", 234 | "var": "bookingStart" 235 | }, 236 | { 237 | "match": "json", 238 | "attr": "data.taocaninfo.course[0].endtime.time", 239 | "var": "bookingEnd" 240 | }, 241 | { 242 | "match": "json", 243 | "attr": "data.taocaninfo.days[0]", 244 | "var": "bookingDay" 245 | } 246 | ] 247 | }, 248 | { 249 | "type": "asserts", 250 | "asserts": [ 251 | { 252 | "match": "status", 253 | "attr": "", 254 | "method": "eq", 255 | "expect": "200" 256 | }, 257 | { 258 | "match": "json", 259 | "attr": "result", 260 | "method": "eq", 261 | "expect": "0" 262 | }, 263 | { 264 | "match": "json", 265 | "attr": "data", 266 | "method": "not_null", 267 | "expect": "" 268 | }, 269 | { 270 | "match": "json", 271 | "attr": "data.xktvid", 272 | "method": "eq", 273 | "expect": "bookingKtvId" 274 | }, 275 | { 276 | "match": "json", 277 | "attr": "data.taocaninfo", 278 | "method": "not_null", 279 | "expect": "" 280 | }, 281 | { 282 | "match": "json", 283 | "attr": "data.taocaninfo.roomtype", 284 | "method": "not_null", 285 | "expect": "" 286 | } 287 | ] 288 | } 289 | ] 290 | }, 291 | { 292 | "id": "1471358836839", 293 | "type": 1, 294 | "name": "提交订单", 295 | "url": "http://production-yedian.chinacloudapp.cn/booking/submitorder_new", 296 | 297 | "method": "POST", 298 | "timeout": "15s", 299 | "header": { 300 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 301 | "X-KTV-Application-Platform": "1", 302 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a", 303 | "cookie": "$loginCookie", 304 | "X-KTV-User-Token": "${token}" 305 | }, 306 | "authorization": {}, 307 | "dataMode": "raw-json", 308 | "data": { 309 | "value": "{\"ktvid\":\"${bookingKtvId}\",\"roomtype\":\"${bookingRoomtype}\",\"starttime\":\"${bookingDay} ${bookingStart}:00\",\"endtime\":\"${bookingDay} ${bookingEnd}:00\",\"couponid\":0,\"taocantype\":1,\"taocanid\":\"\",\"onlinepay\":0}" 310 | }, 311 | "processors": [ 312 | { 313 | "type": "setVar", 314 | "setVar": [] 315 | }, 316 | { 317 | "type": "asserts", 318 | "asserts": [] 319 | } 320 | ] 321 | } 322 | ] 323 | } 324 | } -------------------------------------------------------------------------------- /src/Ws/Http/Automated/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "script_id": "EtSAqRw4Sx5YFD3CYyq", 3 | "script_name": "夜点娱乐", 4 | "trans": { 5 | "parameters": [ 6 | { 7 | "name": "$token", 8 | "value": "1" 9 | }, 10 | { 11 | "name": "$openid", 12 | "value": "''" 13 | }, 14 | { 15 | "name": "$userid", 16 | "value": "0" 17 | }, 18 | { 19 | "name": "$city", 20 | "value": "440100" 21 | }, 22 | { 23 | "name": "$searchKtvName", 24 | "value": "金柜" 25 | }, 26 | { 27 | "name": "$bookingKtvId", 28 | "value": "''" 29 | }, 30 | { 31 | "name": "$bookingRoomtype", 32 | "value": "''" 33 | }, 34 | { 35 | "name": "$bookingStart", 36 | "value": "''" 37 | }, 38 | { 39 | "name": "$bookingEnd", 40 | "value": "''" 41 | }, 42 | { 43 | "name": "$bookingDay", 44 | "value": "''" 45 | }, 46 | { 47 | "name": "$loginCookie", 48 | "value": "''" 49 | } 50 | ], 51 | "steps": [ 52 | { 53 | "actionId": "1471348763956", 54 | "type": 1, 55 | "actionName": "微信用户登录token获取", 56 | "url": "http://letsktv.chinacloudapp.cn/user/oauthlogin", 57 | "protocol": "http", 58 | "method": "POST", 59 | "header": { 60 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 61 | "X-KTV-Application-Platform": "1", 62 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a" 63 | }, 64 | "authorization": {}, 65 | "parameters": {}, 66 | "formdata": { 67 | "type": "raw-text", 68 | "value": "{\"type\":\"wechat\",\"openid\":\"dddddddokwyOwsZZhu7_3zIQNtfB9r3CKhE\",\"display_name\":\"\\u7275\\u732a\\u7684\\u4ed3\\u9f20\",\"avatar_url\":\"http://wx.qlogo.cn/mmopen/JQpUg1oh5aelkvzXvcicURj0n4WzKJqBPr4RPjG6L9wyrKhGGKV1iaB4NJLod8Jn5y8ZCTCqIPED6l3av2iaEX3C88s1TaRLsA6/0\"}" 69 | }, 70 | "postProcessors": [ 71 | { 72 | "type": "propertyExtractor", 73 | "propertyExtractor": [ 74 | { 75 | "matchBody": "json", 76 | "propertyName": "token", 77 | "goalProperty": "$token" 78 | }, 79 | { 80 | "matchBody": "header", 81 | "propertyName": "Set-Cookie", 82 | "goalProperty": "$loginCookie" 83 | } 84 | ] 85 | }, 86 | { 87 | "type": "assertions", 88 | "assertions": [] 89 | } 90 | ] 91 | }, 92 | { 93 | "actionId": "1471348763962", 94 | "type": 1, 95 | "actionName": "根据token获取用户信息", 96 | "url": "http://letsktv.chinacloudapp.cn/user/info", 97 | "protocol": "http", 98 | "method": "GET", 99 | "header": { 100 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 101 | "X-KTV-Application-Platform": "1", 102 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a", 103 | "X-KTV-User-Token": "$token" 104 | }, 105 | "authorization": {}, 106 | "parameters": {}, 107 | "formdata": {}, 108 | "postProcessors": [ 109 | { 110 | "type": "propertyExtractor", 111 | "propertyExtractor": [ 112 | { 113 | "matchBody": "json", 114 | "propertyName": "openid", 115 | "goalProperty": "$openid" 116 | }, 117 | { 118 | "matchBody": "json", 119 | "propertyName": "userid", 120 | "goalProperty": "$userid" 121 | } 122 | ] 123 | }, 124 | { 125 | "type": "assertions", 126 | "assertions": [ 127 | { 128 | "matchBody": "status", 129 | "propertyName": "", 130 | "compareMethod": "eq", 131 | "expectedVal": "200" 132 | }, 133 | { 134 | "matchBody": "json", 135 | "propertyName": "result", 136 | "compareMethod": "eq", 137 | "expectedVal": "0" 138 | } 139 | ] 140 | } 141 | ] 142 | }, 143 | { 144 | "actionId": "1471354174497", 145 | "type": 1, 146 | "actionName": "按名称搜索KTV", 147 | "url": "http://letsktv.chinacloudapp.cn/booking/xktvsearchlist", 148 | "protocol": "http", 149 | "method": "GET", 150 | "header": { 151 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 152 | "X-KTV-Application-Platform": "1", 153 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a", 154 | "X-KTV-User-Token": "$token" 155 | }, 156 | "authorization": {}, 157 | "parameters": { 158 | "offset": "0", 159 | "city": "$city", 160 | "limit": "27", 161 | "name": "$searchKtvName" 162 | }, 163 | "formdata": {}, 164 | "postProcessors": [ 165 | { 166 | "type": "propertyExtractor", 167 | "propertyExtractor": [ 168 | { 169 | "matchBody": "json", 170 | "propertyName": "list[0].xktvid", 171 | "goalProperty": "$bookingKtvId" 172 | } 173 | ] 174 | }, 175 | { 176 | "type": "assertions", 177 | "assertions": [ 178 | { 179 | "matchBody": "status", 180 | "propertyName": "", 181 | "compareMethod": "eq", 182 | "expectedVal": "200" 183 | }, 184 | { 185 | "matchBody": "json", 186 | "propertyName": "result", 187 | "compareMethod": "eq", 188 | "expectedVal": "0" 189 | }, 190 | { 191 | "matchBody": "json", 192 | "propertyName": "total", 193 | "compareMethod": "gt", 194 | "expectedVal": "0" 195 | } 196 | ] 197 | } 198 | ] 199 | }, 200 | { 201 | "actionId": "1471356911011", 202 | "type": 1, 203 | "actionName": "选择具体门店", 204 | "url": "http://letsktv.chinacloudapp.cn/booking/xktv", 205 | "protocol": "http", 206 | "method": "GET", 207 | "header": { 208 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 209 | "X-KTV-Application-Platform": "1", 210 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a", 211 | "X-KTV-User-Token": "$token" 212 | }, 213 | "authorization": {}, 214 | "parameters": { 215 | "xktvid": "$bookingKtvId" 216 | }, 217 | "formdata": {}, 218 | "postProcessors": [ 219 | { 220 | "type": "propertyExtractor", 221 | "propertyExtractor": [ 222 | { 223 | "matchBody": "json", 224 | "propertyName": "data.taocaninfo.roomtype[0].id", 225 | "goalProperty": "$bookingRoomtype" 226 | }, 227 | { 228 | "matchBody": "json", 229 | "propertyName": "data.taocaninfo.course[0].starttime.time", 230 | "goalProperty": "$bookingStart" 231 | }, 232 | { 233 | "matchBody": "json", 234 | "propertyName": "data.taocaninfo.course[0].endtime.time", 235 | "goalProperty": "$bookingEnd" 236 | }, 237 | { 238 | "matchBody": "json", 239 | "propertyName": "data.taocaninfo.days[0]", 240 | "goalProperty": "$bookingDay" 241 | } 242 | ] 243 | }, 244 | { 245 | "type": "assertions", 246 | "assertions": [ 247 | { 248 | "matchBody": "status", 249 | "propertyName": "", 250 | "compareMethod": "eq", 251 | "expectedVal": "200" 252 | }, 253 | { 254 | "matchBody": "json", 255 | "propertyName": "result", 256 | "compareMethod": "eq", 257 | "expectedVal": "0" 258 | }, 259 | { 260 | "matchBody": "json", 261 | "propertyName": "data", 262 | "compareMethod": "not_null", 263 | "expectedVal": "" 264 | }, 265 | { 266 | "matchBody": "json", 267 | "propertyName": "data.xktvid", 268 | "compareMethod": "eq", 269 | "expectedVal": "$bookingKtvId" 270 | }, 271 | { 272 | "matchBody": "json", 273 | "propertyName": "data.taocaninfo", 274 | "compareMethod": "not_null", 275 | "expectedVal": "" 276 | }, 277 | { 278 | "matchBody": "json", 279 | "propertyName": "data.taocaninfo.roomtype", 280 | "compareMethod": "not_null", 281 | "expectedVal": "" 282 | } 283 | ] 284 | } 285 | ] 286 | }, 287 | { 288 | "actionId": "1471358836839", 289 | "type": 1, 290 | "actionName": "提交订单", 291 | "url": "http://letsktv.chinacloudapp.cn/booking/submitorder_new", 292 | "protocol": "http", 293 | "method": "POST", 294 | "header": { 295 | "X-KTV-Vendor-Name": "1d55af1659424cf94d869e2580a11bf8", 296 | "X-KTV-Application-Platform": "1", 297 | "X-KTV-Application-Name": "eec607d1f47c18c9160634fd0954da1a", 298 | "cookie": "$loginCookie", 299 | "X-KTV-User-Token": "$token" 300 | }, 301 | "authorization": {}, 302 | "parameters": {}, 303 | "formdata": { 304 | "type": "raw-json", 305 | "value": "{\"ktvid\":\"$bookingKtvId\",\"roomtype\":\"$bookingRoomtype\",\"starttime\":\"$bookingDay $bookingStart:00\",\"endtime\":\"$bookingDay $bookingEnd:00\",\"couponid\":0,\"taocantype\":1,\"taocanid\":\"\",\"onlinepay\":0}" 306 | }, 307 | "postProcessors": [ 308 | { 309 | "type": "propertyExtractor", 310 | "propertyExtractor": [] 311 | }, 312 | { 313 | "type": "assertions", 314 | "assertions": [] 315 | } 316 | ] 317 | } 318 | ] 319 | } 320 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ws-http 2 | ======== 3 | 4 | #### 简单轻量的HTTP 客户端工具库(An Simplified, lightweight HTTP client library) 5 | 6 | ![ws-http](https://raw.github.com/toohamster/ws-http/master/logo.png) 7 | 8 | #### 可用于 HTTP API 测试,支持 ssl,basic auth,代理,自定义请求头,以及常用HTTP 请求方法.(An HTTP API testing framework, written in PHP using curl. Supports ssl, basic auth, passing custom request headers, and most HTTP request methods. 9 | 10 | 11 | ## 需求(Requirements) 12 | 13 | - [cURL](http://php.net/manual/en/book.curl.php) 14 | - PHP 5.4+ 15 | 16 | ## 安装(Installation) 17 | 18 | ### 使用 (Using) [Composer](https://getcomposer.org) 19 | 20 | 在`composer.json`文件中新增如下行(To install ws-http with Composer, just add the following to your `composer.json` file): 21 | 22 | ```json 23 | { 24 | "require": { 25 | "toohamster/ws-http": "*" 26 | } 27 | } 28 | ``` 29 | 30 | 或者手动运行命令(or by running the following command): 31 | 32 | ```shell 33 | php composer require toohamster/ws-http 34 | ``` 35 | 36 | ## Http Request 使用(Http Request Usage) 37 | 38 | ### 创建一个请求(Creating a Request) 39 | 40 | ````php 41 | $httpRequest = \Ws\Http\Request::create(); 42 | ```` 43 | 44 | 支持的方法(Support Method) 45 | --- 46 | 47 | ````php 48 | // set config 49 | $httpRequest->jsonOpts($assoc = false, $depth = 512, $options = 0); 50 | $httpRequest->verifyPeer($enabled); 51 | $httpRequest->verifyHost($enabled); 52 | $httpRequest->verifyFile($file); 53 | $httpRequest->getVerifyFile(); 54 | $httpRequest->timeout($seconds); 55 | $httpRequest->defaultHeaders($headers); 56 | $httpRequest->defaultHeader($name, $value); 57 | $httpRequest->clearDefaultHeaders(); 58 | $httpRequest->curlOpts($options); 59 | $httpRequest->curlOpt($name, $value); 60 | $httpRequest->clearCurlOpts(); 61 | $httpRequest->cookie($cookie); 62 | $httpRequest->cookieFile($cookieFile); 63 | $httpRequest->auth($username = '', $password = '', $method = CURLAUTH_BASIC); 64 | $httpRequest->proxy($address, $port = 1080, $type = CURLPROXY_HTTP, $tunnel = false); 65 | $httpRequest->proxyAuth($username = '', $password = '', $method = CURLAUTH_BASIC); 66 | 67 | // http call 68 | $httpRequest->get($url, $headers = [], $parameters = null); 69 | $httpRequest->head($url, $headers = [], $parameters = null); 70 | $httpRequest->options($url, $headers = [], $parameters = null); 71 | $httpRequest->connect($url, $headers = [], $parameters = null); 72 | $httpRequest->post($url, $headers = [], $body = null); 73 | $httpRequest->delete($url, $headers = [], $body = null); 74 | $httpRequest->put($url, $headers = [], $body = null); 75 | $httpRequest->patch($url, $headers = [], $body = null); 76 | $httpRequest->trace($url, $headers = [], $body = null); 77 | ```` 78 | 79 | 此处给出一些简单的实例(Let's look at a working example): 80 | 81 | ```php 82 | $headers = array('Accept' => 'application/json'); 83 | $query = array('foo' => 'hello', 'bar' => 'world'); 84 | 85 | $response = $httpRequest->post('http://mockbin.com/request', $headers, $query); 86 | 87 | $response->code; // 请求响应码(HTTP Status code) 88 | $response->curl_info; // curl信息(HTTP Curl info) 89 | $response->headers; // 响应头(Headers) 90 | $response->body; // 处理后的响应消息体(Parsed body), 默认为 false 91 | $response->raw_body; // 原始响应消息体(Unparsed body) 92 | ``` 93 | 94 | ### JSON 请求(Requests) *(`application/json`)* 95 | 96 | ```php 97 | $headers = array('Accept' => 'application/json'); 98 | $data = array('name' => 'ahmad', 'company' => 'mashape'); 99 | 100 | $body = Ws\Http\Request\Body::json($data); 101 | 102 | $response = $httpRequest->post('http://mockbin.com/request', $headers, $body); 103 | ``` 104 | 105 | **注意(Notes):** 106 | - `Content-Type` 会自动设置成(headers will be automatically set to) `application/json` 107 | 108 | ### 表单请求(Form Requests) *(`application/x-www-form-urlencoded`)* 109 | 110 | ```php 111 | $headers = array('Accept' => 'application/json'); 112 | $data = array('name' => 'ahmad', 'company' => 'mashape'); 113 | 114 | $body = Ws\Http\Request\Body::form($data); 115 | 116 | $response = $httpRequest->post('http://mockbin.com/request', $headers, $body); 117 | ``` 118 | 119 | **注意(Notes):** 120 | - `Content-Type` 会自动设置成(headers will be automatically set to) `application/x-www-form-urlencoded` 121 | 122 | ### Multipart Requests *(`multipart/form-data`)* 123 | 124 | ```php 125 | $headers = array('Accept' => 'application/json'); 126 | $data = array('name' => 'ahmad', 'company' => 'mashape'); 127 | 128 | $body = Ws\Http\Request\Body::multipart($data); 129 | 130 | $response = $httpRequest->post('http://mockbin.com/request', $headers, $body); 131 | ``` 132 | 133 | **注意(Notes):** 134 | 135 | - `Content-Type` 会自动设置成(headers will be automatically set to) `multipart/form-data`. 136 | 137 | ### 文件上传(Multipart File Upload) 138 | 139 | ```php 140 | $headers = array('Accept' => 'application/json'); 141 | $data = array('name' => 'ahmad', 'company' => 'mashape'); 142 | $files = array('bio' => '/path/to/bio.txt', 'avatar' => '/path/to/avatar.jpg'); 143 | 144 | $body = Ws\Http\Request\Body::multipart($data, $files); 145 | 146 | $response = $httpRequest->post('http://mockbin.com/request', $headers, $body); 147 | ``` 148 | 149 | ```php 150 | $headers = array('Accept' => 'application/json'); 151 | $body = array( 152 | 'name' => 'ahmad', 153 | 'company' => 'mashape' 154 | 'bio' => Ws\Http\Request\Body::file('/path/to/bio.txt', 'text/plain'), 155 | 'avatar' => Ws\Http\Request\Body::file('/path/to/my_avatar.jpg', 'text/plain', 'avatar.jpg') 156 | ); 157 | 158 | $response = $httpRequest->post('http://mockbin.com/request', $headers, $body); 159 | ``` 160 | 161 | ### 自定义消息体(Custom Body) 162 | 163 | 可以使用`Ws\Http\Request\Body`类提供的方法来生成消息体或使用PHP自带的序列化函数来生成消息体(Sending a custom body such rather than using the `Ws\Http\Request\Body` helpers is also possible, for example, using a [`serialize`](http://php.net/manual/en/function.serialize.php) body string with a custom `Content-Type`): 164 | 165 | ```php 166 | $headers = array('Accept' => 'application/json', 'Content-Type' => 'application/x-php-serialized'); 167 | $body = serialize((array('foo' => 'hello', 'bar' => 'world')); 168 | 169 | $response = $httpRequest->post('http://mockbin.com/request', $headers, $body); 170 | ``` 171 | 172 | ### 授权校验(Authentication) 173 | 174 | ```php 175 | $httpRequest->auth($username, $password, $method);// default is CURLAUTH_BASIC 176 | ``` 177 | 178 | **支持的方法(Supported Methods)** 179 | 180 | | Method | Description | 181 | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 182 | | `CURLAUTH_BASIC` | HTTP Basic authentication. | 183 | | `CURLAUTH_DIGEST` | HTTP Digest authentication. as defined in [RFC 2617](http://www.ietf.org/rfc/rfc2617.txt) | 184 | | `CURLAUTH_DIGEST_IE` | HTTP Digest authentication with an IE flavor. *The IE flavor is simply that libcurl will use a special "quirk" that IE is known to have used before version 7 and that some servers require the client to use.* | 185 | | `CURLAUTH_NEGOTIATE` | HTTP Negotiate (SPNEGO) authentication. as defined in [RFC 4559](http://www.ietf.org/rfc/rfc4559.txt) | 186 | | `CURLAUTH_NTLM` | HTTP NTLM authentication. A proprietary protocol invented and used by Microsoft. | 187 | | `CURLAUTH_NTLM_WB` | NTLM delegating to winbind helper. Authentication is performed by a separate binary application. *see [libcurl docs](http://curl.haxx.se/libcurl/c/CURLOPT_HTTPAUTH.html) for more info* | 188 | | `CURLAUTH_ANY` | This is a convenience macro that sets all bits and thus makes libcurl pick any it finds suitable. libcurl will automatically select the one it finds most secure. | 189 | | `CURLAUTH_ANYSAFE` | This is a convenience macro that sets all bits except Basic and thus makes libcurl pick any it finds suitable. libcurl will automatically select the one it finds most secure. | 190 | | `CURLAUTH_ONLY` | This is a meta symbol. OR this value together with a single specific auth value to force libcurl to probe for un-restricted auth and if not, only that single auth algorithm is acceptable. | 191 | 192 | ```php 193 | // custom auth method 194 | $httpRequest->proxyAuth('username', 'password', CURLAUTH_DIGEST); 195 | ``` 196 | 197 | ### Cookies 198 | 199 | ```php 200 | $httpRequest->cookie($cookie) 201 | ``` 202 | 203 | ```php 204 | $httpRequest->cookieFile($cookieFile) 205 | ``` 206 | 207 | `$cookieFile` 参数必须是可读取的文件路径(must be a correct path with write permission). 208 | 209 | ### 请求对象(Request Object) 210 | 211 | ```php 212 | $httpRequest->get($url, $headers = array(), $parameters = null) 213 | $httpRequest->post($url, $headers = array(), $body = null) 214 | $httpRequest->put($url, $headers = array(), $body = null) 215 | $httpRequest->patch($url, $headers = array(), $body = null) 216 | $httpRequest->delete($url, $headers = array(), $body = null) 217 | ``` 218 | 219 | - `url` - 请求地址(Endpoint, address, or uri to be acted upon and requested information from) 220 | - `headers` - 请求头(Request Headers as associative array or object) 221 | - `body` - 请求消息体(Request Body as associative array or object) 222 | 223 | 可以使用标准的HTTP方法,也可以使用自定义的HTTP方法(You can send a request with any [standard](http://www.iana.org/assignments/http-methods/http-methods.xhtml) or custom HTTP Method): 224 | 225 | ```php 226 | $httpRequest->send(Ws\Http\Method::LINK, $url, $headers = array(), $body); 227 | 228 | $httpRequest->send('CHECKOUT', $url, $headers = array(), $body); 229 | ``` 230 | 231 | ### 响应对象(Response Object) 232 | 233 | - `code` - 请求响应码(HTTP Status code) 234 | - `curl_info` - HTTP curl信息(HTTP Curl info) 235 | - `headers` - 响应头(HTTP Response Headers) 236 | - `body` - 处理后的响应消息体(Parsed body) 237 | - `raw_body` - 原始响应消息体(Unparsed body) 238 | 239 | ### 高级设置(Advanced Configuration) 240 | 241 | #### 自定义json_decode选项(Custom JSON Decode Flags) 242 | 243 | ```php 244 | $httpRequest->jsonOpts(true, 512, JSON_NUMERIC_CHECK & JSON_FORCE_OBJECT & JSON_UNESCAPED_SLASHES); 245 | ``` 246 | 247 | #### 超时设置(Timeout) 248 | 249 | ```php 250 | $httpRequest->timeout(5); // 5s timeout 251 | ``` 252 | 253 | #### 代理(Proxy) 254 | 255 | 可以设置代理类型(you can also set the proxy type to be one of) `CURLPROXY_HTTP`, `CURLPROXY_HTTP_1_0`, `CURLPROXY_SOCKS4`, `CURLPROXY_SOCKS5`, `CURLPROXY_SOCKS4A`, and `CURLPROXY_SOCKS5_HOSTNAME`. 256 | 257 | *check the [cURL docs](http://curl.haxx.se/libcurl/c/CURLOPT_PROXYTYPE.html) for more info*. 258 | 259 | ```php 260 | // quick setup with default port: 1080 261 | $httpRequest->proxy('10.10.10.1'); 262 | 263 | // custom port and proxy type 264 | $httpRequest->proxy('10.10.10.1', 8080, CURLPROXY_HTTP); 265 | 266 | // enable tunneling 267 | $httpRequest->proxy('10.10.10.1', 8080, CURLPROXY_HTTP, true); 268 | ``` 269 | 270 | ##### 代理授权验证 (Proxy Authenticaton) 271 | 272 | ```php 273 | // basic auth 274 | $httpRequest->proxyAuth('username', 'password', CURLAUTH_DIGEST); 275 | ``` 276 | 277 | #### 缺省请求头 (Default Request Headers) 278 | 279 | ```php 280 | $httpRequest->defaultHeader('Header1', 'Value1'); 281 | $httpRequest->defaultHeader('Header2', 'Value2'); 282 | ``` 283 | 284 | 批量配置(You can set default headers in bulk by passing an array): 285 | 286 | ```php 287 | $httpRequest->defaultHeaders(array( 288 | 'Header1' => 'Value1', 289 | 'Header2' => 'Value2' 290 | )); 291 | ``` 292 | 293 | 清除配置(You can clear the default headers anytime with): 294 | 295 | ```php 296 | $httpRequest->clearDefaultHeaders(); 297 | ``` 298 | 299 | #### 缺省Curl选项 (Default cURL Options) 300 | 301 | You can set default [cURL options](http://php.net/manual/en/function.curl-setopt.php) that will be sent on every request: 302 | 303 | ```php 304 | $httpRequest->curlOpt(CURLOPT_COOKIE, 'foo=bar'); 305 | ``` 306 | 307 | 批量配置(You can set options bulk by passing an array): 308 | 309 | ```php 310 | $httpRequest->curlOpts(array( 311 | CURLOPT_COOKIE => 'foo=bar' 312 | )); 313 | ``` 314 | 315 | 清除配置(You can clear the default options anytime with): 316 | 317 | ```php 318 | $httpRequest->clearCurlOpts(); 319 | ``` 320 | 321 | #### SSL validation 322 | 323 | ```php 324 | $httpRequest->verifyPeer(false); // Disables SSL cert validation 325 | ``` 326 | 327 | By default is `true`. 328 | 329 | 330 | ## Http Watcher 使用(Http Watcher Usage) 331 | 332 | #### 支持的方法(Support Method) 333 | 334 | ````php 335 | $watcher = \Ws\Http\Watcher::create($httpResponse); 336 | 337 | $watcher->assertStatusCode($assertedStatusCode); 338 | $watcher->assertTotalTimeLessThan($assertedTime); 339 | $watcher->assertHeadersExist(array $assertedHeaders = []); 340 | $watcher->assertHeaders(array $assertedHeaders = []); 341 | $watcher->assertBody($assertedBody, $useRegularExpression = false); 342 | $watcher->assertBodyJson($asserted, $onNotEqualVarExport = false); 343 | $watcher->assertBodyJsonFile($assertedJsonFile, $onNotEqualPrintJson = false); 344 | ```` 345 | 346 | ##### 例子(Examples) 347 | 348 | ````php 349 | $httpRequest = \Ws\Http\Request::create(); 350 | 351 | $httpResponse = $httpRequest->get("https://api.github.com"); 352 | $watcher = \Ws\Http\Watcher::create($httpResponse); 353 | 354 | $watcher 355 | ->assertStatusCode(200) 356 | ->assertHeadersExist(array( 357 | "X-GitHub-Request-Id", 358 | "ETag" 359 | )) 360 | ->assertHeaders(array( 361 | "Server" => "GitHub.com" 362 | )) 363 | ->assertBody('IS_VALID_JSON') 364 | ->assertTotalTimeLessThan(2); 365 | ```` 366 | 367 | ````php 368 | $httpRequest = \Ws\Http\Request::create(); 369 | $httpResponse = $httpRequest->get("https://freegeoip.net/json/8.8.8.8"); 370 | $watcher = \Ws\Http\Watcher::create($httpResponse); 371 | 372 | $watcher 373 | ->assertStatusCode(200) 374 | ->assertHeadersExist(array( 375 | "Content-Length" 376 | )) 377 | ->assertHeaders(array( 378 | "Access-Control-Allow-Origin" => "*" 379 | )) 380 | ->assertBodyJsonFile(dirname(__DIR__) . "/tests/Ws/Http/_json/freegeoip.net.json"); 381 | ```` 382 | 383 | #### 查看所有例子(See the full examples) https://github.com/toohamster/ws-http/blob/master/tests/Ws/Http/ATest.php. 384 | -------------------------------------------------------------------------------- /src/Ws/Http/Request.php: -------------------------------------------------------------------------------- 1 | '', 20 | 'pass' => '', 21 | 'method' => CURLAUTH_BASIC 22 | ]; 23 | 24 | private $proxy = [ 25 | 'port' => false, 26 | 'tunnel' => false, 27 | 'address' => false, 28 | 'type' => CURLPROXY_HTTP, 29 | 'auth' => [ 30 | 'user' => '', 31 | 'pass' => '', 32 | 'method' => CURLAUTH_BASIC 33 | ] 34 | ]; 35 | 36 | private function __construct() 37 | { 38 | $this->verifyFile = __DIR__ . '/_ssl/ca-bundle.crt'; 39 | } 40 | 41 | /** 42 | * Get a Object 43 | * 44 | * @param string $id object identification, default auto build 45 | * @return \Ws\Http\Request 46 | */ 47 | public static function create($id = null) 48 | { 49 | if ( empty($id) ) 50 | { 51 | $id = md5(__METHOD__) . count(self::$instances); 52 | } 53 | if ( empty(self::$instances[$id]) ) 54 | { 55 | self::$instances[$id] = new self(); 56 | } 57 | return self::$instances[$id]; 58 | } 59 | 60 | /** 61 | * Set JSON decode mode 62 | * 63 | * @param bool $assoc When TRUE, returned objects will be converted into associative arrays. 64 | * @param integer $depth User specified recursion depth. 65 | * @param integer $options Bitmask of JSON decode options. Currently only JSON_BIGINT_AS_STRING is supported (default is to cast large integers as floats) 66 | * @return array 67 | */ 68 | public function jsonOpts($assoc = false, $depth = 512, $options = 0) 69 | { 70 | return $this->jsonOpts = [$assoc, $depth, $options]; 71 | } 72 | 73 | /** 74 | * Verify SSL peer 75 | * 76 | * @param bool $enabled enable SSL verification, by default is true 77 | * @return bool 78 | */ 79 | public function verifyPeer($enabled) 80 | { 81 | return $this->verifyPeer = $enabled; 82 | } 83 | 84 | /** 85 | * Verify SSL host 86 | * 87 | * @param bool $enabled enable SSL host verification, by default is true 88 | * @return bool 89 | */ 90 | public function verifyHost($enabled) 91 | { 92 | return $this->verifyHost = $enabled; 93 | } 94 | 95 | /** 96 | * Verify SSL File 97 | * 98 | * @param string $file SSL verification file 99 | * @return string 100 | */ 101 | public function verifyFile($file) 102 | { 103 | return $this->verifyFile = $file; 104 | } 105 | 106 | /** 107 | * Get Verify SSL File 108 | * 109 | * @return string 110 | */ 111 | public function getVerifyFile() 112 | { 113 | return $this->verifyFile; 114 | } 115 | 116 | /** 117 | * Set a timeout 118 | * 119 | * @param integer $seconds timeout value in seconds 120 | * @return integer 121 | */ 122 | public function timeout($seconds) 123 | { 124 | return $this->socketTimeout = $seconds; 125 | } 126 | 127 | /** 128 | * Set a timeout ms 129 | * 130 | * @param integer $ms timeout value in millisecond 131 | * @return integer 132 | */ 133 | public function timeoutMs($ms) 134 | { 135 | return $this->socketTimeoutMs = $ms; 136 | } 137 | 138 | /** 139 | * Set default headers to send on every request 140 | * 141 | * @param array $headers headers array 142 | * @return array 143 | */ 144 | public function defaultHeaders($headers) 145 | { 146 | return $this->defaultHeaders = array_merge($this->defaultHeaders, $headers); 147 | } 148 | 149 | /** 150 | * Set a new default header to send on every request 151 | * 152 | * @param string $name header name 153 | * @param string $value header value 154 | * @return string 155 | */ 156 | public function defaultHeader($name, $value) 157 | { 158 | return $this->defaultHeaders[$name] = $value; 159 | } 160 | 161 | /** 162 | * Clear all the default headers 163 | */ 164 | public function clearDefaultHeaders() 165 | { 166 | return $this->defaultHeaders = []; 167 | } 168 | 169 | /** 170 | * Set curl options to send on every request 171 | * 172 | * @param array $options options array 173 | * @return array 174 | */ 175 | public function curlOpts($options) 176 | { 177 | return $this->mergeCurlOptions($this->curlOpts, $options); 178 | } 179 | 180 | /** 181 | * Set a new default header to send on every request 182 | * 183 | * @param string $name header name 184 | * @param string $value header value 185 | * @return string 186 | */ 187 | public function curlOpt($name, $value) 188 | { 189 | return $this->curlOpts[$name] = $value; 190 | } 191 | 192 | /** 193 | * Clear all the default headers 194 | */ 195 | public function clearCurlOpts() 196 | { 197 | return $this->curlOpts = []; 198 | } 199 | 200 | /** 201 | * Set a cookie string for enabling cookie handling 202 | * 203 | * @param string $cookie 204 | */ 205 | public function cookie($cookie) 206 | { 207 | $this->cookie = $cookie; 208 | } 209 | 210 | /** 211 | * Set a cookie file path for enabling cookie handling 212 | * 213 | * $cookieFile must be a correct path with write permission 214 | * 215 | * @param string $cookieFile - path to file for saving cookie 216 | */ 217 | public function cookieFile($cookieFile) 218 | { 219 | $this->cookieFile = $cookieFile; 220 | } 221 | 222 | /** 223 | * Set authentication method to use 224 | * 225 | * @param string $username authentication username 226 | * @param string $password authentication password 227 | * @param integer $method authentication method 228 | */ 229 | public function auth($username = '', $password = '', $method = CURLAUTH_BASIC) 230 | { 231 | $this->auth['user'] = $username; 232 | $this->auth['pass'] = $password; 233 | $this->auth['method'] = $method; 234 | } 235 | 236 | /** 237 | * Set proxy to use 238 | * 239 | * @param string $address proxy address 240 | * @param integer $port proxy port 241 | * @param integer $type (Available options for this are CURLPROXY_HTTP, CURLPROXY_HTTP_1_0 CURLPROXY_SOCKS4, CURLPROXY_SOCKS5, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5_HOSTNAME) 242 | * @param bool $tunnel enable/disable tunneling 243 | */ 244 | public function proxy($address, $port = 1080, $type = CURLPROXY_HTTP, $tunnel = false) 245 | { 246 | $this->proxy['type'] = $type; 247 | $this->proxy['port'] = $port; 248 | $this->proxy['tunnel'] = $tunnel; 249 | $this->proxy['address'] = $address; 250 | } 251 | 252 | /** 253 | * Set proxy authentication method to use 254 | * 255 | * @param string $username authentication username 256 | * @param string $password authentication password 257 | * @param integer $method authentication method 258 | */ 259 | public function proxyAuth($username = '', $password = '', $method = CURLAUTH_BASIC) 260 | { 261 | $this->proxy['auth']['user'] = $username; 262 | $this->proxy['auth']['pass'] = $password; 263 | $this->proxy['auth']['method'] = $method; 264 | } 265 | 266 | /** 267 | * Send a GET request to a URL 268 | * 269 | * @param string $url URL to send the GET request to 270 | * @param array $headers additional headers to send 271 | * @param mixed $parameters parameters to send in the querystring 272 | * @return \Ws\Http\Response 273 | */ 274 | public function get($url, $headers = [], $parameters = null) 275 | { 276 | return $this->send(Method::GET, $url, $parameters, $headers); 277 | } 278 | 279 | /** 280 | * Send a HEAD request to a URL 281 | * @param string $url URL to send the HEAD request to 282 | * @param array $headers additional headers to send 283 | * @param mixed $parameters parameters to send in the querystring 284 | * @return \Ws\Http\Response 285 | */ 286 | public function head($url, $headers = [], $parameters = null) 287 | { 288 | return $this->send(Method::HEAD, $url, $parameters, $headers); 289 | } 290 | 291 | /** 292 | * Send a OPTIONS request to a URL 293 | * @param string $url URL to send the OPTIONS request to 294 | * @param array $headers additional headers to send 295 | * @param mixed $parameters parameters to send in the querystring 296 | * @return \Ws\Http\Response 297 | */ 298 | public function options($url, $headers = [], $parameters = null) 299 | { 300 | return $this->send(Method::OPTIONS, $url, $parameters, $headers); 301 | } 302 | 303 | /** 304 | * Send a CONNECT request to a URL 305 | * @param string $url URL to send the CONNECT request to 306 | * @param array $headers additional headers to send 307 | * @param mixed $parameters parameters to send in the querystring 308 | * @return \Ws\Http\Response 309 | */ 310 | public function connect($url, $headers = [], $parameters = null) 311 | { 312 | return $this->send(Method::CONNECT, $url, $parameters, $headers); 313 | } 314 | 315 | /** 316 | * Send POST request to a URL 317 | * @param string $url URL to send the POST request to 318 | * @param array $headers additional headers to send 319 | * @param mixed $body POST body data 320 | * @return \Ws\Http\Response 321 | */ 322 | public function post($url, $headers = [], $body = null) 323 | { 324 | output(func_get_args(),'ppp'); 325 | return $this->send(Method::POST, $url, $body, $headers); 326 | } 327 | 328 | /** 329 | * Send DELETE request to a URL 330 | * @param string $url URL to send the DELETE request to 331 | * @param array $headers additional headers to send 332 | * @param mixed $body DELETE body data 333 | * @return \Ws\Http\Response 334 | */ 335 | public function delete($url, $headers = [], $body = null) 336 | { 337 | return $this->send(Method::DELETE, $url, $body, $headers); 338 | } 339 | 340 | /** 341 | * Send PUT request to a URL 342 | * @param string $url URL to send the PUT request to 343 | * @param array $headers additional headers to send 344 | * @param mixed $body PUT body data 345 | * @return \Ws\Http\Response 346 | */ 347 | public function put($url, $headers = [], $body = null) 348 | { 349 | return $this->send(Method::PUT, $url, $body, $headers); 350 | } 351 | 352 | /** 353 | * Send PATCH request to a URL 354 | * @param string $url URL to send the PATCH request to 355 | * @param array $headers additional headers to send 356 | * @param mixed $body PATCH body data 357 | * @return \Ws\Http\Response 358 | */ 359 | public function patch($url, $headers = [], $body = null) 360 | { 361 | return $this->send(Method::PATCH, $url, $body, $headers); 362 | } 363 | 364 | /** 365 | * Send TRACE request to a URL 366 | * @param string $url URL to send the TRACE request to 367 | * @param array $headers additional headers to send 368 | * @param mixed $body TRACE body data 369 | * @return \Ws\Http\Response 370 | */ 371 | public function trace($url, $headers = [], $body = null) 372 | { 373 | return $this->send(Method::TRACE, $url, $body, $headers); 374 | } 375 | 376 | /** 377 | * This function is useful for serializing multidimensional arrays, and avoid getting 378 | * the 'Array to string conversion' notice 379 | * @param array|object $data array to flatten. 380 | * @param bool|string $parent parent key or false if no parent 381 | * @return array 382 | */ 383 | public static function buildHTTPCurlQuery($data, $parent = false) 384 | { 385 | static $CFClassExist = null; 386 | if ( is_null($CFClassExist) ) 387 | { 388 | $CFClassExist = class_exists('CURLFile', false); 389 | } 390 | 391 | $result = []; 392 | 393 | if (is_object($data)) { 394 | $data = get_object_vars($data); 395 | } 396 | 397 | foreach ($data as $key => $value) { 398 | if ($parent) { 399 | $new_key = sprintf('%s[%s]', $parent, $key); 400 | } else { 401 | $new_key = $key; 402 | } 403 | 404 | if ($CFClassExist && $value instanceof \CURLFile) 405 | { 406 | $result[$new_key] = $value; 407 | } 408 | else if (is_array($value) || is_object($value)) 409 | { 410 | $result = array_merge($result, self::buildHTTPCurlQuery($value, $new_key)); 411 | } 412 | else { 413 | $result[$new_key] = $value; 414 | } 415 | } 416 | 417 | return $result; 418 | } 419 | 420 | /** 421 | * Send a cURL request 422 | * @param string $method HTTP method to use 423 | * @param string $url URL to send the request to 424 | * @param mixed $body request body 425 | * @param array $headers additional headers to send 426 | * @throws \Ws\Http\Exception if a cURL error occurs 427 | * @return \Ws\Http\Response 428 | */ 429 | public function send($method, $url, $body = null, $headers = []) 430 | { 431 | $handle = curl_init(); 432 | 433 | if ($method !== Method::GET) { 434 | if ($method === Method::POST) { 435 | curl_setopt($handle, CURLOPT_POST, true); 436 | } else { 437 | curl_setopt($handle, CURLOPT_CUSTOMREQUEST, $method); 438 | } 439 | 440 | curl_setopt($handle, CURLOPT_POSTFIELDS, $body); 441 | } elseif (is_array($body)) { 442 | if (strpos($url, '?') !== false) { 443 | $url .= '&'; 444 | } else { 445 | $url .= '?'; 446 | } 447 | 448 | $url .= urldecode(http_build_query(self::buildHTTPCurlQuery($body))); 449 | } 450 | 451 | $curl_base_options = [ 452 | CURLOPT_URL => self::encodeUrl($url), 453 | CURLOPT_RETURNTRANSFER => true, 454 | CURLOPT_FOLLOWLOCATION => true, 455 | CURLOPT_MAXREDIRS => 10, 456 | CURLOPT_HTTPHEADER => $this->getFormattedHeaders($headers), 457 | CURLOPT_HEADER => true, 458 | CURLOPT_SSL_VERIFYPEER => $this->verifyPeer, 459 | //CURLOPT_SSL_VERIFYHOST accepts only 0 (false) or 2 (true). Future versions of libcurl will treat values 1 and 2 as equals 460 | CURLOPT_SSL_VERIFYHOST => $this->verifyHost === false ? 0 : 2, 461 | // If an empty string, '', is set, a header containing all supported encoding types is sent 462 | CURLOPT_ENCODING => '' 463 | ]; 464 | 465 | if ( $this->verifyPeer ) 466 | { 467 | $curl_base_options[CURLOPT_CAINFO] = $this->getVerifyFile(); 468 | } 469 | 470 | curl_setopt_array($handle, $this->mergeCurlOptions($curl_base_options, $this->curlOpts)); 471 | 472 | if ($this->socketTimeout !== null) { 473 | curl_setopt($handle, CURLOPT_TIMEOUT, $this->socketTimeout); 474 | } 475 | else if ($this->socketTimeoutMs !== null) { 476 | if ( $this->socketTimeoutMs < 1000 ) 477 | { 478 | // issue: http://www.laruence.com/2014/01/21/2939.html 479 | curl_setopt($handle, CURLOPT_NOSIGNAL, 1); 480 | } 481 | curl_setopt($handle, CURLOPT_TIMEOUT_MS, $this->socketTimeoutMs); 482 | } 483 | 484 | if ($this->cookie) { 485 | curl_setopt($handle, CURLOPT_COOKIE, $this->cookie); 486 | } 487 | 488 | if ($this->cookieFile) { 489 | curl_setopt($handle, CURLOPT_COOKIEFILE, $this->cookieFile); 490 | curl_setopt($handle, CURLOPT_COOKIEJAR, $this->cookieFile); 491 | } 492 | 493 | if (!empty($this->auth['user'])) { 494 | curl_setopt_array($handle, [ 495 | CURLOPT_HTTPAUTH => $this->auth['method'], 496 | CURLOPT_USERPWD => $this->auth['user'] . ':' . $this->auth['pass'] 497 | ]); 498 | } 499 | 500 | if ($this->proxy['address'] !== false) { 501 | curl_setopt_array($handle, [ 502 | CURLOPT_PROXYTYPE => $this->proxy['type'], 503 | CURLOPT_PROXY => $this->proxy['address'], 504 | CURLOPT_PROXYPORT => $this->proxy['port'], 505 | CURLOPT_HTTPPROXYTUNNEL => $this->proxy['tunnel'], 506 | CURLOPT_PROXYAUTH => $this->proxy['auth']['method'], 507 | CURLOPT_PROXYUSERPWD => $this->proxy['auth']['user'] . ':' . $this->proxy['auth']['pass'] 508 | ]); 509 | } 510 | 511 | $response = curl_exec($handle); 512 | $error = curl_error($handle); 513 | $info = curl_getinfo($handle); 514 | 515 | curl_close($handle); 516 | 517 | if ($error) { 518 | throw new Exception($error); 519 | } 520 | 521 | // Split the full response in its headers and body 522 | $header_size = $info['header_size']; 523 | $header = substr($response, 0, $header_size); 524 | $body = substr($response, $header_size); 525 | 526 | return new Response($info, $body, $header, $this->jsonOpts); 527 | } 528 | 529 | public function getFormattedHeaders($headers) 530 | { 531 | $formattedHeaders = []; 532 | 533 | $combinedHeaders = array_change_key_case(array_merge($this->defaultHeaders, (array) $headers)); 534 | 535 | foreach ($combinedHeaders as $key => $val) { 536 | $formattedHeaders[] = $this->getHeaderString($key, $val); 537 | } 538 | 539 | if (!array_key_exists('user-agent', $combinedHeaders)) { 540 | $formattedHeaders[] = 'user-agent: ws-http/1.0'; 541 | } 542 | 543 | if (!array_key_exists('expect', $combinedHeaders)) { 544 | $formattedHeaders[] = 'expect:'; 545 | } 546 | return $formattedHeaders; 547 | } 548 | 549 | private static function getArrayFromQuerystring($query) 550 | { 551 | $query = preg_replace_callback('/(?:^|(?<=&))[^=[]+/', function ($match) { 552 | return bin2hex(urldecode($match[0])); 553 | }, $query); 554 | 555 | parse_str($query, $values); 556 | 557 | return array_combine(array_map('hex2bin', array_keys($values)), $values); 558 | } 559 | 560 | /** 561 | * Ensure that a URL is encoded and safe to use with cURL 562 | * @param string $url URL to encode 563 | * @return string 564 | */ 565 | private static function encodeUrl($url) 566 | { 567 | $url_parsed = parse_url($url); 568 | 569 | $scheme = $url_parsed['scheme'] . '://'; 570 | $host = $url_parsed['host']; 571 | $port = (isset($url_parsed['port']) ? $url_parsed['port'] : null); 572 | $path = (isset($url_parsed['path']) ? $url_parsed['path'] : null); 573 | $query = (isset($url_parsed['query']) ? $url_parsed['query'] : null); 574 | 575 | if ($query !== null) { 576 | $query = '?' . http_build_query(self::getArrayFromQuerystring($query)); 577 | } 578 | 579 | if ($port && $port[0] !== ':') { 580 | $port = ':' . $port; 581 | } 582 | 583 | $result = $scheme . $host . $port . $path . $query; 584 | return $result; 585 | } 586 | 587 | private static function getHeaderString($key, $val) 588 | { 589 | $key = trim(strtolower($key)); 590 | return $key . ': ' . $val; 591 | } 592 | 593 | /** 594 | * @param array $existing_options 595 | * @param array $new_options 596 | * @return array 597 | */ 598 | private static function mergeCurlOptions(&$existing_options, $new_options) 599 | { 600 | $existing_options = $new_options + $existing_options; 601 | return $existing_options; 602 | } 603 | } -------------------------------------------------------------------------------- /src/Ws/Http/Automated/postmanv1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 3 | "name": "Postman Echo", 4 | "description": "Postman Echo is service you can use to test your REST clients and make sample API calls. It provides endpoints for `GET`, `POST`, `PUT`, various auth mechanisms and other utility endpoints.\n\nThe documentation for the endpoints as well as example responses can be found at [https://echo.getpostman.com](https://echo.getpostman.com?source=echo-collection-app-onboarding)", 5 | "order": [], 6 | "folders": [ 7 | { 8 | "owner": 0, 9 | "lastUpdatedBy": "631643", 10 | "lastRevision": 277278520, 11 | "id": "09ddd67c-13fe-4626-8dd4-fc64f1fc27b7", 12 | "name": "Auth: Digest", 13 | "description": "Digest authentication protects an endpoint with a username and password without actually transmitting the password over network.\nOne has to apply a hash function (like MD5, etc) to the username and password before sending them over the network.\n\n> Username: `postman`\n>\n> Password: `password`\n\nUnlike Basic-Auth, authentication happens using two consecutive requests where the first request returns `401 Unauthorised` along with `WWW-Authenticate` header containing information that needs to be used to authenticate subsequent calls.\n\nTo know more about digest authentication, refer to the [Digest Access Authentication](https://en.wikipedia.org/wiki/Digest_access_authentication) wikipedia article.\nThe article on [authentication helpers](https://www.getpostman.com/docs/helpers#digest-auth) elaborates how to use the same within the Postman app.", 14 | "order": [ 15 | "70ed7920-ead1-2d20-645a-c716ab0fd137", 16 | "a4c04e32-72cf-0475-07dc-89c23f85cf0c" 17 | ] 18 | }, 19 | { 20 | "owner": 0, 21 | "lastUpdatedBy": "631643", 22 | "lastRevision": 277278516, 23 | "id": "df815c41-a76b-4b5b-7129-ea59275f254b", 24 | "name": "Auth: Others", 25 | "description": "", 26 | "order": [ 27 | "42c867ca-e72b-3307-169b-26a478b00641", 28 | "2f79ab5b-9029-56c2-7b05-52047790d670", 29 | "843acf02-a33c-c4bb-d742-c07b9212e4b0" 30 | ] 31 | }, 32 | { 33 | "owner": 0, 34 | "lastUpdatedBy": "631643", 35 | "lastRevision": 277278519, 36 | "id": "37368024-f6a8-0f70-85fc-7e876cde9e33", 37 | "name": "Cookies", 38 | "description": "The cookie related endpoints allow one to get, set and delete simple cookies.\n\nCookies are small snippets of information that is stored in the browser and sent back to the server with every subsequent requests in order to store useful information between requests.\nIf you want to know more about cookies, read the [HTTP Cookie](https://en.wikipedia.org/wiki/HTTP_cookie) article on wikipedia.", 39 | "order": [ 40 | "3de3b135-b3cc-3a68-ba27-b6d373e03c8c", 41 | "a4f24593-448b-88de-963f-eeb952d38a57", 42 | "8dc08eee-a543-7c1c-297f-b0b7040c35c6" 43 | ] 44 | }, 45 | { 46 | "owner": 0, 47 | "lastUpdatedBy": "631643", 48 | "lastRevision": 277278521, 49 | "id": "5d3595b3-5e8e-9e33-05ed-855c77298e4e", 50 | "name": "Headers", 51 | "description": "The following set of endpoints allow one to see the headers being sent as part of a request and to get a custom set of headers as part of response.\n\nHTTP header fields provide required information about the request or response, or about the object sent in the message body. Both request headers and response headers can be controlled using these endpoints.", 52 | "order": [ 53 | "da16c006-6293-c1fe-ea42-e9ba8a5e68b1", 54 | "e50f9111-3a52-a325-47f1-fc702bea1fff" 55 | ] 56 | }, 57 | { 58 | "owner": 0, 59 | "lastUpdatedBy": "631643", 60 | "lastRevision": 277278522, 61 | "id": "9a4c3bce-30f7-a496-c9ec-78afecbf1545", 62 | "name": "Request Methods", 63 | "description": "HTTP has multiple request \"verbs\", such as `GET`, `PUT`, `POST`, `DELETE`,\n`PATCH`, `HEAD`, etc. \n\nAn HTTP Method (verb) defines how a request should be interpreted by a server. \nThe endpoints in this section demonstrate various HTTP Verbs. Postman supports \nall the HTTP Verbs, including some rarely used ones, such as `PROPFIND`, `UNLINK`, \netc.\n\nFor details about HTTP Verbs, refer to [RFC 2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9)\n", 64 | "order": [ 65 | "078883ea-ac9e-842e-8f41-784b59a33722", 66 | "1eb1cf9d-2be7-4060-f554-73cd13940174", 67 | "12c51acc-50d2-2d9b-10d6-cc80e3a10d70", 68 | "8c53212f-42cd-cb37-6e02-08c47a7c8bb1", 69 | "1f0fad16-6bff-5130-2056-7f4af6b18912" 70 | ] 71 | }, 72 | { 73 | "owner": 0, 74 | "lastUpdatedBy": "631643", 75 | "lastRevision": 277278518, 76 | "id": "930f54b4-c5cd-2363-7cf5-b9022d3c0aae", 77 | "name": "Utilities", 78 | "description": "", 79 | "order": [ 80 | "6cfd22d8-26cc-7d3e-cf50-16d400211a76", 81 | "159a89e2-110d-0785-9dd7-9e73b2d6878b", 82 | "0189572f-509e-efe0-686d-eed4b3d2f1f0", 83 | "154510d1-65a8-a2d0-f157-aa2c694d7be7", 84 | "fd961ad0-ab24-68d8-4be5-573e8585d526", 85 | "5d3b31c0-fa26-ee03-5c1b-3715825d811d" 86 | ] 87 | } 88 | ], 89 | "timestamp": 0, 90 | "owner": 0, 91 | "public": false, 92 | "hasRequests": true, 93 | "requests": [ 94 | { 95 | "folder": "930f54b4-c5cd-2363-7cf5-b9022d3c0aae", 96 | "id": "0189572f-509e-efe0-686d-eed4b3d2f1f0", 97 | "name": "Delay Response", 98 | "dataMode": "params", 99 | "data": [], 100 | "rawModeData": null, 101 | "descriptionFormat": null, 102 | "description": "Using this endpoint one can configure how long it takes for the server to come back with a response. Appending a number to the URL defines the time (in seconds) the server will wait before responding.\n\nNote that a maximum delay of 10 seconds is accepted by the server.", 103 | "headers": "", 104 | "method": "GET", 105 | "pathVariables": {}, 106 | "url": "https://echo.getpostman.com/delay/3", 107 | "preRequestScript": "", 108 | "tests": "var responseJSON;\ntry { \n responseJSON = JSON.parse(responseBody); \n tests[\"response body has key delay\"] = 'delay' in responseJSON;\n}\ncatch (e) { }\ntests[\"response code is 200\"] = responseCode.code === 200;\n", 109 | "currentHelper": "normal", 110 | "helperAttributes": {}, 111 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 112 | "responses": [ 113 | { 114 | "owner": 0, 115 | "lastUpdatedBy": "631643", 116 | "lastRevision": 277278562, 117 | "request": { 118 | "url": "https://echo.getpostman.com/delay/1", 119 | "headers": [], 120 | "data": [], 121 | "method": "GET", 122 | "dataMode": "params" 123 | }, 124 | "id": "c69827df-5275-773e-4753-7878a72fd156", 125 | "name": "sample delay response", 126 | "status": "", 127 | "responseCode": { 128 | "code": 200, 129 | "name": "OK" 130 | }, 131 | "time": "1396", 132 | "headers": [ 133 | { 134 | "name": "Access-Control-Allow-Credentials", 135 | "key": "Access-Control-Allow-Credentials", 136 | "value": "", 137 | "description": "" 138 | }, 139 | { 140 | "name": "Access-Control-Allow-Headers", 141 | "key": "Access-Control-Allow-Headers", 142 | "value": "", 143 | "description": "" 144 | }, 145 | { 146 | "name": "Access-Control-Allow-Methods", 147 | "key": "Access-Control-Allow-Methods", 148 | "value": "", 149 | "description": "" 150 | }, 151 | { 152 | "name": "Access-Control-Allow-Origin", 153 | "key": "Access-Control-Allow-Origin", 154 | "value": "", 155 | "description": "" 156 | }, 157 | { 158 | "name": "Connection", 159 | "key": "Connection", 160 | "value": "keep-alive", 161 | "description": "" 162 | }, 163 | { 164 | "name": "Content-Length", 165 | "key": "Content-Length", 166 | "value": "13", 167 | "description": "" 168 | }, 169 | { 170 | "name": "Content-Type", 171 | "key": "Content-Type", 172 | "value": "application/json; charset=utf-8", 173 | "description": "" 174 | }, 175 | { 176 | "name": "Date", 177 | "key": "Date", 178 | "value": "Thu, 31 Mar 2016 12:00:02 GMT", 179 | "description": "" 180 | }, 181 | { 182 | "name": "ETag", 183 | "key": "ETag", 184 | "value": "W/\"d-2835810952\"", 185 | "description": "" 186 | }, 187 | { 188 | "name": "Server", 189 | "key": "Server", 190 | "value": "nginx/1.6.2", 191 | "description": "" 192 | }, 193 | { 194 | "name": "Vary", 195 | "key": "Vary", 196 | "value": "Accept-Encoding", 197 | "description": "" 198 | }, 199 | { 200 | "name": "X-Powered-By", 201 | "key": "X-Powered-By", 202 | "value": "Sails ", 203 | "description": "" 204 | } 205 | ], 206 | "cookies": [ 207 | { 208 | "domain": ".getpostman.com", 209 | "expirationDate": 1460718898.059833, 210 | "hostOnly": false, 211 | "httpOnly": false, 212 | "name": "getpostmanlogin", 213 | "path": "/", 214 | "secure": false, 215 | "session": false, 216 | "storeId": "0", 217 | "value": "yes" 218 | }, 219 | { 220 | "domain": ".getpostman.com", 221 | "expirationDate": 1460718898.059992, 222 | "hostOnly": false, 223 | "httpOnly": false, 224 | "name": "postman.sid", 225 | "path": "/", 226 | "secure": false, 227 | "session": false, 228 | "storeId": "0", 229 | "value": "df0c0256028d7ec4d641f766104a9571a8e249685bbc667d7cee030bbf44d3209495c70c03248e31e678a93812591d5e12187a8e99bf6bc5e80c40903f6ff6226938f24e413c0ffa613a7372064ec44a8594e8d3ede6945e34394f369573feeebc4a73a3e24b8c9ac18a53704addb5fd3f71f1ede488ff551feb059e9c1fb208164814e45e0312c4df8ea6e83c26702f42ae634c6afbe82d57c857bbf5598b5527961c1c28688dc2580070a4389f0cf4ec0a179b5b9c11b2ecbaa5460d374065bf5c7a3add9505df0fa89acb9f227f05ed2d4c6b58c39d6d728bd49f6f323ae67d4a75882aa7682f5d6fc5b981ba411d94aa93970bfaefa1953a73e440d50d012b5f288975c888e2345ee7777e746fb5aed3a7b2dbc087c6456621aa78c24a3c17c5f96cf59844933249a352f631e2008cffac6faf06d0e253dcc01cf0067bf56c1fbc5ed61fec1861b60c5accf35ffc2e56154a113004fa1db9d7171c3af8fc063918554092f5" 230 | }, 231 | { 232 | "domain": ".echo.getpostman.com", 233 | "expirationDate": 1522494981, 234 | "hostOnly": false, 235 | "httpOnly": false, 236 | "name": "_ga", 237 | "path": "/", 238 | "secure": false, 239 | "session": false, 240 | "storeId": "0", 241 | "value": "GA1.3.1703443399.1459422978" 242 | }, 243 | { 244 | "domain": "echo.getpostman.com", 245 | "hostOnly": true, 246 | "httpOnly": true, 247 | "name": "sails.sid", 248 | "path": "/", 249 | "secure": false, 250 | "session": true, 251 | "storeId": "0", 252 | "value": "s%3AE3eq7KE5S-hRJvtmRXXYRjTmlNz202zj.pbXR3%2F3TQ4g1827ALbVAcaxSQQjlzrZeHtLNFdbPD9c" 253 | } 254 | ], 255 | "mime": "", 256 | "text": "{\"delay\":\"1\"}", 257 | "language": "javascript", 258 | "rawDataType": "text", 259 | "state": { 260 | "size": "normal" 261 | }, 262 | "previewType": "html", 263 | "searchResultScrolledTo": "-1", 264 | "version": null, 265 | "requestObject": "{\"url\":\"https://echo.getpostman.com/delay/1\",\"headers\":[],\"data\":[],\"method\":\"GET\",\"dataMode\":\"params\"}", 266 | "createdAt": "2016-05-25T12:29:57.000Z", 267 | "updatedAt": "2016-05-25T12:29:58.000Z", 268 | "write": true 269 | } 270 | ] 271 | }, 272 | { 273 | "folder": "9a4c3bce-30f7-a496-c9ec-78afecbf1545", 274 | "id": "078883ea-ac9e-842e-8f41-784b59a33722", 275 | "name": "GET Request", 276 | "dataMode": "params", 277 | "data": [], 278 | "rawModeData": null, 279 | "descriptionFormat": null, 280 | "description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested.", 281 | "headers": "", 282 | "method": "GET", 283 | "pathVariables": {}, 284 | "url": "https://echo.getpostman.com/get?test=123", 285 | "preRequestScript": "", 286 | "tests": "tests[\"Body contains headers\"] = responseBody.has(\"headers\");\ntests[\"Body contains args\"] = responseBody.has(\"args\");\ntests[\"Body contains url\"] = responseBody.has(\"url\");\n\nvar responseJSON;\n\ntry { responseJSON = JSON.parse(responseBody); }\ncatch (e) { }\n\n\ntests[\"Args key contains argument passed as url parameter\"] = 'test' in responseJSON.args", 287 | "currentHelper": "normal", 288 | "helperAttributes": {}, 289 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65" 290 | }, 291 | { 292 | "folder": "9a4c3bce-30f7-a496-c9ec-78afecbf1545", 293 | "id": "12c51acc-50d2-2d9b-10d6-cc80e3a10d70", 294 | "name": "PUT Request", 295 | "dataMode": "raw", 296 | "data": [], 297 | "descriptionFormat": null, 298 | "description": "The HTTP `PUT` request method is similar to HTTP `POST`. It too is meant to \ntransfer data to a server (and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `PUT` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following \nraw HTTP request,\n\n> PUT /hi/there?hand=wave\n>\n> \n\n\n", 299 | "headers": "", 300 | "method": "PUT", 301 | "pathVariables": {}, 302 | "url": "https://echo.getpostman.com/put", 303 | "preRequestScript": "", 304 | "tests": "var data;\n\ntry { responseJSON = JSON.parse(responseBody); }\ncatch (e) {}\n\ntests[\"Body contains files\"] = responseBody.has(\"files\");\ntests[\"Body contains args\"] = responseBody.has(\"args\");\ntests[\"Body contains form\"] = responseBody.has(\"form\");\ntests[\"Body contains headers\"] = responseBody.has(\"headers\");\ntests[\"Body contains url\"] = responseBody.has(\"url\");\n\n\n\ntests[\"Data has been passed\"] = (responseJSON && responseJSON.data && responseJSON.data.length)", 305 | "currentHelper": "normal", 306 | "helperAttributes": {}, 307 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 308 | "rawModeData": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." 309 | }, 310 | { 311 | "folder": "930f54b4-c5cd-2363-7cf5-b9022d3c0aae", 312 | "id": "154510d1-65a8-a2d0-f157-aa2c694d7be7", 313 | "name": "Get UTF8 Encoded Response", 314 | "dataMode": "params", 315 | "data": [], 316 | "rawModeData": null, 317 | "descriptionFormat": null, 318 | "description": "If a response of an endpoint requires to send data beyond the basic English / ASCII character set, the `charset` parameter in the `Content-Type` response header defines the character encoding policy.\n\nThis endpoint returns an `UTF8` character encoded response body with text in various languages such as Greek, Latin, East Asian, etc. Postman can interpret the character encoding and use appropriate methods to display the character set in responses.", 319 | "headers": "", 320 | "method": "GET", 321 | "pathVariables": {}, 322 | "url": "https://echo.getpostman.com/encoding/utf8", 323 | "preRequestScript": "", 324 | "tests": "tests[\"response code is 200\"] = responseCode.code === 200;", 325 | "currentHelper": "normal", 326 | "helperAttributes": {}, 327 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65" 328 | }, 329 | { 330 | "folder": "930f54b4-c5cd-2363-7cf5-b9022d3c0aae", 331 | "id": "159a89e2-110d-0785-9dd7-9e73b2d6878b", 332 | "name": "Streamed Response", 333 | "dataMode": "params", 334 | "data": [], 335 | "rawModeData": null, 336 | "descriptionFormat": null, 337 | "description": "This endpoint allows one to recieve streaming http response using [chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding) of a configurable length.\n\nA streaming response does not wait for the entire response to be generated on server before flushing it out. This implies that for a fairly large response, parts of it can be streamed to the requestee as and when it is generated on server. The client can then take actions of processing this partially received data.", 338 | "headers": "", 339 | "method": "GET", 340 | "pathVariables": {}, 341 | "url": "https://echo.getpostman.com/stream/10", 342 | "preRequestScript": "", 343 | "tests": "tests[\"response code is 200\"] = responseCode.code === 200;\ntests[\"response is sent in chunks\"] = (postman.getResponseHeader('Transfer-Encoding') === 'chunked')\n", 344 | "currentHelper": "normal", 345 | "helperAttributes": {}, 346 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65" 347 | }, 348 | { 349 | "folder": "9a4c3bce-30f7-a496-c9ec-78afecbf1545", 350 | "id": "1eb1cf9d-2be7-4060-f554-73cd13940174", 351 | "name": "POST Request", 352 | "dataMode": "raw", 353 | "data": [], 354 | "descriptionFormat": null, 355 | "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested.", 356 | "headers": "Content-Type: text/plain\n", 357 | "method": "POST", 358 | "pathVariables": {}, 359 | "url": "https://echo.getpostman.com/post", 360 | "preRequestScript": "", 361 | "tests": "var responseJSON;\n\ntry { responseJSON = JSON.parse(responseBody); }\ncatch (e) { }\n\n\ntests[\"response has data\"] = responseJSON && responseJSON.data && (responseJSON.data.length === 256);\ntests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');", 362 | "currentHelper": "normal", 363 | "helperAttributes": {}, 364 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 365 | "rawModeData": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." 366 | }, 367 | { 368 | "folder": "9a4c3bce-30f7-a496-c9ec-78afecbf1545", 369 | "id": "1f0fad16-6bff-5130-2056-7f4af6b18912", 370 | "name": "DELETE Request", 371 | "dataMode": "raw", 372 | "data": [], 373 | "descriptionFormat": null, 374 | "description": "The HTTP `DELETE` method is used to delete resources on a server. The exact\nuse of `DELETE` requests depends on the server implementation. In general, \n`DELETE` requests support both, Query String parameters as well as a Request \nBody.\n\nThis endpoint accepts an HTTP `DELETE` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body.", 375 | "headers": "", 376 | "method": "DELETE", 377 | "pathVariables": {}, 378 | "url": "https://echo.getpostman.com/delete", 379 | "preRequestScript": "", 380 | "tests": "tests[\"Body contains files\"] = responseBody.has(\"files\");\ntests[\"Body contains args\"] = responseBody.has(\"args\");\ntests[\"Body contains form\"] = responseBody.has(\"form\");\ntests[\"Body contains headers\"] = responseBody.has(\"headers\");\ntests[\"Body contains url\"] = responseBody.has(\"url\");\n\nvar responseJSON;\n\ntry { responseJSON = JSON.parse(responseBody); }\ncatch (e) { }\n\ntests[\"Data has been passed\"] = (responseJSON && responseJSON.data && responseJSON.data.length)", 381 | "currentHelper": "normal", 382 | "helperAttributes": {}, 383 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 384 | "rawModeData": "Donec fermentum, nisi sed cursus eleifend, nulla tortor ultricies tellus, ut vehicula orci arcu ut velit. In volutpat egestas dapibus. \nMorbi condimentum vestibulum sapien. Etiam dignissim diam quis eros lobortis gravida vel lobortis est. Etiam gravida sed." 385 | }, 386 | { 387 | "folder": "df815c41-a76b-4b5b-7129-ea59275f254b", 388 | "id": "2f79ab5b-9029-56c2-7b05-52047790d670", 389 | "name": "OAuth1.0 Verify Signature", 390 | "dataMode": "params", 391 | "data": [ 392 | { 393 | "key": "code", 394 | "value": "xWnkliVQJURqB2x1", 395 | "type": "text", 396 | "enabled": true 397 | }, 398 | { 399 | "key": "grant_type", 400 | "value": "authorization_code", 401 | "type": "text", 402 | "enabled": true 403 | }, 404 | { 405 | "key": "redirect_uri", 406 | "value": "https://www.getpostman.com/oauth2/callback", 407 | "type": "text", 408 | "enabled": true 409 | }, 410 | { 411 | "key": "client_id", 412 | "value": "abc123", 413 | "type": "text", 414 | "enabled": true 415 | }, 416 | { 417 | "key": "client_secret", 418 | "value": "ssh-secret", 419 | "type": "text", 420 | "enabled": true 421 | } 422 | ], 423 | "rawModeData": null, 424 | "descriptionFormat": null, 425 | "description": "OAuth1.0a is a specification that defines a protocol that can be used by one\nservice to access \"protected\" resources (endpoints) on another service. A\nmajor part of OAuth1.0 is HTTP Request Signing. This endpoint allows you to \ncheck whether the request calculation works properly in the client. \n\nThe endpoint supports the HTTP ``Authorization`` header. In case the signature\nverification fails, the endpoint provides the four debug values,\n\n* ``base_uri``\n* ``normalized_param_string``\n* ``base_string``\n* ``signing_key``\n\nFor more details about these parameters, check the [OAuth1.0a Specification](http://oauth.net/core/1.0a/)\n\nIn order to use this endpoint, you can set the following values:\n\n> Consumer Key: ``RKCGzna7bv9YD57c``\n>\n> Consumer Secret: ``D+EdQ-gs$-%@2Nu7``\n\nIf you are using Postman, also check the \"Add params to header\" and \n\"Auto add parameters\" boxes.", 426 | "headers": "Authorization: OAuth oauth_consumer_key=\"RKCGzna7bv9YD57c\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1442394747\",oauth_nonce=\"UIGipk\",oauth_version=\"1.0\",oauth_signature=\"CaeyGPr2mns1WCq4Cpm5aLvz6Gs=\"\n", 427 | "method": "GET", 428 | "pathVariables": {}, 429 | "url": "https://echo.getpostman.com/oauth1", 430 | "preRequestScript": "", 431 | "tests": "tests[\"response code is 200\"] = responseCode.code === 200;\nvar body = JSON.parse(responseBody);\ntests[\"Body contains status pass\"] = body[\"status\"] == \"pass\"", 432 | "currentHelper": "normal", 433 | "helperAttributes": {}, 434 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 435 | "responses": [ 436 | { 437 | "owner": 0, 438 | "lastUpdatedBy": "631643", 439 | "lastRevision": 277278569, 440 | "request": { 441 | "url": "https://echo.getpostman.com/oauth1", 442 | "headers": [ 443 | { 444 | "key": "Authorization", 445 | "value": "OAuth oauth_consumer_key=\"RKCGzna7bv9YD57c\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"1442394747\",oauth_nonce=\"UIGipk\",oauth_version=\"1.0\",oauth_signature=\"CaeyGPr2mns1WCq4Cpm5aLvz6Gs=\"", 446 | "enabled": true 447 | } 448 | ], 449 | "data": [ 450 | { 451 | "key": "code", 452 | "value": "xWnkliVQJURqB2x1", 453 | "type": "text", 454 | "enabled": true 455 | }, 456 | { 457 | "key": "grant_type", 458 | "value": "authorization_code", 459 | "type": "text", 460 | "enabled": true 461 | }, 462 | { 463 | "key": "redirect_uri", 464 | "value": "https://www.getpostman.com/oauth2/callback", 465 | "type": "text", 466 | "enabled": true 467 | }, 468 | { 469 | "key": "client_id", 470 | "value": "abc123", 471 | "type": "text", 472 | "enabled": true 473 | }, 474 | { 475 | "key": "client_secret", 476 | "value": "ssh-secret", 477 | "type": "text", 478 | "enabled": true 479 | } 480 | ], 481 | "method": "GET", 482 | "dataMode": "params" 483 | }, 484 | "id": "e504bab6-3d35-fe24-1c4e-c36f5b7b269e", 485 | "name": "Success", 486 | "status": "", 487 | "responseCode": { 488 | "code": 200, 489 | "name": "OK" 490 | }, 491 | "time": "4663", 492 | "headers": [ 493 | { 494 | "name": "Access-Control-Allow-Credentials", 495 | "key": "Access-Control-Allow-Credentials", 496 | "value": "", 497 | "description": "" 498 | }, 499 | { 500 | "name": "Access-Control-Allow-Headers", 501 | "key": "Access-Control-Allow-Headers", 502 | "value": "", 503 | "description": "" 504 | }, 505 | { 506 | "name": "Access-Control-Allow-Methods", 507 | "key": "Access-Control-Allow-Methods", 508 | "value": "", 509 | "description": "" 510 | }, 511 | { 512 | "name": "Access-Control-Allow-Origin", 513 | "key": "Access-Control-Allow-Origin", 514 | "value": "", 515 | "description": "" 516 | }, 517 | { 518 | "name": "Connection", 519 | "key": "Connection", 520 | "value": "keep-alive", 521 | "description": "" 522 | }, 523 | { 524 | "name": "Content-Encoding", 525 | "key": "Content-Encoding", 526 | "value": "gzip", 527 | "description": "" 528 | }, 529 | { 530 | "name": "Content-Length", 531 | "key": "Content-Length", 532 | "value": "95", 533 | "description": "" 534 | }, 535 | { 536 | "name": "Content-Type", 537 | "key": "Content-Type", 538 | "value": "application/json; charset=utf-8", 539 | "description": "" 540 | }, 541 | { 542 | "name": "Date", 543 | "key": "Date", 544 | "value": "Thu, 31 Mar 2016 11:06:58 GMT", 545 | "description": "" 546 | }, 547 | { 548 | "name": "Server", 549 | "key": "Server", 550 | "value": "nginx/1.6.2", 551 | "description": "" 552 | }, 553 | { 554 | "name": "Vary", 555 | "key": "Vary", 556 | "value": "Accept-Encoding", 557 | "description": "" 558 | }, 559 | { 560 | "name": "X-Powered-By", 561 | "key": "X-Powered-By", 562 | "value": "Sails ", 563 | "description": "" 564 | } 565 | ], 566 | "cookies": [ 567 | { 568 | "domain": "echo.getpostman.com", 569 | "hostOnly": true, 570 | "httpOnly": true, 571 | "name": "sails.sid", 572 | "path": "/", 573 | "secure": false, 574 | "session": true, 575 | "storeId": "0", 576 | "value": "s%3ABp7Ij29KkCZa6ZDcop8c91ouBjNzAbD0.ejn6dE%2FhqlpsDh7QUsPXPozN7UBdAzW4i15PEFhHbZg" 577 | }, 578 | { 579 | "domain": ".getpostman.com", 580 | "expirationDate": 1460717668.06945, 581 | "hostOnly": false, 582 | "httpOnly": false, 583 | "name": "getpostmanlogin", 584 | "path": "/", 585 | "secure": false, 586 | "session": false, 587 | "storeId": "0", 588 | "value": "yes" 589 | }, 590 | { 591 | "domain": ".getpostman.com", 592 | "expirationDate": 1460717668.069509, 593 | "hostOnly": false, 594 | "httpOnly": false, 595 | "name": "postman.sid", 596 | "path": "/", 597 | "secure": false, 598 | "session": false, 599 | "storeId": "0", 600 | "value": "9f887f3b7f14b8c29ac4dc4109381b0b89a76e785c7b34251d6c8025b41b24013d2aa49f40e2deac19cbf0594dd984169455534d91ff98d4d1868d67ac857017629f137926e3a04a616bb83a2fb5ab9e6cbea9579ed5d5c1155d47545d72aad5be99f4abd0a7130805b3807d70cd507171dbe9d950d8e35a853f9ec075f5a767c95df4d57f7d521b66605b3bda3801700e26e651d1129c798b729ee3b91702d43ae64ab226c3f426893753def772c15442a7552dc84a3c773d6099a50b0a6af940b64c8176fedfcecd5fc31ccfc3bbc0124bfdaa0d62e4252d4aafb46a3c10963d12391e1fa97a1c0f19a636f57a3ac8cc35567d1cb6cb53b77f8adde3f6754a765596d7d00bdeb9acb5cc8d115e7c3f50ec3228e34d3e6c7464e9039b01868e03d10e9f87772397602453e9e91205de7b86021fad06eb26e69298e99ff1597a670faeb310f8c092041d544851de84f2bee89a92123da6eea286210524035c85361e2af42166a6" 601 | } 602 | ], 603 | "mime": "", 604 | "text": "{\"status\":\"pass\",\"message\":\"OAuth-1.0a signature verification was successful\"}", 605 | "language": "javascript", 606 | "rawDataType": "text", 607 | "state": { 608 | "size": "normal" 609 | }, 610 | "previewType": "html", 611 | "searchResultScrolledTo": "-1", 612 | "version": null, 613 | "requestObject": "{\"url\":\"https://echo.getpostman.com/oauth1\",\"headers\":[{\"key\":\"Authorization\",\"value\":\"OAuth oauth_consumer_key=\\\"RKCGzna7bv9YD57c\\\",oauth_signature_method=\\\"HMAC-SHA1\\\",oauth_timestamp=\\\"1442394747\\\",oauth_nonce=\\\"UIGipk\\\",oauth_version=\\\"1.0\\\",oauth_signature=\\\"CaeyGPr2mns1WCq4Cpm5aLvz6Gs=\\\"\",\"enabled\":true}],\"data\":[{\"key\":\"code\",\"value\":\"xWnkliVQJURqB2x1\",\"type\":\"text\",\"enabled\":true},{\"key\":\"grant_type\",\"value\":\"authorization_code\",\"type\":\"text\",\"enabled\":true},{\"key\":\"redirect_uri\",\"value\":\"https://www.getpostman.com/oauth2/callback\",\"type\":\"text\",\"enabled\":true},{\"key\":\"client_id\",\"value\":\"abc123\",\"type\":\"text\",\"enabled\":true},{\"key\":\"client_secret\",\"value\":\"ssh-secret\",\"type\":\"text\",\"enabled\":true}],\"method\":\"GET\",\"dataMode\":\"params\"}", 614 | "createdAt": "2016-05-25T12:29:58.000Z", 615 | "updatedAt": "2016-05-25T12:29:58.000Z", 616 | "write": true 617 | } 618 | ] 619 | }, 620 | { 621 | "folder": "37368024-f6a8-0f70-85fc-7e876cde9e33", 622 | "id": "3de3b135-b3cc-3a68-ba27-b6d373e03c8c", 623 | "name": "Set Cookies", 624 | "dataMode": "params", 625 | "data": [], 626 | "rawModeData": null, 627 | "descriptionFormat": null, 628 | "description": "The cookie setter endpoint accepts a list of cookies and their values as part of URL parameters of a `GET` request. These cookies are saved and can be subsequently retrieved or deleted. The response of this request returns a JSON with all cookies listed.\n\nTo set your own set of cookies, simply replace the URL parameters \"foo1=bar1&foo2=bar2\" with your own set of key-value pairs.", 629 | "headers": "", 630 | "method": "GET", 631 | "pathVariables": {}, 632 | "url": "https://echo.getpostman.com/cookies/set?foo1=bar1&foo2=bar", 633 | "preRequestScript": "", 634 | "tests": "var responseJSON;\ntry {\n tests[\"Body contains cookies\"] = responseBody.has(\"cookies\");\n responseJSON = JSON.parse(responseBody);\n tests[\"Body contains cookie foo1\"] = 'foo1' in responseJSON.cookies;\n tests[\"Body contains cookie foo2\"] = 'foo2' in responseJSON.cookies; \n}\ncatch (e) { }\n\ntests[\"Status code is 200\"] = responseCode.code === 200;\n\n", 635 | "currentHelper": "normal", 636 | "helperAttributes": {}, 637 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 638 | "responses": [ 639 | { 640 | "owner": 0, 641 | "lastUpdatedBy": "631643", 642 | "lastRevision": 277278568, 643 | "request": "96a24790-4951-ba7e-aa4f-fb40a45a7fcb", 644 | "id": "6c09f286-f74c-4d60-c349-f4a7ff7bac28", 645 | "name": "Cookies", 646 | "status": "", 647 | "responseCode": { 648 | "code": 200, 649 | "name": "OK" 650 | }, 651 | "time": "3063", 652 | "headers": [ 653 | { 654 | "name": "Access-Control-Allow-Credentials", 655 | "key": "Access-Control-Allow-Credentials", 656 | "value": "", 657 | "description": "" 658 | }, 659 | { 660 | "name": "Access-Control-Allow-Headers", 661 | "key": "Access-Control-Allow-Headers", 662 | "value": "", 663 | "description": "" 664 | }, 665 | { 666 | "name": "Access-Control-Allow-Methods", 667 | "key": "Access-Control-Allow-Methods", 668 | "value": "", 669 | "description": "" 670 | }, 671 | { 672 | "name": "Access-Control-Allow-Origin", 673 | "key": "Access-Control-Allow-Origin", 674 | "value": "", 675 | "description": "" 676 | }, 677 | { 678 | "name": "Connection", 679 | "key": "Connection", 680 | "value": "keep-alive", 681 | "description": "" 682 | }, 683 | { 684 | "name": "Content-Encoding", 685 | "key": "Content-Encoding", 686 | "value": "gzip", 687 | "description": "" 688 | }, 689 | { 690 | "name": "Content-Length", 691 | "key": "Content-Length", 692 | "value": "51", 693 | "description": "" 694 | }, 695 | { 696 | "name": "Content-Type", 697 | "key": "Content-Type", 698 | "value": "application/json; charset=utf-8", 699 | "description": "" 700 | }, 701 | { 702 | "name": "Date", 703 | "key": "Date", 704 | "value": "Thu, 29 Oct 2015 06:15:28 GMT", 705 | "description": "" 706 | }, 707 | { 708 | "name": "Server", 709 | "key": "Server", 710 | "value": "nginx/1.6.2", 711 | "description": "" 712 | }, 713 | { 714 | "name": "Vary", 715 | "key": "Vary", 716 | "value": "Accept-Encoding", 717 | "description": "" 718 | }, 719 | { 720 | "name": "X-Powered-By", 721 | "key": "X-Powered-By", 722 | "value": "Sails ", 723 | "description": "" 724 | } 725 | ], 726 | "cookies": [], 727 | "mime": "", 728 | "text": "{\"cookies\":{\"foo1\":\"bar\",\"foo2\":\"bar\"}}", 729 | "language": "javascript", 730 | "rawDataType": "text", 731 | "state": { 732 | "size": "normal" 733 | }, 734 | "previewType": "html", 735 | "searchResultScrolledTo": "-1", 736 | "version": null, 737 | "requestObject": "\"96a24790-4951-ba7e-aa4f-fb40a45a7fcb\"", 738 | "createdAt": "2016-05-25T12:29:57.000Z", 739 | "updatedAt": "2016-05-25T12:29:58.000Z", 740 | "write": true 741 | } 742 | ] 743 | }, 744 | { 745 | "folder": "df815c41-a76b-4b5b-7129-ea59275f254b", 746 | "id": "42c867ca-e72b-3307-169b-26a478b00641", 747 | "name": "Basic Auth", 748 | "dataMode": "params", 749 | "data": [], 750 | "rawModeData": null, 751 | "descriptionFormat": null, 752 | "description": "This endpoint simulates a **basic-auth** protected endpoint. \nThe endpoint accepts a default username and password and returns a status code of `200 ok` only if the same is provided. \nOtherwise it will return a status code `401 unauthorized`.\n\n> Username: `postman`\n> \n> Password: `password`\n\nTo use this endpoint, send a request with the header `Authorization: Basic cG9zdG1hbjpwYXNzd29yZA==`. \nThe cryptic latter half of the header value is a base64 encoded concatenation of the default username and password. \nUsing Postman, to send this request, you can simply fill in the username and password in the \"Authorization\" tab and Postman will do the rest for you.\n\nTo know more about basic authentication, refer to the [Basic Access Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) wikipedia article.\nThe article on [authentication helpers](https://www.getpostman.com/docs/helpers#basic-auth?source=echo-collection-app-onboarding) elaborates how to use the same within the Postman app.", 753 | "headers": "Authorization: Basic cG9zdG1hbjpwYXNzd29yZA==\n", 754 | "method": "GET", 755 | "pathVariables": {}, 756 | "url": "https://echo.getpostman.com/basic-auth", 757 | "preRequestScript": "", 758 | "tests": "tests[\"response code is 200\"] = responseCode.code === 200;\ntests[\"Body contains authenticated\"] = responseBody.has(\"authenticated\");", 759 | "currentHelper": "basicAuth", 760 | "helperAttributes": { 761 | "id": "basic", 762 | "username": "postman", 763 | "password": "password", 764 | "saveToRequest": true 765 | }, 766 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 767 | "responses": [ 768 | { 769 | "owner": 0, 770 | "lastUpdatedBy": "631643", 771 | "lastRevision": 277278560, 772 | "request": "ef90671a-ab14-16f5-0a57-41b32fc2a36f", 773 | "id": "97223e54-e9ac-810d-41df-6b53e1c917e4", 774 | "name": "200", 775 | "status": "", 776 | "responseCode": { 777 | "code": 200, 778 | "name": "OK" 779 | }, 780 | "time": "377", 781 | "headers": [ 782 | { 783 | "name": "Access-Control-Allow-Credentials", 784 | "key": "Access-Control-Allow-Credentials", 785 | "value": "", 786 | "description": "" 787 | }, 788 | { 789 | "name": "Access-Control-Allow-Headers", 790 | "key": "Access-Control-Allow-Headers", 791 | "value": "", 792 | "description": "" 793 | }, 794 | { 795 | "name": "Access-Control-Allow-Methods", 796 | "key": "Access-Control-Allow-Methods", 797 | "value": "", 798 | "description": "" 799 | }, 800 | { 801 | "name": "Access-Control-Allow-Origin", 802 | "key": "Access-Control-Allow-Origin", 803 | "value": "", 804 | "description": "" 805 | }, 806 | { 807 | "name": "Connection", 808 | "key": "Connection", 809 | "value": "keep-alive", 810 | "description": "" 811 | }, 812 | { 813 | "name": "Content-Encoding", 814 | "key": "Content-Encoding", 815 | "value": "gzip", 816 | "description": "" 817 | }, 818 | { 819 | "name": "Content-Length", 820 | "key": "Content-Length", 821 | "value": "42", 822 | "description": "" 823 | }, 824 | { 825 | "name": "Content-Type", 826 | "key": "Content-Type", 827 | "value": "application/json; charset=utf-8", 828 | "description": "" 829 | }, 830 | { 831 | "name": "Date", 832 | "key": "Date", 833 | "value": "Sat, 31 Oct 2015 06:38:25 GMT", 834 | "description": "" 835 | }, 836 | { 837 | "name": "Server", 838 | "key": "Server", 839 | "value": "nginx/1.6.2", 840 | "description": "" 841 | }, 842 | { 843 | "name": "Vary", 844 | "key": "Vary", 845 | "value": "Accept-Encoding", 846 | "description": "" 847 | }, 848 | { 849 | "name": "X-Powered-By", 850 | "key": "X-Powered-By", 851 | "value": "Sails ", 852 | "description": "" 853 | } 854 | ], 855 | "cookies": [], 856 | "mime": "", 857 | "text": "{\"authenticated\":true}", 858 | "language": "javascript", 859 | "rawDataType": "text", 860 | "state": { 861 | "size": "normal" 862 | }, 863 | "previewType": "html", 864 | "searchResultScrolledTo": "-1", 865 | "version": null, 866 | "requestObject": "\"ef90671a-ab14-16f5-0a57-41b32fc2a36f\"", 867 | "createdAt": "2016-05-25T12:29:57.000Z", 868 | "updatedAt": "2016-05-25T12:29:58.000Z", 869 | "write": true 870 | } 871 | ] 872 | }, 873 | { 874 | "folder": "930f54b4-c5cd-2363-7cf5-b9022d3c0aae", 875 | "id": "5d3b31c0-fa26-ee03-5c1b-3715825d811d", 876 | "name": "Deflate Compressed Response", 877 | "dataMode": "params", 878 | "data": [], 879 | "rawModeData": null, 880 | "descriptionFormat": null, 881 | "description": "This endpoint returns the response using [deflate compression algoritm](https://en.wikipedia.org/wiki/DEFLATE). \nThe uncompressed response is a JSON string containing the details of the request sent by the client. For this endpoint to work, one should request with `Accept-encoding` header containing `deflate` as part of its value. Postman supports gzip, deflate and SDCH decoding and automatically sends them as part of the request.\n\nHTTP Compression allows the server to send responses in a compressed format, which is uncompressed by the client before processing. This reduces network bandwidth consumption at the cost of increase in CPU usage.\nTo know more about this, refer the [HTTP Compression](https://en.wikipedia.org/wiki/HTTP_compression) wikipedia article.", 882 | "headers": "", 883 | "method": "GET", 884 | "pathVariables": {}, 885 | "url": "https://echo.getpostman.com/deflate", 886 | "preRequestScript": "", 887 | "tests": "tests[\"response code is 200\"] = responseCode.code === 200;\n\ntry {\n var data = JSON.parse(responseBody);\n tests[\"Body contains deflated\"] = responseBody.has(\"deflated\");\n tests[\"Body contains headers\"] = responseBody.has(\"headers\");\n tests[\"Body contains method\"] = responseBody.has(\"method\");\n}\ncatch(e) {\n console.log('Cannot parse response,probably not a JSON');\n}\n", 888 | "currentHelper": "normal", 889 | "helperAttributes": {}, 890 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65" 891 | }, 892 | { 893 | "folder": "930f54b4-c5cd-2363-7cf5-b9022d3c0aae", 894 | "id": "6cfd22d8-26cc-7d3e-cf50-16d400211a76", 895 | "name": "Response Status Code", 896 | "dataMode": "params", 897 | "data": [], 898 | "rawModeData": null, 899 | "descriptionFormat": null, 900 | "description": "This endpoint allows one to instruct the server which status code to respond with.\n\nEvery response is accompanied by a status code. The status code provides a summary of the nature of response sent by the server. For example, a status code of `200` means everything is okay with the response and a code of `404` implies that the requested URL does not exist on server. \nA list of all valid HTTP status code can be found at the [List of Status Codes](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) wikipedia article. When using Postman, the response status code is described for easy reference.\n\nNote that if an invalid status code is requested to be sent, the server returns a status code of `400 Bad Request`.", 901 | "headers": "", 902 | "method": "GET", 903 | "pathVariables": {}, 904 | "url": "https://echo.getpostman.com/status/200", 905 | "preRequestScript": "", 906 | "tests": "var responseJSON;\ntry {\n responseJSON = JSON.parse(responseBody); \n tests[\"Status equals 200\"] = responseJSON.status === 200;\n}\ncatch (e) { }\ntests[\"Body contains status\"] = responseBody.has(\"status\");\n\n", 907 | "currentHelper": "normal", 908 | "helperAttributes": {}, 909 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 910 | "responses": [ 911 | { 912 | "owner": 0, 913 | "lastUpdatedBy": "631643", 914 | "lastRevision": 277278566, 915 | "request": { 916 | "url": "https://echo.getpostman.com/status/200", 917 | "headers": [], 918 | "data": [], 919 | "method": "GET", 920 | "dataMode": "params" 921 | }, 922 | "id": "a2bda27d-2c7b-eb8e-1740-be4e1fe9a079", 923 | "name": "200", 924 | "status": "", 925 | "responseCode": { 926 | "code": 200, 927 | "name": "OK" 928 | }, 929 | "time": "251", 930 | "headers": [ 931 | { 932 | "name": "Access-Control-Allow-Credentials", 933 | "key": "Access-Control-Allow-Credentials", 934 | "value": "", 935 | "description": "" 936 | }, 937 | { 938 | "name": "Access-Control-Allow-Headers", 939 | "key": "Access-Control-Allow-Headers", 940 | "value": "", 941 | "description": "" 942 | }, 943 | { 944 | "name": "Access-Control-Allow-Methods", 945 | "key": "Access-Control-Allow-Methods", 946 | "value": "", 947 | "description": "" 948 | }, 949 | { 950 | "name": "Access-Control-Allow-Origin", 951 | "key": "Access-Control-Allow-Origin", 952 | "value": "", 953 | "description": "" 954 | }, 955 | { 956 | "name": "Connection", 957 | "key": "Connection", 958 | "value": "keep-alive", 959 | "description": "" 960 | }, 961 | { 962 | "name": "Content-Length", 963 | "key": "Content-Length", 964 | "value": "14", 965 | "description": "" 966 | }, 967 | { 968 | "name": "Content-Type", 969 | "key": "Content-Type", 970 | "value": "application/json; charset=utf-8", 971 | "description": "" 972 | }, 973 | { 974 | "name": "Date", 975 | "key": "Date", 976 | "value": "Thu, 31 Mar 2016 11:58:47 GMT", 977 | "description": "" 978 | }, 979 | { 980 | "name": "ETag", 981 | "key": "ETag", 982 | "value": "W/\"e-1056260003\"", 983 | "description": "" 984 | }, 985 | { 986 | "name": "Server", 987 | "key": "Server", 988 | "value": "nginx/1.6.2", 989 | "description": "" 990 | }, 991 | { 992 | "name": "Vary", 993 | "key": "Vary", 994 | "value": "Accept-Encoding", 995 | "description": "" 996 | }, 997 | { 998 | "name": "X-Powered-By", 999 | "key": "X-Powered-By", 1000 | "value": "Sails ", 1001 | "description": "" 1002 | } 1003 | ], 1004 | "cookies": [ 1005 | { 1006 | "domain": ".getpostman.com", 1007 | "expirationDate": 1460718898.059833, 1008 | "hostOnly": false, 1009 | "httpOnly": false, 1010 | "name": "getpostmanlogin", 1011 | "path": "/", 1012 | "secure": false, 1013 | "session": false, 1014 | "storeId": "0", 1015 | "value": "yes" 1016 | }, 1017 | { 1018 | "domain": ".getpostman.com", 1019 | "expirationDate": 1460718898.059992, 1020 | "hostOnly": false, 1021 | "httpOnly": false, 1022 | "name": "postman.sid", 1023 | "path": "/", 1024 | "secure": false, 1025 | "session": false, 1026 | "storeId": "0", 1027 | "value": "df0c0256028d7ec4d641f766104a9571a8e249685bbc667d7cee030bbf44d3209495c70c03248e31e678a93812591d5e12187a8e99bf6bc5e80c40903f6ff6226938f24e413c0ffa613a7372064ec44a8594e8d3ede6945e34394f369573feeebc4a73a3e24b8c9ac18a53704addb5fd3f71f1ede488ff551feb059e9c1fb208164814e45e0312c4df8ea6e83c26702f42ae634c6afbe82d57c857bbf5598b5527961c1c28688dc2580070a4389f0cf4ec0a179b5b9c11b2ecbaa5460d374065bf5c7a3add9505df0fa89acb9f227f05ed2d4c6b58c39d6d728bd49f6f323ae67d4a75882aa7682f5d6fc5b981ba411d94aa93970bfaefa1953a73e440d50d012b5f288975c888e2345ee7777e746fb5aed3a7b2dbc087c6456621aa78c24a3c17c5f96cf59844933249a352f631e2008cffac6faf06d0e253dcc01cf0067bf56c1fbc5ed61fec1861b60c5accf35ffc2e56154a113004fa1db9d7171c3af8fc063918554092f5" 1028 | }, 1029 | { 1030 | "domain": ".echo.getpostman.com", 1031 | "expirationDate": 1522494981, 1032 | "hostOnly": false, 1033 | "httpOnly": false, 1034 | "name": "_ga", 1035 | "path": "/", 1036 | "secure": false, 1037 | "session": false, 1038 | "storeId": "0", 1039 | "value": "GA1.3.1703443399.1459422978" 1040 | }, 1041 | { 1042 | "domain": "echo.getpostman.com", 1043 | "hostOnly": true, 1044 | "httpOnly": true, 1045 | "name": "sails.sid", 1046 | "path": "/", 1047 | "secure": false, 1048 | "session": true, 1049 | "storeId": "0", 1050 | "value": "s%3AvuHU0EKeDbyNjVrEc7U30dMPzVu8CRaD.GOV1H9olcVzXqrwqP%2BC%2B6MVj2UczXivcN00jgPoDYfs" 1051 | } 1052 | ], 1053 | "mime": "", 1054 | "text": "{\"status\":200}", 1055 | "language": "javascript", 1056 | "rawDataType": "text", 1057 | "state": { 1058 | "size": "normal" 1059 | }, 1060 | "previewType": "html", 1061 | "searchResultScrolledTo": "-1", 1062 | "version": null, 1063 | "requestObject": "{\"url\":\"https://echo.getpostman.com/status/200\",\"headers\":[],\"data\":[],\"method\":\"GET\",\"dataMode\":\"params\"}", 1064 | "createdAt": "2016-05-25T12:29:57.000Z", 1065 | "updatedAt": "2016-05-25T12:29:58.000Z", 1066 | "write": true 1067 | } 1068 | ] 1069 | }, 1070 | { 1071 | "folder": "09ddd67c-13fe-4626-8dd4-fc64f1fc27b7", 1072 | "id": "70ed7920-ead1-2d20-645a-c716ab0fd137", 1073 | "name": "DigestAuth Request", 1074 | "dataMode": "params", 1075 | "data": [ 1076 | { 1077 | "key": "code", 1078 | "value": "xWnkliVQJURqB2x1", 1079 | "type": "text", 1080 | "enabled": true 1081 | }, 1082 | { 1083 | "key": "grant_type", 1084 | "value": "authorization_code", 1085 | "type": "text", 1086 | "enabled": true 1087 | }, 1088 | { 1089 | "key": "redirect_uri", 1090 | "value": "https://www.getpostman.com/oauth2/callback", 1091 | "type": "text", 1092 | "enabled": true 1093 | }, 1094 | { 1095 | "key": "client_id", 1096 | "value": "abc123", 1097 | "type": "text", 1098 | "enabled": true 1099 | }, 1100 | { 1101 | "key": "client_secret", 1102 | "value": "ssh-secret", 1103 | "type": "text", 1104 | "enabled": true 1105 | } 1106 | ], 1107 | "rawModeData": null, 1108 | "descriptionFormat": null, 1109 | "description": "Performing a simple `GET` request to this endpoint returns status code `401 Unauthorized` with `WWW-Authenticate` header containing information to successfully authenticate subsequent requests.\nThe `WWW-Authenticate` header must be processed to extract `realm` and `nonce` values to hash subsequent requests.\n\nWhen this request is executed within Postman, the script attached with this request does the hard work of extracting realm and nonce from the header and set it as [global variables](https://www.getpostman.com/docs/environments#global-variables?source=echo-collection-app-onboarding) named `echo_digest_nonce` and `echo_digest_realm`.\nThese variables are re-used in subsequent request for seamless integration of the two requests.", 1110 | "headers": "", 1111 | "method": "GET", 1112 | "pathVariables": {}, 1113 | "url": "https://echo.getpostman.com/digest-auth", 1114 | "preRequestScript": "", 1115 | "tests": "tests[\"response code is 401\"] = responseCode.code === 401;\ntests[\"response has WWW-Authenticate header\"] = (postman.getResponseHeader('WWW-Authenticate'));\n\nvar authenticateHeader = postman.getResponseHeader('WWW-Authenticate'),\n realmStart = authenticateHeader.indexOf('\"',authenticateHeader.indexOf(\"realm\")) + 1 ,\n realmEnd = authenticateHeader.indexOf('\"',realmStart),\n realm = authenticateHeader.slice(realmStart,realmEnd),\n nonceStart = authenticateHeader.indexOf('\"',authenticateHeader.indexOf(\"nonce\")) + 1,\n nonceEnd = authenticateHeader.indexOf('\"',nonceStart),\n nonce = authenticateHeader.slice(nonceStart,nonceEnd);\n \npostman.setGlobalVariable('echo_digest_realm', realm);\npostman.setGlobalVariable('echo_digest_nonce', nonce);", 1116 | "currentHelper": "normal", 1117 | "helperAttributes": {}, 1118 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65" 1119 | }, 1120 | { 1121 | "folder": "df815c41-a76b-4b5b-7129-ea59275f254b", 1122 | "id": "843acf02-a33c-c4bb-d742-c07b9212e4b0", 1123 | "name": "Hawk Auth", 1124 | "dataMode": "params", 1125 | "data": [ 1126 | { 1127 | "key": "access_token", 1128 | "value": "xyz1", 1129 | "type": "text", 1130 | "enabled": true 1131 | }, 1132 | { 1133 | "key": "id", 1134 | "value": "U1", 1135 | "type": "text", 1136 | "enabled": true 1137 | }, 1138 | { 1139 | "key": "server_secret", 1140 | "value": "zeppelin", 1141 | "type": "text", 1142 | "enabled": true 1143 | }, 1144 | { 1145 | "key": "admin", 1146 | "value": "true", 1147 | "type": "text", 1148 | "enabled": true 1149 | } 1150 | ], 1151 | "rawModeData": null, 1152 | "descriptionFormat": "html", 1153 | "description": "This endpoint is a Hawk Authentication protected endpoint. [Hawk authentication](https://github.com/hueniverse/hawk) is a widely used protocol for protecting API endpoints. One of Hawk's main goals is to enable HTTP authentication for services that do not use TLS (although it can be used in conjunction with TLS as well).\n\nIn order to use this endpoint, select the \"Hawk Auth\" helper inside Postman, and set the following values:\n\nHawk Auth ID: `dh37fgj492je`\n\nHawk Auth Key: `werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn`\n\nAlgorithm: `sha256`\n\nThe rest of the values are optional, and can be left blank. Hitting send should give you a response with a status code of 200 OK.", 1154 | "headers": "", 1155 | "method": "GET", 1156 | "pathVariables": {}, 1157 | "url": "https://echo.getpostman.com/auth/hawk", 1158 | "preRequestScript": "", 1159 | "tests": "tests[\"Status code is 200\"] = responseCode.code === 200;", 1160 | "currentHelper": "hawkAuth", 1161 | "helperAttributes": { 1162 | "id": "hawk", 1163 | "hawk_id": "dh37fgj492je", 1164 | "hawk_key": "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn", 1165 | "algorithm": "sha256", 1166 | "user": "", 1167 | "saveToRequest": true, 1168 | "nonce": "RZKGNz", 1169 | "timestamp": "" 1170 | }, 1171 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 1172 | "responses": [ 1173 | { 1174 | "owner": 0, 1175 | "lastUpdatedBy": "631643", 1176 | "lastRevision": 277278564, 1177 | "request": { 1178 | "url": "https://echo.getpostman.com/auth/hawk", 1179 | "headers": [ 1180 | { 1181 | "key": "Authorization", 1182 | "type": "text", 1183 | "name": "Authorization", 1184 | "value": "Hawk id=\"dh37fgj492je\", ts=\"1459422734\", nonce=\"XiwiCU\", mac=\"KzMHk67BYCC9zZqRy5hRdWFEFLHX5bNlRWGdmOAWKp0=\"" 1185 | } 1186 | ], 1187 | "data": [ 1188 | { 1189 | "key": "access_token", 1190 | "value": "xyz1", 1191 | "type": "text", 1192 | "enabled": true 1193 | }, 1194 | { 1195 | "key": "id", 1196 | "value": "U1", 1197 | "type": "text", 1198 | "enabled": true 1199 | }, 1200 | { 1201 | "key": "server_secret", 1202 | "value": "zeppelin", 1203 | "type": "text", 1204 | "enabled": true 1205 | }, 1206 | { 1207 | "key": "admin", 1208 | "value": "true", 1209 | "type": "text", 1210 | "enabled": true 1211 | } 1212 | ], 1213 | "method": "GET", 1214 | "dataMode": "params" 1215 | }, 1216 | "id": "8bcfebdc-a6fe-7607-cef3-7ee28a0b75a2", 1217 | "name": "Success", 1218 | "status": "", 1219 | "responseCode": { 1220 | "code": 200, 1221 | "name": "OK" 1222 | }, 1223 | "time": "1855", 1224 | "headers": [ 1225 | { 1226 | "name": "Access-Control-Allow-Credentials", 1227 | "key": "Access-Control-Allow-Credentials", 1228 | "value": "", 1229 | "description": "" 1230 | }, 1231 | { 1232 | "name": "Access-Control-Allow-Headers", 1233 | "key": "Access-Control-Allow-Headers", 1234 | "value": "", 1235 | "description": "" 1236 | }, 1237 | { 1238 | "name": "Access-Control-Allow-Methods", 1239 | "key": "Access-Control-Allow-Methods", 1240 | "value": "", 1241 | "description": "" 1242 | }, 1243 | { 1244 | "name": "Access-Control-Allow-Origin", 1245 | "key": "Access-Control-Allow-Origin", 1246 | "value": "", 1247 | "description": "" 1248 | }, 1249 | { 1250 | "name": "Connection", 1251 | "key": "Connection", 1252 | "value": "keep-alive", 1253 | "description": "" 1254 | }, 1255 | { 1256 | "name": "Content-Encoding", 1257 | "key": "Content-Encoding", 1258 | "value": "gzip", 1259 | "description": "" 1260 | }, 1261 | { 1262 | "name": "Content-Type", 1263 | "key": "Content-Type", 1264 | "value": "application/json", 1265 | "description": "" 1266 | }, 1267 | { 1268 | "name": "Date", 1269 | "key": "Date", 1270 | "value": "Thu, 31 Mar 2016 11:12:16 GMT", 1271 | "description": "" 1272 | }, 1273 | { 1274 | "name": "Server", 1275 | "key": "Server", 1276 | "value": "nginx/1.6.2", 1277 | "description": "" 1278 | }, 1279 | { 1280 | "name": "Server-Authorization", 1281 | "key": "Server-Authorization", 1282 | "value": "Hawk mac=\"vRrUzDdcHu2NaNts/r4zg2xmXMdX8wPiTGTM398BDRg=\", hash=\"qmtflETMybaZiOQ2dLT17yiRunFT5OCIxZRZ0boQaiE=\"", 1283 | "description": "" 1284 | }, 1285 | { 1286 | "name": "Vary", 1287 | "key": "Vary", 1288 | "value": "Accept-Encoding", 1289 | "description": "" 1290 | }, 1291 | { 1292 | "name": "X-Powered-By", 1293 | "key": "X-Powered-By", 1294 | "value": "Sails ", 1295 | "description": "" 1296 | }, 1297 | { 1298 | "name": "transfer-encoding", 1299 | "key": "transfer-encoding", 1300 | "value": "chunked", 1301 | "description": "" 1302 | } 1303 | ], 1304 | "cookies": [ 1305 | { 1306 | "domain": ".getpostman.com", 1307 | "expirationDate": 1460717668.06945, 1308 | "hostOnly": false, 1309 | "httpOnly": false, 1310 | "name": "getpostmanlogin", 1311 | "path": "/", 1312 | "secure": false, 1313 | "session": false, 1314 | "storeId": "0", 1315 | "value": "yes" 1316 | }, 1317 | { 1318 | "domain": ".getpostman.com", 1319 | "expirationDate": 1460717668.069509, 1320 | "hostOnly": false, 1321 | "httpOnly": false, 1322 | "name": "postman.sid", 1323 | "path": "/", 1324 | "secure": false, 1325 | "session": false, 1326 | "storeId": "0", 1327 | "value": "9f887f3b7f14b8c29ac4dc4109381b0b89a76e785c7b34251d6c8025b41b24013d2aa49f40e2deac19cbf0594dd984169455534d91ff98d4d1868d67ac857017629f137926e3a04a616bb83a2fb5ab9e6cbea9579ed5d5c1155d47545d72aad5be99f4abd0a7130805b3807d70cd507171dbe9d950d8e35a853f9ec075f5a767c95df4d57f7d521b66605b3bda3801700e26e651d1129c798b729ee3b91702d43ae64ab226c3f426893753def772c15442a7552dc84a3c773d6099a50b0a6af940b64c8176fedfcecd5fc31ccfc3bbc0124bfdaa0d62e4252d4aafb46a3c10963d12391e1fa97a1c0f19a636f57a3ac8cc35567d1cb6cb53b77f8adde3f6754a765596d7d00bdeb9acb5cc8d115e7c3f50ec3228e34d3e6c7464e9039b01868e03d10e9f87772397602453e9e91205de7b86021fad06eb26e69298e99ff1597a670faeb310f8c092041d544851de84f2bee89a92123da6eea286210524035c85361e2af42166a6" 1328 | }, 1329 | { 1330 | "domain": "echo.getpostman.com", 1331 | "hostOnly": true, 1332 | "httpOnly": true, 1333 | "name": "sails.sid", 1334 | "path": "/", 1335 | "secure": false, 1336 | "session": true, 1337 | "storeId": "0", 1338 | "value": "s%3AryJV7v-PE4PuTjBK6nH5XOynQ4atuATV.n17KcaLhVmV8TBHNLwdwXgGR7lmqs3i478WPlTbRgZ4" 1339 | } 1340 | ], 1341 | "mime": "", 1342 | "text": "{\"status\":\"pass\",\"message\":\"Hawk Authentication successful\"}", 1343 | "language": "javascript", 1344 | "rawDataType": "text", 1345 | "state": { 1346 | "size": "normal" 1347 | }, 1348 | "previewType": "html", 1349 | "searchResultScrolledTo": "-1", 1350 | "version": null, 1351 | "requestObject": "{\"url\":\"https://echo.getpostman.com/auth/hawk\",\"headers\":[{\"key\":\"Authorization\",\"type\":\"text\",\"name\":\"Authorization\",\"value\":\"Hawk id=\\\"dh37fgj492je\\\", ts=\\\"1459422734\\\", nonce=\\\"XiwiCU\\\", mac=\\\"KzMHk67BYCC9zZqRy5hRdWFEFLHX5bNlRWGdmOAWKp0=\\\"\"}],\"data\":[{\"key\":\"access_token\",\"value\":\"xyz1\",\"type\":\"text\",\"enabled\":true},{\"key\":\"id\",\"value\":\"U1\",\"type\":\"text\",\"enabled\":true},{\"key\":\"server_secret\",\"value\":\"zeppelin\",\"type\":\"text\",\"enabled\":true},{\"key\":\"admin\",\"value\":\"true\",\"type\":\"text\",\"enabled\":true}],\"method\":\"GET\",\"dataMode\":\"params\"}", 1352 | "createdAt": "2016-05-25T12:29:57.000Z", 1353 | "updatedAt": "2016-05-25T12:29:58.000Z", 1354 | "write": true 1355 | } 1356 | ] 1357 | }, 1358 | { 1359 | "folder": "9a4c3bce-30f7-a496-c9ec-78afecbf1545", 1360 | "id": "8c53212f-42cd-cb37-6e02-08c47a7c8bb1", 1361 | "name": "PATCH Request", 1362 | "dataMode": "raw", 1363 | "data": [], 1364 | "descriptionFormat": null, 1365 | "description": "The HTTP `PATCH` method is used to update resources on a server. The exact\nuse of `PATCH` requests depends on the server in question. There are a number\nof server implementations which handle `PATCH` differently. Technically, \n`PATCH` supports both Query String parameters and a Request Body.\n\nThis endpoint accepts an HTTP `PATCH` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body.", 1366 | "headers": "", 1367 | "method": "PATCH", 1368 | "pathVariables": {}, 1369 | "url": "https://echo.getpostman.com/patch", 1370 | "preRequestScript": "", 1371 | "tests": "tests[\"Body contains files\"] = responseBody.has(\"files\");\ntests[\"Body contains args\"] = responseBody.has(\"args\");\ntests[\"Body contains form\"] = responseBody.has(\"form\");\ntests[\"Body contains headers\"] = responseBody.has(\"headers\");\ntests[\"Body contains url\"] = responseBody.has(\"url\");\n\nvar responseJSON;\n\ntry { responseJSON = JSON.parse(responseBody); }\ncatch (e) { }\n\ntests[\"Data has been passed\"] = (responseJSON && responseJSON.data && responseJSON.data.length)", 1372 | "currentHelper": "normal", 1373 | "helperAttributes": {}, 1374 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 1375 | "rawModeData": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." 1376 | }, 1377 | { 1378 | "folder": "37368024-f6a8-0f70-85fc-7e876cde9e33", 1379 | "id": "8dc08eee-a543-7c1c-297f-b0b7040c35c6", 1380 | "name": "Get Cookies", 1381 | "dataMode": "params", 1382 | "data": [], 1383 | "rawModeData": null, 1384 | "descriptionFormat": null, 1385 | "description": "Use this endpoint to get a list of all cookies that are stored with respect to this domain. Whatever key-value pairs that has been previously set by calling the \"Set Cookies\" endpoint, will be returned as response JSON.", 1386 | "headers": "", 1387 | "method": "GET", 1388 | "pathVariables": {}, 1389 | "url": "https://echo.getpostman.com/cookies", 1390 | "preRequestScript": "", 1391 | "tests": "var responseJSON;\ntry {\n tests[\"Body contains cookies\"] = responseBody.has(\"cookies\");\n responseJSON = JSON.parse(responseBody);\n tests[\"Cookies object is empty\"] = (Object.keys(responseJSON.cookies).length > 0)\n}\ncatch (e) { }\n\ntests[\"Status code is 200\"] = responseCode.code === 200;\n", 1392 | "currentHelper": "normal", 1393 | "helperAttributes": {}, 1394 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 1395 | "responses": [ 1396 | { 1397 | "owner": 0, 1398 | "lastUpdatedBy": "631643", 1399 | "lastRevision": 277278565, 1400 | "request": "4118ca21-f216-410f-510c-2d0e465022c5", 1401 | "id": "403109d9-a2e7-2bf7-9af3-1282aa7d74cd", 1402 | "name": "Cookies", 1403 | "status": "", 1404 | "responseCode": { 1405 | "code": 200, 1406 | "name": "OK" 1407 | }, 1408 | "time": "462", 1409 | "headers": [ 1410 | { 1411 | "name": "Access-Control-Allow-Credentials", 1412 | "key": "Access-Control-Allow-Credentials", 1413 | "value": "", 1414 | "description": "" 1415 | }, 1416 | { 1417 | "name": "Access-Control-Allow-Headers", 1418 | "key": "Access-Control-Allow-Headers", 1419 | "value": "", 1420 | "description": "" 1421 | }, 1422 | { 1423 | "name": "Access-Control-Allow-Methods", 1424 | "key": "Access-Control-Allow-Methods", 1425 | "value": "", 1426 | "description": "" 1427 | }, 1428 | { 1429 | "name": "Access-Control-Allow-Origin", 1430 | "key": "Access-Control-Allow-Origin", 1431 | "value": "", 1432 | "description": "" 1433 | }, 1434 | { 1435 | "name": "Connection", 1436 | "key": "Connection", 1437 | "value": "keep-alive", 1438 | "description": "" 1439 | }, 1440 | { 1441 | "name": "Content-Encoding", 1442 | "key": "Content-Encoding", 1443 | "value": "gzip", 1444 | "description": "" 1445 | }, 1446 | { 1447 | "name": "Content-Length", 1448 | "key": "Content-Length", 1449 | "value": "46", 1450 | "description": "" 1451 | }, 1452 | { 1453 | "name": "Content-Type", 1454 | "key": "Content-Type", 1455 | "value": "application/json; charset=utf-8", 1456 | "description": "" 1457 | }, 1458 | { 1459 | "name": "Date", 1460 | "key": "Date", 1461 | "value": "Thu, 29 Oct 2015 06:16:29 GMT", 1462 | "description": "" 1463 | }, 1464 | { 1465 | "name": "Server", 1466 | "key": "Server", 1467 | "value": "nginx/1.6.2", 1468 | "description": "" 1469 | }, 1470 | { 1471 | "name": "Vary", 1472 | "key": "Vary", 1473 | "value": "Accept-Encoding", 1474 | "description": "" 1475 | }, 1476 | { 1477 | "name": "X-Powered-By", 1478 | "key": "X-Powered-By", 1479 | "value": "Sails ", 1480 | "description": "" 1481 | } 1482 | ], 1483 | "cookies": [], 1484 | "mime": "", 1485 | "text": "{\"cookies\":{\"foo2\":\"bar\"}}", 1486 | "language": "javascript", 1487 | "rawDataType": "text", 1488 | "state": { 1489 | "size": "normal" 1490 | }, 1491 | "previewType": "html", 1492 | "searchResultScrolledTo": "-1", 1493 | "version": null, 1494 | "requestObject": "\"4118ca21-f216-410f-510c-2d0e465022c5\"", 1495 | "createdAt": "2016-05-25T12:29:57.000Z", 1496 | "updatedAt": "2016-05-25T12:29:58.000Z", 1497 | "write": true 1498 | } 1499 | ] 1500 | }, 1501 | { 1502 | "folder": "09ddd67c-13fe-4626-8dd4-fc64f1fc27b7", 1503 | "id": "a4c04e32-72cf-0475-07dc-89c23f85cf0c", 1504 | "name": "DigestAuth Success", 1505 | "dataMode": "params", 1506 | "data": [], 1507 | "rawModeData": null, 1508 | "descriptionFormat": null, 1509 | "description": "This endpoint sends a hashed Digest Authorization header to gain access to a valid `200 Ok` response code. In Postman, it uses the stored [global variables](https://www.getpostman.com/docs/environments#gloval-variables?source=echo-collection-app-onboarding), `echo_digest_realm` and `echo_digest_nonce`, to generate the hashed authorisation header.\n\nWithin Postman, for this request to successfully authenticate, running the previous request \"DigestAuth Request\" stores the relevant information within the global variables.", 1510 | "headers": "Authorization: Digest username=\"postman\", realm=\"Users\", nonce=\"ni1LiL0O37PRRhofWdCLmwFsnEtH1lew\", uri=\"/digest-auth\", response=\"254679099562cf07df9b6f5d8d15db44\", opaque=\"\"\n", 1511 | "method": "GET", 1512 | "pathVariables": {}, 1513 | "url": "https://echo.getpostman.com/digest-auth", 1514 | "preRequestScript": "", 1515 | "tests": "tests[\"response code is 200\"] = responseCode.code === 200;\ntests[\"body contains authenticated\"] = responseBody.has(\"authenticated\");", 1516 | "currentHelper": "digestAuth", 1517 | "helperAttributes": { 1518 | "id": "digest", 1519 | "time": 1446551845396, 1520 | "algorithm": "MD5", 1521 | "username": "postman", 1522 | "realm": "{{echo_digest_realm}}", 1523 | "password": "password", 1524 | "nonce": "{{echo_digest_nonce}}", 1525 | "nonceCount": "", 1526 | "clientNonce": "", 1527 | "opaque": "", 1528 | "qop": "", 1529 | "saveToRequest": true 1530 | }, 1531 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 1532 | "responses": [ 1533 | { 1534 | "owner": 0, 1535 | "lastUpdatedBy": "631643", 1536 | "lastRevision": 277278567, 1537 | "request": "eecb504e-1736-d34c-990a-b86d36f06ddd", 1538 | "id": "50a1e424-f5a6-3017-3777-6c5d0d381be0", 1539 | "name": "200", 1540 | "status": "", 1541 | "responseCode": { 1542 | "code": 200, 1543 | "name": "OK" 1544 | }, 1545 | "time": "9843", 1546 | "headers": [ 1547 | { 1548 | "name": "Access-Control-Allow-Credentials", 1549 | "key": "Access-Control-Allow-Credentials", 1550 | "value": "", 1551 | "description": "" 1552 | }, 1553 | { 1554 | "name": "Access-Control-Allow-Headers", 1555 | "key": "Access-Control-Allow-Headers", 1556 | "value": "", 1557 | "description": "" 1558 | }, 1559 | { 1560 | "name": "Access-Control-Allow-Methods", 1561 | "key": "Access-Control-Allow-Methods", 1562 | "value": "", 1563 | "description": "" 1564 | }, 1565 | { 1566 | "name": "Access-Control-Allow-Origin", 1567 | "key": "Access-Control-Allow-Origin", 1568 | "value": "", 1569 | "description": "" 1570 | }, 1571 | { 1572 | "name": "Connection", 1573 | "key": "Connection", 1574 | "value": "keep-alive", 1575 | "description": "" 1576 | }, 1577 | { 1578 | "name": "Content-Encoding", 1579 | "key": "Content-Encoding", 1580 | "value": "gzip", 1581 | "description": "" 1582 | }, 1583 | { 1584 | "name": "Content-Length", 1585 | "key": "Content-Length", 1586 | "value": "42", 1587 | "description": "" 1588 | }, 1589 | { 1590 | "name": "Content-Type", 1591 | "key": "Content-Type", 1592 | "value": "application/json; charset=utf-8", 1593 | "description": "" 1594 | }, 1595 | { 1596 | "name": "Date", 1597 | "key": "Date", 1598 | "value": "Thu, 29 Oct 2015 06:17:51 GMT", 1599 | "description": "" 1600 | }, 1601 | { 1602 | "name": "Server", 1603 | "key": "Server", 1604 | "value": "nginx/1.6.2", 1605 | "description": "" 1606 | }, 1607 | { 1608 | "name": "Vary", 1609 | "key": "Vary", 1610 | "value": "Accept-Encoding", 1611 | "description": "" 1612 | }, 1613 | { 1614 | "name": "X-Powered-By", 1615 | "key": "X-Powered-By", 1616 | "value": "Sails ", 1617 | "description": "" 1618 | } 1619 | ], 1620 | "cookies": [], 1621 | "mime": "", 1622 | "text": "{\"authenticated\":true}", 1623 | "language": "javascript", 1624 | "rawDataType": "text", 1625 | "state": { 1626 | "size": "normal" 1627 | }, 1628 | "previewType": "html", 1629 | "searchResultScrolledTo": "-1", 1630 | "version": null, 1631 | "requestObject": "\"eecb504e-1736-d34c-990a-b86d36f06ddd\"", 1632 | "createdAt": "2016-05-25T12:29:57.000Z", 1633 | "updatedAt": "2016-05-25T12:29:58.000Z", 1634 | "write": true 1635 | } 1636 | ] 1637 | }, 1638 | { 1639 | "id": "a4f24593-448b-88de-963f-eeb952d38a57", 1640 | "headers": "", 1641 | "url": "https://echo.getpostman.com/cookies/delete?foo1", 1642 | "preRequestScript": "", 1643 | "pathVariables": {}, 1644 | "method": "GET", 1645 | "data": [], 1646 | "dataMode": "params", 1647 | "tests": "var responseJSON;\ntry {\n tests[\"Body contains cookies\"] = responseBody.has(\"cookies\");\n responseJSON = JSON.parse(responseBody);\n tests[\"Body contains cookie foo1\"] = ('foo1' in responseJSON.cookies);\n tests[\"Body does not contain cookie foo2\"] = !('foo2' in responseJSON.cookies);\n}\ncatch (e) { }\n\ntests[\"Status code is 200\"] = responseCode.code === 200;\n", 1648 | "currentHelper": "normal", 1649 | "helperAttributes": {}, 1650 | "time": 1466082161277, 1651 | "name": "Delete Cookies", 1652 | "description": "One or more cookies that has been set for this domain can be deleted by providing the cookie names as part of the URL parameter. The response of this request is a JSON containing the list of currently set cookies.", 1653 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 1654 | "responses": [ 1655 | { 1656 | "owner": 0, 1657 | "lastUpdatedBy": "631643", 1658 | "lastRevision": 277278563, 1659 | "request": "4ac1e980-6990-fc1d-5f80-4e5cedce9812", 1660 | "id": "522b3689-fb79-32fe-a253-7fb170b65555", 1661 | "name": "Cookies Response", 1662 | "status": "", 1663 | "responseCode": { 1664 | "code": 200, 1665 | "name": "OK" 1666 | }, 1667 | "time": "1417", 1668 | "headers": [ 1669 | { 1670 | "name": "Access-Control-Allow-Credentials", 1671 | "key": "Access-Control-Allow-Credentials", 1672 | "value": "", 1673 | "description": "" 1674 | }, 1675 | { 1676 | "name": "Access-Control-Allow-Headers", 1677 | "key": "Access-Control-Allow-Headers", 1678 | "value": "", 1679 | "description": "" 1680 | }, 1681 | { 1682 | "name": "Access-Control-Allow-Methods", 1683 | "key": "Access-Control-Allow-Methods", 1684 | "value": "", 1685 | "description": "" 1686 | }, 1687 | { 1688 | "name": "Access-Control-Allow-Origin", 1689 | "key": "Access-Control-Allow-Origin", 1690 | "value": "", 1691 | "description": "" 1692 | }, 1693 | { 1694 | "name": "Connection", 1695 | "key": "Connection", 1696 | "value": "keep-alive", 1697 | "description": "" 1698 | }, 1699 | { 1700 | "name": "Content-Encoding", 1701 | "key": "Content-Encoding", 1702 | "value": "gzip", 1703 | "description": "" 1704 | }, 1705 | { 1706 | "name": "Content-Length", 1707 | "key": "Content-Length", 1708 | "value": "46", 1709 | "description": "" 1710 | }, 1711 | { 1712 | "name": "Content-Type", 1713 | "key": "Content-Type", 1714 | "value": "application/json; charset=utf-8", 1715 | "description": "" 1716 | }, 1717 | { 1718 | "name": "Date", 1719 | "key": "Date", 1720 | "value": "Thu, 29 Oct 2015 06:16:00 GMT", 1721 | "description": "" 1722 | }, 1723 | { 1724 | "name": "Server", 1725 | "key": "Server", 1726 | "value": "nginx/1.6.2", 1727 | "description": "" 1728 | }, 1729 | { 1730 | "name": "Vary", 1731 | "key": "Vary", 1732 | "value": "Accept-Encoding", 1733 | "description": "" 1734 | }, 1735 | { 1736 | "name": "X-Powered-By", 1737 | "key": "X-Powered-By", 1738 | "value": "Sails ", 1739 | "description": "" 1740 | } 1741 | ], 1742 | "cookies": [], 1743 | "mime": "", 1744 | "text": "{\"cookies\":{\"foo2\":\"bar\"}}", 1745 | "language": "javascript", 1746 | "rawDataType": "text", 1747 | "state": { 1748 | "size": "normal" 1749 | }, 1750 | "previewType": "html", 1751 | "searchResultScrolledTo": "-1", 1752 | "version": null, 1753 | "requestObject": "\"4ac1e980-6990-fc1d-5f80-4e5cedce9812\"", 1754 | "createdAt": "2016-05-25T12:29:57.000Z", 1755 | "updatedAt": "2016-05-25T12:29:58.000Z", 1756 | "write": true 1757 | } 1758 | ], 1759 | "folder": "37368024-f6a8-0f70-85fc-7e876cde9e33" 1760 | }, 1761 | { 1762 | "folder": "5d3595b3-5e8e-9e33-05ed-855c77298e4e", 1763 | "id": "da16c006-6293-c1fe-ea42-e9ba8a5e68b1", 1764 | "name": "Request Headers", 1765 | "dataMode": "params", 1766 | "data": [], 1767 | "rawModeData": null, 1768 | "descriptionFormat": null, 1769 | "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response.", 1770 | "headers": "my-sample-header: Lorem ipsum dolor sit amet\n", 1771 | "method": "GET", 1772 | "pathVariables": {}, 1773 | "url": "https://echo.getpostman.com/headers", 1774 | "preRequestScript": "", 1775 | "tests": "var responseJSON;\ntry {\n tests[\"Body contains headers\"] = responseBody.has(\"headers\");\n responseJSON = JSON.parse(responseBody);\n tests[\"Header contains host\"] = \"host\" in responseJSON.headers;\n tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;\n}\ncatch (e) { }\n\n\n\n", 1776 | "currentHelper": "normal", 1777 | "helperAttributes": {}, 1778 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 1779 | "responses": [ 1780 | { 1781 | "owner": 0, 1782 | "lastUpdatedBy": "631643", 1783 | "lastRevision": 277278561, 1784 | "request": { 1785 | "url": "https://echo.getpostman.com/headers", 1786 | "headers": [ 1787 | { 1788 | "key": "my-sample-header", 1789 | "value": "Lorem ipsum dolor sit amet", 1790 | "enabled": true 1791 | } 1792 | ], 1793 | "data": [], 1794 | "method": "GET", 1795 | "dataMode": "params" 1796 | }, 1797 | "id": "881f141f-44c5-702f-211d-475360c6ccb3", 1798 | "name": "my-sample-header", 1799 | "status": "", 1800 | "responseCode": { 1801 | "code": 200, 1802 | "name": "OK" 1803 | }, 1804 | "time": "460", 1805 | "headers": [ 1806 | { 1807 | "name": "Access-Control-Allow-Credentials", 1808 | "key": "Access-Control-Allow-Credentials", 1809 | "value": "", 1810 | "description": "" 1811 | }, 1812 | { 1813 | "name": "Access-Control-Allow-Headers", 1814 | "key": "Access-Control-Allow-Headers", 1815 | "value": "", 1816 | "description": "" 1817 | }, 1818 | { 1819 | "name": "Access-Control-Allow-Methods", 1820 | "key": "Access-Control-Allow-Methods", 1821 | "value": "", 1822 | "description": "" 1823 | }, 1824 | { 1825 | "name": "Access-Control-Allow-Origin", 1826 | "key": "Access-Control-Allow-Origin", 1827 | "value": "", 1828 | "description": "" 1829 | }, 1830 | { 1831 | "name": "Connection", 1832 | "key": "Connection", 1833 | "value": "keep-alive", 1834 | "description": "" 1835 | }, 1836 | { 1837 | "name": "Content-Encoding", 1838 | "key": "Content-Encoding", 1839 | "value": "gzip", 1840 | "description": "" 1841 | }, 1842 | { 1843 | "name": "Content-Length", 1844 | "key": "Content-Length", 1845 | "value": "342", 1846 | "description": "" 1847 | }, 1848 | { 1849 | "name": "Content-Type", 1850 | "key": "Content-Type", 1851 | "value": "application/json; charset=utf-8", 1852 | "description": "" 1853 | }, 1854 | { 1855 | "name": "Date", 1856 | "key": "Date", 1857 | "value": "Thu, 31 Mar 2016 11:14:00 GMT", 1858 | "description": "" 1859 | }, 1860 | { 1861 | "name": "Server", 1862 | "key": "Server", 1863 | "value": "nginx/1.6.2", 1864 | "description": "" 1865 | }, 1866 | { 1867 | "name": "Vary", 1868 | "key": "Vary", 1869 | "value": "Accept-Encoding", 1870 | "description": "" 1871 | }, 1872 | { 1873 | "name": "X-Powered-By", 1874 | "key": "X-Powered-By", 1875 | "value": "Sails ", 1876 | "description": "" 1877 | } 1878 | ], 1879 | "cookies": [ 1880 | { 1881 | "domain": "echo.getpostman.com", 1882 | "hostOnly": true, 1883 | "httpOnly": true, 1884 | "name": "sails.sid", 1885 | "path": "/", 1886 | "secure": false, 1887 | "session": true, 1888 | "storeId": "0", 1889 | "value": "s%3A9stja5zKmIILxq9Jvtha7Lp9LIz1VIdK.Vp8MHC%2BEUJe4ICZPXn2JAoXaV2bTgtoQd%2B3XJLNr51Y" 1890 | } 1891 | ], 1892 | "mime": "", 1893 | "text": "{\"headers\":{\"host\":\"echo.getpostman.com\",\"accept\":\"*/*\",\"accept-encoding\":\"gzip, deflate, sdch\",\"accept-language\":\"en-US,en;q=0.8\",\"cache-control\":\"no-cache\",\"my-sample-header\":\"Lorem ipsum dolor sit amet\",\"postman-token\":\"3c8ea80b-f599-fba6-e0b4-a0910440e7b6\",\"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36\",\"x-forwarded-port\":\"443\",\"x-forwarded-proto\":\"https\"}}", 1894 | "language": "javascript", 1895 | "rawDataType": "text", 1896 | "state": { 1897 | "size": "normal" 1898 | }, 1899 | "previewType": "html", 1900 | "searchResultScrolledTo": "-1", 1901 | "version": null, 1902 | "requestObject": "{\"url\":\"https://echo.getpostman.com/headers\",\"headers\":[{\"key\":\"my-sample-header\",\"value\":\"Lorem ipsum dolor sit amet\",\"enabled\":true}],\"data\":[],\"method\":\"GET\",\"dataMode\":\"params\"}", 1903 | "createdAt": "2016-05-25T12:29:57.000Z", 1904 | "updatedAt": "2016-05-25T12:29:58.000Z", 1905 | "write": true 1906 | } 1907 | ] 1908 | }, 1909 | { 1910 | "folder": "5d3595b3-5e8e-9e33-05ed-855c77298e4e", 1911 | "id": "e50f9111-3a52-a325-47f1-fc702bea1fff", 1912 | "name": "Response Headers", 1913 | "dataMode": "params", 1914 | "data": [], 1915 | "rawModeData": null, 1916 | "descriptionFormat": null, 1917 | "description": "This endpoint causes the server to send custom set of response headers. Providing header values as part of the URL parameters of a `GET` request to this endpoint returns the same as part of response header.\n\nTo send your own set of headers, simply add or replace the the URL parameters with your own set.", 1918 | "headers": "", 1919 | "method": "GET", 1920 | "pathVariables": {}, 1921 | "url": "https://echo.getpostman.com/response-headers?Content-Type=text/html&test=response_headers", 1922 | "preRequestScript": "", 1923 | "tests": "tests[\"Body contains Content-Type\"] = responseBody.has(\"Content-Type\");\ntests[\"response headers have key sent as part of request\"] = (postman.getResponseHeader('test') == 'response_headers')", 1924 | "currentHelper": "normal", 1925 | "helperAttributes": {}, 1926 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65", 1927 | "responses": [ 1928 | { 1929 | "owner": 0, 1930 | "lastUpdatedBy": "631643", 1931 | "lastRevision": 277278570, 1932 | "request": { 1933 | "url": "https://echo.getpostman.com/response-headers?Content-Type=text/html&test=response_headers", 1934 | "headers": [], 1935 | "data": [], 1936 | "method": "GET", 1937 | "dataMode": "params" 1938 | }, 1939 | "id": "85a7208f-3c37-f297-9772-81b97d28dae0", 1940 | "name": "Response headers", 1941 | "status": "", 1942 | "responseCode": { 1943 | "code": 200, 1944 | "name": "OK" 1945 | }, 1946 | "time": "568", 1947 | "headers": [ 1948 | { 1949 | "name": "Access-Control-Allow-Credentials", 1950 | "key": "Access-Control-Allow-Credentials", 1951 | "value": "", 1952 | "description": "" 1953 | }, 1954 | { 1955 | "name": "Access-Control-Allow-Headers", 1956 | "key": "Access-Control-Allow-Headers", 1957 | "value": "", 1958 | "description": "" 1959 | }, 1960 | { 1961 | "name": "Access-Control-Allow-Methods", 1962 | "key": "Access-Control-Allow-Methods", 1963 | "value": "", 1964 | "description": "" 1965 | }, 1966 | { 1967 | "name": "Access-Control-Allow-Origin", 1968 | "key": "Access-Control-Allow-Origin", 1969 | "value": "", 1970 | "description": "" 1971 | }, 1972 | { 1973 | "name": "Connection", 1974 | "key": "Connection", 1975 | "value": "keep-alive", 1976 | "description": "" 1977 | }, 1978 | { 1979 | "name": "Content-Encoding", 1980 | "key": "Content-Encoding", 1981 | "value": "gzip", 1982 | "description": "" 1983 | }, 1984 | { 1985 | "name": "Content-Length", 1986 | "key": "Content-Length", 1987 | "value": "71", 1988 | "description": "" 1989 | }, 1990 | { 1991 | "name": "Content-Type", 1992 | "key": "Content-Type", 1993 | "value": "text/html; charset=utf-8", 1994 | "description": "" 1995 | }, 1996 | { 1997 | "name": "Date", 1998 | "key": "Date", 1999 | "value": "Thu, 31 Mar 2016 11:14:18 GMT", 2000 | "description": "" 2001 | }, 2002 | { 2003 | "name": "Server", 2004 | "key": "Server", 2005 | "value": "nginx/1.6.2", 2006 | "description": "" 2007 | }, 2008 | { 2009 | "name": "Vary", 2010 | "key": "Vary", 2011 | "value": "Accept-Encoding", 2012 | "description": "" 2013 | }, 2014 | { 2015 | "name": "X-Powered-By", 2016 | "key": "X-Powered-By", 2017 | "value": "Sails ", 2018 | "description": "" 2019 | }, 2020 | { 2021 | "name": "test", 2022 | "key": "test", 2023 | "value": "response_headers", 2024 | "description": "" 2025 | } 2026 | ], 2027 | "cookies": [ 2028 | { 2029 | "domain": "echo.getpostman.com", 2030 | "hostOnly": true, 2031 | "httpOnly": true, 2032 | "name": "sails.sid", 2033 | "path": "/", 2034 | "secure": false, 2035 | "session": true, 2036 | "storeId": "0", 2037 | "value": "s%3A9stja5zKmIILxq9Jvtha7Lp9LIz1VIdK.Vp8MHC%2BEUJe4ICZPXn2JAoXaV2bTgtoQd%2B3XJLNr51Y" 2038 | } 2039 | ], 2040 | "mime": "", 2041 | "text": "{\"Content-Type\":\"text/html\",\"test\":\"response_headers\"}", 2042 | "language": "html", 2043 | "rawDataType": "text", 2044 | "state": { 2045 | "size": "normal" 2046 | }, 2047 | "previewType": "html", 2048 | "searchResultScrolledTo": "-1", 2049 | "version": null, 2050 | "requestObject": "{\"url\":\"https://echo.getpostman.com/response-headers?Content-Type=text/html&test=response_headers\",\"headers\":[],\"data\":[],\"method\":\"GET\",\"dataMode\":\"params\"}", 2051 | "createdAt": "2016-05-25T12:29:58.000Z", 2052 | "updatedAt": "2016-05-25T12:29:58.000Z", 2053 | "write": true 2054 | } 2055 | ] 2056 | }, 2057 | { 2058 | "folder": "930f54b4-c5cd-2363-7cf5-b9022d3c0aae", 2059 | "id": "fd961ad0-ab24-68d8-4be5-573e8585d526", 2060 | "name": "GZip Compressed Response", 2061 | "dataMode": "params", 2062 | "data": [], 2063 | "rawModeData": null, 2064 | "descriptionFormat": null, 2065 | "description": "This endpoint returns the response using [gzip compression algoritm](https://en.wikipedia.org/wiki/Gzip).\nThe uncompressed response is a JSON string containing the details of the request sent by the client. For this endpoint to work, one should request with `Accept-encoding` header containing `gzip` as part of its value. Postman supports gzip, deflate and SDCH decoding and automatically sends them as part of the request.\n\nHTTP Compression allows the server to send responses in a compressed format, which is uncompressed by the client before processing. This reduces network bandwidth consumption at the cost of increase in CPU usage.\nTo know more about this, refer the [HTTP Compression](https://en.wikipedia.org/wiki/HTTP_compression) wikipedia article.", 2066 | "headers": "", 2067 | "method": "GET", 2068 | "pathVariables": {}, 2069 | "url": "https://echo.getpostman.com/gzip", 2070 | "preRequestScript": "", 2071 | "tests": "try {\n var data = JSON.parse(responseBody);\n tests[\"Body contains gzipped\"] = responseBody.has(\"gzipped\");\n tests[\"Body contains headers\"] = responseBody.has(\"headers\");\n tests[\"Body contains method\"] = responseBody.has(\"method\");\n}\ncatch(e) {\n console.log('Cannot parse response,probably not a JSON');\n}\ntests[\"response code is 200\"] = responseCode.code === 200;", 2072 | "currentHelper": "normal", 2073 | "helperAttributes": {}, 2074 | "collectionId": "f695cab7-6878-eb55-7943-ad88e1ccfd65" 2075 | } 2076 | ] 2077 | } --------------------------------------------------------------------------------