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{})
+}