github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/api/client/client.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:43</date> 10 //</624450111354703872> 11 12 13 package client 14 15 import ( 16 "archive/tar" 17 "bytes" 18 "context" 19 "encoding/json" 20 "errors" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "mime/multipart" 25 "net/http" 26 "net/http/httptrace" 27 "net/textproto" 28 "net/url" 29 "os" 30 "path/filepath" 31 "regexp" 32 "strconv" 33 "strings" 34 "time" 35 36 "github.com/ethereum/go-ethereum/log" 37 "github.com/ethereum/go-ethereum/metrics" 38 "github.com/ethereum/go-ethereum/swarm/api" 39 "github.com/ethereum/go-ethereum/swarm/spancontext" 40 "github.com/ethereum/go-ethereum/swarm/storage/feed" 41 "github.com/pborman/uuid" 42 ) 43 44 var ( 45 ErrUnauthorized = errors.New("unauthorized") 46 ) 47 48 func NewClient(gateway string) *Client { 49 return &Client{ 50 Gateway: gateway, 51 } 52 } 53 54 //客户端将与Swarm HTTP网关的交互进行包装。 55 type Client struct { 56 Gateway string 57 } 58 59 //uploadraw将原始数据上载到swarm并返回结果哈希。如果加密是真的 60 //上载加密数据 61 func (c *Client) UploadRaw(r io.Reader, size int64, toEncrypt bool) (string, error) { 62 if size <= 0 { 63 return "", errors.New("data size must be greater than zero") 64 } 65 addr := "" 66 if toEncrypt { 67 addr = "encrypt" 68 } 69 req, err := http.NewRequest("POST", c.Gateway+"/bzz-raw:/"+addr, r) 70 if err != nil { 71 return "", err 72 } 73 req.ContentLength = size 74 res, err := http.DefaultClient.Do(req) 75 if err != nil { 76 return "", err 77 } 78 defer res.Body.Close() 79 if res.StatusCode != http.StatusOK { 80 return "", fmt.Errorf("unexpected HTTP status: %s", res.Status) 81 } 82 data, err := ioutil.ReadAll(res.Body) 83 if err != nil { 84 return "", err 85 } 86 return string(data), nil 87 } 88 89 //downloadraw从swarm下载原始数据,它返回readcloser和bool 90 //内容已加密 91 func (c *Client) DownloadRaw(hash string) (io.ReadCloser, bool, error) { 92 uri := c.Gateway + "/bzz-raw:/" + hash 93 res, err := http.DefaultClient.Get(uri) 94 if err != nil { 95 return nil, false, err 96 } 97 if res.StatusCode != http.StatusOK { 98 res.Body.Close() 99 return nil, false, fmt.Errorf("unexpected HTTP status: %s", res.Status) 100 } 101 isEncrypted := (res.Header.Get("X-Decrypted") == "true") 102 return res.Body, isEncrypted, nil 103 } 104 105 //文件表示群清单中的文件,用于上传和 106 //从Swarm下载内容 107 type File struct { 108 io.ReadCloser 109 api.ManifestEntry 110 } 111 112 //打开打开一个本地文件,然后可以将其传递到客户端。上载以上载 113 //它蜂拥而至 114 func Open(path string) (*File, error) { 115 f, err := os.Open(path) 116 if err != nil { 117 return nil, err 118 } 119 stat, err := f.Stat() 120 if err != nil { 121 f.Close() 122 return nil, err 123 } 124 125 contentType, err := api.DetectContentType(f.Name(), f) 126 if err != nil { 127 return nil, err 128 } 129 130 return &File{ 131 ReadCloser: f, 132 ManifestEntry: api.ManifestEntry{ 133 ContentType: contentType, 134 Mode: int64(stat.Mode()), 135 Size: stat.Size(), 136 ModTime: stat.ModTime(), 137 }, 138 }, nil 139 } 140 141 //上载将文件上载到Swarm,并将其添加到现有清单中 142 //(如果manifest参数非空)或创建包含 143 //文件,返回生成的清单哈希(然后该文件将 144 //可在bzz:/<hash>/<path>)获取 145 func (c *Client) Upload(file *File, manifest string, toEncrypt bool) (string, error) { 146 if file.Size <= 0 { 147 return "", errors.New("file size must be greater than zero") 148 } 149 return c.TarUpload(manifest, &FileUploader{file}, "", toEncrypt) 150 } 151 152 //下载从swarm manifest下载具有给定路径的文件 153 //给定的哈希(即它得到bzz:/<hash>/<path>) 154 func (c *Client) Download(hash, path string) (*File, error) { 155 uri := c.Gateway + "/bzz:/" + hash + "/" + path 156 res, err := http.DefaultClient.Get(uri) 157 if err != nil { 158 return nil, err 159 } 160 if res.StatusCode != http.StatusOK { 161 res.Body.Close() 162 return nil, fmt.Errorf("unexpected HTTP status: %s", res.Status) 163 } 164 return &File{ 165 ReadCloser: res.Body, 166 ManifestEntry: api.ManifestEntry{ 167 ContentType: res.Header.Get("Content-Type"), 168 Size: res.ContentLength, 169 }, 170 }, nil 171 } 172 173 //uploadDirectory将目录树上载到swarm并添加文件 174 //到现有清单(如果清单参数非空)或创建 175 //新清单,返回生成的清单哈希(来自 176 //目录将在bzz:/<hash>/path/to/file处可用,其中 177 //默认路径中指定的文件正在上载到清单的根目录 178 //(即bzz/<hash>/) 179 func (c *Client) UploadDirectory(dir, defaultPath, manifest string, toEncrypt bool) (string, error) { 180 stat, err := os.Stat(dir) 181 if err != nil { 182 return "", err 183 } else if !stat.IsDir() { 184 return "", fmt.Errorf("not a directory: %s", dir) 185 } 186 if defaultPath != "" { 187 if _, err := os.Stat(filepath.Join(dir, defaultPath)); err != nil { 188 if os.IsNotExist(err) { 189 return "", fmt.Errorf("the default path %q was not found in the upload directory %q", defaultPath, dir) 190 } 191 return "", fmt.Errorf("default path: %v", err) 192 } 193 } 194 return c.TarUpload(manifest, &DirectoryUploader{dir}, defaultPath, toEncrypt) 195 } 196 197 //下载目录下载群清单中包含的文件 198 //到本地目录的给定路径(现有文件将被覆盖) 199 func (c *Client) DownloadDirectory(hash, path, destDir, credentials string) error { 200 stat, err := os.Stat(destDir) 201 if err != nil { 202 return err 203 } else if !stat.IsDir() { 204 return fmt.Errorf("not a directory: %s", destDir) 205 } 206 207 uri := c.Gateway + "/bzz:/" + hash + "/" + path 208 req, err := http.NewRequest("GET", uri, nil) 209 if err != nil { 210 return err 211 } 212 if credentials != "" { 213 req.SetBasicAuth("", credentials) 214 } 215 req.Header.Set("Accept", "application/x-tar") 216 res, err := http.DefaultClient.Do(req) 217 if err != nil { 218 return err 219 } 220 defer res.Body.Close() 221 switch res.StatusCode { 222 case http.StatusOK: 223 case http.StatusUnauthorized: 224 return ErrUnauthorized 225 default: 226 return fmt.Errorf("unexpected HTTP status: %s", res.Status) 227 } 228 tr := tar.NewReader(res.Body) 229 for { 230 hdr, err := tr.Next() 231 if err == io.EOF { 232 return nil 233 } else if err != nil { 234 return err 235 } 236 //忽略默认路径文件 237 if hdr.Name == "" { 238 continue 239 } 240 241 dstPath := filepath.Join(destDir, filepath.Clean(strings.TrimPrefix(hdr.Name, path))) 242 if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil { 243 return err 244 } 245 var mode os.FileMode = 0644 246 if hdr.Mode > 0 { 247 mode = os.FileMode(hdr.Mode) 248 } 249 dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) 250 if err != nil { 251 return err 252 } 253 n, err := io.Copy(dst, tr) 254 dst.Close() 255 if err != nil { 256 return err 257 } else if n != hdr.Size { 258 return fmt.Errorf("expected %s to be %d bytes but got %d", hdr.Name, hdr.Size, n) 259 } 260 } 261 } 262 263 //下载文件将单个文件下载到目标目录中 264 //如果清单项未指定文件名-它将回退 265 //以文件名的形式传递到文件的哈希 266 func (c *Client) DownloadFile(hash, path, dest, credentials string) error { 267 hasDestinationFilename := false 268 if stat, err := os.Stat(dest); err == nil { 269 hasDestinationFilename = !stat.IsDir() 270 } else { 271 if os.IsNotExist(err) { 272 //不存在-应创建 273 hasDestinationFilename = true 274 } else { 275 return fmt.Errorf("could not stat path: %v", err) 276 } 277 } 278 279 manifestList, err := c.List(hash, path, credentials) 280 if err != nil { 281 return err 282 } 283 284 switch len(manifestList.Entries) { 285 case 0: 286 return fmt.Errorf("could not find path requested at manifest address. make sure the path you've specified is correct") 287 case 1: 288 //持续 289 default: 290 return fmt.Errorf("got too many matches for this path") 291 } 292 293 uri := c.Gateway + "/bzz:/" + hash + "/" + path 294 req, err := http.NewRequest("GET", uri, nil) 295 if err != nil { 296 return err 297 } 298 if credentials != "" { 299 req.SetBasicAuth("", credentials) 300 } 301 res, err := http.DefaultClient.Do(req) 302 if err != nil { 303 return err 304 } 305 defer res.Body.Close() 306 switch res.StatusCode { 307 case http.StatusOK: 308 case http.StatusUnauthorized: 309 return ErrUnauthorized 310 default: 311 return fmt.Errorf("unexpected HTTP status: expected 200 OK, got %d", res.StatusCode) 312 } 313 filename := "" 314 if hasDestinationFilename { 315 filename = dest 316 } else { 317 //尝试断言 318 re := regexp.MustCompile("[^/]+$") //最后一个斜线后的所有内容 319 320 if results := re.FindAllString(path, -1); len(results) > 0 { 321 filename = results[len(results)-1] 322 } else { 323 if entry := manifestList.Entries[0]; entry.Path != "" && entry.Path != "/" { 324 filename = entry.Path 325 } else { 326 //如果命令行中没有任何内容,则假定hash为名称 327 filename = hash 328 } 329 } 330 filename = filepath.Join(dest, filename) 331 } 332 filePath, err := filepath.Abs(filename) 333 if err != nil { 334 return err 335 } 336 337 if err := os.MkdirAll(filepath.Dir(filePath), 0777); err != nil { 338 return err 339 } 340 341 dst, err := os.Create(filename) 342 if err != nil { 343 return err 344 } 345 defer dst.Close() 346 347 _, err = io.Copy(dst, res.Body) 348 return err 349 } 350 351 //上载清单将给定清单上载到Swarm 352 func (c *Client) UploadManifest(m *api.Manifest, toEncrypt bool) (string, error) { 353 data, err := json.Marshal(m) 354 if err != nil { 355 return "", err 356 } 357 return c.UploadRaw(bytes.NewReader(data), int64(len(data)), toEncrypt) 358 } 359 360 //下载清单下载群清单 361 func (c *Client) DownloadManifest(hash string) (*api.Manifest, bool, error) { 362 res, isEncrypted, err := c.DownloadRaw(hash) 363 if err != nil { 364 return nil, isEncrypted, err 365 } 366 defer res.Close() 367 var manifest api.Manifest 368 if err := json.NewDecoder(res).Decode(&manifest); err != nil { 369 return nil, isEncrypted, err 370 } 371 return &manifest, isEncrypted, nil 372 } 373 374 //列出具有给定前缀、分组的群清单中的列表文件 375 //使用“/”作为分隔符的常见前缀。 376 // 377 //例如,如果清单表示以下目录结构: 378 // 379 //文件1.TXT 380 //文件2.TXT 381 //DRI1/FIL3.TXT 382 //dir1/dir2/file4.txt文件 383 // 384 //然后: 385 // 386 //-前缀“”将返回[dir1/,file1.txt,file2.txt] 387 //-前缀“file”将返回[file1.txt,file2.txt] 388 //-前缀“dir1/”将返回[dir1/dir2/,dir1/file3.txt] 389 // 390 //其中以“/”结尾的条目是常见的前缀。 391 func (c *Client) List(hash, prefix, credentials string) (*api.ManifestList, error) { 392 req, err := http.NewRequest(http.MethodGet, c.Gateway+"/bzz-list:/"+hash+"/"+prefix, nil) 393 if err != nil { 394 return nil, err 395 } 396 if credentials != "" { 397 req.SetBasicAuth("", credentials) 398 } 399 res, err := http.DefaultClient.Do(req) 400 if err != nil { 401 return nil, err 402 } 403 defer res.Body.Close() 404 switch res.StatusCode { 405 case http.StatusOK: 406 case http.StatusUnauthorized: 407 return nil, ErrUnauthorized 408 default: 409 return nil, fmt.Errorf("unexpected HTTP status: %s", res.Status) 410 } 411 var list api.ManifestList 412 if err := json.NewDecoder(res.Body).Decode(&list); err != nil { 413 return nil, err 414 } 415 return &list, nil 416 } 417 418 //上载程序使用提供的上载将文件上载到Swarm fn 419 type Uploader interface { 420 Upload(UploadFn) error 421 } 422 423 type UploaderFunc func(UploadFn) error 424 425 func (u UploaderFunc) Upload(upload UploadFn) error { 426 return u(upload) 427 } 428 429 //DirectoryUploader上载目录中的所有文件,可以选择上载 430 //默认路径的文件 431 type DirectoryUploader struct { 432 Dir string 433 } 434 435 //上载执行目录和默认路径的上载 436 func (d *DirectoryUploader) Upload(upload UploadFn) error { 437 return filepath.Walk(d.Dir, func(path string, f os.FileInfo, err error) error { 438 if err != nil { 439 return err 440 } 441 if f.IsDir() { 442 return nil 443 } 444 file, err := Open(path) 445 if err != nil { 446 return err 447 } 448 relPath, err := filepath.Rel(d.Dir, path) 449 if err != nil { 450 return err 451 } 452 file.Path = filepath.ToSlash(relPath) 453 return upload(file) 454 }) 455 } 456 457 //文件上载程序上载单个文件 458 type FileUploader struct { 459 File *File 460 } 461 462 //上载执行文件上载 463 func (f *FileUploader) Upload(upload UploadFn) error { 464 return upload(f.File) 465 } 466 467 //uploadfn是传递给上载程序以执行上载的函数类型。 468 //对于单个文件(例如,目录上载程序将调用 469 //目录树中每个文件的uploadfn) 470 type UploadFn func(file *File) error 471 472 //tar upload使用给定的上传器将文件作为tar流上传到swarm, 473 //返回结果清单哈希 474 func (c *Client) TarUpload(hash string, uploader Uploader, defaultPath string, toEncrypt bool) (string, error) { 475 ctx, sp := spancontext.StartSpan(context.Background(), "api.client.tarupload") 476 defer sp.Finish() 477 478 var tn time.Time 479 480 reqR, reqW := io.Pipe() 481 defer reqR.Close() 482 addr := hash 483 484 //如果已经存在哈希(清单),那么该清单将确定上载是否 485 //是否加密。如果没有清单,则toEncrypt参数决定 486 //是否加密。 487 if hash == "" && toEncrypt { 488 //这是加密上载端点的内置地址 489 addr = "encrypt" 490 } 491 req, err := http.NewRequest("POST", c.Gateway+"/bzz:/"+addr, reqR) 492 if err != nil { 493 return "", err 494 } 495 496 trace := GetClientTrace("swarm api client - upload tar", "api.client.uploadtar", uuid.New()[:8], &tn) 497 498 req = req.WithContext(httptrace.WithClientTrace(ctx, trace)) 499 transport := http.DefaultTransport 500 501 req.Header.Set("Content-Type", "application/x-tar") 502 if defaultPath != "" { 503 q := req.URL.Query() 504 q.Set("defaultpath", defaultPath) 505 req.URL.RawQuery = q.Encode() 506 } 507 508 //使用“expect:100 continue”,以便在以下情况下不发送请求正文: 509 //服务器拒绝请求 510 req.Header.Set("Expect", "100-continue") 511 512 tw := tar.NewWriter(reqW) 513 514 //定义将文件添加到tar流的uploadfn 515 uploadFn := func(file *File) error { 516 hdr := &tar.Header{ 517 Name: file.Path, 518 Mode: file.Mode, 519 Size: file.Size, 520 ModTime: file.ModTime, 521 Xattrs: map[string]string{ 522 "user.swarm.content-type": file.ContentType, 523 }, 524 } 525 if err := tw.WriteHeader(hdr); err != nil { 526 return err 527 } 528 _, err = io.Copy(tw, file) 529 return err 530 } 531 532 //在Goroutine中运行上载,以便我们可以发送请求头和 533 //在发送tar流之前,等待“100 continue”响应 534 go func() { 535 err := uploader.Upload(uploadFn) 536 if err == nil { 537 err = tw.Close() 538 } 539 reqW.CloseWithError(err) 540 }() 541 tn = time.Now() 542 res, err := transport.RoundTrip(req) 543 if err != nil { 544 return "", err 545 } 546 defer res.Body.Close() 547 if res.StatusCode != http.StatusOK { 548 return "", fmt.Errorf("unexpected HTTP status: %s", res.Status) 549 } 550 data, err := ioutil.ReadAll(res.Body) 551 if err != nil { 552 return "", err 553 } 554 return string(data), nil 555 } 556 557 //multipartupload使用给定的上载程序将文件作为 558 //多部分表单,返回结果清单哈希 559 func (c *Client) MultipartUpload(hash string, uploader Uploader) (string, error) { 560 reqR, reqW := io.Pipe() 561 defer reqR.Close() 562 req, err := http.NewRequest("POST", c.Gateway+"/bzz:/"+hash, reqR) 563 if err != nil { 564 return "", err 565 } 566 567 //使用“expect:100 continue”,以便在以下情况下不发送请求正文: 568 //服务器拒绝请求 569 req.Header.Set("Expect", "100-continue") 570 571 mw := multipart.NewWriter(reqW) 572 req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%q", mw.Boundary())) 573 574 //定义将文件添加到多部分表单的uploadfn 575 uploadFn := func(file *File) error { 576 hdr := make(textproto.MIMEHeader) 577 hdr.Set("Content-Disposition", fmt.Sprintf("form-data; name=%q", file.Path)) 578 hdr.Set("Content-Type", file.ContentType) 579 hdr.Set("Content-Length", strconv.FormatInt(file.Size, 10)) 580 w, err := mw.CreatePart(hdr) 581 if err != nil { 582 return err 583 } 584 _, err = io.Copy(w, file) 585 return err 586 } 587 588 //在Goroutine中运行上载,以便我们可以发送请求头和 589 //在发送多部分表单之前,请等待“100继续”响应 590 go func() { 591 err := uploader.Upload(uploadFn) 592 if err == nil { 593 err = mw.Close() 594 } 595 reqW.CloseWithError(err) 596 }() 597 598 res, err := http.DefaultClient.Do(req) 599 if err != nil { 600 return "", err 601 } 602 defer res.Body.Close() 603 if res.StatusCode != http.StatusOK { 604 return "", fmt.Errorf("unexpected HTTP status: %s", res.Status) 605 } 606 data, err := ioutil.ReadAll(res.Body) 607 if err != nil { 608 return "", err 609 } 610 return string(data), nil 611 } 612 613 //当Swarm找不到给定源的更新时,返回errnoFeedUpdatesFund。 614 var ErrNoFeedUpdatesFound = errors.New("No updates found for this feed") 615 616 //CreateFeedWithManifest创建源清单,并使用提供的 617 //数据 618 //返回可用于包含在ENS解析程序(setcontent)中的结果源清单地址。 619 //或引用将来的更新(client.updatefeed) 620 func (c *Client) CreateFeedWithManifest(request *feed.Request) (string, error) { 621 responseStream, err := c.updateFeed(request, true) 622 if err != nil { 623 return "", err 624 } 625 defer responseStream.Close() 626 627 body, err := ioutil.ReadAll(responseStream) 628 if err != nil { 629 return "", err 630 } 631 632 var manifestAddress string 633 if err = json.Unmarshal(body, &manifestAddress); err != nil { 634 return "", err 635 } 636 return manifestAddress, nil 637 } 638 639 //更新源允许您设置内容的新版本 640 func (c *Client) UpdateFeed(request *feed.Request) error { 641 _, err := c.updateFeed(request, false) 642 return err 643 } 644 645 func (c *Client) updateFeed(request *feed.Request, createManifest bool) (io.ReadCloser, error) { 646 URL, err := url.Parse(c.Gateway) 647 if err != nil { 648 return nil, err 649 } 650 URL.Path = "/bzz-feed:/" 651 values := URL.Query() 652 body := request.AppendValues(values) 653 if createManifest { 654 values.Set("manifest", "1") 655 } 656 URL.RawQuery = values.Encode() 657 658 req, err := http.NewRequest("POST", URL.String(), bytes.NewBuffer(body)) 659 if err != nil { 660 return nil, err 661 } 662 663 res, err := http.DefaultClient.Do(req) 664 if err != nil { 665 return nil, err 666 } 667 668 return res.Body, nil 669 } 670 671 //queryfeed返回具有源更新的原始内容的字节流 672 //ManifestAddressOrDomain是您在CreateFeedWithManifest或其解析程序的ENS域中获得的地址。 673 //指向那个地址 674 func (c *Client) QueryFeed(query *feed.Query, manifestAddressOrDomain string) (io.ReadCloser, error) { 675 return c.queryFeed(query, manifestAddressOrDomain, false) 676 } 677 678 //queryfeed返回具有源更新的原始内容的字节流 679 //ManifestAddressOrDomain是您在CreateFeedWithManifest或其解析程序的ENS域中获得的地址。 680 //指向那个地址 681 //meta设置为true将指示节点返回feed metainformation,而不是 682 func (c *Client) queryFeed(query *feed.Query, manifestAddressOrDomain string, meta bool) (io.ReadCloser, error) { 683 URL, err := url.Parse(c.Gateway) 684 if err != nil { 685 return nil, err 686 } 687 URL.Path = "/bzz-feed:/" + manifestAddressOrDomain 688 values := URL.Query() 689 if query != nil { 690 query.AppendValues(values) //添加查询参数 691 } 692 if meta { 693 values.Set("meta", "1") 694 } 695 URL.RawQuery = values.Encode() 696 res, err := http.Get(URL.String()) 697 if err != nil { 698 return nil, err 699 } 700 701 if res.StatusCode != http.StatusOK { 702 if res.StatusCode == http.StatusNotFound { 703 return nil, ErrNoFeedUpdatesFound 704 } 705 errorMessageBytes, err := ioutil.ReadAll(res.Body) 706 var errorMessage string 707 if err != nil { 708 errorMessage = "cannot retrieve error message: " + err.Error() 709 } else { 710 errorMessage = string(errorMessageBytes) 711 } 712 return nil, fmt.Errorf("Error retrieving feed updates: %s", errorMessage) 713 } 714 715 return res.Body, nil 716 } 717 718 //GetFeedRequest返回一个描述引用的源状态的结构 719 //ManifestAddressOrDomain是您在CreateFeedWithManifest或其解析程序的ENS域中获得的地址。 720 //指向那个地址 721 func (c *Client) GetFeedRequest(query *feed.Query, manifestAddressOrDomain string) (*feed.Request, error) { 722 723 responseStream, err := c.queryFeed(query, manifestAddressOrDomain, true) 724 if err != nil { 725 return nil, err 726 } 727 defer responseStream.Close() 728 729 body, err := ioutil.ReadAll(responseStream) 730 if err != nil { 731 return nil, err 732 } 733 734 var metadata feed.Request 735 if err := metadata.UnmarshalJSON(body); err != nil { 736 return nil, err 737 } 738 return &metadata, nil 739 } 740 741 func GetClientTrace(traceMsg, metricPrefix, ruid string, tn *time.Time) *httptrace.ClientTrace { 742 trace := &httptrace.ClientTrace{ 743 GetConn: func(_ string) { 744 log.Trace(traceMsg+" - http get", "event", "GetConn", "ruid", ruid) 745 metrics.GetOrRegisterResettingTimer(metricPrefix+".getconn", nil).Update(time.Since(*tn)) 746 }, 747 GotConn: func(_ httptrace.GotConnInfo) { 748 log.Trace(traceMsg+" - http get", "event", "GotConn", "ruid", ruid) 749 metrics.GetOrRegisterResettingTimer(metricPrefix+".gotconn", nil).Update(time.Since(*tn)) 750 }, 751 PutIdleConn: func(err error) { 752 log.Trace(traceMsg+" - http get", "event", "PutIdleConn", "ruid", ruid, "err", err) 753 metrics.GetOrRegisterResettingTimer(metricPrefix+".putidle", nil).Update(time.Since(*tn)) 754 }, 755 GotFirstResponseByte: func() { 756 log.Trace(traceMsg+" - http get", "event", "GotFirstResponseByte", "ruid", ruid) 757 metrics.GetOrRegisterResettingTimer(metricPrefix+".firstbyte", nil).Update(time.Since(*tn)) 758 }, 759 Got100Continue: func() { 760 log.Trace(traceMsg, "event", "Got100Continue", "ruid", ruid) 761 metrics.GetOrRegisterResettingTimer(metricPrefix+".got100continue", nil).Update(time.Since(*tn)) 762 }, 763 DNSStart: func(_ httptrace.DNSStartInfo) { 764 log.Trace(traceMsg, "event", "DNSStart", "ruid", ruid) 765 metrics.GetOrRegisterResettingTimer(metricPrefix+".dnsstart", nil).Update(time.Since(*tn)) 766 }, 767 DNSDone: func(_ httptrace.DNSDoneInfo) { 768 log.Trace(traceMsg, "event", "DNSDone", "ruid", ruid) 769 metrics.GetOrRegisterResettingTimer(metricPrefix+".dnsdone", nil).Update(time.Since(*tn)) 770 }, 771 ConnectStart: func(network, addr string) { 772 log.Trace(traceMsg, "event", "ConnectStart", "ruid", ruid, "network", network, "addr", addr) 773 metrics.GetOrRegisterResettingTimer(metricPrefix+".connectstart", nil).Update(time.Since(*tn)) 774 }, 775 ConnectDone: func(network, addr string, err error) { 776 log.Trace(traceMsg, "event", "ConnectDone", "ruid", ruid, "network", network, "addr", addr, "err", err) 777 metrics.GetOrRegisterResettingTimer(metricPrefix+".connectdone", nil).Update(time.Since(*tn)) 778 }, 779 WroteHeaders: func() { 780 log.Trace(traceMsg, "event", "WroteHeaders(request)", "ruid", ruid) 781 metrics.GetOrRegisterResettingTimer(metricPrefix+".wroteheaders", nil).Update(time.Since(*tn)) 782 }, 783 Wait100Continue: func() { 784 log.Trace(traceMsg, "event", "Wait100Continue", "ruid", ruid) 785 metrics.GetOrRegisterResettingTimer(metricPrefix+".wait100continue", nil).Update(time.Since(*tn)) 786 }, 787 WroteRequest: func(_ httptrace.WroteRequestInfo) { 788 log.Trace(traceMsg, "event", "WroteRequest", "ruid", ruid) 789 metrics.GetOrRegisterResettingTimer(metricPrefix+".wroterequest", nil).Update(time.Since(*tn)) 790 }, 791 } 792 return trace 793 } 794