browser
The browser.example microservice is a one-page web “browser”: an address bar plus the raw source of whatever URL you submit. It demonstrates two related patterns:
- Outbound HTTP through the HTTP egress core microservice. A Microbus microservice should not call
http.Getdirectly — outbound HTTP runs through the egress proxy so it picks up the framework’s tracing, retries, time budgets, and (in test runs) a mock you can substitute for the real network. - A clean test seam. Because the call goes through the
httpegressapiclient, swapping in a mock egress service in tests is a one-line change. The example’sservice_test.goexercises this.
Depends On
httpegress.core— the egress proxy that owns the actual outbound HTTP call.
Try It
With the examples app running, open the browser UI:
http://localhost:8080/browser.example/browse
Enter any URL in the address bar and submit. The HTML/text source of the response is rendered below it inside a <pre> block.
Code Walkthrough
Source: exampleservices/browser/service.go.
The Service struct is bare; no state is needed because every request is independent:
type Service struct {
*Intermediate // IMPORTANT: Do not remove
}OnStartup and OnShutdown are empty.
Wiring (intermediate.go)
A single web subscription from intermediate.go:
func NewService() *Service {
svc := &Service{}
svc.Intermediate = NewIntermediate(svc)
return svc
}svc.Subscribe(
"Browse", svc.Browse,
sub.At(browserapi.Browse.Method, browserapi.Browse.Route),
sub.Description(`Browse shows a simple address bar and the source code of a URL.`),
sub.Web(),
)Browse
func (svc *Service) Browse(w http.ResponseWriter, r *http.Request) (err error) {
ctx := r.Context()
u := r.URL.Query().Get("url")
if u != "" && !strings.Contains(u, "://") {
u = "https://" + u
}
var page strings.Builder
page.WriteString("<html><head>")
page.WriteString("</head><body>")
// Address bar and button
page.WriteString(`<form method=GET action=browse>`)
page.WriteString(`<input type=text name=url size=80 placeholder="Enter a URL" value="`)
page.WriteString(html.EscapeString(u))
page.WriteString(`">`)
page.WriteString(`<input type=submit value="View Source">`)
page.WriteString(`</form>`)
// Source code
if u != "" {
resp, err := httpegressapi.NewClient(svc).Get(ctx, u)
if err != nil {
return errors.Trace(err)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Trace(err)
}
page.WriteString(`<pre style="white-space:pre-wrap">`)
page.WriteString(html.EscapeString(string(body)))
page.WriteString("</pre>")
}
page.WriteString("</body></html>")
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(page.String()))
return nil
}The handler itself is mostly HTML-string assembly. The Microbus-relevant line is in the middle:
resp, err := httpegressapi.NewClient(svc).Get(ctx, u)httpegressapi.NewClient(svc).Get(ctx, u) looks identical to a normal HTTP call but routes the request through the bus to the httpegress.core microservice, which performs the actual outbound call. From the perspective of the caller this is just another service-to-service call — same context propagation, same tracing, same time budget. In tests, httpegress is replaced with a mock that returns canned responses, so browser’s test never touches the real network.
The url input is wrapped with "https://" if no scheme is present so a user can type microbus.io and get the expected behavior. URL validation is left to the egress proxy; bad URLs surface as a normal error from Get and are returned to the caller.
See Also
- HTTP egress core microservice — the proxy that performs the outbound call, and the contract
httpegressapiwraps.