hancock

Check-in [ceb5b81ccf]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:cmd/hancock: client of http testimony store server side is implemented by hancockd
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ceb5b81ccf9ac57dca8ba996ea9d95512ae915b026bfc979f8b0607443267695
User & Date: dnc 2020-01-04 16:12:23
Context
2020-01-04
16:13
remove deprecated code; other minor cleanup check-in: 715d5551f2 user: dnc tags: trunk
16:12
cmd/hancock: client of http testimony store server side is implemented by hancockd check-in: ceb5b81ccf user: dnc tags: trunk
2020-01-03
17:28
cmd/hancock: command helper, parse args with help mode support check-in: 62d0ed9c79 user: dnc tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to cmd/hancock/hancock.go.

156
157
158
159
160
161
162
163

164
165
166
167
168
169
170
			}
			s, err = boltstore.NewBoltStore(db)
			if err != nil {
				return nil, err
			}
		}

		// TODO(dnc): case "http(s)"


	default:
		return nil, fmt.Errorf("unexpected store sheme (%q)", u.Scheme)
	}
	stor[flag] = s
	return s, nil
}







|
>







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
			}
			s, err = boltstore.NewBoltStore(db)
			if err != nil {
				return nil, err
			}
		}

	case "http":
		s = &httpclient{url: *u}

	default:
		return nil, fmt.Errorf("unexpected store sheme (%q)", u.Scheme)
	}
	stor[flag] = s
	return s, nil
}

Added cmd/hancock/http.go.

































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright (C) 2020  David N. Cohen

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path"

	"src.d10.dev/hancock/model"
	"src.d10.dev/hancock/store"
)

// An HTTP client to hancockd server.

type httpclient struct {
	http.Client

	url url.URL
}

func (this *httpclient) Close() error {
	this.CloseIdleConnections()
	return nil
}

func (this *httpclient) PutTestimony(testimony model.Testimony) (*model.TestimonyKey, error) {
	tmp, err := model.Encode(testimony)
	if err != nil {
		return nil, err
	}

	u := this.url // copy
	u.Path = path.Join(u.Path, "testimony")

	// TODO(dnc): content type
	req, err := http.NewRequest(http.MethodPut, u.String(), bytes.NewReader(tmp))
	if err != nil {
		return nil, err
	}

	resp, err := this.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		// TODO(dnc): determine underlying cause of error, i.e. display body
		return nil, fmt.Errorf("failed to post testimony: %s", resp.Status)
	}

	key := &model.TestimonyKey{}
	// http API encodes to JSON
	err = json.Unmarshal(body, key)

	return key, err
}

func (this *httpclient) GetTestimony(key model.TestimonyKey) (*model.Testimony, error) {
	u := this.url                           // copy base
	u.Path = path.Join(u.Path, "testimony") // append full path
	for _, segment := range key.Split() {
		u.Path = path.Join(u.Path, url.PathEscape(segment)) // because authority strings may have slashes
	}

	resp, err := http.Get(u.String())
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if resp.StatusCode == http.StatusNotFound {
		return nil, store.NotFound
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, fmt.Errorf("failed to get testimony: %s", resp.Status)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	testimony := &model.Testimony{}
	err = json.Unmarshal(body, testimony)
	return testimony, err
}