diff --git a/docs/static/swagger-versions/v1.75.0.json b/docs/static/swagger-versions/v1.75.0.json index 49b66a4d..6ca23277 100644 --- a/docs/static/swagger-versions/v1.75.0.json +++ b/docs/static/swagger-versions/v1.75.0.json @@ -4786,6 +4786,12 @@ "$ref": "#/definitions/convert.Note" } }, + "plan": { + "type": "array", + "items": { + "$ref": "#/definitions/convert.SetPlan" + } + }, "sets": { "type": "array", "items": { @@ -4821,6 +4827,47 @@ } } }, + "convert.SetPlan": { + "type": "object", + "properties": { + "accepts_targets": { + "type": "boolean" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "enabled": { + "type": "boolean" + }, + "faking": { + "type": "boolean" + }, + "fallback_for": { + "type": "integer" + }, + "ips": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "profile": { + "type": "integer" + }, + "role": { + "type": "string" + }, + "strategy": { + "type": "string" + } + } + }, "convert.Status": { "type": "string", "enum": [ @@ -6080,6 +6127,15 @@ "name_prefix": { "type": "string" }, + "profile_domains": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, "text": { "type": "string" }, diff --git a/docs/static/swagger.json b/docs/static/swagger.json index 49b66a4d..6ca23277 100644 --- a/docs/static/swagger.json +++ b/docs/static/swagger.json @@ -4786,6 +4786,12 @@ "$ref": "#/definitions/convert.Note" } }, + "plan": { + "type": "array", + "items": { + "$ref": "#/definitions/convert.SetPlan" + } + }, "sets": { "type": "array", "items": { @@ -4821,6 +4827,47 @@ } } }, + "convert.SetPlan": { + "type": "object", + "properties": { + "accepts_targets": { + "type": "boolean" + }, + "domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "enabled": { + "type": "boolean" + }, + "faking": { + "type": "boolean" + }, + "fallback_for": { + "type": "integer" + }, + "ips": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "profile": { + "type": "integer" + }, + "role": { + "type": "string" + }, + "strategy": { + "type": "string" + } + } + }, "convert.Status": { "type": "string", "enum": [ @@ -6080,6 +6127,15 @@ "name_prefix": { "type": "string" }, + "profile_domains": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, "text": { "type": "string" }, diff --git a/src/http/handler/convert.go b/src/http/handler/convert.go index 9f1b5777..d5232365 100644 --- a/src/http/handler/convert.go +++ b/src/http/handler/convert.go @@ -5,6 +5,7 @@ import ( "errors" "net/http" "strconv" + "strings" "github.com/daniellavrushin/b4/config" "github.com/daniellavrushin/b4/convert" @@ -171,13 +172,29 @@ func (api *API) runConvert(req convertRequest) (*convert.Result, error) { case errors.Is(err, convert.ErrNothingToParse): return nil, ErrBadRequest("No recognizable options were found in the supplied text") case errors.Is(err, convert.ErrUnsupportedTool): - return nil, ErrBadRequest("These options belong to a tool b4 cannot convert yet. Only byedpi command lines are supported so far") + return nil, ErrBadRequest("These options belong to a tool b4 cannot convert yet. Supported: " + + supportedToolList()) } return nil, ErrBadRequest(err.Error()) } return res, nil } +func supportedToolList() string { + tools, err := convert.Tools() + if err != nil { + return "none" + } + names := make([]string, 0, len(tools)) + for _, t := range tools { + names = append(names, t.Label) + } + if len(names) == 0 { + return "none" + } + return strings.Join(names, ", ") +} + func assignSetIDs(sets []config.SetConfig) []*config.SetConfig { ids := make(map[string]string, len(sets)) out := make([]*config.SetConfig, 0, len(sets)) diff --git a/src/http/handler/convert_test.go b/src/http/handler/convert_test.go index 7dd7af19..53f114f6 100644 --- a/src/http/handler/convert_test.go +++ b/src/http/handler/convert_test.go @@ -143,6 +143,41 @@ func TestAssignSetIDs_DropsDanglingEscalation(t *testing.T) { } } +func TestSupportedToolList_NamesEveryRegisteredTool(t *testing.T) { + tools, err := convert.Tools() + if err != nil { + t.Fatal(err) + } + if len(tools) == 0 { + t.Fatal("no tools registered") + } + list := supportedToolList() + for _, x := range tools { + if !strings.Contains(list, x.Label) { + t.Errorf("%q is a supported tool but the message does not name it: %q", x.Label, list) + } + } +} + +func TestHandleConvertAnalyze_UnsupportedToolNamesWhatIsSupported(t *testing.T) { + mux := convertAPI(t) + rec := postConvert(t, mux, "/api/convert/analyze", + `{"text":"--dpi-desync=fake --tlsrec=1 --mod-http=h"}`) + + if rec.Code != http.StatusBadRequest { + t.Fatalf("got %d (%s), want 400", rec.Code, rec.Body.String()) + } + tools, err := convert.Tools() + if err != nil { + t.Fatal(err) + } + for _, x := range tools { + if !strings.Contains(rec.Body.String(), x.Label) { + t.Errorf("error body does not name %q: %s", x.Label, rec.Body.String()) + } + } +} + func TestNormalizeDomains(t *testing.T) { got := normalizeDomains([]string{" YouTube.com ", "https://youtube.com/watch", "", "vk.com:443"}) want := []string{"youtube.com", "vk.com"}