diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index cbfde9c8d8cc3..a589e91df8260 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -19,6 +19,8 @@ RUN_USER = ; git ;; ;; Application run mode, affects performance and debugging. Either "dev", "prod" or "test", default is "prod" RUN_MODE = ; prod +;; JSON library to use. Either "std", "goccy", or "jsoniter". Will default to "jsoniter" +; JSON_LIBRARY = jsoniter ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 84703aa56345d..ea59831ea431a 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -39,6 +39,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. - `RUN_USER`: **git**: The user Gitea will run as. This should be a dedicated system (non-user) account. Setting this incorrectly will cause Gitea to not start. - `RUN_MODE`: **prod**: Application run mode, affects performance and debugging. Either "dev", "prod" or "test". +- `JSON_LIBRARY`: **jsoniter**: JSON library to use. Either "std", "goccy", or "jsoniter". Will default to "jsoniter" + ## Repository (`repository`) diff --git a/docs/content/doc/installation/from-source.en-us.md b/docs/content/doc/installation/from-source.en-us.md index 54e79769ea143..abae074f3b24a 100644 --- a/docs/content/doc/installation/from-source.en-us.md +++ b/docs/content/doc/installation/from-source.en-us.md @@ -161,6 +161,7 @@ using the `LDFLAGS` environment variable for `make`. The appropriate settings ar - For `AppWorkPath` you should use `-X \"code.gitea.io/gitea/modules/setting.AppWorkPath=working-path\"` - For `StaticRootPath` you should use `-X \"code.gitea.io/gitea/modules/setting.StaticRootPath=static-root-path\"` - To change the default PID file location use `-X \"code.gitea.io/gitea/modules/setting.PIDFile=/run/gitea.pid\"` +- To change the default JSON library use `-X \"code.gitea.io/gitea/modules/json.DefaultJSONHandlerType=\"` Add as many of the strings with their preceding `-X` to the `LDFLAGS` variable and run `make build` with the appropriate `TAGS` as above. diff --git a/docs/content/doc/usage/command-line.en-us.md b/docs/content/doc/usage/command-line.en-us.md index 8cc420ed11b1f..91c562f3e4df3 100644 --- a/docs/content/doc/usage/command-line.en-us.md +++ b/docs/content/doc/usage/command-line.en-us.md @@ -32,6 +32,7 @@ All global options can be placed at the command level. - `--custom-path path`, `-C path`: Location of the Gitea custom folder. Optional. (default: `AppWorkPath`/custom or `$GITEA_CUSTOM`). - `--config path`, `-c path`: Gitea configuration file path. Optional. (default: `custom`/conf/app.ini). - `--work-path path`, `-w path`: Gitea `AppWorkPath`. Optional. (default: LOCATION_OF_GITEA_BINARY or `$GITEA_WORK_DIR`) +- `--json-library`: JSON library to use. Optional. (default: `jsoniter`, but may be: `std`, `goccy` or `jsoniter`) NB: The defaults custom-path, config and work-path can also be changed at build time (if preferred). diff --git a/go.mod b/go.mod index bfb87a1b37a8b..f76b93479c17e 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/go-swagger/go-swagger v0.29.0 github.com/go-testfixtures/testfixtures/v3 v3.6.1 github.com/gobwas/glob v0.2.3 + github.com/goccy/go-json v0.9.5 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 @@ -171,7 +172,6 @@ require ( github.com/go-openapi/swag v0.19.15 // indirect github.com/go-openapi/validate v0.20.3 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/goccy/go-json v0.9.5 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect diff --git a/main.go b/main.go index 19b9dd63275fc..8da7228bcbad4 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "time" "code.gitea.io/gitea/cmd" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -99,6 +100,11 @@ arguments - which can alternatively be run by running the subcommand web.` Value: setting.AppWorkPath, Usage: "Set the gitea working path", }, + cli.StringFlag{ + Name: "json-library", + Value: json.DefaultJSONHandlerType, + Usage: "Set the default json library type", + }, } // Set the default to be equivalent to cmdWeb and add the default flags @@ -130,10 +136,14 @@ func establishCustomPath(ctx *cli.Context) error { var providedCustom string var providedConf string var providedWorkPath string + var defaultJSONHandler string currentCtx := ctx for { - if len(providedCustom) != 0 && len(providedConf) != 0 && len(providedWorkPath) != 0 { + if len(providedCustom) != 0 && + len(providedConf) != 0 && + len(providedWorkPath) != 0 && + len(defaultJSONHandler) != 0 { break } if currentCtx == nil { @@ -148,9 +158,13 @@ func establishCustomPath(ctx *cli.Context) error { if currentCtx.IsSet("work-path") && len(providedWorkPath) == 0 { providedWorkPath = currentCtx.String("work-path") } + if currentCtx.IsSet("json-library") && len(defaultJSONHandler) == 0 { + defaultJSONHandler = currentCtx.String("json-library") + } currentCtx = currentCtx.Parent() } + json.SelectDefaultJSONHandler(defaultJSONHandler) setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath) setAppHelpTemplates() diff --git a/modules/json/json.go b/modules/json/json.go index 4361262a2f241..00919519f87aa 100644 --- a/modules/json/json.go +++ b/modules/json/json.go @@ -10,7 +10,9 @@ import ( "encoding/binary" "encoding/json" //nolint:depguard "io" + "strings" + goccy_json "github.com/goccy/go-json" jsoniter "github.com/json-iterator/go" ) @@ -33,14 +35,43 @@ type Interface interface { Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error } +// DefaultJSONHandlerType is the type of library used as the backend for the JSON library +var DefaultJSONHandlerType = "jsoniter" + var ( // DefaultJSONHandler default json handler DefaultJSONHandler Interface = JSONiter{jsoniter.ConfigCompatibleWithStandardLibrary} _ Interface = StdJSON{} _ Interface = JSONiter{} + _ Interface = GoCcyJSON{} ) +func init() { + SelectDefaultJSONHandler(DefaultJSONHandlerType) +} + +// SelectDefaultJSONHandler selects the default JSON handler +// Note: this function is not race safe and would need to be run before other uses +func SelectDefaultJSONHandler(typ string) { + if typ == DefaultJSONHandlerType { + return + } + switch strings.ToLower(typ) { + case "std": + DefaultJSONHandler = StdJSON{} + DefaultJSONHandlerType = "std" + case "goccy": + DefaultJSONHandler = GoCcyJSON{} + DefaultJSONHandlerType = "goccy" + case "jsoniter": + fallthrough + default: + DefaultJSONHandler = JSONiter{jsoniter.ConfigCompatibleWithStandardLibrary} + DefaultJSONHandlerType = "jsoniter" + } +} + // StdJSON implements Interface via encoding/json type StdJSON struct{} @@ -99,6 +130,34 @@ func (j JSONiter) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) e return json.Indent(dst, src, prefix, indent) } +// GoCcyJSON implements Interface +type GoCcyJSON struct{} + +// Marshal implements Interface +func (j GoCcyJSON) Marshal(v interface{}) ([]byte, error) { + return goccy_json.Marshal(v) +} + +// Unmarshal implements Interface +func (j GoCcyJSON) Unmarshal(data []byte, v interface{}) error { + return goccy_json.Unmarshal(data, v) +} + +// NewEncoder implements Interface +func (j GoCcyJSON) NewEncoder(writer io.Writer) Encoder { + return goccy_json.NewEncoder(writer) +} + +// NewDecoder implements Interface +func (j GoCcyJSON) NewDecoder(reader io.Reader) Decoder { + return goccy_json.NewDecoder(reader) +} + +// Indent implements Interface +func (j GoCcyJSON) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { + return goccy_json.Indent(dst, src, prefix, indent) +} + // Marshal converts object as bytes func Marshal(v interface{}) ([]byte, error) { return DefaultJSONHandler.Marshal(v) diff --git a/modules/lfs/http_client.go b/modules/lfs/http_client.go index a1a3e7f363e38..73786618448b7 100644 --- a/modules/lfs/http_client.go +++ b/modules/lfs/http_client.go @@ -113,7 +113,7 @@ func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Poin err = json.NewDecoder(res.Body).Decode(&response) if err != nil { log.Error("Error decoding json: %v", err) - return nil, err + return nil, fmt.Errorf("invalid json: %w", err) } if len(response.Transfer) == 0 { diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 5e317b39ea289..8e433246c8a0a 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -612,6 +612,8 @@ func loadFromConf(allowEmpty bool, extraConfig string) { Cfg.NameMapper = ini.SnackCase + json.SelectDefaultJSONHandler(Cfg.Section("").Key("JSON_LIBRARY").MustString(json.DefaultJSONHandlerType)) + homeDir, err := util.HomeDir() if err != nil { log.Fatal("Failed to get home directory: %v", err) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5ec001b6d46cb..cb1fd350df0fb 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2702,6 +2702,7 @@ config.offline_mode = Local Mode config.disable_router_log = Disable Router Log config.run_user = Run As Username config.run_mode = Run Mode +config.json_library = JSON library config.git_version = Git Version config.repo_root_path = Repository Root Path config.lfs_root_path = LFS Root Path diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go index d4093f2049ac4..b49e8f335fe49 100644 --- a/routers/web/admin/admin.go +++ b/routers/web/admin/admin.go @@ -318,6 +318,7 @@ func Config(ctx *context.Context) { ctx.Data["DisableRouterLog"] = setting.DisableRouterLog ctx.Data["EnableXORMLog"] = setting.EnableXORMLog ctx.Data["LogSQL"] = setting.Database.LogSQL + ctx.Data["JSONLibrary"] = json.DefaultJSONHandlerType ctx.HTML(http.StatusOK, tplConfig) } diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 2a27baf53596b..f32fba4d59f4d 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -29,6 +29,8 @@
{{.RunUser}}
{{.i18n.Tr "admin.config.run_mode"}}
{{.RunMode}}
+
{{.i18n.Tr "admin.config.json_library"}}
+
{{.JSONLibrary}}