examples/go: http handler & http server
Both applications implement a simple http server.
http handler uses mojo http server and provides an http handler;
http server uses mojo network service, TCP connections and go
net package.
R=jamesr@chromium.org
Review URL: https://codereview.chromium.org/1105553002
diff --git a/examples/go/BUILD.gn b/examples/go/BUILD.gn
index 78412ab..59a9c74 100644
--- a/examples/go/BUILD.gn
+++ b/examples/go/BUILD.gn
@@ -9,6 +9,8 @@
deps = [
":go_echo_client",
":go_echo_server",
+ ":go_http_handler",
+ ":go_http_server",
]
}
@@ -37,4 +39,30 @@
"//mojo/public/platform/native:system",
]
}
+
+ go_mojo_application("go_http_handler") {
+ sources = [
+ "http_handler.go",
+ ]
+ deps = [
+ "//mojo/go:application",
+ "//mojo/go:platform_cgo",
+ "//mojo/public/c/system",
+ "//mojo/public/platform/native:system",
+ "//mojo/services/network/public/interfaces",
+ ]
+ }
+
+ go_mojo_application("go_http_server") {
+ sources = [
+ "http_server.go",
+ ]
+ deps = [
+ "//mojo/go:application",
+ "//mojo/go:platform_cgo",
+ "//mojo/public/c/system",
+ "//mojo/public/platform/native:system",
+ "//mojo/services/network/public/interfaces",
+ ]
+ }
}
diff --git a/examples/go/README.txt b/examples/go/README.txt
index cdfc603..ac540ae 100644
--- a/examples/go/README.txt
+++ b/examples/go/README.txt
@@ -1,8 +1,21 @@
-Sample Go echo application.
+Sample Go applications.
-Instructions to run on android emulator:
+echo_client, echo_server - sample applications that talk to each other
+
+http_handler - application that implements HTTP server; uses the mojo HTTP
+Server service to handle the HTTP protocol details, and just contains the logic
+for handling its registered urls.
+
+http_server - application that implements HTTP server; uses go net stack on top
+of the mojo network service.
1) Follow steps from //mojo/go/sample_app/README.txt
-To run client:
+To run echo client:
$ mojo/tools/android_mojo_shell.py --enable-multiprocess mojo:go_echo_client
+
+To run http handler:
+$ mojo/tools/android_mojo_shell.py --enable-multiprocess mojo:go_http_handler
+
+To run http server:
+$ mojo/tools/android_mojo_shell.py --enable-multiprocess mojo:go_http_server
diff --git a/examples/go/http_handler.go b/examples/go/http_handler.go
new file mode 100644
index 0000000..3794c9f
--- /dev/null
+++ b/examples/go/http_handler.go
@@ -0,0 +1,108 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "golang.org/x/mobile/app"
+
+ "mojo/public/go/application"
+ "mojo/public/go/bindings"
+ "mojo/public/go/system"
+
+ "mojo/services/http_server/public/interfaces/http_request"
+ "mojo/services/http_server/public/interfaces/http_response"
+ "mojo/services/http_server/public/interfaces/http_server"
+ "mojo/services/http_server/public/interfaces/http_server_factory"
+ "mojo/services/network/public/interfaces/net_address"
+)
+
+//#include "mojo/public/c/system/types.h"
+import "C"
+
+type HttpHandler struct{}
+
+func (h *HttpHandler) HandleRequest(request http_request.HttpRequest) (http_response.HttpResponse, error) {
+ resp := "Hello, Go http handler!"
+ r, producer, consumer := system.GetCore().CreateDataPipe(&system.DataPipeOptions{
+ system.MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+ 1,
+ uint32(len(resp)),
+ })
+ if r != system.MOJO_RESULT_OK {
+ panic(fmt.Sprintf("can't create data pipe: %v", r))
+ }
+
+ producer.WriteData([]byte(resp), system.MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)
+ producer.Close()
+ return http_response.HttpResponse{
+ 200,
+ &consumer,
+ int64(len(resp)),
+ "text/html; charset=utf-8",
+ nil,
+ }, nil
+}
+
+type HttpHandlerDelegate struct {
+ server *http_server.HttpServerProxy
+ handlerStub *bindings.Stub
+}
+
+func (d *HttpHandlerDelegate) Initialize(ctx application.Context) {
+ request, pointer := http_server_factory.CreateMessagePipeForHttpServerFactory()
+ ctx.ConnectToApplication("mojo:http_server").ConnectToService(&request)
+ factory := http_server_factory.NewHttpServerFactoryProxy(pointer, bindings.GetAsyncWaiter())
+
+ addr := &net_address.NetAddress{
+ net_address.NetAddressFamily_IpV4,
+ &net_address.NetAddressIPv4{
+ 8080,
+ [4]uint8{127, 0, 0, 1},
+ },
+ nil,
+ }
+ serverRequest, serverPointer := http_server.CreateMessagePipeForHttpServer()
+ factory.CreateHttpServer(serverRequest, addr)
+ d.server = http_server.NewHttpServerProxy(serverPointer, bindings.GetAsyncWaiter())
+ handlerRequest, handlerPointer := http_server.CreateMessagePipeForHttpHandler()
+ ok, err := d.server.SetHandler("/go", handlerPointer)
+ if !ok {
+ log.Println("can't set handler:", err)
+ return
+ }
+
+ d.handlerStub = http_server.NewHttpHandlerStub(handlerRequest, &HttpHandler{}, bindings.GetAsyncWaiter())
+ go func() {
+ for {
+ if err := d.handlerStub.ServeRequest(); err != nil {
+ log.Println("can't handle http request:", err)
+ return
+ }
+ }
+ }()
+ factory.Close_proxy()
+}
+
+func (d *HttpHandlerDelegate) AcceptConnection(c *application.Connection) {
+ c.Close()
+}
+
+func (d *HttpHandlerDelegate) Quit() {
+ d.server.Close_proxy()
+ d.handlerStub.Close()
+}
+
+//export MojoMain
+func MojoMain(handle C.MojoHandle) C.MojoResult {
+ application.Run(&HttpHandlerDelegate{}, system.MojoHandle(handle))
+ return C.MOJO_RESULT_OK
+}
+
+func main() {
+ app.Run(app.Callbacks{})
+}
diff --git a/examples/go/http_server.go b/examples/go/http_server.go
new file mode 100644
index 0000000..0d5eab8
--- /dev/null
+++ b/examples/go/http_server.go
@@ -0,0 +1,247 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "log"
+ "net"
+ "net/http"
+ "time"
+
+ "golang.org/x/mobile/app"
+
+ "mojo/public/go/application"
+ "mojo/public/go/bindings"
+ "mojo/public/go/system"
+
+ "mojo/services/network/public/interfaces/net_address"
+ "mojo/services/network/public/interfaces/network_service"
+ "mojo/services/network/public/interfaces/tcp_bound_socket"
+ "mojo/services/network/public/interfaces/tcp_connected_socket"
+ "mojo/services/network/public/interfaces/tcp_server_socket"
+)
+
+//#include "mojo/public/c/system/types.h"
+import "C"
+
+type MojoConnection struct {
+ sendStream system.ProducerHandle
+ receiveStream system.ConsumerHandle
+ socket system.MessagePipeHandle
+ localAddr net.Addr
+ remoteAddr net.Addr
+}
+
+// Implements net.Conn.
+func (c *MojoConnection) Read(b []byte) (int, error) {
+ l := len(b)
+ r, read := c.receiveStream.BeginReadData(l, system.MOJO_READ_DATA_FLAG_NONE)
+ if r != system.MOJO_RESULT_OK {
+ return 0, fmt.Errorf("can't read %v bytes: %v", l, r)
+ }
+ if l > len(read) {
+ l = len(read)
+ }
+ copy(b, read[:l])
+ if r := c.receiveStream.EndReadData(l); r != system.MOJO_RESULT_OK {
+ return 0, fmt.Errorf("can't read %v bytes: %v", l, r)
+ }
+ return l, nil
+}
+
+// Implements net.Conn.
+func (c *MojoConnection) Write(b []byte) (int, error) {
+ l := len(b)
+ r, buf := c.sendStream.BeginWriteData(l, system.MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)
+ if r != system.MOJO_RESULT_OK {
+ return 0, fmt.Errorf("can't write %v bytes: %v", l, r)
+ }
+ copy(buf, b)
+ if r := c.sendStream.EndWriteData(l); r != system.MOJO_RESULT_OK {
+ return 0, fmt.Errorf("can't write %v bytes: %v", l, r)
+ }
+ return l, nil
+}
+
+// Implements net.Conn.
+func (c *MojoConnection) Close() error {
+ c.sendStream.Close()
+ c.receiveStream.Close()
+ c.socket.Close()
+ return nil
+}
+
+// Implements net.Conn.
+func (c *MojoConnection) LocalAddr() net.Addr {
+ return c.localAddr
+}
+
+// Implements net.Conn.
+func (c *MojoConnection) RemoteAddr() net.Addr {
+ return c.remoteAddr
+}
+
+// Implements net.Conn.
+func (c *MojoConnection) SetDeadline(t time.Time) error {
+ if err := c.SetReadDeadline(t); err != nil {
+ return err
+ }
+ return c.SetWriteDeadline(t)
+}
+
+// Implements net.Conn.
+func (c *MojoConnection) SetReadDeadline(t time.Time) error {
+ // Do nothing.
+ return nil
+}
+
+// Implements net.Conn.
+func (c *MojoConnection) SetWriteDeadline(t time.Time) error {
+ // Do nothing.
+ return nil
+}
+
+type MojoListener struct {
+ serverSocket *tcp_server_socket.TcpServerSocketProxy
+}
+
+// Implements net.Listener.
+func (l *MojoListener) Accept() (net.Conn, error) {
+ r, sendProducer, sendConsumer := system.GetCore().CreateDataPipe(nil)
+ if r != system.MOJO_RESULT_OK {
+ panic(fmt.Sprintf("can't create data pipe: %v", r))
+ }
+ r, receiveProducer, receiveConsumer := system.GetCore().CreateDataPipe(nil)
+ if r != system.MOJO_RESULT_OK {
+ panic(fmt.Sprintf("can't create data pipe: %v", r))
+ }
+ request, pointer := tcp_connected_socket.CreateMessagePipeForTcpConnectedSocket()
+ networkError, remoteAddress, err := l.serverSocket.Accept(sendConsumer, receiveProducer, request)
+ if err != nil {
+ return nil, err
+ }
+ if networkError.Code != 0 {
+ return nil, fmt.Errorf("%s", *networkError.Description)
+ }
+
+ var addr net.Addr
+ if remoteAddress.Ipv4 != nil {
+ addr = &net.TCPAddr{
+ IP: remoteAddress.Ipv4.Addr[:4],
+ Port: int(remoteAddress.Ipv4.Port),
+ }
+ } else {
+ addr = &net.TCPAddr{
+ IP: remoteAddress.Ipv6.Addr[:16],
+ Port: int(remoteAddress.Ipv6.Port),
+ }
+ }
+ return &MojoConnection{
+ sendProducer,
+ receiveConsumer,
+ pointer.PassMessagePipe(),
+ l.Addr(),
+ addr,
+ }, nil
+}
+
+// Implements net.Listener.
+func (l *MojoListener) Close() error {
+ l.serverSocket.Close_proxy()
+ return nil
+}
+
+// Implements net.Listener.
+func (l *MojoListener) Addr() net.Addr {
+ return &net.TCPAddr{
+ IP: []byte{127, 0, 0, 1},
+ Port: 8080,
+ }
+}
+
+type HttpServerDelegate struct {
+ networkService *network_service.NetworkServiceProxy
+ tcpBoundSocket *tcp_bound_socket.TcpBoundSocketProxy
+ serverSocket *tcp_server_socket.TcpServerSocketProxy
+}
+
+func (d *HttpServerDelegate) InitTCPBoundSocket() error {
+ addr := &net_address.NetAddress{
+ net_address.NetAddressFamily_IpV4,
+ &net_address.NetAddressIPv4{
+ 8080,
+ [4]uint8{127, 0, 0, 1},
+ },
+ nil,
+ }
+ request, pointer := tcp_bound_socket.CreateMessagePipeForTcpBoundSocket()
+ networkError, _, err := d.networkService.CreateTcpBoundSocket(addr, request)
+ if err != nil {
+ return err
+ }
+ if networkError.Code != 0 {
+ return fmt.Errorf("%s", *networkError.Description)
+ }
+ d.tcpBoundSocket = tcp_bound_socket.NewTcpBoundSocketProxy(pointer, bindings.GetAsyncWaiter())
+ return nil
+}
+
+func (d *HttpServerDelegate) InitServerSocket() error {
+ request, pointer := tcp_server_socket.CreateMessagePipeForTcpServerSocket()
+ networkError, err := d.tcpBoundSocket.StartListening(request)
+ if err != nil {
+ return err
+ }
+ if networkError.Code != 0 {
+ return fmt.Errorf("%s", *networkError.Description)
+ }
+ d.serverSocket = tcp_server_socket.NewTcpServerSocketProxy(pointer, bindings.GetAsyncWaiter())
+ return nil
+}
+
+func (d *HttpServerDelegate) Initialize(ctx application.Context) {
+ request, pointer := network_service.CreateMessagePipeForNetworkService()
+ ctx.ConnectToApplication("mojo:network_service").ConnectToService(&request)
+ d.networkService = network_service.NewNetworkServiceProxy(pointer, bindings.GetAsyncWaiter())
+
+ if err := d.InitTCPBoundSocket(); err != nil {
+ log.Printf("can't create TCP socket: %v\n", err)
+ return
+ }
+ if err := d.InitServerSocket(); err != nil {
+ log.Printf("can't create server socket: %v\n", err)
+ return
+ }
+
+ http.HandleFunc("/go", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, "Hello, Go http server!")
+ })
+ l := &MojoListener{d.serverSocket}
+ if err := http.Serve(l, nil); err != nil {
+ log.Printf("can't serve request: %v\n", err)
+ return
+ }
+}
+
+func (d *HttpServerDelegate) AcceptConnection(c *application.Connection) {
+ c.Close()
+}
+
+func (d *HttpServerDelegate) Quit() {
+ d.networkService.Close_proxy()
+ d.tcpBoundSocket.Close_proxy()
+ d.serverSocket.Close_proxy()
+}
+
+//export MojoMain
+func MojoMain(handle C.MojoHandle) C.MojoResult {
+ application.Run(&HttpServerDelegate{}, system.MojoHandle(handle))
+ return C.MOJO_RESULT_OK
+}
+
+func main() {
+ app.Run(app.Callbacks{})
+}