{"thread":{"id":"30601","subject":"[PATCH] Add persistent-https to contrib","startedAt":"2012-05-23T17:06:07Z","lastAt":"2012-05-29T23:34:12Z","messageCount":12,"participants":["Colby Ranger","Junio C Hamano","Shawn Pearce","Jeff King","Daniel Stenberg"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"192008","messageId":"1337792767-18914-1-git-send-email-cranger@google.com","threadId":"30601","inReplyTo":null,"subject":"[PATCH] Add persistent-https to contrib","fromName":"Colby Ranger","fromEmail":"cranger@google.com","sentAt":"2012-05-23T17:06:07Z","receivedAt":"2012-05-23T17:06:07Z","isPatch":true,"sender":{"key":"cranger@google.com","avatar":"https://avatars.githubusercontent.com/u/2480655?v=4"},"body":"Git over HTTPS has a high request startup latency, since the SSL\nnegotiation can take up to a second. In order to reduce this latency,\nconnections should be left open to the Git server across requests\n(or invocations of the git commandline).\n\nReduce SSL startup latency by running a daemon job that keeps\nconnections open to a Git server. The daemon job\n(git-remote-persistent-https--proxy) is started on the first request\nthrough the client binary (git-remote-persistent-https) and remains\nrunning for 24 hours after the last request, or until a new daemon\nbinary is placed in the PATH. The client determines the daemon's\nHTTP address by communicating over a UNIX socket with the daemon.\nFrom there, the rest of the Git protocol work is delegated to the\n\"git-remote-http\" binary, with the environment's http_proxy set to\nthe daemon.\n\nSigned-off-by: Colby Ranger <cranger@google.com>\n---\n contrib/persistent-https/COPYING    |  202 +++++++++++++++++++++++++++++++++++\n contrib/persistent-https/README     |   62 +++++++++++\n contrib/persistent-https/client.go  |  178 ++++++++++++++++++++++++++++++\n contrib/persistent-https/main.go    |   82 ++++++++++++++\n contrib/persistent-https/proxy.go   |  190 ++++++++++++++++++++++++++++++++\n contrib/persistent-https/release.sh |   45 ++++++++\n contrib/persistent-https/socket.go  |   97 +++++++++++++++++\n contrib/persistent-https/tar.sh     |   40 +++++++\n 8 files changed, 896 insertions(+)\n create mode 100644 contrib/persistent-https/COPYING\n create mode 100644 contrib/persistent-https/README\n create mode 100644 contrib/persistent-https/client.go\n create mode 100644 contrib/persistent-https/main.go\n create mode 100644 contrib/persistent-https/proxy.go\n create mode 100755 contrib/persistent-https/release.sh\n create mode 100644 contrib/persistent-https/socket.go\n create mode 100755 contrib/persistent-https/tar.sh\n\ndiff --git a/contrib/persistent-https/COPYING b/contrib/persistent-https/COPYING\nnew file mode 100644\nindex 0000000..d645695\n--- /dev/null\n+++ b/contrib/persistent-https/COPYING\n@@ -0,0 +1,202 @@\n+\n+                                 Apache License\n+                           Version 2.0, January 2004\n+                        http://www.apache.org/licenses/\n+\n+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n+\n+   1. Definitions.\n+\n+      \"License\" shall mean the terms and conditions for use, reproduction,\n+      and distribution as defined by Sections 1 through 9 of this document.\n+\n+      \"Licensor\" shall mean the copyright owner or entity authorized by\n+      the copyright owner that is granting the License.\n+\n+      \"Legal Entity\" shall mean the union of the acting entity and all\n+      other entities that control, are controlled by, or are under common\n+      control with that entity. For the purposes of this definition,\n+      \"control\" means (i) the power, direct or indirect, to cause the\n+      direction or management of such entity, whether by contract or\n+      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n+      outstanding shares, or (iii) beneficial ownership of such entity.\n+\n+      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n+      exercising permissions granted by this License.\n+\n+      \"Source\" form shall mean the preferred form for making modifications,\n+      including but not limited to software source code, documentation\n+      source, and configuration files.\n+\n+      \"Object\" form shall mean any form resulting from mechanical\n+      transformation or translation of a Source form, including but\n+      not limited to compiled object code, generated documentation,\n+      and conversions to other media types.\n+\n+      \"Work\" shall mean the work of authorship, whether in Source or\n+      Object form, made available under the License, as indicated by a\n+      copyright notice that is included in or attached to the work\n+      (an example is provided in the Appendix below).\n+\n+      \"Derivative Works\" shall mean any work, whether in Source or Object\n+      form, that is based on (or derived from) the Work and for which the\n+      editorial revisions, annotations, elaborations, or other modifications\n+      represent, as a whole, an original work of authorship. For the purposes\n+      of this License, Derivative Works shall not include works that remain\n+      separable from, or merely link (or bind by name) to the interfaces of,\n+      the Work and Derivative Works thereof.\n+\n+      \"Contribution\" shall mean any work of authorship, including\n+      the original version of the Work and any modifications or additions\n+      to that Work or Derivative Works thereof, that is intentionally\n+      submitted to Licensor for inclusion in the Work by the copyright owner\n+      or by an individual or Legal Entity authorized to submit on behalf of\n+      the copyright owner. For the purposes of this definition, \"submitted\"\n+      means any form of electronic, verbal, or written communication sent\n+      to the Licensor or its representatives, including but not limited to\n+      communication on electronic mailing lists, source code control systems,\n+      and issue tracking systems that are managed by, or on behalf of, the\n+      Licensor for the purpose of discussing and improving the Work, but\n+      excluding communication that is conspicuously marked or otherwise\n+      designated in writing by the copyright owner as \"Not a Contribution.\"\n+\n+      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n+      on behalf of whom a Contribution has been received by Licensor and\n+      subsequently incorporated within the Work.\n+\n+   2. Grant of Copyright License. Subject to the terms and conditions of\n+      this License, each Contributor hereby grants to You a perpetual,\n+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n+      copyright license to reproduce, prepare Derivative Works of,\n+      publicly display, publicly perform, sublicense, and distribute the\n+      Work and such Derivative Works in Source or Object form.\n+\n+   3. Grant of Patent License. Subject to the terms and conditions of\n+      this License, each Contributor hereby grants to You a perpetual,\n+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n+      (except as stated in this section) patent license to make, have made,\n+      use, offer to sell, sell, import, and otherwise transfer the Work,\n+      where such license applies only to those patent claims licensable\n+      by such Contributor that are necessarily infringed by their\n+      Contribution(s) alone or by combination of their Contribution(s)\n+      with the Work to which such Contribution(s) was submitted. If You\n+      institute patent litigation against any entity (including a\n+      cross-claim or counterclaim in a lawsuit) alleging that the Work\n+      or a Contribution incorporated within the Work constitutes direct\n+      or contributory patent infringement, then any patent licenses\n+      granted to You under this License for that Work shall terminate\n+      as of the date such litigation is filed.\n+\n+   4. Redistribution. You may reproduce and distribute copies of the\n+      Work or Derivative Works thereof in any medium, with or without\n+      modifications, and in Source or Object form, provided that You\n+      meet the following conditions:\n+\n+      (a) You must give any other recipients of the Work or\n+          Derivative Works a copy of this License; and\n+\n+      (b) You must cause any modified files to carry prominent notices\n+          stating that You changed the files; and\n+\n+      (c) You must retain, in the Source form of any Derivative Works\n+          that You distribute, all copyright, patent, trademark, and\n+          attribution notices from the Source form of the Work,\n+          excluding those notices that do not pertain to any part of\n+          the Derivative Works; and\n+\n+      (d) If the Work includes a \"NOTICE\" text file as part of its\n+          distribution, then any Derivative Works that You distribute must\n+          include a readable copy of the attribution notices contained\n+          within such NOTICE file, excluding those notices that do not\n+          pertain to any part of the Derivative Works, in at least one\n+          of the following places: within a NOTICE text file distributed\n+          as part of the Derivative Works; within the Source form or\n+          documentation, if provided along with the Derivative Works; or,\n+          within a display generated by the Derivative Works, if and\n+          wherever such third-party notices normally appear. The contents\n+          of the NOTICE file are for informational purposes only and\n+          do not modify the License. You may add Your own attribution\n+          notices within Derivative Works that You distribute, alongside\n+          or as an addendum to the NOTICE text from the Work, provided\n+          that such additional attribution notices cannot be construed\n+          as modifying the License.\n+\n+      You may add Your own copyright statement to Your modifications and\n+      may provide additional or different license terms and conditions\n+      for use, reproduction, or distribution of Your modifications, or\n+      for any such Derivative Works as a whole, provided Your use,\n+      reproduction, and distribution of the Work otherwise complies with\n+      the conditions stated in this License.\n+\n+   5. Submission of Contributions. Unless You explicitly state otherwise,\n+      any Contribution intentionally submitted for inclusion in the Work\n+      by You to the Licensor shall be under the terms and conditions of\n+      this License, without any additional terms or conditions.\n+      Notwithstanding the above, nothing herein shall supersede or modify\n+      the terms of any separate license agreement you may have executed\n+      with Licensor regarding such Contributions.\n+\n+   6. Trademarks. This License does not grant permission to use the trade\n+      names, trademarks, service marks, or product names of the Licensor,\n+      except as required for reasonable and customary use in describing the\n+      origin of the Work and reproducing the content of the NOTICE file.\n+\n+   7. Disclaimer of Warranty. Unless required by applicable law or\n+      agreed to in writing, Licensor provides the Work (and each\n+      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n+      implied, including, without limitation, any warranties or conditions\n+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n+      PARTICULAR PURPOSE. You are solely responsible for determining the\n+      appropriateness of using or redistributing the Work and assume any\n+      risks associated with Your exercise of permissions under this License.\n+\n+   8. Limitation of Liability. In no event and under no legal theory,\n+      whether in tort (including negligence), contract, or otherwise,\n+      unless required by applicable law (such as deliberate and grossly\n+      negligent acts) or agreed to in writing, shall any Contributor be\n+      liable to You for damages, including any direct, indirect, special,\n+      incidental, or consequential damages of any character arising as a\n+      result of this License or out of the use or inability to use the\n+      Work (including but not limited to damages for loss of goodwill,\n+      work stoppage, computer failure or malfunction, or any and all\n+      other commercial damages or losses), even if such Contributor\n+      has been advised of the possibility of such damages.\n+\n+   9. Accepting Warranty or Additional Liability. While redistributing\n+      the Work or Derivative Works thereof, You may choose to offer,\n+      and charge a fee for, acceptance of support, warranty, indemnity,\n+      or other liability obligations and/or rights consistent with this\n+      License. However, in accepting such obligations, You may act only\n+      on Your own behalf and on Your sole responsibility, not on behalf\n+      of any other Contributor, and only if You agree to indemnify,\n+      defend, and hold each Contributor harmless for any liability\n+      incurred by, or claims asserted against, such Contributor by reason\n+      of your accepting any such warranty or additional liability.\n+\n+   END OF TERMS AND CONDITIONS\n+\n+   APPENDIX: How to apply the Apache License to your work.\n+\n+      To apply the Apache License to your work, attach the following\n+      boilerplate notice, with the fields enclosed by brackets \"[]\"\n+      replaced with your own identifying information. (Don't include\n+      the brackets!)  The text should be enclosed in the appropriate\n+      comment syntax for the file format. We also recommend that a\n+      file or class name and description of purpose be included on the\n+      same \"printed page\" as the copyright notice for easier\n+      identification within third-party archives.\n+\n+   Copyright [yyyy] [name of copyright owner]\n+\n+   Licensed under the Apache License, Version 2.0 (the \"License\");\n+   you may not use this file except in compliance with the License.\n+   You may obtain a copy of the License at\n+\n+       http://www.apache.org/licenses/LICENSE-2.0\n+\n+   Unless required by applicable law or agreed to in writing, software\n+   distributed under the License is distributed on an \"AS IS\" BASIS,\n+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+   See the License for the specific language governing permissions and\n+   limitations under the License.\ndiff --git a/contrib/persistent-https/README b/contrib/persistent-https/README\nnew file mode 100644\nindex 0000000..e433c1a\n--- /dev/null\n+++ b/contrib/persistent-https/README\n@@ -0,0 +1,62 @@\n+git-remote-persistent-https\n+\n+The git-remote-persistent-https binary speeds up SSL operations\n+by running a daemon job (git-remote-persistent-https--proxy) that\n+keeps a connection open to a server.\n+\n+\n+PRE-BUILT BINARIES\n+\n+Darwin amd64:\n+https://commondatastorage.googleapis.com/git-remote-persistent-https/darwin_amd64.tar.gz\n+\n+Linux amd64:\n+https://commondatastorage.googleapis.com/git-remote-persistent-https/linux_amd64.tar.gz\n+\n+\n+INSTALLING\n+\n+Move all of the git-remote-persistent-http* binaries to a directory\n+in PATH.\n+\n+\n+USAGE\n+\n+HTTPS requests can be delegated to the proxy by using the\n+\"persistent-https\" scheme, e.g.\n+\n+git clone persistent-https://kernel.googlesource.com/pub/scm/git/git\n+\n+Likewise, .gitconfig can be updated as follows to rewrite https urls\n+to use persistent-https:\n+\n+[url \"persistent-https\"]\n+\tinsteadof = https\n+[url \"persistent-http\"]\n+\tinsteadof = http\n+\n+\n+#####################################################################\n+# BUILDING FROM SOURCE\n+#####################################################################\n+\n+LOCATION\n+\n+The source is available in the contrib/persistent-https directory of\n+the Git source repository. The Git source repository is available at\n+git://git.kernel.org/pub/scm/git/git.git/\n+https://kernel.googlesource.com/pub/scm/git/git\n+\n+\n+PREREQUISITES\n+\n+The code is written in Go (http://golang.org/) and the Go compiler is\n+required. Currently, the compiler must be built and installed from tip\n+of source, in order to include a fix in the reverse http proxy:\n+http://code.google.com/p/go/source/detail?r=a615b796570a2cd8591884767a7d67ede74f6648\n+\n+\n+BUILDING\n+\n+Run ./release.sh to build the binaries. See the section on\n+INSTALLING above.\ndiff --git a/contrib/persistent-https/client.go b/contrib/persistent-https/client.go\nnew file mode 100644\nindex 0000000..96e161d\n--- /dev/null\n+++ b/contrib/persistent-https/client.go\n@@ -0,0 +1,178 @@\n+// Copyright 2012 Google Inc. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package main\n+\n+import (\n+\t\"bufio\"\n+\t\"errors\"\n+\t\"fmt\"\n+\t\"net\"\n+\t\"net/url\"\n+\t\"os\"\n+\t\"os/exec\"\n+\t\"strings\"\n+\t\"syscall\"\n+\t\"time\"\n+)\n+\n+type Client struct {\n+\tProxyBin string\n+\tArgs     []string\n+\n+\tinsecure bool\n+}\n+\n+func (c *Client) Run() error {\n+\tif err := c.resolveArgs(); err != nil {\n+\t\treturn fmt.Errorf(\"resolveArgs() got error: %v\", err)\n+\t}\n+\n+\t// Connect to the proxy.\n+\tuconn, hconn, addr, err := c.connect()\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"connect() got error: %v\", err)\n+\t}\n+\t// Keep the unix socket connection open for the duration of the request.\n+\tdefer uconn.Close()\n+\t// Keep a connection to the HTTP server open, so no other user can\n+\t// bind on the same address so long as the process is running.\n+\tdefer hconn.Close()\n+\n+\t// Start the git-remote-http subprocess.\n+\tcargs := []string{\"-c\", fmt.Sprintf(\"http.proxy=%v\", addr), \"remote-http\"}\n+\tcargs = append(cargs, c.Args...)\n+\tcmd := exec.Command(\"git\", cargs...)\n+\tif !c.insecure {\n+\t\tcmd.Env = append(os.Environ(), \"GIT_GOOGLE_CREDENTIAL_CORPSSO_ENABLE=1\")\n+\t}\n+\tcmd.Stdin = os.Stdin\n+\tcmd.Stdout = os.Stdout\n+\tcmd.Stderr = os.Stderr\n+\tif err := cmd.Run(); err != nil {\n+\t\tif eerr, ok := err.(*exec.ExitError); ok {\n+\t\t\tif stat, ok := eerr.ProcessState.Sys().(syscall.WaitStatus); ok && stat.ExitStatus() != 0 {\n+\t\t\t\tos.Exit(stat.ExitStatus())\n+\t\t\t}\n+\t\t}\n+\t\treturn fmt.Errorf(\"git-remote-http subprocess got error: %v\", err)\n+\t}\n+\treturn nil\n+}\n+\n+func (c *Client) connect() (uconn net.Conn, hconn net.Conn, addr string, err error) {\n+\tuconn, err = DefaultSocket.Dial()\n+\tif err != nil {\n+\t\tif e, ok := err.(*net.OpError); ok && (os.IsNotExist(e.Err) || e.Err == syscall.ECONNREFUSED) {\n+\t\t\tif err = c.startProxy(); err == nil {\n+\t\t\t\tuconn, err = DefaultSocket.Dial()\n+\t\t\t}\n+\t\t}\n+\t\tif err != nil {\n+\t\t\treturn\n+\t\t}\n+\t}\n+\n+\tif addr, err = c.readAddr(uconn); err != nil {\n+\t\treturn\n+\t}\n+\n+\t// Open a tcp connection to the proxy.\n+\tif hconn, err = net.Dial(\"tcp\", addr); err != nil {\n+\t\treturn\n+\t}\n+\n+\t// Verify the address hasn't changed ownership.\n+\tvar addr2 string\n+\tif addr2, err = c.readAddr(uconn); err != nil {\n+\t\treturn\n+\t} else if addr != addr2 {\n+\t\terr = fmt.Errorf(\"address changed after connect. got %q, want %q\", addr2, addr)\n+\t\treturn\n+\t}\n+\treturn\n+}\n+\n+func (c *Client) readAddr(conn net.Conn) (string, error) {\n+\tconn.SetDeadline(time.Now().Add(5 * time.Second))\n+\tdata := make([]byte, 100)\n+\tn, err := conn.Read(data)\n+\tif err != nil {\n+\t\treturn \"\", fmt.Errorf(\"error reading unix socket: %v\", err)\n+\t} else if n == 0 {\n+\t\treturn \"\", errors.New(\"empty data response\")\n+\t}\n+\tconn.Write([]byte{1}) // Ack\n+\n+\tvar addr string\n+\tif addrs := strings.Split(string(data[:n]), \"\\n\"); len(addrs) != 2 {\n+\t\treturn \"\", fmt.Errorf(\"got %q, wanted 2 addresses\", data[:n])\n+\t} else if c.insecure {\n+\t\taddr = addrs[1]\n+\t} else {\n+\t\taddr = addrs[0]\n+\t}\n+\treturn addr, nil\n+}\n+\n+func (c *Client) startProxy() error {\n+\tcmd := exec.Command(c.ProxyBin)\n+\tcmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}\n+\tstdout, err := cmd.StdoutPipe()\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tdefer stdout.Close()\n+\tif err := cmd.Start(); err != nil {\n+\t\treturn err\n+\t}\n+\tresult := make(chan error)\n+\tgo func() {\n+\t\tbytes, _, err := bufio.NewReader(stdout).ReadLine()\n+\t\tif line := string(bytes); err == nil && line != \"OK\" {\n+\t\t\terr = fmt.Errorf(\"proxy returned %q, want \\\"OK\\\"\", line)\n+\t\t}\n+\t\tresult <- err\n+\t}()\n+\tselect {\n+\tcase err := <-result:\n+\t\treturn err\n+\tcase <-time.After(5 * time.Second):\n+\t\treturn errors.New(\"timeout waiting for proxy to start\")\n+\t}\n+\tpanic(\"not reachable\")\n+}\n+\n+func (c *Client) resolveArgs() error {\n+\tif nargs := len(c.Args); nargs == 0 {\n+\t\treturn errors.New(\"remote needed\")\n+\t} else if nargs > 2 {\n+\t\treturn fmt.Errorf(\"want at most 2 args, got %v\", c.Args)\n+\t}\n+\n+\t// Rewrite the url scheme to be http.\n+\tidx := len(c.Args) - 1\n+\trawurl := c.Args[idx]\n+\trurl, err := url.Parse(rawurl)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"invalid remote: %v\", err)\n+\t}\n+\tc.insecure = rurl.Scheme == \"persistent-http\"\n+\trurl.Scheme = \"http\"\n+\tc.Args[idx] = rurl.String()\n+\tif idx != 0 && c.Args[0] == rawurl {\n+\t\tc.Args[0] = c.Args[idx]\n+\t}\n+\treturn nil\n+}\ndiff --git a/contrib/persistent-https/main.go b/contrib/persistent-https/main.go\nnew file mode 100644\nindex 0000000..56199ec\n--- /dev/null\n+++ b/contrib/persistent-https/main.go\n@@ -0,0 +1,82 @@\n+// Copyright 2012 Google Inc. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+// The git-remote-persistent-https binary speeds up SSL operations by running\n+// a daemon job that keeps a connection open to a Git server. This ensures the\n+// git-remote-persistent-https--proxy is running and delegating execution\n+// to the git-remote-http binary with the http_proxy set to the daemon job.\n+// A unix socket is used to authenticate the proxy and discover the\n+// HTTP address. Note, both the client and proxy are included in the same\n+// binary.\n+package main\n+\n+import (\n+\t\"flag\"\n+\t\"fmt\"\n+\t\"log\"\n+\t\"os\"\n+\t\"strings\"\n+\t\"time\"\n+)\n+\n+var (\n+\tforceProxy = flag.Bool(\"proxy\", false, \"Whether to start the binary in proxy mode\")\n+\tproxyBin   = flag.String(\"proxy_bin\", \"git-remote-persistent-https--proxy\", \"Path to the proxy binary\")\n+\tprintLabel = flag.Bool(\"print_label\", false, \"Prints the build label for the binary\")\n+\n+\t// Variable that should be defined through the -X linker flag.\n+\t_BUILD_EMBED_LABEL string\n+)\n+\n+const (\n+\tdefaultMaxIdleDuration    = 24 * time.Hour\n+\tdefaultPollUpdateInterval = 15 * time.Minute\n+)\n+\n+func main() {\n+\tflag.Parse()\n+\tif *printLabel {\n+\t\t// Short circuit execution to print the build label\n+\t\tfmt.Println(buildLabel())\n+\t\treturn\n+\t}\n+\n+\tvar err error\n+\tif *forceProxy || strings.HasSuffix(os.Args[0], \"--proxy\") {\n+\t\tlog.SetPrefix(\"git-remote-persistent-https--proxy: \")\n+\t\tproxy := &Proxy{\n+\t\t\tBuildLabel:         buildLabel(),\n+\t\t\tMaxIdleDuration:    defaultMaxIdleDuration,\n+\t\t\tPollUpdateInterval: defaultPollUpdateInterval,\n+\t\t}\n+\t\terr = proxy.Run()\n+\t} else {\n+\t\tlog.SetPrefix(\"git-remote-persistent-https: \")\n+\t\tclient := &Client{\n+\t\t\tProxyBin: *proxyBin,\n+\t\t\tArgs:     flag.Args(),\n+\t\t}\n+\t\terr = client.Run()\n+\t}\n+\tif err != nil {\n+\t\tlog.Fatalln(err)\n+\t}\n+}\n+\n+func buildLabel() string {\n+\tif _BUILD_EMBED_LABEL == \"\" {\n+\t\tlog.Println(\"unlabeled build; build with release.sh to label\")\n+\t}\n+\treturn _BUILD_EMBED_LABEL\n+}\ndiff --git a/contrib/persistent-https/proxy.go b/contrib/persistent-https/proxy.go\nnew file mode 100644\nindex 0000000..bb0cdba\n--- /dev/null\n+++ b/contrib/persistent-https/proxy.go\n@@ -0,0 +1,190 @@\n+// Copyright 2012 Google Inc. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package main\n+\n+import (\n+\t\"fmt\"\n+\t\"log\"\n+\t\"net\"\n+\t\"net/http\"\n+\t\"net/http/httputil\"\n+\t\"os\"\n+\t\"os/exec\"\n+\t\"os/signal\"\n+\t\"sync\"\n+\t\"syscall\"\n+\t\"time\"\n+)\n+\n+type Proxy struct {\n+\tBuildLabel         string\n+\tMaxIdleDuration    time.Duration\n+\tPollUpdateInterval time.Duration\n+\n+\tul        net.Listener\n+\thttpAddr  string\n+\thttpsAddr string\n+}\n+\n+func (p *Proxy) Run() error {\n+\thl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"http listen failed: %v\", err)\n+\t}\n+\tdefer hl.Close()\n+\n+\thsl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"https listen failed: %v\", err)\n+\t}\n+\tdefer hsl.Close()\n+\n+\tp.ul, err = DefaultSocket.Listen()\n+\tif err != nil {\n+\t\tc, derr := DefaultSocket.Dial()\n+\t\tif derr == nil {\n+\t\t\tc.Close()\n+\t\t\tfmt.Println(\"OK\\nA proxy is already running... exiting\")\n+\t\t\treturn nil\n+\t\t} else if e, ok := derr.(*net.OpError); ok && e.Err == syscall.ECONNREFUSED {\n+\t\t\t// Nothing is listening on the socket, unlink it and try again.\n+\t\t\tsyscall.Unlink(DefaultSocket.Path())\n+\t\t\tp.ul, err = DefaultSocket.Listen()\n+\t\t}\n+\t\tif err != nil {\n+\t\t\treturn fmt.Errorf(\"unix listen failed on %v: %v\", DefaultSocket.Path(), err)\n+\t\t}\n+\t}\n+\tdefer p.ul.Close()\n+\tgo p.closeOnSignal()\n+\tgo p.closeOnUpdate()\n+\n+\tp.httpAddr = hl.Addr().String()\n+\tp.httpsAddr = hsl.Addr().String()\n+\tfmt.Printf(\"OK\\nListening on unix socket=%v http=%v https=%v\\n\",\n+\t\tp.ul.Addr(), p.httpAddr, p.httpsAddr)\n+\n+\tresult := make(chan error, 2)\n+\tgo p.serveUnix(result)\n+\tgo func() {\n+\t\tresult <- http.Serve(hl, &httputil.ReverseProxy{\n+\t\t\tFlushInterval: 500 * time.Millisecond,\n+\t\t\tDirector:      func(r *http.Request) {},\n+\t\t})\n+\t}()\n+\tgo func() {\n+\t\tresult <- http.Serve(hsl, &httputil.ReverseProxy{\n+\t\t\tFlushInterval: 500 * time.Millisecond,\n+\t\t\tDirector: func(r *http.Request) {\n+\t\t\t\tr.URL.Scheme = \"https\"\n+\t\t\t},\n+\t\t})\n+\t}()\n+\treturn <-result\n+}\n+\n+type socketContext struct {\n+\tsync.WaitGroup\n+\tmutex sync.Mutex\n+\tlast  time.Time\n+}\n+\n+func (sc *socketContext) Done() {\n+\tsc.mutex.Lock()\n+\tdefer sc.mutex.Unlock()\n+\tsc.last = time.Now()\n+\tsc.WaitGroup.Done()\n+}\n+\n+func (p *Proxy) serveUnix(result chan<- error) {\n+\tsockCtx := &socketContext{}\n+\tgo p.closeOnIdle(sockCtx)\n+\n+\tvar err error\n+\tfor {\n+\t\tvar uconn net.Conn\n+\t\tuconn, err = p.ul.Accept()\n+\t\tif err != nil {\n+\t\t\terr = fmt.Errorf(\"accept failed: %v\", err)\n+\t\t\tbreak\n+\t\t}\n+\t\tsockCtx.Add(1)\n+\t\tgo p.handleUnixConn(sockCtx, uconn)\n+\t}\n+\tsockCtx.Wait()\n+\tresult <- err\n+}\n+\n+func (p *Proxy) handleUnixConn(sockCtx *socketContext, uconn net.Conn) {\n+\tdefer sockCtx.Done()\n+\tdefer uconn.Close()\n+\tdata := []byte(fmt.Sprintf(\"%v\\n%v\", p.httpsAddr, p.httpAddr))\n+\tuconn.SetDeadline(time.Now().Add(5 * time.Second))\n+\tfor i := 0; i < 2; i++ {\n+\t\tif n, err := uconn.Write(data); err != nil {\n+\t\t\tlog.Printf(\"error sending http addresses: %+v\\n\", err)\n+\t\t\treturn\n+\t\t} else if n != len(data) {\n+\t\t\tlog.Printf(\"sent %d data bytes, wanted %d\\n\", n, len(data))\n+\t\t\treturn\n+\t\t}\n+\t\tif _, err := uconn.Read([]byte{0, 0, 0, 0}); err != nil {\n+\t\t\tlog.Printf(\"error waiting for Ack: %+v\\n\", err)\n+\t\t\treturn\n+\t\t}\n+\t}\n+\t// Wait without a deadline for the client to finish via EOF\n+\tuconn.SetDeadline(time.Time{})\n+\tuconn.Read([]byte{0, 0, 0, 0})\n+}\n+\n+func (p *Proxy) closeOnIdle(sockCtx *socketContext) {\n+\tfor d := p.MaxIdleDuration; d > 0; {\n+\t\ttime.Sleep(d)\n+\t\tsockCtx.Wait()\n+\t\tsockCtx.mutex.Lock()\n+\t\tif d = sockCtx.last.Add(p.MaxIdleDuration).Sub(time.Now()); d <= 0 {\n+\t\t\tlog.Println(\"graceful shutdown from idle timeout\")\n+\t\t\tp.ul.Close()\n+\t\t}\n+\t\tsockCtx.mutex.Unlock()\n+\t}\n+}\n+\n+func (p *Proxy) closeOnUpdate() {\n+\tfor {\n+\t\ttime.Sleep(p.PollUpdateInterval)\n+\t\tif out, err := exec.Command(os.Args[0], \"--print_label\").Output(); err != nil {\n+\t\t\tlog.Printf(\"error polling for updated binary: %v\\n\", err)\n+\t\t} else if s := string(out[:len(out)-1]); p.BuildLabel != s {\n+\t\t\tlog.Printf(\"graceful shutdown from updated binary: %q --> %q\\n\", p.BuildLabel, s)\n+\t\t\tp.ul.Close()\n+\t\t\tbreak\n+\t\t}\n+\t}\n+}\n+\n+func (p *Proxy) closeOnSignal() {\n+\tch := make(chan os.Signal, 10)\n+\tsignal.Notify(ch, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGHUP))\n+\tsig := <-ch\n+\tp.ul.Close()\n+\tswitch sig {\n+\tcase os.Signal(syscall.SIGHUP):\n+\t\tlog.Printf(\"graceful shutdown from signal: %v\\n\", sig)\n+\tdefault:\n+\t\tlog.Fatalf(\"exiting from signal: %v\\n\", sig)\n+\t}\n+}\ndiff --git a/contrib/persistent-https/release.sh b/contrib/persistent-https/release.sh\nnew file mode 100755\nindex 0000000..9ce2fe2\n--- /dev/null\n+++ b/contrib/persistent-https/release.sh\n@@ -0,0 +1,45 @@\n+#!/bin/bash\n+#\n+# Copyright 2012 Google Inc. All Rights Reserved.\n+#\n+# Licensed under the Apache License, Version 2.0 (the \"License\");\n+# you may not use this file except in compliance with the License.\n+# You may obtain a copy of the License at\n+#\n+#     http://www.apache.org/licenses/LICENSE-2.0\n+#\n+# Unless required by applicable law or agreed to in writing, software\n+# distributed under the License is distributed on an \"AS IS\" BASIS,\n+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+# See the License for the specific language governing permissions and\n+# limitations under the License.\n+\n+# Builds the binaries for release. The script is mainly used to set the\n+#_BUILD_EMBED_LABEL to the current system time through a linker flag.\n+\n+set -e\n+\n+rm -f git-remote-persistent-https \\\n+  git-remote-persistent-https--proxy \\\n+  git-remote-persistent-http\n+\n+BUILD_LABEL=`date +\"%s\"`\n+go build -o git-remote-persistent-https \\\n+  -ldflags \"-X main._BUILD_EMBED_LABEL $BUILD_LABEL\"\n+\n+chmod 555 git-remote-persistent-https\n+ln -s git-remote-persistent-https git-remote-persistent-https--proxy\n+ln -s git-remote-persistent-https git-remote-persistent-http\n+\n+LIVE_LABEL=`./git-remote-persistent-https --print_label`\n+\n+tput setaf 2 # green\n+echo \"Successfully built with label: $LIVE_LABEL\"\n+tput sgr0\n+\n+echo\n+echo \"Move the following binaries to a directory in your PATH:\"\n+echo \"  git-remote-persistent-https\"\n+echo \"  git-remote-persistent-https--proxy\"\n+echo \"  git-remote-persistent-http\"\n+\ndiff --git a/contrib/persistent-https/socket.go b/contrib/persistent-https/socket.go\nnew file mode 100644\nindex 0000000..193b911\n--- /dev/null\n+++ b/contrib/persistent-https/socket.go\n@@ -0,0 +1,97 @@\n+// Copyright 2012 Google Inc. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package main\n+\n+import (\n+\t\"fmt\"\n+\t\"log\"\n+\t\"net\"\n+\t\"os\"\n+\t\"path/filepath\"\n+\t\"syscall\"\n+)\n+\n+// A Socket is a wrapper around a Unix socket that verifies directory\n+// permissions.\n+type Socket struct {\n+\tDir string\n+}\n+\n+func defaultDir() string {\n+\tsockPath := \".git-credential-cache\"\n+\tif home := os.Getenv(\"HOME\"); home != \"\" {\n+\t\treturn filepath.Join(home, sockPath)\n+\t}\n+\tlog.Printf(\"socket: cannot find HOME path. using relative directory %q for socket\", sockPath)\n+\treturn sockPath\n+}\n+\n+// DefaultSocket is a Socket in the $HOME/.git-credential-cache directory.\n+var DefaultSocket = Socket{Dir: defaultDir()}\n+\n+// Listen announces the local network address of the unix socket. The\n+// permissions on the socket directory are verified before attempting\n+// the actual listen.\n+func (s Socket) Listen() (net.Listener, error) {\n+\tnetwork, addr := \"unix\", s.Path()\n+\tif err := s.mkdir(); err != nil {\n+\t\treturn nil, &net.OpError{Op: \"listen\", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}\n+\t}\n+\treturn net.Listen(network, addr)\n+}\n+\n+// Dial connects to the unix socket. The permissions on the socket directory\n+// are verified before attempting the actual dial.\n+func (s Socket) Dial() (net.Conn, error) {\n+\tnetwork, addr := \"unix\", s.Path()\n+\tif err := s.checkPermissions(); err != nil {\n+\t\treturn nil, &net.OpError{Op: \"dial\", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}\n+\t}\n+\treturn net.Dial(network, addr)\n+}\n+\n+// Path returns the fully specified file name of the unix socket.\n+func (s Socket) Path() string {\n+\treturn filepath.Join(s.Dir, \"persistent-https-proxy-socket\")\n+}\n+\n+func (s Socket) mkdir() error {\n+\tif err := s.checkPermissions(); err == nil {\n+\t\treturn nil\n+\t} else if !os.IsNotExist(err) {\n+\t\treturn err\n+\t}\n+\tif err := os.MkdirAll(s.Dir, 0700); err != nil {\n+\t\treturn err\n+\t}\n+\treturn s.checkPermissions()\n+}\n+\n+func (s Socket) checkPermissions() error {\n+\tfi, err := os.Stat(s.Dir)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tif !fi.IsDir() {\n+\t\treturn fmt.Errorf(\"socket: got file, want directory for %q\", s.Dir)\n+\t}\n+\tif fi.Mode().Perm() != 0700 {\n+\t\treturn fmt.Errorf(\"socket: got perm %o, want 700 for %q\", fi.Mode().Perm(), s.Dir)\n+\t}\n+\tif st := fi.Sys().(*syscall.Stat_t); int(st.Uid) != os.Getuid() {\n+\t\treturn fmt.Errorf(\"socket: got uid %d, want %d for %q\", st.Uid, os.Getuid(), s.Dir)\n+\t}\n+\treturn nil\n+}\ndiff --git a/contrib/persistent-https/tar.sh b/contrib/persistent-https/tar.sh\nnew file mode 100755\nindex 0000000..0288753\n--- /dev/null\n+++ b/contrib/persistent-https/tar.sh\n@@ -0,0 +1,40 @@\n+#!/bin/bash\n+#\n+# Copyright 2012 Google Inc. All Rights Reserved.\n+#\n+# Licensed under the Apache License, Version 2.0 (the \"License\");\n+# you may not use this file except in compliance with the License.\n+# You may obtain a copy of the License at\n+#\n+#     http://www.apache.org/licenses/LICENSE-2.0\n+#\n+# Unless required by applicable law or agreed to in writing, software\n+# distributed under the License is distributed on an \"AS IS\" BASIS,\n+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+# See the License for the specific language governing permissions and\n+# limitations under the License.\n+\n+# Builds the binaries for release and produces them in a tarball.\n+\n+set -e\n+\n+OUT=$(go env GOOS)_$(go env GOARCH).tar.gz\n+\n+rm -rf bin git-remote-persistent-https ${OUT}\n+\n+./release.sh\n+\n+echo\n+tput setaf 2 # green\n+echo \"Creating ${OUT}:\"\n+tput sgr0\n+\n+mkdir bin\n+cp COPYING README bin\n+chmod 444 bin/*\n+mv git-remote-persistent-http* bin\n+mv bin git-remote-persistent-https\n+\n+tar -czvf ${OUT} git-remote-persistent-https\n+\n+rm -rf git-remote-persistent-https\n-- \n1.7.9.2\n"},{"id":"192093","messageId":"7v62blxx2m.fsf@alter.siamese.dyndns.org","threadId":"30601","inReplyTo":"1337792767-18914-1-git-send-email-cranger@google.com","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-05-24T18:17:53Z","receivedAt":"2012-05-24T18:17:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Colby Ranger <cranger@google.com> writes:\n\n> Git over HTTPS has a high request startup latency, since the SSL\n> negotiation can take up to a second. In order to reduce this latency,\n> connections should be left open to the Git server across requests\n> (or invocations of the git commandline).\n>\n> Reduce SSL startup latency by running a daemon job that keeps\n> connections open to a Git server. The daemon job\n> (git-remote-persistent-https--proxy) is started on the first request\n> through the client binary (git-remote-persistent-https) and remains\n> running for 24 hours after the last request, or until a new daemon\n> binary is placed in the PATH. The client determines the daemon's\n> HTTP address by communicating over a UNIX socket with the daemon.\n>>From there, the rest of the Git protocol work is delegated to the\n> \"git-remote-http\" binary, with the environment's http_proxy set to\n> the daemon.\n>\n> Signed-off-by: Colby Ranger <cranger@google.com>\n\nClever.  Do you have some numbers?\n\nIf this persistent proxy weren't in the picture, the git client would\ndirectly delegate its communication with the origin server to\ngit-remote-https, and git-remote-https would interact with the credential\nAPI to handle authentication for \"https://origin.server.xz/repo\".\n\nHow does the persistent proxy sitting in between your git client and the\norigin server and/or the real proxy you have at the perimeter of your\nnetwork interact with respect to authentication/authorization to access\nthe repository or the real proxy?  They will talk with the persistent\nproxy, and the persistent proxy will talk with the git client via\ngit-remote-http, and I am assuming that this last git-remote-http will be\nthe one that uses the credential API.  Would it ask credential for\n\"https://origin.server.xz/repo\"?  \"http://origin.server.xz/repo\"?  Or\nwould it be \"persistent-https://origin.server.xz.repo\"?\n\n>  contrib/persistent-https/COPYING    |  202 +++++++++++++++++++++++++++++++++++\n\nI do not mind carrying this in the contrib/ area (I am assuming that\ndistributing Apache licensed software that does not link with GPLv2 core\nis OK).  It may be just me, but a file called COPYING that does not have\nGPL text in it was a bit surprising.  I wonder if it is more customary to\ncall it either LICENSE (or perhaps LICENSE-2.0)?\n\n>  contrib/persistent-https/README     |   62 +++++++++++\n>  contrib/persistent-https/client.go  |  178 ++++++++++++++++++++++++++++++\n>  contrib/persistent-https/main.go    |   82 ++++++++++++++\n>  contrib/persistent-https/proxy.go   |  190 ++++++++++++++++++++++++++++++++\n>  contrib/persistent-https/release.sh |   45 ++++++++\n>  contrib/persistent-https/socket.go  |   97 +++++++++++++++++\n>  contrib/persistent-https/tar.sh     |   40 +++++++\n>  8 files changed, 896 insertions(+)\n\nIt might deserve its own contrib/persistent-https/Makefile in addition to\nyour internal \"release\" scripts, though.\n"},{"id":"192102","messageId":"CAJo=hJt=q-ZnLrqzcfGrKNcao2MPDSRt3Y_r2OOfu75++N+3zw@mail.gmail.com","threadId":"30601","inReplyTo":"7v62blxx2m.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2012-05-24T19:33:08Z","receivedAt":"2012-05-24T19:33:08Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Thu, May 24, 2012 at 11:17 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Colby Ranger <cranger@google.com> writes:\n>\n>> Git over HTTPS has a high request startup latency, since the SSL\n>> negotiation can take up to a second. In order to reduce this latency,\n>> connections should be left open to the Git server across requests\n>> (or invocations of the git commandline).\n>>\n>> Reduce SSL startup latency by running a daemon job that keeps\n>> connections open to a Git server. The daemon job\n>> (git-remote-persistent-https--proxy) is started on the first request\n>> through the client binary (git-remote-persistent-https) and remains\n>> running for 24 hours after the last request, or until a new daemon\n>> binary is placed in the PATH. The client determines the daemon's\n>> HTTP address by communicating over a UNIX socket with the daemon.\n>>>From there, the rest of the Git protocol work is delegated to the\n>> \"git-remote-http\" binary, with the environment's http_proxy set to\n>> the daemon.\n>>\n>> Signed-off-by: Colby Ranger <cranger@google.com>\n>\n> Clever.  Do you have some numbers?\n\n$ (for i in {1..5}; do time git ls-remote\nhttps://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux\n>/dev/null;done) 2>&1 | grep real\nreal\t0m0.193s\nreal\t0m0.175s\nreal\t0m0.191s\nreal\t0m0.173s\nreal\t0m0.194s\n\n$ (for i in {1..5}; do time git ls-remote\npersistent-https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux\n>/dev/null;done) 2>&1 | grep real\nreal\t0m0.208s\nreal\t0m0.085s\nreal\t0m0.079s\nreal\t0m0.067s\nreal\t0m0.059s\n\nThat initial 208ms run was the local proxy starting, and the\nconnection being established. Its similar to the normal case.\nSubsequent executions have lower latency.\n\n> If this persistent proxy weren't in the picture, the git client would\n> directly delegate its communication with the origin server to\n> git-remote-https, and git-remote-https would interact with the credential\n> API to handle authentication for \"https://origin.server.xz/repo\".\n\nYes.\n\n> How does the persistent proxy sitting in between your git client and the\n> origin server and/or the real proxy you have at the perimeter of your\n> network interact with respect to authentication/authorization to access\n> the repository or the real proxy?  They will talk with the persistent\n> proxy, and the persistent proxy will talk with the git client via\n> git-remote-http, and I am assuming that this last git-remote-http will be\n> the one that uses the credential API.\n\nYes.\n\n>  Would it ask credential for\n> \"https://origin.server.xz/repo\"?  \"http://origin.server.xz/repo\"?  Or\n> would it be \"persistent-https://origin.server.xz.repo\"?\n\nIIRC it asks for \"http://origin.server.xz/repo\".\n\nThe persistent-https code tells the git credential helper the\nconnection is \"secure\" (that is the proxy will use SSL as it exits the\nlocal machine) by setting GIT_GOOGLE_CREDENTIAL_CORPSSO_ENABLE=1 in\nthe environment. This leaked from our internal version of the proxy,\nColby was supposed to scrub this string before open sourcing. :-)\n\nSo now everyone knows $DAYJOB = Google, we have a credential helper,\nand it supports some sort of corporate single sign on. Whee.\n\n>>  contrib/persistent-https/COPYING    |  202 +++++++++++++++++++++++++++++++++++\n>\n> I do not mind carrying this in the contrib/ area (I am assuming that\n> distributing Apache licensed software that does not link with GPLv2 core\n> is OK).  It may be just me, but a file called COPYING that does not have\n> GPL text in it was a bit surprising.  I wonder if it is more customary to\n> call it either LICENSE (or perhaps LICENSE-2.0)?\n\nAgreed, LICENSE or LICENSE-2.0 is the right name for this file, not COPYING.\n\n>>  contrib/persistent-https/README     |   62 +++++++++++\n>>  contrib/persistent-https/client.go  |  178 ++++++++++++++++++++++++++++++\n>>  contrib/persistent-https/main.go    |   82 ++++++++++++++\n>>  contrib/persistent-https/proxy.go   |  190 ++++++++++++++++++++++++++++++++\n>>  contrib/persistent-https/release.sh |   45 ++++++++\n>>  contrib/persistent-https/socket.go  |   97 +++++++++++++++++\n>>  contrib/persistent-https/tar.sh     |   40 +++++++\n>>  8 files changed, 896 insertions(+)\n>\n> It might deserve its own contrib/persistent-https/Makefile in addition to\n> your internal \"release\" scripts, though.\n\nFair point.\n"},{"id":"192109","messageId":"20120524202930.GB18936@sigill.intra.peff.net","threadId":"30601","inReplyTo":"CAJo=hJt=q-ZnLrqzcfGrKNcao2MPDSRt3Y_r2OOfu75++N+3zw@mail.gmail.com","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-05-24T20:29:30Z","receivedAt":"2012-05-24T20:29:30Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 24, 2012 at 12:33:08PM -0700, Shawn O. Pearce wrote:\n\n> $ (for i in {1..5}; do time git ls-remote\n> persistent-https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux\n> >/dev/null;done) 2>&1 | grep real\n> real\t0m0.208s\n> real\t0m0.085s\n> real\t0m0.079s\n> real\t0m0.067s\n> real\t0m0.059s\n\nNice numbers. And as clever as I find this helper-wrapping-a-helper\nsolution, I wonder if the right layer for a fix isn't inside curl. It\nalready keeps an ssl session-id cache in memory; how hard would it be to\nturn that into an on-disk cache?\n\nI don't think that is grounds for rejecting this patch; obviously it is\nworking for you guys, and it is available right now, and it is only\ngoing into contrib/ anyway. But a curl solution seems like a cleaner\nlong-term fix, and would benefit all curl users. It is even mentioned in\ncurl's doc/TODO file. :)\n\n-Peff\n"},{"id":"192113","messageId":"CAJo=hJsfRr7MKcmn8owuyYf7LJ2jzpqgCfkijQ6ahrZfz3CDpw@mail.gmail.com","threadId":"30601","inReplyTo":"20120524202930.GB18936@sigill.intra.peff.net","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2012-05-24T20:42:35Z","receivedAt":"2012-05-24T20:42:35Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Thu, May 24, 2012 at 1:29 PM, Jeff King <peff@peff.net> wrote:\n> On Thu, May 24, 2012 at 12:33:08PM -0700, Shawn O. Pearce wrote:\n>\n>> $ (for i in {1..5}; do time git ls-remote\n>> persistent-https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux\n>> >/dev/null;done) 2>&1 | grep real\n>> real  0m0.208s\n>> real  0m0.085s\n>> real  0m0.079s\n>> real  0m0.067s\n>> real  0m0.059s\n>\n> Nice numbers. And as clever as I find this helper-wrapping-a-helper\n> solution, I wonder if the right layer for a fix isn't inside curl. It\n> already keeps an ssl session-id cache in memory; how hard would it be to\n> turn that into an on-disk cache?\n>\n> I don't think that is grounds for rejecting this patch; obviously it is\n> working for you guys, and it is available right now, and it is only\n> going into contrib/ anyway. But a curl solution seems like a cleaner\n> long-term fix, and would benefit all curl users. It is even mentioned in\n> curl's doc/TODO file. :)\n\nWell, this helper \"solution\" also has the benefit of HTTP keep-alive\nworking across Git command invocations. Its common for servers to use\na 5 minute keep-alive on an HTTP 1.1 connection. Git-over-HTTP\ncommonly uses Transfer-Encoding: chunked on replies, so keep-alive\nwill generally just work, even though a pack stream's length isn't\nknown in advance. Because the helper is an external process holding\nthat connection open, we also benefit from being able to reuse an\nexisting TCP connection to the server.\n\nBut sure, it would be nice if libcurl was able to share SSL sessions\nacross Git command invocations without this black magic proxy thing.\n"},{"id":"192114","messageId":"alpine.DEB.2.00.1205242238430.4374@tvnag.unkk.fr","threadId":"30601","inReplyTo":"20120524202930.GB18936@sigill.intra.peff.net","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Daniel Stenberg","fromEmail":"daniel@haxx.se","sentAt":"2012-05-24T20:42:53Z","receivedAt":"2012-05-24T20:42:53Z","isPatch":true,"sender":{"key":"daniel@haxx.se","avatar":"https://gravatar.com/avatar/69fdca87edd17cee21ca2e79fc2ff671d644603c3dc27167430f3cd3dbab7ba8?d=mp&s=160"},"body":"On Thu, 24 May 2012, Jeff King wrote:\n\n> Nice numbers. And as clever as I find this helper-wrapping-a-helper \n> solution, I wonder if the right layer for a fix isn't inside curl. It \n> already keeps an ssl session-id cache in memory; how hard would it be to \n> turn that into an on-disk cache?\n>\n> I don't think that is grounds for rejecting this patch; obviously it is \n> working for you guys, and it is available right now, and it is only going \n> into contrib/ anyway. But a curl solution seems like a cleaner long-term \n> fix, and would benefit all curl users. It is even mentioned in curl's \n> doc/TODO file. :)\n\nYes, in the curl project we would certainly not object to being able to \nexport/import SSL-session-ids (which then could be used to implement a cache, \non disk or in memory). However, I don't know how easily done that is to \nimplement with the different TLS libraries in use.\n\n-- \n\n  / daniel.haxx.se\n"},{"id":"192115","messageId":"CAJo=hJt4Suu5=kYh3kyOpDSPKr0jiZka=9g6U841PbiWCAQ_pQ@mail.gmail.com","threadId":"30601","inReplyTo":"CAJo=hJsfRr7MKcmn8owuyYf7LJ2jzpqgCfkijQ6ahrZfz3CDpw@mail.gmail.com","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2012-05-24T20:46:36Z","receivedAt":"2012-05-24T20:46:36Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Thu, May 24, 2012 at 1:42 PM, Shawn Pearce <spearce@spearce.org> wrote:\n> On Thu, May 24, 2012 at 1:29 PM, Jeff King <peff@peff.net> wrote:\n>> On Thu, May 24, 2012 at 12:33:08PM -0700, Shawn O. Pearce wrote:\n>>\n>>> $ (for i in {1..5}; do time git ls-remote\n>>> persistent-https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux\n>>> >/dev/null;done) 2>&1 | grep real\n>>> real  0m0.208s\n>>> real  0m0.085s\n>>> real  0m0.079s\n>>> real  0m0.067s\n>>> real  0m0.059s\n>>\n>> Nice numbers. And as clever as I find this helper-wrapping-a-helper\n>> solution, I wonder if the right layer for a fix isn't inside curl. It\n>> already keeps an ssl session-id cache in memory; how hard would it be to\n>> turn that into an on-disk cache?\n...\n> Well, this helper \"solution\" also has the benefit of HTTP keep-alive\n> working across Git command invocations.\n\nHere is plaintext HTTP, where the benefit is from HTTP keep-alive:\n\n  (for i in {1..5}; do time git ls-remote\nhttp://android.googlesource.com/tools/repo >/dev/null;done) 2>&1  |\ngrep real\n  real\t0m0.098s\n  real\t0m0.097s\n  real\t0m0.106s\n  real\t0m0.095s\n  real\t0m0.105s\n\n  (for i in {1..5}; do time git ls-remote\npersistent-http://android.googlesource.com/tools/repo >/dev/null;done)\n2>&1  | grep real\n  real\t0m0.134s\n  real\t0m0.065s\n  real\t0m0.063s\n  real\t0m0.061s\n  real\t0m0.067s\n\nNotice we still save 30ms or so in this case. That is about the RTT\nfor my workstation to that server. :-)\n"},{"id":"192118","messageId":"20120524205107.GA21329@sigill.intra.peff.net","threadId":"30601","inReplyTo":"CAJo=hJt4Suu5=kYh3kyOpDSPKr0jiZka=9g6U841PbiWCAQ_pQ@mail.gmail.com","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2012-05-24T20:51:07Z","receivedAt":"2012-05-24T20:51:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 24, 2012 at 01:46:36PM -0700, Shawn O. Pearce wrote:\n\n> >> Nice numbers. And as clever as I find this helper-wrapping-a-helper\n> >> solution, I wonder if the right layer for a fix isn't inside curl. It\n> >> already keeps an ssl session-id cache in memory; how hard would it be to\n> >> turn that into an on-disk cache?\n> ...\n> > Well, this helper \"solution\" also has the benefit of HTTP keep-alive\n> > working across Git command invocations.\n\nTrue. It also works even when the server does not support ssl id\ncaching.\n\n> Here is plaintext HTTP, where the benefit is from HTTP keep-alive:\n> \n>   (for i in {1..5}; do time git ls-remote\n> http://android.googlesource.com/tools/repo >/dev/null;done) 2>&1  |\n> grep real\n>   real\t0m0.098s\n>   real\t0m0.097s\n>   real\t0m0.106s\n>   real\t0m0.095s\n>   real\t0m0.105s\n> \n>   (for i in {1..5}; do time git ls-remote\n> persistent-http://android.googlesource.com/tools/repo >/dev/null;done)\n> 2>&1  | grep real\n>   real\t0m0.134s\n>   real\t0m0.065s\n>   real\t0m0.063s\n>   real\t0m0.061s\n>   real\t0m0.067s\n> \n> Notice we still save 30ms or so in this case. That is about the RTT\n> for my workstation to that server. :-)\n\nNice. I assumed most of your speedup was coming from dropping the SSL\nsetup, but it seems that a good chunk of it is just the TCP setup.\nThanks for providing numbers.\n\n-Peff\n"},{"id":"192119","messageId":"7vlikhwbdc.fsf@alter.siamese.dyndns.org","threadId":"30601","inReplyTo":"CAJo=hJt=q-ZnLrqzcfGrKNcao2MPDSRt3Y_r2OOfu75++N+3zw@mail.gmail.com","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-05-24T20:51:59Z","receivedAt":"2012-05-24T20:51:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Shawn Pearce <spearce@spearce.org> writes:\n\n> The persistent-https code tells the git credential helper the\n> connection is \"secure\" (that is the proxy will use SSL as it exits the\n> local machine) by setting GIT_GOOGLE_CREDENTIAL_CORPSSO_ENABLE=1 in\n> the environment. This leaked from our internal version of the proxy,\n> Colby was supposed to scrub this string before open sourcing. :-)\n>\n> So now everyone knows $DAYJOB = Google, we have a credential helper,\n> and it supports some sort of corporate single sign on. Whee.\n\nIt is obviously needed to drop that bit from the public version (and have\nyou guys keep an internal fork to add it back), but I have to wonder if\nthis is an indication that something like that is useful in general.\n\nMore specifically, this environment variable is a way to tell the wrapped\nhelper who is wrapping it.  Users outside Google's environment of the\npersistent-https helper obviously would not care about the corporate\nsanitary sewer overflow mechanism, but they may have a similar need to\ntweak what happens inside the git-remote-http that is driven by the\npersistent helper. They would not care about \"we can enable corpsso\", but\nthey would benefit from knowing that either:\n\n (1) the connection is \"secure\" (by the definition above); or\n (2) the connection is going to this particular helper.\n\nConceptually, an approach to allow chain of helpers tell which one(s) of\ndefined set of attributes (e.g. \"secure\") are in effect, e.g. (1), might\nbe cleaner, but it probably is a bit too early overengineering (I do not\nthink we know if there is a good set of common attributes various helpers\nmight want to implement upstream and pay attention downstream) at this\npoint.  But at least it might not hurt to give the downstream to find out\nwhat upstream is driving them.\n\nHrm?\n"},{"id":"192440","messageId":"CAFFbUKJz=-KdNJSgqwkaBNAL6gyBzjxCkVjKgAmHQ7ztzGh=UA@mail.gmail.com","threadId":"30601","inReplyTo":"7vlikhwbdc.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Add persistent-https to contrib","fromName":"Colby Ranger","fromEmail":"cranger@google.com","sentAt":"2012-05-29T21:29:14Z","receivedAt":"2012-05-29T21:29:14Z","isPatch":true,"sender":{"key":"cranger@google.com","avatar":"https://avatars.githubusercontent.com/u/2480655?v=4"},"body":"> I do not mind carrying this in the contrib/ area (I am assuming that\n> distributing Apache licensed software that does not link with GPLv2 core\n> is OK).  It may be just me, but a file called COPYING that does not have\n> GPL text in it was a bit surprising.  I wonder if it is more customary to\n> call it either LICENSE (or perhaps LICENSE-2.0)?\n\nAgreed. I'll change the name to LICENSE.\n\n\n> It might deserve its own contrib/persistent-https/Makefile in addition to\n> your internal \"release\" scripts, though.\n\nOK. I'll update the code to use a simple Makefile for everything,\ninstead of the shell scripts.\n\n\n> More specifically, this environment variable is a way to tell the wrapped\n> helper who is wrapping it.  Users outside Google's environment of the\n> persistent-https helper obviously would not care about the corporate\n> sanitary sewer overflow mechanism, but they may have a similar need to\n> tweak what happens inside the git-remote-http that is driven by the\n> persistent helper. They would not care about \"we can enable corpsso\", but\n> they would benefit from knowing that either:\n>\n>  (1) the connection is \"secure\" (by the definition above); or\n>  (2) the connection is going to this particular helper.\n\nAgreed, I'll go with approach (1) and change the name of the var to be\nGIT_PERSISTENT_HTTPS_SECURE. Also, I will update the code to remove\nany GIT_PERSISTENT_HTTPS_SECURE from the environment.\n"},{"id":"192443","messageId":"1338328320-5653-1-git-send-email-cranger@google.com","threadId":"30601","inReplyTo":"1337792767-18914-1-git-send-email-cranger@google.com","subject":"[PATCH v2] Add persistent-https to contrib","fromName":"Colby Ranger","fromEmail":"cranger@google.com","sentAt":"2012-05-29T21:52:00Z","receivedAt":"2012-05-29T21:52:00Z","isPatch":true,"sender":{"key":"cranger@google.com","avatar":"https://avatars.githubusercontent.com/u/2480655?v=4"},"body":"Git over HTTPS has a high request startup latency, since the SSL\nnegotiation can take up to a second. In order to reduce this latency,\nconnections should be left open to the Git server across requests\n(or invocations of the git commandline).\n\nReduce SSL startup latency by running a daemon job that keeps\nconnections open to a Git server. The daemon job\n(git-remote-persistent-https--proxy) is started on the first request\nthrough the client binary (git-remote-persistent-https) and remains\nrunning for 24 hours after the last request, or until a new daemon\nbinary is placed in the PATH. The client determines the daemon's\nHTTP address by communicating over a UNIX socket with the daemon.\nFrom there, the rest of the Git protocol work is delegated to the\n\"git-remote-http\" binary, with the environment's http_proxy set to\nthe daemon.\n\nSigned-off-by: Colby Ranger <cranger@google.com>\n---\n contrib/persistent-https/LICENSE   |  202 ++++++++++++++++++++++++++++++++++++\n contrib/persistent-https/Makefile  |   38 +++++++\n contrib/persistent-https/README    |   62 +++++++++++\n contrib/persistent-https/client.go |  189 +++++++++++++++++++++++++++++++++\n contrib/persistent-https/main.go   |   82 +++++++++++++++\n contrib/persistent-https/proxy.go  |  190 +++++++++++++++++++++++++++++++++\n contrib/persistent-https/socket.go |   97 +++++++++++++++++\n 7 files changed, 860 insertions(+)\n create mode 100644 contrib/persistent-https/LICENSE\n create mode 100644 contrib/persistent-https/Makefile\n create mode 100644 contrib/persistent-https/README\n create mode 100644 contrib/persistent-https/client.go\n create mode 100644 contrib/persistent-https/main.go\n create mode 100644 contrib/persistent-https/proxy.go\n create mode 100644 contrib/persistent-https/socket.go\n\ndiff --git a/contrib/persistent-https/LICENSE b/contrib/persistent-https/LICENSE\nnew file mode 100644\nindex 0000000..d645695\n--- /dev/null\n+++ b/contrib/persistent-https/LICENSE\n@@ -0,0 +1,202 @@\n+\n+                                 Apache License\n+                           Version 2.0, January 2004\n+                        http://www.apache.org/licenses/\n+\n+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n+\n+   1. Definitions.\n+\n+      \"License\" shall mean the terms and conditions for use, reproduction,\n+      and distribution as defined by Sections 1 through 9 of this document.\n+\n+      \"Licensor\" shall mean the copyright owner or entity authorized by\n+      the copyright owner that is granting the License.\n+\n+      \"Legal Entity\" shall mean the union of the acting entity and all\n+      other entities that control, are controlled by, or are under common\n+      control with that entity. For the purposes of this definition,\n+      \"control\" means (i) the power, direct or indirect, to cause the\n+      direction or management of such entity, whether by contract or\n+      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n+      outstanding shares, or (iii) beneficial ownership of such entity.\n+\n+      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n+      exercising permissions granted by this License.\n+\n+      \"Source\" form shall mean the preferred form for making modifications,\n+      including but not limited to software source code, documentation\n+      source, and configuration files.\n+\n+      \"Object\" form shall mean any form resulting from mechanical\n+      transformation or translation of a Source form, including but\n+      not limited to compiled object code, generated documentation,\n+      and conversions to other media types.\n+\n+      \"Work\" shall mean the work of authorship, whether in Source or\n+      Object form, made available under the License, as indicated by a\n+      copyright notice that is included in or attached to the work\n+      (an example is provided in the Appendix below).\n+\n+      \"Derivative Works\" shall mean any work, whether in Source or Object\n+      form, that is based on (or derived from) the Work and for which the\n+      editorial revisions, annotations, elaborations, or other modifications\n+      represent, as a whole, an original work of authorship. For the purposes\n+      of this License, Derivative Works shall not include works that remain\n+      separable from, or merely link (or bind by name) to the interfaces of,\n+      the Work and Derivative Works thereof.\n+\n+      \"Contribution\" shall mean any work of authorship, including\n+      the original version of the Work and any modifications or additions\n+      to that Work or Derivative Works thereof, that is intentionally\n+      submitted to Licensor for inclusion in the Work by the copyright owner\n+      or by an individual or Legal Entity authorized to submit on behalf of\n+      the copyright owner. For the purposes of this definition, \"submitted\"\n+      means any form of electronic, verbal, or written communication sent\n+      to the Licensor or its representatives, including but not limited to\n+      communication on electronic mailing lists, source code control systems,\n+      and issue tracking systems that are managed by, or on behalf of, the\n+      Licensor for the purpose of discussing and improving the Work, but\n+      excluding communication that is conspicuously marked or otherwise\n+      designated in writing by the copyright owner as \"Not a Contribution.\"\n+\n+      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n+      on behalf of whom a Contribution has been received by Licensor and\n+      subsequently incorporated within the Work.\n+\n+   2. Grant of Copyright License. Subject to the terms and conditions of\n+      this License, each Contributor hereby grants to You a perpetual,\n+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n+      copyright license to reproduce, prepare Derivative Works of,\n+      publicly display, publicly perform, sublicense, and distribute the\n+      Work and such Derivative Works in Source or Object form.\n+\n+   3. Grant of Patent License. Subject to the terms and conditions of\n+      this License, each Contributor hereby grants to You a perpetual,\n+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n+      (except as stated in this section) patent license to make, have made,\n+      use, offer to sell, sell, import, and otherwise transfer the Work,\n+      where such license applies only to those patent claims licensable\n+      by such Contributor that are necessarily infringed by their\n+      Contribution(s) alone or by combination of their Contribution(s)\n+      with the Work to which such Contribution(s) was submitted. If You\n+      institute patent litigation against any entity (including a\n+      cross-claim or counterclaim in a lawsuit) alleging that the Work\n+      or a Contribution incorporated within the Work constitutes direct\n+      or contributory patent infringement, then any patent licenses\n+      granted to You under this License for that Work shall terminate\n+      as of the date such litigation is filed.\n+\n+   4. Redistribution. You may reproduce and distribute copies of the\n+      Work or Derivative Works thereof in any medium, with or without\n+      modifications, and in Source or Object form, provided that You\n+      meet the following conditions:\n+\n+      (a) You must give any other recipients of the Work or\n+          Derivative Works a copy of this License; and\n+\n+      (b) You must cause any modified files to carry prominent notices\n+          stating that You changed the files; and\n+\n+      (c) You must retain, in the Source form of any Derivative Works\n+          that You distribute, all copyright, patent, trademark, and\n+          attribution notices from the Source form of the Work,\n+          excluding those notices that do not pertain to any part of\n+          the Derivative Works; and\n+\n+      (d) If the Work includes a \"NOTICE\" text file as part of its\n+          distribution, then any Derivative Works that You distribute must\n+          include a readable copy of the attribution notices contained\n+          within such NOTICE file, excluding those notices that do not\n+          pertain to any part of the Derivative Works, in at least one\n+          of the following places: within a NOTICE text file distributed\n+          as part of the Derivative Works; within the Source form or\n+          documentation, if provided along with the Derivative Works; or,\n+          within a display generated by the Derivative Works, if and\n+          wherever such third-party notices normally appear. The contents\n+          of the NOTICE file are for informational purposes only and\n+          do not modify the License. You may add Your own attribution\n+          notices within Derivative Works that You distribute, alongside\n+          or as an addendum to the NOTICE text from the Work, provided\n+          that such additional attribution notices cannot be construed\n+          as modifying the License.\n+\n+      You may add Your own copyright statement to Your modifications and\n+      may provide additional or different license terms and conditions\n+      for use, reproduction, or distribution of Your modifications, or\n+      for any such Derivative Works as a whole, provided Your use,\n+      reproduction, and distribution of the Work otherwise complies with\n+      the conditions stated in this License.\n+\n+   5. Submission of Contributions. Unless You explicitly state otherwise,\n+      any Contribution intentionally submitted for inclusion in the Work\n+      by You to the Licensor shall be under the terms and conditions of\n+      this License, without any additional terms or conditions.\n+      Notwithstanding the above, nothing herein shall supersede or modify\n+      the terms of any separate license agreement you may have executed\n+      with Licensor regarding such Contributions.\n+\n+   6. Trademarks. This License does not grant permission to use the trade\n+      names, trademarks, service marks, or product names of the Licensor,\n+      except as required for reasonable and customary use in describing the\n+      origin of the Work and reproducing the content of the NOTICE file.\n+\n+   7. Disclaimer of Warranty. Unless required by applicable law or\n+      agreed to in writing, Licensor provides the Work (and each\n+      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n+      implied, including, without limitation, any warranties or conditions\n+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n+      PARTICULAR PURPOSE. You are solely responsible for determining the\n+      appropriateness of using or redistributing the Work and assume any\n+      risks associated with Your exercise of permissions under this License.\n+\n+   8. Limitation of Liability. In no event and under no legal theory,\n+      whether in tort (including negligence), contract, or otherwise,\n+      unless required by applicable law (such as deliberate and grossly\n+      negligent acts) or agreed to in writing, shall any Contributor be\n+      liable to You for damages, including any direct, indirect, special,\n+      incidental, or consequential damages of any character arising as a\n+      result of this License or out of the use or inability to use the\n+      Work (including but not limited to damages for loss of goodwill,\n+      work stoppage, computer failure or malfunction, or any and all\n+      other commercial damages or losses), even if such Contributor\n+      has been advised of the possibility of such damages.\n+\n+   9. Accepting Warranty or Additional Liability. While redistributing\n+      the Work or Derivative Works thereof, You may choose to offer,\n+      and charge a fee for, acceptance of support, warranty, indemnity,\n+      or other liability obligations and/or rights consistent with this\n+      License. However, in accepting such obligations, You may act only\n+      on Your own behalf and on Your sole responsibility, not on behalf\n+      of any other Contributor, and only if You agree to indemnify,\n+      defend, and hold each Contributor harmless for any liability\n+      incurred by, or claims asserted against, such Contributor by reason\n+      of your accepting any such warranty or additional liability.\n+\n+   END OF TERMS AND CONDITIONS\n+\n+   APPENDIX: How to apply the Apache License to your work.\n+\n+      To apply the Apache License to your work, attach the following\n+      boilerplate notice, with the fields enclosed by brackets \"[]\"\n+      replaced with your own identifying information. (Don't include\n+      the brackets!)  The text should be enclosed in the appropriate\n+      comment syntax for the file format. We also recommend that a\n+      file or class name and description of purpose be included on the\n+      same \"printed page\" as the copyright notice for easier\n+      identification within third-party archives.\n+\n+   Copyright [yyyy] [name of copyright owner]\n+\n+   Licensed under the Apache License, Version 2.0 (the \"License\");\n+   you may not use this file except in compliance with the License.\n+   You may obtain a copy of the License at\n+\n+       http://www.apache.org/licenses/LICENSE-2.0\n+\n+   Unless required by applicable law or agreed to in writing, software\n+   distributed under the License is distributed on an \"AS IS\" BASIS,\n+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+   See the License for the specific language governing permissions and\n+   limitations under the License.\ndiff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile\nnew file mode 100644\nindex 0000000..92baa3b\n--- /dev/null\n+++ b/contrib/persistent-https/Makefile\n@@ -0,0 +1,38 @@\n+# Copyright 2012 Google Inc. All Rights Reserved.\n+#\n+# Licensed under the Apache License, Version 2.0 (the \"License\");\n+# you may not use this file except in compliance with the License.\n+# You may obtain a copy of the License at\n+#\n+#     http://www.apache.org/licenses/LICENSE-2.0\n+#\n+# Unless required by applicable law or agreed to in writing, software\n+# distributed under the License is distributed on an \"AS IS\" BASIS,\n+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+# See the License for the specific language governing permissions and\n+# limitations under the License.\n+\n+BUILD_LABEL=$(shell date +\"%s\")\n+TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz\n+\n+all: git-remote-persistent-https git-remote-persistent-https--proxy \\\n+\tgit-remote-persistent-http\n+\n+git-remote-persistent-https--proxy: git-remote-persistent-https\n+\tln -f -s git-remote-persistent-https git-remote-persistent-https--proxy\n+\n+git-remote-persistent-http: git-remote-persistent-https\n+\tln -f -s git-remote-persistent-https git-remote-persistent-http\n+\n+git-remote-persistent-https:\n+\tgo build -o git-remote-persistent-https \\\n+\t\t-ldflags \"-X main._BUILD_EMBED_LABEL $(BUILD_LABEL)\"\n+\n+clean:\n+\trm -f git-remote-persistent-http* *.tar.gz\n+\n+tar: clean all\n+\t@chmod 555 git-remote-persistent-https\n+\t@tar -czf $(TAR_OUT) git-remote-persistent-http* README LICENSE\n+\t@echo\n+\t@echo \"Created $(TAR_OUT)\"\ndiff --git a/contrib/persistent-https/README b/contrib/persistent-https/README\nnew file mode 100644\nindex 0000000..f784dd2\n--- /dev/null\n+++ b/contrib/persistent-https/README\n@@ -0,0 +1,62 @@\n+git-remote-persistent-https\n+\n+The git-remote-persistent-https binary speeds up SSL operations\n+by running a daemon job (git-remote-persistent-https--proxy) that\n+keeps a connection open to a server.\n+\n+\n+PRE-BUILT BINARIES\n+\n+Darwin amd64:\n+https://commondatastorage.googleapis.com/git-remote-persistent-https/darwin_amd64.tar.gz\n+\n+Linux amd64:\n+https://commondatastorage.googleapis.com/git-remote-persistent-https/linux_amd64.tar.gz\n+\n+\n+INSTALLING\n+\n+Move all of the git-remote-persistent-http* binaries to a directory\n+in PATH.\n+\n+\n+USAGE\n+\n+HTTPS requests can be delegated to the proxy by using the\n+\"persistent-https\" scheme, e.g.\n+\n+git clone persistent-https://kernel.googlesource.com/pub/scm/git/git\n+\n+Likewise, .gitconfig can be updated as follows to rewrite https urls\n+to use persistent-https:\n+\n+[url \"persistent-https\"]\n+\tinsteadof = https\n+[url \"persistent-http\"]\n+\tinsteadof = http\n+\n+\n+#####################################################################\n+# BUILDING FROM SOURCE\n+#####################################################################\n+\n+LOCATION\n+\n+The source is available in the contrib/persistent-https directory of\n+the Git source repository. The Git source repository is available at\n+git://git.kernel.org/pub/scm/git/git.git/\n+https://kernel.googlesource.com/pub/scm/git/git\n+\n+\n+PREREQUISITES\n+\n+The code is written in Go (http://golang.org/) and the Go compiler is\n+required. Currently, the compiler must be built and installed from tip\n+of source, in order to include a fix in the reverse http proxy:\n+http://code.google.com/p/go/source/detail?r=a615b796570a2cd8591884767a7d67ede74f6648\n+\n+\n+BUILDING\n+\n+Run \"make\" to build the binaries. See the section on\n+INSTALLING above.\ndiff --git a/contrib/persistent-https/client.go b/contrib/persistent-https/client.go\nnew file mode 100644\nindex 0000000..71125b5\n--- /dev/null\n+++ b/contrib/persistent-https/client.go\n@@ -0,0 +1,189 @@\n+// Copyright 2012 Google Inc. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package main\n+\n+import (\n+\t\"bufio\"\n+\t\"errors\"\n+\t\"fmt\"\n+\t\"net\"\n+\t\"net/url\"\n+\t\"os\"\n+\t\"os/exec\"\n+\t\"strings\"\n+\t\"syscall\"\n+\t\"time\"\n+)\n+\n+type Client struct {\n+\tProxyBin string\n+\tArgs     []string\n+\n+\tinsecure bool\n+}\n+\n+func (c *Client) Run() error {\n+\tif err := c.resolveArgs(); err != nil {\n+\t\treturn fmt.Errorf(\"resolveArgs() got error: %v\", err)\n+\t}\n+\n+\t// Connect to the proxy.\n+\tuconn, hconn, addr, err := c.connect()\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"connect() got error: %v\", err)\n+\t}\n+\t// Keep the unix socket connection open for the duration of the request.\n+\tdefer uconn.Close()\n+\t// Keep a connection to the HTTP server open, so no other user can\n+\t// bind on the same address so long as the process is running.\n+\tdefer hconn.Close()\n+\n+\t// Start the git-remote-http subprocess.\n+\tcargs := []string{\"-c\", fmt.Sprintf(\"http.proxy=%v\", addr), \"remote-http\"}\n+\tcargs = append(cargs, c.Args...)\n+\tcmd := exec.Command(\"git\", cargs...)\n+\n+\tfor _, v := range os.Environ() {\n+\t\tif !strings.HasPrefix(v, \"GIT_PERSISTENT_HTTPS_SECURE=\") {\n+\t\t\tcmd.Env = append(cmd.Env, v)\n+\t\t}\n+\t}\n+\t// Set the GIT_PERSISTENT_HTTPS_SECURE environment variable when\n+\t// the proxy is using a SSL connection.  This allows credential helpers\n+\t// to identify secure proxy connections, despite being passed an HTTP\n+\t// scheme.\n+\tif !c.insecure {\n+\t\tcmd.Env = append(cmd.Env, \"GIT_PERSISTENT_HTTPS_SECURE=1\")\n+\t}\n+\n+\tcmd.Stdin = os.Stdin\n+\tcmd.Stdout = os.Stdout\n+\tcmd.Stderr = os.Stderr\n+\tif err := cmd.Run(); err != nil {\n+\t\tif eerr, ok := err.(*exec.ExitError); ok {\n+\t\t\tif stat, ok := eerr.ProcessState.Sys().(syscall.WaitStatus); ok && stat.ExitStatus() != 0 {\n+\t\t\t\tos.Exit(stat.ExitStatus())\n+\t\t\t}\n+\t\t}\n+\t\treturn fmt.Errorf(\"git-remote-http subprocess got error: %v\", err)\n+\t}\n+\treturn nil\n+}\n+\n+func (c *Client) connect() (uconn net.Conn, hconn net.Conn, addr string, err error) {\n+\tuconn, err = DefaultSocket.Dial()\n+\tif err != nil {\n+\t\tif e, ok := err.(*net.OpError); ok && (os.IsNotExist(e.Err) || e.Err == syscall.ECONNREFUSED) {\n+\t\t\tif err = c.startProxy(); err == nil {\n+\t\t\t\tuconn, err = DefaultSocket.Dial()\n+\t\t\t}\n+\t\t}\n+\t\tif err != nil {\n+\t\t\treturn\n+\t\t}\n+\t}\n+\n+\tif addr, err = c.readAddr(uconn); err != nil {\n+\t\treturn\n+\t}\n+\n+\t// Open a tcp connection to the proxy.\n+\tif hconn, err = net.Dial(\"tcp\", addr); err != nil {\n+\t\treturn\n+\t}\n+\n+\t// Verify the address hasn't changed ownership.\n+\tvar addr2 string\n+\tif addr2, err = c.readAddr(uconn); err != nil {\n+\t\treturn\n+\t} else if addr != addr2 {\n+\t\terr = fmt.Errorf(\"address changed after connect. got %q, want %q\", addr2, addr)\n+\t\treturn\n+\t}\n+\treturn\n+}\n+\n+func (c *Client) readAddr(conn net.Conn) (string, error) {\n+\tconn.SetDeadline(time.Now().Add(5 * time.Second))\n+\tdata := make([]byte, 100)\n+\tn, err := conn.Read(data)\n+\tif err != nil {\n+\t\treturn \"\", fmt.Errorf(\"error reading unix socket: %v\", err)\n+\t} else if n == 0 {\n+\t\treturn \"\", errors.New(\"empty data response\")\n+\t}\n+\tconn.Write([]byte{1}) // Ack\n+\n+\tvar addr string\n+\tif addrs := strings.Split(string(data[:n]), \"\\n\"); len(addrs) != 2 {\n+\t\treturn \"\", fmt.Errorf(\"got %q, wanted 2 addresses\", data[:n])\n+\t} else if c.insecure {\n+\t\taddr = addrs[1]\n+\t} else {\n+\t\taddr = addrs[0]\n+\t}\n+\treturn addr, nil\n+}\n+\n+func (c *Client) startProxy() error {\n+\tcmd := exec.Command(c.ProxyBin)\n+\tcmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}\n+\tstdout, err := cmd.StdoutPipe()\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tdefer stdout.Close()\n+\tif err := cmd.Start(); err != nil {\n+\t\treturn err\n+\t}\n+\tresult := make(chan error)\n+\tgo func() {\n+\t\tbytes, _, err := bufio.NewReader(stdout).ReadLine()\n+\t\tif line := string(bytes); err == nil && line != \"OK\" {\n+\t\t\terr = fmt.Errorf(\"proxy returned %q, want \\\"OK\\\"\", line)\n+\t\t}\n+\t\tresult <- err\n+\t}()\n+\tselect {\n+\tcase err := <-result:\n+\t\treturn err\n+\tcase <-time.After(5 * time.Second):\n+\t\treturn errors.New(\"timeout waiting for proxy to start\")\n+\t}\n+\tpanic(\"not reachable\")\n+}\n+\n+func (c *Client) resolveArgs() error {\n+\tif nargs := len(c.Args); nargs == 0 {\n+\t\treturn errors.New(\"remote needed\")\n+\t} else if nargs > 2 {\n+\t\treturn fmt.Errorf(\"want at most 2 args, got %v\", c.Args)\n+\t}\n+\n+\t// Rewrite the url scheme to be http.\n+\tidx := len(c.Args) - 1\n+\trawurl := c.Args[idx]\n+\trurl, err := url.Parse(rawurl)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"invalid remote: %v\", err)\n+\t}\n+\tc.insecure = rurl.Scheme == \"persistent-http\"\n+\trurl.Scheme = \"http\"\n+\tc.Args[idx] = rurl.String()\n+\tif idx != 0 && c.Args[0] == rawurl {\n+\t\tc.Args[0] = c.Args[idx]\n+\t}\n+\treturn nil\n+}\ndiff --git a/contrib/persistent-https/main.go b/contrib/persistent-https/main.go\nnew file mode 100644\nindex 0000000..fd1b107\n--- /dev/null\n+++ b/contrib/persistent-https/main.go\n@@ -0,0 +1,82 @@\n+// Copyright 2012 Google Inc. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+// The git-remote-persistent-https binary speeds up SSL operations by running\n+// a daemon job that keeps a connection open to a Git server. This ensures the\n+// git-remote-persistent-https--proxy is running and delegating execution\n+// to the git-remote-http binary with the http_proxy set to the daemon job.\n+// A unix socket is used to authenticate the proxy and discover the\n+// HTTP address. Note, both the client and proxy are included in the same\n+// binary.\n+package main\n+\n+import (\n+\t\"flag\"\n+\t\"fmt\"\n+\t\"log\"\n+\t\"os\"\n+\t\"strings\"\n+\t\"time\"\n+)\n+\n+var (\n+\tforceProxy = flag.Bool(\"proxy\", false, \"Whether to start the binary in proxy mode\")\n+\tproxyBin   = flag.String(\"proxy_bin\", \"git-remote-persistent-https--proxy\", \"Path to the proxy binary\")\n+\tprintLabel = flag.Bool(\"print_label\", false, \"Prints the build label for the binary\")\n+\n+\t// Variable that should be defined through the -X linker flag.\n+\t_BUILD_EMBED_LABEL string\n+)\n+\n+const (\n+\tdefaultMaxIdleDuration    = 24 * time.Hour\n+\tdefaultPollUpdateInterval = 15 * time.Minute\n+)\n+\n+func main() {\n+\tflag.Parse()\n+\tif *printLabel {\n+\t\t// Short circuit execution to print the build label\n+\t\tfmt.Println(buildLabel())\n+\t\treturn\n+\t}\n+\n+\tvar err error\n+\tif *forceProxy || strings.HasSuffix(os.Args[0], \"--proxy\") {\n+\t\tlog.SetPrefix(\"git-remote-persistent-https--proxy: \")\n+\t\tproxy := &Proxy{\n+\t\t\tBuildLabel:         buildLabel(),\n+\t\t\tMaxIdleDuration:    defaultMaxIdleDuration,\n+\t\t\tPollUpdateInterval: defaultPollUpdateInterval,\n+\t\t}\n+\t\terr = proxy.Run()\n+\t} else {\n+\t\tlog.SetPrefix(\"git-remote-persistent-https: \")\n+\t\tclient := &Client{\n+\t\t\tProxyBin: *proxyBin,\n+\t\t\tArgs:     flag.Args(),\n+\t\t}\n+\t\terr = client.Run()\n+\t}\n+\tif err != nil {\n+\t\tlog.Fatalln(err)\n+\t}\n+}\n+\n+func buildLabel() string {\n+\tif _BUILD_EMBED_LABEL == \"\" {\n+\t\tlog.Println(`unlabeled build; build with \"make\" to label`)\n+\t}\n+\treturn _BUILD_EMBED_LABEL\n+}\ndiff --git a/contrib/persistent-https/proxy.go b/contrib/persistent-https/proxy.go\nnew file mode 100644\nindex 0000000..bb0cdba\n--- /dev/null\n+++ b/contrib/persistent-https/proxy.go\n@@ -0,0 +1,190 @@\n+// Copyright 2012 Google Inc. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package main\n+\n+import (\n+\t\"fmt\"\n+\t\"log\"\n+\t\"net\"\n+\t\"net/http\"\n+\t\"net/http/httputil\"\n+\t\"os\"\n+\t\"os/exec\"\n+\t\"os/signal\"\n+\t\"sync\"\n+\t\"syscall\"\n+\t\"time\"\n+)\n+\n+type Proxy struct {\n+\tBuildLabel         string\n+\tMaxIdleDuration    time.Duration\n+\tPollUpdateInterval time.Duration\n+\n+\tul        net.Listener\n+\thttpAddr  string\n+\thttpsAddr string\n+}\n+\n+func (p *Proxy) Run() error {\n+\thl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"http listen failed: %v\", err)\n+\t}\n+\tdefer hl.Close()\n+\n+\thsl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"https listen failed: %v\", err)\n+\t}\n+\tdefer hsl.Close()\n+\n+\tp.ul, err = DefaultSocket.Listen()\n+\tif err != nil {\n+\t\tc, derr := DefaultSocket.Dial()\n+\t\tif derr == nil {\n+\t\t\tc.Close()\n+\t\t\tfmt.Println(\"OK\\nA proxy is already running... exiting\")\n+\t\t\treturn nil\n+\t\t} else if e, ok := derr.(*net.OpError); ok && e.Err == syscall.ECONNREFUSED {\n+\t\t\t// Nothing is listening on the socket, unlink it and try again.\n+\t\t\tsyscall.Unlink(DefaultSocket.Path())\n+\t\t\tp.ul, err = DefaultSocket.Listen()\n+\t\t}\n+\t\tif err != nil {\n+\t\t\treturn fmt.Errorf(\"unix listen failed on %v: %v\", DefaultSocket.Path(), err)\n+\t\t}\n+\t}\n+\tdefer p.ul.Close()\n+\tgo p.closeOnSignal()\n+\tgo p.closeOnUpdate()\n+\n+\tp.httpAddr = hl.Addr().String()\n+\tp.httpsAddr = hsl.Addr().String()\n+\tfmt.Printf(\"OK\\nListening on unix socket=%v http=%v https=%v\\n\",\n+\t\tp.ul.Addr(), p.httpAddr, p.httpsAddr)\n+\n+\tresult := make(chan error, 2)\n+\tgo p.serveUnix(result)\n+\tgo func() {\n+\t\tresult <- http.Serve(hl, &httputil.ReverseProxy{\n+\t\t\tFlushInterval: 500 * time.Millisecond,\n+\t\t\tDirector:      func(r *http.Request) {},\n+\t\t})\n+\t}()\n+\tgo func() {\n+\t\tresult <- http.Serve(hsl, &httputil.ReverseProxy{\n+\t\t\tFlushInterval: 500 * time.Millisecond,\n+\t\t\tDirector: func(r *http.Request) {\n+\t\t\t\tr.URL.Scheme = \"https\"\n+\t\t\t},\n+\t\t})\n+\t}()\n+\treturn <-result\n+}\n+\n+type socketContext struct {\n+\tsync.WaitGroup\n+\tmutex sync.Mutex\n+\tlast  time.Time\n+}\n+\n+func (sc *socketContext) Done() {\n+\tsc.mutex.Lock()\n+\tdefer sc.mutex.Unlock()\n+\tsc.last = time.Now()\n+\tsc.WaitGroup.Done()\n+}\n+\n+func (p *Proxy) serveUnix(result chan<- error) {\n+\tsockCtx := &socketContext{}\n+\tgo p.closeOnIdle(sockCtx)\n+\n+\tvar err error\n+\tfor {\n+\t\tvar uconn net.Conn\n+\t\tuconn, err = p.ul.Accept()\n+\t\tif err != nil {\n+\t\t\terr = fmt.Errorf(\"accept failed: %v\", err)\n+\t\t\tbreak\n+\t\t}\n+\t\tsockCtx.Add(1)\n+\t\tgo p.handleUnixConn(sockCtx, uconn)\n+\t}\n+\tsockCtx.Wait()\n+\tresult <- err\n+}\n+\n+func (p *Proxy) handleUnixConn(sockCtx *socketContext, uconn net.Conn) {\n+\tdefer sockCtx.Done()\n+\tdefer uconn.Close()\n+\tdata := []byte(fmt.Sprintf(\"%v\\n%v\", p.httpsAddr, p.httpAddr))\n+\tuconn.SetDeadline(time.Now().Add(5 * time.Second))\n+\tfor i := 0; i < 2; i++ {\n+\t\tif n, err := uconn.Write(data); err != nil {\n+\t\t\tlog.Printf(\"error sending http addresses: %+v\\n\", err)\n+\t\t\treturn\n+\t\t} else if n != len(data) {\n+\t\t\tlog.Printf(\"sent %d data bytes, wanted %d\\n\", n, len(data))\n+\t\t\treturn\n+\t\t}\n+\t\tif _, err := uconn.Read([]byte{0, 0, 0, 0}); err != nil {\n+\t\t\tlog.Printf(\"error waiting for Ack: %+v\\n\", err)\n+\t\t\treturn\n+\t\t}\n+\t}\n+\t// Wait without a deadline for the client to finish via EOF\n+\tuconn.SetDeadline(time.Time{})\n+\tuconn.Read([]byte{0, 0, 0, 0})\n+}\n+\n+func (p *Proxy) closeOnIdle(sockCtx *socketContext) {\n+\tfor d := p.MaxIdleDuration; d > 0; {\n+\t\ttime.Sleep(d)\n+\t\tsockCtx.Wait()\n+\t\tsockCtx.mutex.Lock()\n+\t\tif d = sockCtx.last.Add(p.MaxIdleDuration).Sub(time.Now()); d <= 0 {\n+\t\t\tlog.Println(\"graceful shutdown from idle timeout\")\n+\t\t\tp.ul.Close()\n+\t\t}\n+\t\tsockCtx.mutex.Unlock()\n+\t}\n+}\n+\n+func (p *Proxy) closeOnUpdate() {\n+\tfor {\n+\t\ttime.Sleep(p.PollUpdateInterval)\n+\t\tif out, err := exec.Command(os.Args[0], \"--print_label\").Output(); err != nil {\n+\t\t\tlog.Printf(\"error polling for updated binary: %v\\n\", err)\n+\t\t} else if s := string(out[:len(out)-1]); p.BuildLabel != s {\n+\t\t\tlog.Printf(\"graceful shutdown from updated binary: %q --> %q\\n\", p.BuildLabel, s)\n+\t\t\tp.ul.Close()\n+\t\t\tbreak\n+\t\t}\n+\t}\n+}\n+\n+func (p *Proxy) closeOnSignal() {\n+\tch := make(chan os.Signal, 10)\n+\tsignal.Notify(ch, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGHUP))\n+\tsig := <-ch\n+\tp.ul.Close()\n+\tswitch sig {\n+\tcase os.Signal(syscall.SIGHUP):\n+\t\tlog.Printf(\"graceful shutdown from signal: %v\\n\", sig)\n+\tdefault:\n+\t\tlog.Fatalf(\"exiting from signal: %v\\n\", sig)\n+\t}\n+}\ndiff --git a/contrib/persistent-https/socket.go b/contrib/persistent-https/socket.go\nnew file mode 100644\nindex 0000000..193b911\n--- /dev/null\n+++ b/contrib/persistent-https/socket.go\n@@ -0,0 +1,97 @@\n+// Copyright 2012 Google Inc. All Rights Reserved.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package main\n+\n+import (\n+\t\"fmt\"\n+\t\"log\"\n+\t\"net\"\n+\t\"os\"\n+\t\"path/filepath\"\n+\t\"syscall\"\n+)\n+\n+// A Socket is a wrapper around a Unix socket that verifies directory\n+// permissions.\n+type Socket struct {\n+\tDir string\n+}\n+\n+func defaultDir() string {\n+\tsockPath := \".git-credential-cache\"\n+\tif home := os.Getenv(\"HOME\"); home != \"\" {\n+\t\treturn filepath.Join(home, sockPath)\n+\t}\n+\tlog.Printf(\"socket: cannot find HOME path. using relative directory %q for socket\", sockPath)\n+\treturn sockPath\n+}\n+\n+// DefaultSocket is a Socket in the $HOME/.git-credential-cache directory.\n+var DefaultSocket = Socket{Dir: defaultDir()}\n+\n+// Listen announces the local network address of the unix socket. The\n+// permissions on the socket directory are verified before attempting\n+// the actual listen.\n+func (s Socket) Listen() (net.Listener, error) {\n+\tnetwork, addr := \"unix\", s.Path()\n+\tif err := s.mkdir(); err != nil {\n+\t\treturn nil, &net.OpError{Op: \"listen\", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}\n+\t}\n+\treturn net.Listen(network, addr)\n+}\n+\n+// Dial connects to the unix socket. The permissions on the socket directory\n+// are verified before attempting the actual dial.\n+func (s Socket) Dial() (net.Conn, error) {\n+\tnetwork, addr := \"unix\", s.Path()\n+\tif err := s.checkPermissions(); err != nil {\n+\t\treturn nil, &net.OpError{Op: \"dial\", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err}\n+\t}\n+\treturn net.Dial(network, addr)\n+}\n+\n+// Path returns the fully specified file name of the unix socket.\n+func (s Socket) Path() string {\n+\treturn filepath.Join(s.Dir, \"persistent-https-proxy-socket\")\n+}\n+\n+func (s Socket) mkdir() error {\n+\tif err := s.checkPermissions(); err == nil {\n+\t\treturn nil\n+\t} else if !os.IsNotExist(err) {\n+\t\treturn err\n+\t}\n+\tif err := os.MkdirAll(s.Dir, 0700); err != nil {\n+\t\treturn err\n+\t}\n+\treturn s.checkPermissions()\n+}\n+\n+func (s Socket) checkPermissions() error {\n+\tfi, err := os.Stat(s.Dir)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tif !fi.IsDir() {\n+\t\treturn fmt.Errorf(\"socket: got file, want directory for %q\", s.Dir)\n+\t}\n+\tif fi.Mode().Perm() != 0700 {\n+\t\treturn fmt.Errorf(\"socket: got perm %o, want 700 for %q\", fi.Mode().Perm(), s.Dir)\n+\t}\n+\tif st := fi.Sys().(*syscall.Stat_t); int(st.Uid) != os.Getuid() {\n+\t\treturn fmt.Errorf(\"socket: got uid %d, want %d for %q\", st.Uid, os.Getuid(), s.Dir)\n+\t}\n+\treturn nil\n+}\n-- \n1.7.9.2\n"},{"id":"192451","messageId":"7vobp61s0b.fsf@alter.siamese.dyndns.org","threadId":"30601","inReplyTo":"1338328320-5653-1-git-send-email-cranger@google.com","subject":"Re: [PATCH v2] Add persistent-https to contrib","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-05-29T23:34:12Z","receivedAt":"2012-05-29T23:34:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Colby Ranger <cranger@google.com> writes:\n\n> Git over HTTPS has a high request startup latency, since the SSL\n> negotiation can take up to a second. In order to reduce this latency,\n> connections should be left open to the Git server across requests\n> (or invocations of the git commandline).\n>\n> Reduce SSL startup latency by running a daemon job that keeps\n> connections open to a Git server. The daemon job\n> (git-remote-persistent-https--proxy) is started on the first request\n> through the client binary (git-remote-persistent-https) and remains\n> running for 24 hours after the last request, or until a new daemon\n> binary is placed in the PATH. The client determines the daemon's\n> HTTP address by communicating over a UNIX socket with the daemon.\n> From there, the rest of the Git protocol work is delegated to the\n> \"git-remote-http\" binary, with the environment's http_proxy set to\n> the daemon.\n>\n> Signed-off-by: Colby Ranger <cranger@google.com>\n> ---\n\nThanks.  I'll add a few paragraphs taken from Shawn's message in\nthis thread before queuing, but it'll take some time until I get to\nthis patch (I am looking at a totally unrelated topic that brought an\nunpleasant regression to cherry-pick).\n"}]}