-
Notifications
You must be signed in to change notification settings - Fork 7
feat: [EPIC-GW-04] 搭建网络访问面骨架 (HTTP/WS/SSE) #312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,6 +55,24 @@ Gateway 子命令(Step 1 骨架): | |
| go run ./cmd/neocode gateway | ||
| ``` | ||
|
|
||
| 指定网络访问面监听地址(默认 `127.0.0.1:8080`,仅允许 Loopback): | ||
|
|
||
| ```bash | ||
| go run ./cmd/neocode gateway --http-listen 127.0.0.1:8080 | ||
| ``` | ||
|
|
||
| 网络访问面骨架端点(EPIC-GW-04): | ||
|
|
||
| - `POST /rpc`:单次 JSON-RPC 请求入口 | ||
| - `GET /ws`:WebSocket 流式入口(含心跳) | ||
| - `GET /sse`:SSE 流式入口(MVP 默认触发 `gateway.ping`,含心跳) | ||
|
|
||
| 安全限制:为防止跨站攻击,网关网络面默认开启严格的 Origin 校验。当前仅允许 | ||
| `http://localhost`、`http://127.0.0.1`、`http://[::1]` 以及 `app://` 前缀来源连入; | ||
| 非允许来源的跨域调用会被拦截并返回 `403`。 | ||
| 注:上述白名单机制仅针对携带 `Origin` 头的浏览器跨站请求生效。若请求不携带 `Origin` 头 | ||
| (例如 `cURL`、Postman 或本地后端脚本直连),网关默认放行。 | ||
|
|
||
| URL Scheme 派发骨架命令(EPIC-GW-02A): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Docs say only allowlisted origins are allowed, but implementation permits requests with empty |
||
|
|
||
| ```bash | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ var ( | |
| runGatewayCommand = defaultGatewayCommandRunner | ||
| runURLDispatchCommand = defaultURLDispatchCommandRunner | ||
| newGatewayServer = defaultNewGatewayServer | ||
| newGatewayNetwork = defaultNewGatewayNetworkServer | ||
| dispatchURLThroughIPC = urlscheme.Dispatch | ||
| exitProcess = os.Exit | ||
| writeDispatchError = writeURLDispatchErrorOutput | ||
|
|
@@ -35,6 +36,7 @@ var ( | |
|
|
||
| type gatewayCommandOptions struct { | ||
| ListenAddress string | ||
| HTTPAddress string | ||
| LogLevel string | ||
| } | ||
|
|
||
|
|
@@ -63,6 +65,12 @@ type gatewayServer interface { | |
| Close(ctx context.Context) error | ||
| } | ||
|
|
||
| type gatewayNetworkServer interface { | ||
| ListenAddress() string | ||
| Serve(ctx context.Context, runtimePort gateway.RuntimePort) error | ||
| Close(ctx context.Context) error | ||
| } | ||
|
|
||
| // newGatewayCommand 创建并返回网关子命令,负责启动本地 Gateway 进程。 | ||
| func newGatewayCommand() *cobra.Command { | ||
| options := &gatewayCommandOptions{} | ||
|
|
@@ -80,12 +88,19 @@ func newGatewayCommand() *cobra.Command { | |
|
|
||
| return runGatewayCommand(cmd.Context(), gatewayCommandOptions{ | ||
| ListenAddress: strings.TrimSpace(options.ListenAddress), | ||
| HTTPAddress: strings.TrimSpace(options.HTTPAddress), | ||
| LogLevel: normalizedLogLevel, | ||
| }) | ||
| }, | ||
| } | ||
|
|
||
| cmd.Flags().StringVar(&options.ListenAddress, "listen", "", "gateway listen address (optional override)") | ||
| cmd.Flags().StringVar( | ||
| &options.HTTPAddress, | ||
| "http-listen", | ||
| gateway.DefaultNetworkListenAddress, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| "gateway network listen address (loopback only)", | ||
| ) | ||
| cmd.Flags().StringVar(&options.LogLevel, "log-level", defaultGatewayLogLevel, "gateway log level: debug|info|warn|error") | ||
|
|
||
| return cmd | ||
|
|
@@ -110,26 +125,53 @@ func defaultGatewayCommandRunner(ctx context.Context, options gatewayCommandOpti | |
| signalContext, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) | ||
| defer stop() | ||
|
|
||
| server, err := newGatewayServer(gateway.ServerOptions{ | ||
| ipcServer, err := newGatewayServer(gateway.ServerOptions{ | ||
| ListenAddress: options.ListenAddress, | ||
| Logger: logger, | ||
| }) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| networkServer, err := newGatewayNetwork(gateway.NetworkServerOptions{ | ||
| ListenAddress: options.HTTPAddress, | ||
| Logger: logger, | ||
| }) | ||
| if err != nil { | ||
| _ = ipcServer.Close(context.Background()) | ||
| return err | ||
| } | ||
| defer func() { | ||
| _ = server.Close(context.Background()) | ||
| _ = networkServer.Close(context.Background()) | ||
| _ = ipcServer.Close(context.Background()) | ||
| }() | ||
|
|
||
| logger.Printf("gateway listen address: %s", server.ListenAddress()) | ||
| return server.Serve(signalContext, nil) | ||
| logger.Printf("gateway ipc listen address: %s", ipcServer.ListenAddress()) | ||
| logger.Printf("gateway network listen address: %s", networkServer.ListenAddress()) | ||
|
|
||
| go func() { | ||
| serveErr := networkServer.Serve(signalContext, nil) | ||
| if serveErr != nil && signalContext.Err() == nil { | ||
| logger.Printf( | ||
| "warning: HTTP server failed to start on %s (port in use?), but IPC server is still running: %v", | ||
| networkServer.ListenAddress(), | ||
| serveErr, | ||
| ) | ||
| } | ||
| }() | ||
|
|
||
| return ipcServer.Serve(signalContext, nil) | ||
| } | ||
|
|
||
| // defaultNewGatewayServer 创建默认网关服务实例,供命令层启动流程调用。 | ||
| func defaultNewGatewayServer(options gateway.ServerOptions) (gatewayServer, error) { | ||
| return gateway.NewServer(options) | ||
| } | ||
|
|
||
| // defaultNewGatewayNetworkServer 创建默认网关网络访问面服务实例,供命令层启动流程调用。 | ||
| func defaultNewGatewayNetworkServer(options gateway.NetworkServerOptions) (gatewayNetworkServer, error) { | ||
| return gateway.NewNetworkServer(options) | ||
| } | ||
|
|
||
| // newURLDispatchCommand 创建 URL Scheme 派发子命令骨架,仅做参数收敛与调用转发。 | ||
| func newURLDispatchCommand() *cobra.Command { | ||
| options := &urlDispatchCommandOptions{} | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.