Projects STRLCPY cdebug Commits 9defecb7
🤬
  • ■ ■ ■ ■ ■ ■
    go.mod
     1 +module github.com/iximiuz/cdebug
     2 + 
     3 +go 1.18
     4 + 
     5 +require (
     6 + github.com/docker/cli v20.10.20+incompatible
     7 + github.com/docker/docker v20.10.20+incompatible
     8 + github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae
     9 + github.com/sirupsen/logrus v1.9.0
     10 +)
     11 + 
     12 +require (
     13 + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
     14 + github.com/Microsoft/go-winio v0.6.0 // indirect
     15 + github.com/docker/distribution v2.8.1+incompatible // indirect
     16 + github.com/docker/go-connections v0.4.0 // indirect
     17 + github.com/docker/go-units v0.5.0 // indirect
     18 + github.com/gogo/protobuf v1.3.2 // indirect
     19 + github.com/google/go-cmp v0.5.5 // indirect
     20 + github.com/morikuni/aec v1.0.0 // indirect
     21 + github.com/opencontainers/go-digest v1.0.0 // indirect
     22 + github.com/opencontainers/image-spec v1.0.2 // indirect
     23 + github.com/pkg/errors v0.9.1 // indirect
     24 + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
     25 + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
     26 + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
     27 + golang.org/x/time v0.1.0 // indirect
     28 + golang.org/x/tools v0.1.12 // indirect
     29 + gotest.tools/v3 v3.4.0 // indirect
     30 +)
     31 + 
  • ■ ■ ■ ■ ■ ■
    go.sum
     1 +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
     2 +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
     3 +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
     4 +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
     5 +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
     6 +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
     7 +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
     8 +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
     9 +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
     10 +github.com/docker/cli v20.10.20+incompatible h1:lWQbHSHUFs7KraSN2jOJK7zbMS2jNCHI4mt4xUFUVQ4=
     11 +github.com/docker/cli v20.10.20+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
     12 +github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
     13 +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
     14 +github.com/docker/docker v20.10.20+incompatible h1:kH9tx6XO+359d+iAkumyKDc5Q1kOwPuAUaeri48nD6E=
     15 +github.com/docker/docker v20.10.20+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
     16 +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
     17 +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
     18 +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
     19 +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
     20 +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
     21 +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
     22 +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
     23 +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
     24 +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
     25 +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
     26 +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
     27 +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
     28 +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
     29 +github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
     30 +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
     31 +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
     32 +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
     33 +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
     34 +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
     35 +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
     36 +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
     37 +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
     38 +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
     39 +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
     40 +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
     41 +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
     42 +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
     43 +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
     44 +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
     45 +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
     46 +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
     47 +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
     48 +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
     49 +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
     50 +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
     51 +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
     52 +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
     53 +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
     54 +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
     55 +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
     56 +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
     57 +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
     58 +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
     59 +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
     60 +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
     61 +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
     62 +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
     63 +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
     64 +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
     65 +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
     66 +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
     67 +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
     68 +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
     69 +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
     70 +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
     71 +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
     72 +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
     73 +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
     74 +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
     75 +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
     76 +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
     77 +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
     78 +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
     79 +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
     80 +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
     81 +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
     82 +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
     83 +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
     84 +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
     85 +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
     86 +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
     87 +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
     88 +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
     89 +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
     90 +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
     91 +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
     92 +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
     93 +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
     94 +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
     95 +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
     96 + 
  • ■ ■ ■ ■ ■ ■
    main.go
     1 +package main
     2 + 
     3 +import (
     4 + "context"
     5 + "io"
     6 + "os"
     7 + 
     8 + clistreams "github.com/docker/cli/cli/streams"
     9 + "github.com/docker/docker/api/types"
     10 + "github.com/docker/docker/api/types/container"
     11 + "github.com/docker/docker/client"
     12 + "github.com/moby/term"
     13 + "github.com/sirupsen/logrus"
     14 +)
     15 + 
     16 +// cdebug exec [--image busybox] <CONT> [CMD]
     17 +// cdebug exec [-it] --image nixery.dev/shell/ps <CONT> [CMD]
     18 + 
     19 +// cdebug images
     20 +// - busybox
     21 +// - alpine
     22 +// - nixery:shell/ps
     23 + 
     24 +const chrootProgram = `
     25 +set -euxo pipefail
     26 + 
     27 +rm -rf /proc/1/root/.cdebug
     28 +ln -s /proc/$$/root/bin/ /proc/1/root/.cdebug
     29 +export PATH=$PATH:/.cdebug
     30 +chroot /proc/1/root sh
     31 +`
     32 + 
     33 +type Streams struct {
     34 + In *clistreams.In
     35 + Out *clistreams.Out
     36 + Err io.Writer
     37 +}
     38 + 
     39 +func main() {
     40 + stdin, stdout, stderr := term.StdStreams()
     41 + streams := &Streams{
     42 + In: clistreams.NewIn(stdin),
     43 + Out: clistreams.NewOut(stdout),
     44 + Err: stderr,
     45 + }
     46 + 
     47 + ctx := context.Background()
     48 + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
     49 + if err != nil {
     50 + panic(err)
     51 + }
     52 + 
     53 + reader, err := cli.ImagePull(ctx, "docker.io/library/busybox", types.ImagePullOptions{})
     54 + if err != nil {
     55 + panic(err)
     56 + }
     57 + 
     58 + io.Copy(os.Stdout, reader)
     59 + reader.Close()
     60 + 
     61 + resp, err := cli.ContainerCreate(ctx, &container.Config{
     62 + Image: "busybox",
     63 + Cmd: []string{"sh", "-c", chrootProgram},
     64 + // AttachStdin: true,
     65 + OpenStdin: true,
     66 + Tty: true,
     67 + }, &container.HostConfig{
     68 + NetworkMode: container.NetworkMode("container:my-distroless"),
     69 + PidMode: container.PidMode("container:my-distroless"),
     70 + // IpcMode: container.IpcMode("container:my-distroless"),
     71 + UTSMode: container.UTSMode("container:my-distroless"),
     72 + }, nil, nil, "")
     73 + if err != nil {
     74 + panic(err)
     75 + }
     76 + 
     77 + close, err := attachContainer(ctx, cli, streams, resp.ID)
     78 + if err != nil {
     79 + panic(err)
     80 + }
     81 + defer close()
     82 + 
     83 + if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
     84 + panic(err)
     85 + }
     86 + 
     87 + statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
     88 + select {
     89 + case err := <-errCh:
     90 + if err != nil {
     91 + panic(err)
     92 + }
     93 + case <-statusCh:
     94 + }
     95 +}
     96 + 
     97 +func attachContainer(ctx context.Context, cli *client.Client, streams *Streams, contID string) (func(), error) {
     98 + options := types.ContainerAttachOptions{
     99 + Stream: true,
     100 + Stdin: true,
     101 + Stdout: true,
     102 + Stderr: true,
     103 + }
     104 + 
     105 + resp, err := cli.ContainerAttach(ctx, contID, options)
     106 + if err != nil {
     107 + return nil, err
     108 + }
     109 + 
     110 + var (
     111 + out io.Writer = os.Stdout
     112 + cerr io.Writer = os.Stdout
     113 + in io.ReadCloser = os.Stdin
     114 + )
     115 + 
     116 + go func() {
     117 + s := ioStreamer{
     118 + streams: streams,
     119 + inputStream: in,
     120 + outputStream: out,
     121 + errorStream: cerr,
     122 + resp: resp,
     123 + }
     124 + 
     125 + if err := s.stream(ctx); err != nil {
     126 + logrus.WithError(err).Warn("ioStreamer.stream() failed")
     127 + }
     128 + }()
     129 + 
     130 + return resp.Close, nil
     131 +}
     132 + 
     133 +type ioStreamer struct {
     134 + streams *Streams
     135 + 
     136 + inputStream io.ReadCloser
     137 + outputStream io.Writer
     138 + errorStream io.Writer
     139 + 
     140 + resp types.HijackedResponse
     141 +}
     142 + 
     143 +func (s *ioStreamer) stream(ctx context.Context) error {
     144 + s.streams.In.SetRawTerminal()
     145 + s.streams.Out.SetRawTerminal()
     146 + defer func() {
     147 + s.streams.In.RestoreTerminal()
     148 + s.streams.Out.RestoreTerminal()
     149 + }()
     150 + 
     151 + inDone := make(chan error)
     152 + go func() {
     153 + io.Copy(s.resp.Conn, s.inputStream)
     154 + close(inDone)
     155 + }()
     156 + 
     157 + outDone := make(chan error)
     158 + go func() {
     159 + io.Copy(s.outputStream, s.resp.Reader)
     160 + close(outDone)
     161 + }()
     162 + 
     163 + select {
     164 + case <-ctx.Done():
     165 + return ctx.Err()
     166 + case <-inDone:
     167 + <-outDone
     168 + return nil
     169 + case <-outDone:
     170 + return nil
     171 + }
     172 + 
     173 + return nil
     174 +}
     175 + 
Please wait...
Page is in error, reload to recover