Skip to content

Commit 00b292a

Browse files
authored
Improve dmsgcurl (skycoin#296)
* add errors from libcurl that we will have in dmsgcurl * add `as below` to README.md * update errors * add os.Exit and pre-defined error to output
1 parent 770537a commit 00b292a

File tree

3 files changed

+136
-36
lines changed

3 files changed

+136
-36
lines changed

cmd/dmsgcurl/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
## DMSGCURL
2+
#### Usage
3+
```
4+
$ skywire dmsg curl dmsg://{pk}:{port}/xxx
5+
```
6+
7+
#### Errors
8+
We trying to use same error's status code like what libcurl used as below:
9+
| ERROR CODE | SHORT DESCRIPTION | LONG DESCRIPTION |
10+
|---|---|---|
11+
| 0 | OK | All fine. Proceed as usual. |
12+
| 2 | FAILED_INIT | Very early initialization code failed. |
13+
| 3 | URL_MALFORMAT | The URL was not properly formatted. |
14+
| 4 | DMSG_INIT | Couldn't resolve dmsg initialziation. |
15+
| 5 | COULDNT_RESOLVE_PROXY | Couldn't resolve proxy. The given proxy host could not be resolved. |
16+
| 6 | COULDNT_RESOLVE_HOST | Couldn't resolve host. The given remote host was not resolved. |
17+
| 22 | WRITE_INIT | An error occurred when creating output file. |
18+
| 23 | WRITE_ERROR | An error occurred when writing received data to a local file, or an error was returned to dmsgcurl from a write callback. |
19+
| 26 | READ_ERROR | There was a problem reading a local file or an error returned by the read callback. |
20+
| 55 | SEND_ERROR | Failed sending network data. |
21+
| 56 | RECV_ERROR | Failure with receiving network data. |
22+
| 57 | DOWNLOAD_ERROR | Failure with downloading data. |
23+
| 63 | FILESIZE_EXCEEDED | Maximum file size exceeded. |
24+
| 64 | CONTEXT_CANCELED | Operation canceled by user. |
25+

cmd/dmsgcurl/commands/dmsgcurl.go

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ var RootCmd = &cobra.Command{
102102
// Use SOCKS5 proxy dialer if specified
103103
dialer, err := proxy.SOCKS5("tcp", proxyAddr[i], nil, proxy.Direct)
104104
if err != nil {
105-
log.Fatalf("Error creating SOCKS5 dialer: %v", err)
105+
dmsgcurlLog.WithError(fmt.Errorf("Error creating SOCKS5 dialer: %v", err)).Error(errorDesc["COULDNT_RESOLVE_PROXY"])
106+
os.Exit(errorCode["COULDNT_RESOLVE_PROXY"])
106107
}
107108
transport := &http.Transport{
108109
Dial: dialer.Dial,
@@ -119,100 +120,124 @@ var RootCmd = &cobra.Command{
119120
pk, sk = cipher.GenerateKeyPair()
120121
}
121122
if len(args) == 0 {
122-
return errors.New("no URL(s) provided")
123+
dmsgcurlLog.WithError(fmt.Errorf("no URL(s) provided")).Error(errorDesc["FAILED_INIT"])
124+
os.Exit(errorCode["FAILED_INIT"])
123125
}
124126
if len(args) > 1 {
125-
return errors.New("multiple URLs is not yet supported")
127+
dmsgcurlLog.WithError(fmt.Errorf("multiple URLs is not yet supported")).Error(errorDesc["FAILED_INIT"])
128+
os.Exit(errorCode["FAILED_INIT"])
126129
}
127130
parsedURL, err := url.Parse(args[0])
128131
if err != nil {
129-
dmsgcurlLog.WithError(err).Fatal("failed to parse provided URL")
132+
dmsgcurlLog.WithError(fmt.Errorf("failed to parse provided URL")).Error(errorDesc["URL_MALFORMAT"])
133+
os.Exit(errorCode["URL_MALFORMAT"])
130134
}
131135
for i := range dmsgDiscs {
132136
if dmsgcurlData != "" {
133-
err = handlePostRequest(ctxs[i], dmsgcurlLog, pk, sk, httpClients[i], dmsgDiscs[i], dmsgSessions, parsedURL, dmsgcurlData)
134-
if err == nil {
135-
return nil
137+
err := handlePostRequest(ctxs[i], dmsgcurlLog, pk, sk, httpClients[i], dmsgDiscs[i], dmsgSessions, parsedURL, dmsgcurlData)
138+
if err.Code != 0 {
139+
dmsgcurlLog.Error(err.Error)
140+
os.Exit(err.Code)
136141
}
137-
dmsgcurlLog.WithError(err).Debug("An error occurred")
138142
}
139-
err = handleDownload(ctxs[i], dmsgcurlLog, pk, sk, httpClients[i], dmsgDiscs[i], dmsgSessions, parsedURL)
140-
if err == nil {
141-
return nil
143+
err := handleDownload(ctxs[i], dmsgcurlLog, pk, sk, httpClients[i], dmsgDiscs[i], dmsgSessions, parsedURL)
144+
if err.Code != 0 {
145+
dmsgcurlLog.Error(err.Error)
146+
os.Exit(err.Code)
142147
}
143-
dmsgcurlLog.WithError(err).Debug("An error occurred")
144148
}
145-
return err
149+
return nil
146150
},
147151
}
148152

149-
func handlePostRequest(ctx context.Context, dmsgLogger *logging.Logger, pk cipher.PubKey, sk cipher.SecKey, httpClient *http.Client, dmsgDisc string, dmsgSessions int, parsedURL *url.URL, dmsgcurlData string) error {
153+
func handlePostRequest(ctx context.Context, dmsgLogger *logging.Logger, pk cipher.PubKey, sk cipher.SecKey, httpClient *http.Client, dmsgDisc string, dmsgSessions int, parsedURL *url.URL, dmsgcurlData string) curlError {
150154
dmsgC, closeDmsg, err := cli.StartDmsg(ctx, dmsgLogger, pk, sk, httpClient, dmsgDisc, dmsgSessions)
151155
if err != nil {
152-
dmsgcurlLog.WithError(err).Warnf("Failed to start dmsg")
153-
return err
156+
return curlError{
157+
Error: fmt.Errorf("%s", errorDesc["DMSG_INIT"]),
158+
Code: errorCode["DMSG_INIT"],
159+
}
154160
}
155161
defer closeDmsg()
156162

157163
httpC := http.Client{Transport: dmsghttp.MakeHTTPTransport(ctx, dmsgC)}
158164
req, err := http.NewRequest(http.MethodPost, parsedURL.String(), strings.NewReader(dmsgcurlData))
159165
if err != nil {
160-
dmsgcurlLog.WithError(err).Fatal("Failed to formulate HTTP request.")
166+
return curlError{
167+
Error: fmt.Errorf("%s", errorDesc["URL_MALFORMAT"]),
168+
Code: errorCode["URL_MALFORMAT"],
169+
}
161170
}
162171
req.Header.Set("Content-Type", "text/plain")
163172

164173
resp, err := httpC.Do(req)
165174
if err != nil {
166-
dmsgcurlLog.WithError(err).Debug("Failed to execute HTTP request")
175+
return curlError{
176+
Error: fmt.Errorf("%s", errorDesc["COULDNT_RESOLVE_HOST"]),
177+
Code: errorCode["COULDNT_RESOLVE_HOST"],
178+
}
167179
}
168180
defer closeResponseBody(resp)
169181

170182
respBody, err := io.ReadAll(resp.Body)
171183
if err != nil {
172-
dmsgcurlLog.WithError(err).Debug("Failed to read response body.")
173-
return err
184+
return curlError{
185+
Error: fmt.Errorf("%s", errorDesc["READ_ERROR"]),
186+
Code: errorCode["READ_ERROR"],
187+
}
174188
}
175189
fmt.Println(string(respBody))
176-
return nil
190+
return curlError{}
177191

178192
}
179193

180-
func handleDownload(ctx context.Context, dmsgLogger *logging.Logger, pk cipher.PubKey, sk cipher.SecKey, httpClient *http.Client, dmsgDisc string, dmsgSessions int, parsedURL *url.URL) error {
194+
func handleDownload(ctx context.Context, dmsgLogger *logging.Logger, pk cipher.PubKey, sk cipher.SecKey, httpClient *http.Client, dmsgDisc string, dmsgSessions int, parsedURL *url.URL) curlError {
181195
file, err := prepareOutputFile()
182196
if err != nil {
183-
return fmt.Errorf("failed to prepare output file: %w", err)
197+
return curlError{
198+
Error: fmt.Errorf("%s", errorDesc["WRITE_INIT"]),
199+
Code: errorCode["WRITE_INIT"],
200+
}
184201
}
185202
defer closeAndCleanFile(file, err)
186203

187204
dmsgC, closeDmsg, err := cli.StartDmsg(ctx, dmsgLogger, pk, sk, httpClient, dmsgDisc, dmsgSessions)
188205
if err != nil {
189-
dmsgcurlLog.WithError(err).Warnf("Failed to start dmsg")
190-
return err
206+
return curlError{
207+
Error: fmt.Errorf("%s", errorDesc["DMSG_INIT"]),
208+
Code: errorCode["DMSG_INIT"],
209+
}
191210
}
192211
defer closeDmsg()
193212

194213
httpC := http.Client{Transport: dmsghttp.MakeHTTPTransport(ctx, dmsgC)}
195214

215+
var downloadErr curlError
196216
for i := 0; i < dmsgcurlTries; i++ {
197217
if dmsgcurlOutput != "" {
198218
dmsgcurlLog.Debugf("Download attempt %d/%d ...", i, dmsgcurlTries)
199219
if _, err := file.Seek(0, 0); err != nil {
200-
return fmt.Errorf("failed to reset file: %w", err)
220+
return curlError{
221+
Error: fmt.Errorf("%s", errorDesc["WRITE_ERROR"]),
222+
Code: errorCode["WRITE_ERROR"],
223+
}
201224
}
202225
}
203-
if err := download(ctx, dmsgcurlLog, &httpC, file, parsedURL.String(), 0); err != nil {
226+
if downloadErr = download(ctx, dmsgcurlLog, &httpC, file, parsedURL.String(), 0); downloadErr.Code != 0 {
204227
dmsgcurlLog.WithError(err).Error()
205228
select {
206229
case <-ctx.Done():
207-
return ctx.Err()
230+
return curlError{
231+
Error: fmt.Errorf("%s", errorDesc["CONTEXT_CANCELED"]),
232+
Code: errorCode["CONTEXT_CANCELED"],
233+
}
208234
case <-time.After(time.Duration(dmsgcurlWait) * time.Second):
209235
continue
210236
}
211237
}
212-
213-
return nil
238+
return downloadErr
214239
}
215-
return err
240+
return downloadErr
216241
}
217242

218243
func prepareOutputFile() (*os.File, error) {
@@ -260,25 +285,37 @@ func parseOutputFile(output string, replace bool) (*os.File, error) {
260285
return nil, os.ErrExist
261286
}
262287

263-
func download(ctx context.Context, log logrus.FieldLogger, httpC *http.Client, w io.Writer, urlStr string, maxSize int64) error {
288+
func download(ctx context.Context, log logrus.FieldLogger, httpC *http.Client, w io.Writer, urlStr string, maxSize int64) curlError {
264289
req, err := http.NewRequest(http.MethodGet, urlStr, nil)
265290
if err != nil {
266291
log.WithError(err).Fatal("Failed to formulate HTTP request.")
267292
}
268293
resp, err := httpC.Do(req)
269294
if err != nil {
270-
return fmt.Errorf("failed to connect to HTTP server: %w", err)
295+
log.Errorf("failed to connect to HTTP server: %w", err)
296+
return curlError{
297+
Error: fmt.Errorf("%s", errorDesc["RECV_ERROR"]),
298+
Code: errorCode["RECV_ERROR"],
299+
}
271300
}
272301
if maxSize > 0 && resp.ContentLength > maxSize*1024 {
273-
return fmt.Errorf("requested file size is more than allowed size: %d KB > %d KB", (resp.ContentLength / 1024), maxSize)
302+
log.Errorf("requested file size is more than allowed size: %d KB > %d KB", (resp.ContentLength / 1024), maxSize)
303+
return curlError{
304+
Error: fmt.Errorf("%s", errorDesc["FILESIZE_EXCEEDED"]),
305+
Code: errorCode["FILESIZE_EXCEEDED"],
306+
}
274307
}
275308
n, err := cancellableCopy(ctx, w, resp.Body, resp.ContentLength)
276309
if err != nil {
277-
return fmt.Errorf("download failed at %d/%dB: %w", n, resp.ContentLength, err)
310+
log.Errorf("download failed at %d/%dB: %w", n, resp.ContentLength, err)
311+
return curlError{
312+
Error: fmt.Errorf("%s", errorDesc["DOWNLOAD_ERROR"]),
313+
Code: errorCode["DOWNLOAD_ERROR"],
314+
}
278315
}
279316
defer closeResponseBody(resp)
280317

281-
return nil
318+
return curlError{}
282319
}
283320

284321
type readerFunc func(p []byte) (n int, err error)
@@ -324,3 +361,9 @@ func Execute() {
324361
log.Fatal("Failed to execute command: ", err)
325362
}
326363
}
364+
365+
// curlError the struct of dmsgcurl functions output for use exit code as result
366+
type curlError struct {
367+
Error error
368+
Code int
369+
}

cmd/dmsgcurl/commands/errors.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package commands
2+
3+
var errorCode = map[string]int{
4+
"FAILED_INIT": 2,
5+
"URL_MALFORMAT": 3,
6+
"DMSG_INIT": 4,
7+
"COULDNT_RESOLVE_PROXY": 5,
8+
"COULDNT_RESOLVE_HOST": 6,
9+
"WRITE_INIT": 22,
10+
"WRITE_ERROR": 23,
11+
"READ_ERROR": 26,
12+
"SEND_ERROR": 55,
13+
"RECV_ERROR": 56,
14+
"DOWNLOAD_ERROR": 57,
15+
"FILESIZE_EXCEEDED": 63,
16+
"CONTEXT_CANCELED": 64,
17+
}
18+
var errorDesc = map[string]string{
19+
"FAILED_INIT": "Very early initialization code failed.",
20+
"URL_MALFORMAT": "The URL was not properly formatted.",
21+
"DMSG_INIT": "Couldn't resolve dmsg initialziation.",
22+
"COULDNT_RESOLVE_PROXY": "Couldn't resolve proxy. The given proxy host could not be resolved.",
23+
"COULDNT_RESOLVE_HOST": "Couldn't resolve host. The given remote host was not resolved.",
24+
"WRITE_INIT": "An error occurred when creating output file.",
25+
"WRITE_ERROR": "An error occurred when writing received data to a local file, or an error was returned to dmsgcurl from a write callback.",
26+
"READ_ERROR": "There was a problem reading a local file or an error returned by the read callback.",
27+
"SEND_ERROR": "Failed sending network data.",
28+
"RECV_ERROR": "Failure with receiving network data.",
29+
"DOWNLOAD_ERROR": "Failure with downloading data.",
30+
"FILESIZE_EXCEEDED": "Maximum file size exceeded.",
31+
"CONTEXT_CANCELED": "Operation canceled by user",
32+
}

0 commit comments

Comments
 (0)