forked from goadesign/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
27 changed files
with
1,636 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Sample endpoints that return multiple status codes in HTTP transport. | ||
=== | ||
|
||
This sample changes the response status according to the contents of the request body. See the [Tag documentation](https://pkg.go.dev/goa.design/goa/v3/dsl#Tag) for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package main | ||
|
||
import ( | ||
"net/http" | ||
"time" | ||
|
||
cli "goa.design/examples/httpstatus/gen/http/cli/hello" | ||
goahttp "goa.design/goa/v3/http" | ||
goa "goa.design/goa/v3/pkg" | ||
) | ||
|
||
func doHTTP(scheme, host string, timeout int, debug bool) (goa.Endpoint, any, error) { | ||
var ( | ||
doer goahttp.Doer | ||
) | ||
{ | ||
doer = &http.Client{Timeout: time.Duration(timeout) * time.Second} | ||
if debug { | ||
doer = goahttp.NewDebugDoer(doer) | ||
} | ||
} | ||
|
||
return cli.ParseEndpoint( | ||
scheme, | ||
host, | ||
doer, | ||
goahttp.RequestEncoder, | ||
goahttp.ResponseDecoder, | ||
debug, | ||
) | ||
} | ||
|
||
func httpUsageCommands() string { | ||
return cli.UsageCommands() | ||
} | ||
|
||
func httpUsageExamples() string { | ||
return cli.UsageExamples() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"net/url" | ||
"os" | ||
"strings" | ||
|
||
goa "goa.design/goa/v3/pkg" | ||
) | ||
|
||
func main() { | ||
var ( | ||
hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") | ||
addrF = flag.String("url", "", "URL to service host") | ||
|
||
verboseF = flag.Bool("verbose", false, "Print request and response details") | ||
vF = flag.Bool("v", false, "Print request and response details") | ||
timeoutF = flag.Int("timeout", 30, "Maximum number of seconds to wait for response") | ||
) | ||
flag.Usage = usage | ||
flag.Parse() | ||
var ( | ||
addr string | ||
timeout int | ||
debug bool | ||
) | ||
{ | ||
addr = *addrF | ||
if addr == "" { | ||
switch *hostF { | ||
case "localhost": | ||
addr = "http://localhost:80" | ||
default: | ||
fmt.Fprintf(os.Stderr, "invalid host argument: %q (valid hosts: localhost)\n", *hostF) | ||
os.Exit(1) | ||
} | ||
} | ||
timeout = *timeoutF | ||
debug = *verboseF || *vF | ||
} | ||
|
||
var ( | ||
scheme string | ||
host string | ||
) | ||
{ | ||
u, err := url.Parse(addr) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "invalid URL %#v: %s\n", addr, err) | ||
os.Exit(1) | ||
} | ||
scheme = u.Scheme | ||
host = u.Host | ||
} | ||
var ( | ||
endpoint goa.Endpoint | ||
payload any | ||
err error | ||
) | ||
{ | ||
switch scheme { | ||
case "http", "https": | ||
endpoint, payload, err = doHTTP(scheme, host, timeout, debug) | ||
default: | ||
fmt.Fprintf(os.Stderr, "invalid scheme: %q (valid schemes: grpc|http)\n", scheme) | ||
os.Exit(1) | ||
} | ||
} | ||
if err != nil { | ||
if err == flag.ErrHelp { | ||
os.Exit(0) | ||
} | ||
fmt.Fprintln(os.Stderr, err.Error()) | ||
fmt.Fprintln(os.Stderr, "run '"+os.Args[0]+" --help' for detailed usage.") | ||
os.Exit(1) | ||
} | ||
|
||
data, err := endpoint(context.Background(), payload) | ||
if err != nil { | ||
fmt.Fprintln(os.Stderr, err.Error()) | ||
os.Exit(1) | ||
} | ||
|
||
if data != nil { | ||
m, _ := json.MarshalIndent(data, "", " ") | ||
fmt.Println(string(m)) | ||
} | ||
} | ||
|
||
func usage() { | ||
fmt.Fprintf(os.Stderr, `%s is a command line client for the hello API. | ||
Usage: | ||
%s [-host HOST][-url URL][-timeout SECONDS][-verbose|-v] SERVICE ENDPOINT [flags] | ||
-host HOST: server host (localhost). valid values: localhost | ||
-url URL: specify service URL overriding host URL (http://localhost:8080) | ||
-timeout: maximum number of seconds to wait for response (30) | ||
-verbose|-v: print request and response details (false) | ||
Commands: | ||
%s | ||
Additional help: | ||
%s SERVICE [ENDPOINT] --help | ||
Example: | ||
%s | ||
`, os.Args[0], os.Args[0], indent(httpUsageCommands()), os.Args[0], indent(httpUsageExamples())) | ||
} | ||
|
||
func indent(s string) string { | ||
if s == "" { | ||
return "" | ||
} | ||
return " " + strings.Replace(s, "\n", "\n ", -1) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"net/http" | ||
"net/url" | ||
"os" | ||
"sync" | ||
"time" | ||
|
||
hello "goa.design/examples/httpstatus/gen/hello" | ||
hellosvr "goa.design/examples/httpstatus/gen/http/hello/server" | ||
goahttp "goa.design/goa/v3/http" | ||
httpmdlwr "goa.design/goa/v3/http/middleware" | ||
"goa.design/goa/v3/middleware" | ||
) | ||
|
||
// handleHTTPServer starts configures and starts a HTTP server on the given | ||
// URL. It shuts down the server if any error is received in the error channel. | ||
func handleHTTPServer(ctx context.Context, u *url.URL, helloEndpoints *hello.Endpoints, wg *sync.WaitGroup, errc chan error, logger *log.Logger, debug bool) { | ||
|
||
// Setup goa log adapter. | ||
var ( | ||
adapter middleware.Logger | ||
) | ||
{ | ||
adapter = middleware.NewLogger(logger) | ||
} | ||
|
||
// Provide the transport specific request decoder and response encoder. | ||
// The goa http package has built-in support for JSON, XML and gob. | ||
// Other encodings can be used by providing the corresponding functions, | ||
// see goa.design/implement/encoding. | ||
var ( | ||
dec = goahttp.RequestDecoder | ||
enc = goahttp.ResponseEncoder | ||
) | ||
|
||
// Build the service HTTP request multiplexer and configure it to serve | ||
// HTTP requests to the service endpoints. | ||
var mux goahttp.Muxer | ||
{ | ||
mux = goahttp.NewMuxer() | ||
} | ||
|
||
// Wrap the endpoints with the transport specific layers. The generated | ||
// server packages contains code generated from the design which maps | ||
// the service input and output data structures to HTTP requests and | ||
// responses. | ||
var ( | ||
helloServer *hellosvr.Server | ||
) | ||
{ | ||
eh := errorHandler(logger) | ||
helloServer = hellosvr.New(helloEndpoints, mux, dec, enc, eh, nil) | ||
if debug { | ||
servers := goahttp.Servers{ | ||
helloServer, | ||
} | ||
servers.Use(httpmdlwr.Debug(mux, os.Stdout)) | ||
} | ||
} | ||
// Configure the mux. | ||
hellosvr.Mount(mux, helloServer) | ||
|
||
// Wrap the multiplexer with additional middlewares. Middlewares mounted | ||
// here apply to all the service endpoints. | ||
var handler http.Handler = mux | ||
{ | ||
handler = httpmdlwr.Log(adapter)(handler) | ||
handler = httpmdlwr.RequestID()(handler) | ||
} | ||
|
||
// Start HTTP server using default configuration, change the code to | ||
// configure the server as required by your service. | ||
srv := &http.Server{Addr: u.Host, Handler: handler, ReadHeaderTimeout: time.Second * 60} | ||
for _, m := range helloServer.Mounts { | ||
logger.Printf("HTTP %q mounted on %s %s", m.Method, m.Verb, m.Pattern) | ||
} | ||
|
||
(*wg).Add(1) | ||
go func() { | ||
defer (*wg).Done() | ||
|
||
// Start HTTP server in a separate goroutine. | ||
go func() { | ||
logger.Printf("HTTP server listening on %q", u.Host) | ||
errc <- srv.ListenAndServe() | ||
}() | ||
|
||
<-ctx.Done() | ||
logger.Printf("shutting down HTTP server at %q", u.Host) | ||
|
||
// Shutdown gracefully with a 30s timeout. | ||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
defer cancel() | ||
|
||
err := srv.Shutdown(ctx) | ||
if err != nil { | ||
logger.Printf("failed to shutdown: %v", err) | ||
} | ||
}() | ||
} | ||
|
||
// errorHandler returns a function that writes and logs the given error. | ||
// The function also writes and logs the error unique ID so that it's possible | ||
// to correlate. | ||
func errorHandler(logger *log.Logger) func(context.Context, http.ResponseWriter, error) { | ||
return func(ctx context.Context, w http.ResponseWriter, err error) { | ||
id := ctx.Value(middleware.RequestIDKey).(string) | ||
_, _ = w.Write([]byte("[" + id + "] encoding: " + err.Error())) | ||
logger.Printf("[%s] ERROR: %s", id, err.Error()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net" | ||
"net/url" | ||
"os" | ||
"os/signal" | ||
"sync" | ||
"syscall" | ||
|
||
helloapi "goa.design/examples/httpstatus" | ||
hello "goa.design/examples/httpstatus/gen/hello" | ||
) | ||
|
||
func main() { | ||
// Define command line flags, add any other flag required to configure the | ||
// service. | ||
var ( | ||
hostF = flag.String("host", "localhost", "Server host (valid values: localhost)") | ||
domainF = flag.String("domain", "", "Host domain name (overrides host domain specified in service design)") | ||
httpPortF = flag.String("http-port", "", "HTTP port (overrides host HTTP port specified in service design)") | ||
secureF = flag.Bool("secure", false, "Use secure scheme (https or grpcs)") | ||
dbgF = flag.Bool("debug", false, "Log request and response bodies") | ||
) | ||
flag.Parse() | ||
|
||
// Setup logger. Replace logger with your own log package of choice. | ||
var ( | ||
logger *log.Logger | ||
) | ||
{ | ||
logger = log.New(os.Stderr, "[helloapi] ", log.Ltime) | ||
} | ||
|
||
// Initialize the services. | ||
var ( | ||
helloSvc hello.Service | ||
) | ||
{ | ||
helloSvc = helloapi.NewHello(logger) | ||
} | ||
|
||
// Wrap the services in endpoints that can be invoked from other services | ||
// potentially running in different processes. | ||
var ( | ||
helloEndpoints *hello.Endpoints | ||
) | ||
{ | ||
helloEndpoints = hello.NewEndpoints(helloSvc) | ||
} | ||
|
||
// Create channel used by both the signal handler and server goroutines | ||
// to notify the main goroutine when to stop the server. | ||
errc := make(chan error) | ||
|
||
// Setup interrupt handler. This optional step configures the process so | ||
// that SIGINT and SIGTERM signals cause the services to stop gracefully. | ||
go func() { | ||
c := make(chan os.Signal, 1) | ||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) | ||
errc <- fmt.Errorf("%s", <-c) | ||
}() | ||
|
||
var wg sync.WaitGroup | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
|
||
// Start the servers and send errors (if any) to the error channel. | ||
switch *hostF { | ||
case "localhost": | ||
{ | ||
addr := "http://localhost:80" | ||
u, err := url.Parse(addr) | ||
if err != nil { | ||
logger.Fatalf("invalid URL %#v: %s\n", addr, err) | ||
} | ||
if *secureF { | ||
u.Scheme = "https" | ||
} | ||
if *domainF != "" { | ||
u.Host = *domainF | ||
} | ||
if *httpPortF != "" { | ||
h, _, err := net.SplitHostPort(u.Host) | ||
if err != nil { | ||
logger.Fatalf("invalid URL %#v: %s\n", u.Host, err) | ||
} | ||
u.Host = net.JoinHostPort(h, *httpPortF) | ||
} else if u.Port() == "" { | ||
u.Host = net.JoinHostPort(u.Host, "80") | ||
} | ||
handleHTTPServer(ctx, u, helloEndpoints, &wg, errc, logger, *dbgF) | ||
} | ||
|
||
default: | ||
logger.Fatalf("invalid host argument: %q (valid hosts: localhost)\n", *hostF) | ||
} | ||
|
||
// Wait for signal. | ||
logger.Printf("exiting (%v)", <-errc) | ||
|
||
// Send cancellation signal to the goroutines. | ||
cancel() | ||
|
||
wg.Wait() | ||
logger.Println("exited") | ||
} |
Oops, something went wrong.