1 // Copyright 2012 Google Inc. All Rights Reserved.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
37 func (c
*Client
) Run() error
{
38 if err
:= c
.resolveArgs(); err
!= nil {
39 return fmt
.Errorf("resolveArgs() got error: %v", err
)
42 // Connect to the proxy.
43 uconn
, hconn
, addr
, err
:= c
.connect()
45 return fmt
.Errorf("connect() got error: %v", err
)
47 // Keep the unix socket connection open for the duration of the request.
49 // Keep a connection to the HTTP server open, so no other user can
50 // bind on the same address so long as the process is running.
53 // Start the git-remote-http subprocess.
54 cargs
:= []string{"-c", fmt
.Sprintf("http.proxy=%v", addr
), "remote-http"}
55 cargs
= append(cargs
, c
.Args
...)
56 cmd
:= exec
.Command("git", cargs
...)
58 for _
, v
:= range os
.Environ() {
59 if !strings
.HasPrefix(v
, "GIT_PERSISTENT_HTTPS_SECURE=") {
60 cmd
.Env
= append(cmd
.Env
, v
)
63 // Set the GIT_PERSISTENT_HTTPS_SECURE environment variable when
64 // the proxy is using a SSL connection. This allows credential helpers
65 // to identify secure proxy connections, despite being passed an HTTP
68 cmd
.Env
= append(cmd
.Env
, "GIT_PERSISTENT_HTTPS_SECURE=1")
72 cmd
.Stdout
= os
.Stdout
73 cmd
.Stderr
= os
.Stderr
74 if err
:= cmd
.Run(); err
!= nil {
75 if eerr
, ok
:= err
.(*exec
.ExitError
); ok
{
76 if stat
, ok
:= eerr
.ProcessState
.Sys().(syscall
.WaitStatus
); ok
&& stat
.ExitStatus() != 0 {
77 os
.Exit(stat
.ExitStatus())
80 return fmt
.Errorf("git-remote-http subprocess got error: %v", err
)
85 func (c
*Client
) connect() (uconn net
.Conn
, hconn net
.Conn
, addr
string, err error
) {
86 uconn
, err
= DefaultSocket
.Dial()
88 if e
, ok
:= err
.(*net
.OpError
); ok
&& (os
.IsNotExist(e
.Err
) || e
.Err
== syscall
.ECONNREFUSED
) {
89 if err
= c
.startProxy(); err
== nil {
90 uconn
, err
= DefaultSocket
.Dial()
98 if addr
, err
= c
.readAddr(uconn
); err
!= nil {
102 // Open a tcp connection to the proxy.
103 if hconn
, err
= net
.Dial("tcp", addr
); err
!= nil {
107 // Verify the address hasn't changed ownership.
109 if addr2
, err
= c
.readAddr(uconn
); err
!= nil {
111 } else if addr
!= addr2
{
112 err
= fmt
.Errorf("address changed after connect. got %q, want %q", addr2
, addr
)
118 func (c
*Client
) readAddr(conn net
.Conn
) (string, error
) {
119 conn
.SetDeadline(time
.Now().Add(5 * time
.Second
))
120 data
:= make([]byte, 100)
121 n
, err
:= conn
.Read(data
)
123 return "", fmt
.Errorf("error reading unix socket: %v", err
)
125 return "", errors
.New("empty data response")
127 conn
.Write([]byte{1}) // Ack
130 if addrs
:= strings
.Split(string(data
[:n
]), "\n"); len(addrs
) != 2 {
131 return "", fmt
.Errorf("got %q, wanted 2 addresses", data
[:n
])
132 } else if c
.insecure
{
140 func (c
*Client
) startProxy() error
{
141 cmd
:= exec
.Command(c
.ProxyBin
)
142 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{Setpgid
: true}
143 stdout
, err
:= cmd
.StdoutPipe()
148 if err
:= cmd
.Start(); err
!= nil {
151 result
:= make(chan error
)
153 bytes
, _
, err
:= bufio
.NewReader(stdout
).ReadLine()
154 if line
:= string(bytes
); err
== nil && line
!= "OK" {
155 err
= fmt
.Errorf("proxy returned %q, want \"OK\"", line
)
160 case err
:= <-result
:
162 case <-time
.After(5 * time
.Second
):
163 return errors
.New("timeout waiting for proxy to start")
165 panic("not reachable")
168 func (c
*Client
) resolveArgs() error
{
169 if nargs
:= len(c
.Args
); nargs
== 0 {
170 return errors
.New("remote needed")
171 } else if nargs
> 2 {
172 return fmt
.Errorf("want at most 2 args, got %v", c
.Args
)
175 // Rewrite the url scheme to be http.
176 idx
:= len(c
.Args
) - 1
177 rawurl
:= c
.Args
[idx
]
178 rurl
, err
:= url
.Parse(rawurl
)
180 return fmt
.Errorf("invalid remote: %v", err
)
182 c
.insecure
= rurl
.Scheme
== "persistent-http"
184 c
.Args
[idx
] = rurl
.String()
185 if idx
!= 0 && c
.Args
[0] == rawurl
{
186 c
.Args
[0] = c
.Args
[idx
]