2013-10-04 17:09:03 +00:00
|
|
|
package gitmediaclient
|
|
|
|
|
|
|
|
import (
|
2013-10-31 22:12:30 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/base64"
|
2013-10-31 19:00:09 +00:00
|
|
|
"encoding/json"
|
2013-10-04 17:09:03 +00:00
|
|
|
"fmt"
|
2013-10-22 18:21:01 +00:00
|
|
|
"io"
|
2013-10-04 17:09:03 +00:00
|
|
|
"net/http"
|
2013-10-31 22:12:30 +00:00
|
|
|
"net/url"
|
2013-10-04 17:09:03 +00:00
|
|
|
"os"
|
2013-10-31 22:12:30 +00:00
|
|
|
"os/exec"
|
2013-10-04 17:09:03 +00:00
|
|
|
"path/filepath"
|
2013-10-31 22:12:30 +00:00
|
|
|
"strings"
|
2013-10-04 17:09:03 +00:00
|
|
|
)
|
|
|
|
|
2013-10-22 18:11:11 +00:00
|
|
|
func Put(filename string) error {
|
2013-10-31 21:33:57 +00:00
|
|
|
oid := filepath.Base(filename)
|
2013-10-31 19:22:33 +00:00
|
|
|
stat, err := os.Stat(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-10-04 17:09:03 +00:00
|
|
|
file, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-11-01 23:19:04 +00:00
|
|
|
req, _, err := clientRequest("PUT", oid)
|
2013-10-04 17:09:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-10-31 21:33:57 +00:00
|
|
|
req.Body = file
|
|
|
|
req.ContentLength = stat.Size()
|
2013-10-04 17:09:03 +00:00
|
|
|
|
2013-11-02 00:23:37 +00:00
|
|
|
res, err := doRequest(req)
|
2013-10-04 17:09:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2013-10-31 21:33:57 +00:00
|
|
|
fmt.Printf("Sending %s from %s: %d\n", oid, filename, res.StatusCode)
|
2013-10-04 17:09:03 +00:00
|
|
|
return nil
|
|
|
|
}
|
2013-10-22 18:21:01 +00:00
|
|
|
|
|
|
|
func Get(filename string) (io.ReadCloser, error) {
|
2013-10-31 21:33:57 +00:00
|
|
|
oid := filepath.Base(filename)
|
2013-10-22 18:21:01 +00:00
|
|
|
if stat, err := os.Stat(filename); err != nil || stat == nil {
|
2013-11-01 23:19:04 +00:00
|
|
|
req, _, err := clientRequest("GET", oid)
|
2013-10-22 18:21:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
req.Header.Set("Accept", "application/vnd.git-media")
|
2013-11-02 00:23:37 +00:00
|
|
|
res, err := doRequest(req)
|
2013-10-22 18:21:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return res.Body, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return os.Open(filename)
|
|
|
|
}
|
|
|
|
|
2013-11-02 00:23:37 +00:00
|
|
|
func doRequest(req *http.Request) (*http.Response, error) {
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
|
|
if err == nil {
|
|
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode > 299 {
|
|
|
|
apierr := &Error{}
|
|
|
|
dec := json.NewDecoder(res.Body)
|
|
|
|
if err := dec.Decode(apierr); err != nil {
|
|
|
|
return res, err
|
|
|
|
}
|
|
|
|
return res, apierr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res, err
|
|
|
|
}
|
|
|
|
|
2013-11-01 23:19:04 +00:00
|
|
|
func clientRequest(method, oid string) (*http.Request, map[string]string, error) {
|
2013-10-31 22:12:30 +00:00
|
|
|
u := objectUrl(oid)
|
|
|
|
req, err := http.NewRequest(method, u.String(), nil)
|
|
|
|
if err == nil {
|
|
|
|
creds, err := credentials(u)
|
|
|
|
if err != nil {
|
2013-11-01 23:19:04 +00:00
|
|
|
return req, nil, err
|
2013-10-31 22:12:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
|
|
|
|
auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
|
|
|
|
req.Header.Set("Authorization", auth)
|
2013-11-01 23:19:04 +00:00
|
|
|
return req, creds, nil
|
2013-10-31 22:12:30 +00:00
|
|
|
}
|
|
|
|
|
2013-11-01 23:19:04 +00:00
|
|
|
return req, nil, err
|
2013-10-31 21:33:57 +00:00
|
|
|
}
|
|
|
|
|
2013-10-31 22:12:30 +00:00
|
|
|
func objectUrl(oid string) *url.URL {
|
|
|
|
u, _ := url.Parse("http://localhost:8080")
|
|
|
|
u.Path = "/objects/" + oid
|
|
|
|
return u
|
|
|
|
}
|
|
|
|
|
|
|
|
func credentials(u *url.URL) (map[string]string, error) {
|
|
|
|
credInput := fmt.Sprintf("protocol=%s\nhost=%s\n", u.Scheme, u.Host)
|
2013-11-01 23:16:10 +00:00
|
|
|
cmd, err := execCreds(credInput, "fill")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return cmd.Credentials(), nil
|
|
|
|
}
|
2013-10-31 22:12:30 +00:00
|
|
|
|
2013-11-01 23:16:10 +00:00
|
|
|
func execCreds(input, subCommand string) (*CredentialCmd, error) {
|
|
|
|
cmd := NewCommand(input, subCommand)
|
2013-10-31 22:12:30 +00:00
|
|
|
err := cmd.Start()
|
|
|
|
if err != nil {
|
2013-11-01 23:16:10 +00:00
|
|
|
return cmd, err
|
2013-10-31 22:12:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = cmd.Wait()
|
2013-11-01 23:16:10 +00:00
|
|
|
return cmd, err
|
|
|
|
}
|
|
|
|
|
|
|
|
type CredentialCmd struct {
|
|
|
|
bufOut *bytes.Buffer
|
|
|
|
bufErr *bytes.Buffer
|
|
|
|
*exec.Cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewCommand(input, subCommand string) *CredentialCmd {
|
|
|
|
buf1 := new(bytes.Buffer)
|
|
|
|
buf2 := new(bytes.Buffer)
|
|
|
|
cmd := exec.Command("git", "credential", subCommand)
|
|
|
|
cmd.Stdin = bytes.NewBufferString(input)
|
|
|
|
cmd.Stdout = buf1
|
|
|
|
cmd.Stderr = buf2
|
|
|
|
return &CredentialCmd{buf1, buf2, cmd}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *CredentialCmd) StderrString() string {
|
|
|
|
return c.bufErr.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *CredentialCmd) StdoutString() string {
|
|
|
|
return c.bufOut.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *CredentialCmd) Credentials() map[string]string {
|
|
|
|
creds := make(map[string]string)
|
2013-10-31 22:12:30 +00:00
|
|
|
|
2013-11-01 23:16:10 +00:00
|
|
|
for _, line := range strings.Split(c.StdoutString(), "\n") {
|
2013-10-31 22:12:30 +00:00
|
|
|
pieces := strings.SplitN(line, "=", 2)
|
|
|
|
if len(pieces) < 2 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
creds[pieces[0]] = pieces[1]
|
|
|
|
}
|
|
|
|
|
2013-11-01 23:16:10 +00:00
|
|
|
return creds
|
2013-10-22 18:21:01 +00:00
|
|
|
}
|
2013-10-31 19:00:09 +00:00
|
|
|
|
|
|
|
type Error struct {
|
|
|
|
Message string `json:"message"`
|
|
|
|
RequestId string `json:"request_id,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Error) Error() string {
|
|
|
|
return e.Message
|
|
|
|
}
|