add initial lesnet source code and tentative build files

All the build files are currently symlinked into mojo repo (../src)
except this file, which was copied from the mojo repo and modified:

mojo/tools/mojob.py
(sha1: dd752cf0606cbac8d9bc68c9752e0de27b124686)

Change-Id: I217631719c0c720afcf39bfa9413d3431216a649
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..6fdf1dc
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,8 @@
+# Defines the Chromium style for automatic reformatting.
+# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
+BasedOnStyle: Chromium
+# This defaults to 'Auto'. Explicitly set it for a while, so that
+# 'vector<vector<int> >' in existing files gets formatted to
+# 'vector<vector<int>>'. ('Auto' means that clang-format will only use
+# 'int>>' if the file already contains at least one such instance.)
+Standard: Cpp11
diff --git a/.gitignore b/.gitignore
index 3c9067e..0dc5930 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,8 @@
 tags
 Thumbs.db
 v8.log
+
+# project-specific stuff
+/build/util/LASTCHANGE*
+/build/linux/bin/eu-strip
+/out/
diff --git a/.gn b/.gn
new file mode 100644
index 0000000..a3bd14d
--- /dev/null
+++ b/.gn
@@ -0,0 +1,10 @@
+# This file is used by the experimental meta-buildsystem in src/tools/gn to
+# find the root of the source tree and to set startup options.
+
+# The location of the build configuration file.
+buildconfig = "//build/config/BUILDCONFIG.gn"
+
+# The secondary source root is a parallel directory tree where
+# GN build files are placed when they can not be placed directly
+# in the source tree, e.g. for third party source trees.
+secondary_source = "//build/secondary/"
diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000..fa8f5e8
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,5 @@
+group("root") {
+  deps = [
+    "//mojo/services/network",
+  ]
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8c1b6b8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# lesnet network service
+
+Building lesnet in effenel tree is still a work-in-progress..
+
+## Before building first time
+
+Please run download_prebuilts.sh
+
+$ sh ./download_prebuilts.sh
+
+## To build
+
+$ mojo/tools/mojob gn --release
+$ mojo/tools/mojob build --release
diff --git a/base b/base
new file mode 120000
index 0000000..90ad92a
--- /dev/null
+++ b/base
@@ -0,0 +1 @@
+../src/base
\ No newline at end of file
diff --git a/build/build_config.h b/build/build_config.h
new file mode 120000
index 0000000..38e5e66
--- /dev/null
+++ b/build/build_config.h
@@ -0,0 +1 @@
+../../src/build/build_config.h
\ No newline at end of file
diff --git a/build/compiler_version.py b/build/compiler_version.py
new file mode 120000
index 0000000..0f9a6e2
--- /dev/null
+++ b/build/compiler_version.py
@@ -0,0 +1 @@
+../../src/build/compiler_version.py
\ No newline at end of file
diff --git a/build/config b/build/config
new file mode 120000
index 0000000..5595137
--- /dev/null
+++ b/build/config
@@ -0,0 +1 @@
+../../src/build/config
\ No newline at end of file
diff --git a/build/gn_helpers.py b/build/gn_helpers.py
new file mode 120000
index 0000000..6a2aa60
--- /dev/null
+++ b/build/gn_helpers.py
@@ -0,0 +1 @@
+../../src/build/gn_helpers.py
\ No newline at end of file
diff --git a/build/gypi_to_gn.py b/build/gypi_to_gn.py
new file mode 120000
index 0000000..cd3a34e
--- /dev/null
+++ b/build/gypi_to_gn.py
@@ -0,0 +1 @@
+../../src/build/gypi_to_gn.py
\ No newline at end of file
diff --git a/build/linux b/build/linux
new file mode 120000
index 0000000..ab0e9d4
--- /dev/null
+++ b/build/linux
@@ -0,0 +1 @@
+../../src/build/linux
\ No newline at end of file
diff --git a/build/module_args/dart.gni b/build/module_args/dart.gni
new file mode 120000
index 0000000..5a12692
--- /dev/null
+++ b/build/module_args/dart.gni
@@ -0,0 +1 @@
+../../../src/build/module_args/dart.gni
\ No newline at end of file
diff --git a/build/module_args/mojo.gni b/build/module_args/mojo.gni
new file mode 100644
index 0000000..699543a
--- /dev/null
+++ b/build/module_args/mojo.gni
@@ -0,0 +1,20 @@
+# 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.
+
+# This variable should point to the parent directory of the Mojo SDK.
+mojo_sdk_root = "//third_party/mojo/src"
+
+# To build the Mojo shell from source, set this variable to true. To use the
+# prebuilt shell, omit this variable or set it to false. Note that the prebuilt
+# shell will be used only on platforms for which it is published (currently
+# Linux and Android).
+mojo_build_mojo_shell_from_source = false
+
+# To build the dart_snapshotter from source, set this variable to true. To use
+# the prebuilt dart_snapshotter, omit this variable or set it to false.
+mojo_build_dart_snapshotter_from_source = false
+
+# To build the network service from source, set this variable to true. To use
+# the prebuilt network service, omit this variable or set it to false.
+mojo_build_network_service_from_source = true
diff --git a/build/module_args/nacl.gni b/build/module_args/nacl.gni
new file mode 120000
index 0000000..ffda9b9
--- /dev/null
+++ b/build/module_args/nacl.gni
@@ -0,0 +1 @@
+../../../src/build/module_args/nacl.gni
\ No newline at end of file
diff --git a/build/module_args/v8.gni b/build/module_args/v8.gni
new file mode 120000
index 0000000..93fc75d
--- /dev/null
+++ b/build/module_args/v8.gni
@@ -0,0 +1 @@
+../../../src/build/module_args/v8.gni
\ No newline at end of file
diff --git a/build/sanitizers b/build/sanitizers
new file mode 120000
index 0000000..5b4e632
--- /dev/null
+++ b/build/sanitizers
@@ -0,0 +1 @@
+../../src/build/sanitizers
\ No newline at end of file
diff --git a/build/secondary b/build/secondary
new file mode 120000
index 0000000..01205a2
--- /dev/null
+++ b/build/secondary
@@ -0,0 +1 @@
+../../src/build/secondary
\ No newline at end of file
diff --git a/build/toolchain b/build/toolchain
new file mode 120000
index 0000000..d236062
--- /dev/null
+++ b/build/toolchain
@@ -0,0 +1 @@
+../../src/build/toolchain
\ No newline at end of file
diff --git a/buildtools b/buildtools
new file mode 120000
index 0000000..d9620da
--- /dev/null
+++ b/buildtools
@@ -0,0 +1 @@
+../src/buildtools
\ No newline at end of file
diff --git a/download_prebuilts.sh b/download_prebuilts.sh
new file mode 100755
index 0000000..dff714a
--- /dev/null
+++ b/download_prebuilts.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+./third_party/mojo/src/mojo/public/tools/download_shell_binary.py \
+	--tools-directory=../../../../lesnet/tools \
+	--version-file=../../../../lesnet/third_party/mojo/MOJO_VERSION
+
+./third_party/mojo/src/mojo/public/tools/download_dart_snapshotter.py \
+	--tools-directory=../../../../lesnet/tools \
+	--version-file=../../../../lesnet/third_party/mojo/MOJO_VERSION
diff --git a/mojo/services/network/BUILD.gn b/mojo/services/network/BUILD.gn
new file mode 100644
index 0000000..a8d5457
--- /dev/null
+++ b/mojo/services/network/BUILD.gn
@@ -0,0 +1,96 @@
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/mojo/src/mojo/public/mojo_application.gni")
+
+if (is_android) {
+# TODO(toshik)
+} else {
+  mojo_native_application("network") {
+    output_name = "network_service"
+    deps = [
+      ":sources",
+    ]
+  }
+  mojo_native_application("network_secure") {
+    output_name = "network_service-secure"
+    deps = [
+      ":sources_secure",
+    ]
+  }
+}
+
+source_set("sources") {
+  defines = []
+
+  sources = [
+    "main.cc",
+    "network_service_delegate.cc",
+    "network_service_delegate.h",
+    "network_service_impl.cc",
+    "network_service_impl.h",
+    "url_loader_impl.cc",
+    "url_loader_impl.h",
+    "http_client.h",
+    "network_error.h",
+  ]
+
+  include_dirs = [ "//third_party/asio/asio/include" ]
+
+  defines += [
+    "ASIO_STANDALONE",
+    "ASIO_NO_EXCEPTIONS",
+    "ASIO_NO_TYPEID",
+    "ASIO_HAS_STD_SYSTEM_ERROR",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/services/network/interfaces",
+    "//third_party/mojo/src/mojo/public/cpp/application",
+    "//third_party/mojo/src/mojo/public/cpp/application:standalone",
+    "//third_party/mojo/src/mojo/public/cpp/bindings",
+    "//third_party/mojo/src/mojo/public/cpp/system",
+    "//third_party/mojo/src/mojo/public/cpp/utility",
+    "//third_party/boringssl:boringssl",
+  ]
+}
+
+source_set("sources_secure") {
+  defines = [
+    "NETWORK_SERVICE_USE_HTTPS",
+    "NETWORK_SERVICE_HTTPS_CERT_HACK",
+  ]
+
+  sources = [
+    "main.cc",
+    "network_service_delegate.cc",
+    "network_service_delegate.h",
+    "network_service_impl.cc",
+    "network_service_impl.h",
+    "url_loader_impl.cc",
+    "url_loader_impl.h",
+    "http_client.h",
+    "network_error.h",
+  ]
+
+  include_dirs = [ "//third_party/asio/asio/include" ]
+
+  defines += [
+    "ASIO_STANDALONE",
+    "ASIO_NO_EXCEPTIONS",
+    "ASIO_NO_TYPEID",
+    "ASIO_HAS_STD_SYSTEM_ERROR",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/services/network/interfaces",
+    "//third_party/mojo/src/mojo/public/cpp/application",
+    "//third_party/mojo/src/mojo/public/cpp/application:standalone",
+    "//third_party/mojo/src/mojo/public/cpp/bindings",
+    "//third_party/mojo/src/mojo/public/cpp/system",
+    "//third_party/mojo/src/mojo/public/cpp/utility",
+    "//third_party/boringssl:boringssl",
+  ]
+}
diff --git a/mojo/services/network/http_client.h b/mojo/services/network/http_client.h
new file mode 100644
index 0000000..624662e
--- /dev/null
+++ b/mojo/services/network/http_client.h
@@ -0,0 +1,394 @@
+// 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.
+
+#ifndef MOJO_SERVICES_NETWORK_HTTP_CLIENT_H_
+#define MOJO_SERVICES_NETWORK_HTTP_CLIENT_H_
+
+#include <iostream>
+#include "mojo/services/network/network_error.h"
+
+#include <asio.hpp>
+#include <asio/ssl.hpp>
+
+using asio::ip::tcp;
+
+namespace mojo {
+
+typedef asio::ssl::stream<tcp::socket> ssl_socket_t;
+typedef tcp::socket nonssl_socket_t;
+
+template<typename T>
+class URLLoaderImpl::HTTPClient {
+
+  static_assert(std::is_same<T, ssl_socket_t>::value || std::is_same<T, nonssl_socket_t>::value, "requires either ssl_socket_t or nonssl_socket_t");
+
+ public:
+  HTTPClient<T>(URLLoaderImpl* loader,
+             asio::io_service& io_service,
+             asio::ssl::context& context,
+             const std::string& server, const std::string& port,
+             const std::string& path);
+
+  HTTPClient<T>(URLLoaderImpl* loader,
+            asio::io_service& io_service,
+            const std::string& server, const std::string& port,
+            const std::string& path);
+ private:
+  void CreateRequest(const std::string& server, const std::string& path);
+  void OnResolve(const asio::error_code& err,
+                 tcp::resolver::iterator endpoint_iterator);
+  bool OnVerifyCertificate(bool preverified,
+                           asio::ssl::verify_context& ctx);
+  void OnConnect(const asio::error_code& err);
+  void OnHandShake(const asio::error_code& err);
+  void OnWriteRequest(const asio::error_code& err);
+  void OnReadStatusLine(const asio::error_code& err);
+  MojoResult SendBody();
+  void ParseHeaderField(const std::string& header, std::string& name, std::string& value);
+  void OnReadHeaders(const asio::error_code& err);
+  void OnReadBody(const asio::error_code& err);
+
+ public:
+  unsigned int status_code_;
+        std::string redirect_location_;
+
+ private:
+  URLLoaderImpl* loader_;
+
+  tcp::resolver resolver_;
+  T socket_;
+  asio::streambuf request_buf_;
+  asio::streambuf response_buf_;
+
+  std::string http_version_;
+  std::string status_message_;
+
+  ScopedDataPipeProducerHandle response_body_stream_;
+};
+
+template<>
+void URLLoaderImpl::HTTPClient<ssl_socket_t>::OnResolve(const asio::error_code& err,
+                              tcp::resolver::iterator endpoint_iterator);
+template<>
+void URLLoaderImpl::HTTPClient<nonssl_socket_t>::OnResolve(const asio::error_code& err,
+                              tcp::resolver::iterator endpoint_iterator);
+template<>
+void URLLoaderImpl::HTTPClient<ssl_socket_t>::OnConnect(const asio::error_code& err);
+template<>
+void URLLoaderImpl::HTTPClient<nonssl_socket_t>::OnConnect(const asio::error_code& err);
+
+
+template<>
+URLLoaderImpl::HTTPClient<ssl_socket_t>::HTTPClient(URLLoaderImpl* loader,
+                        asio::io_service& io_service,
+                        asio::ssl::context& context,
+                        const std::string& server, const std::string& port,
+                        const std::string& path)
+  : loader_(loader),
+    resolver_(io_service),
+    socket_(io_service, context) {
+  CreateRequest(server, path);
+
+  tcp::resolver::query query(server, port);
+  resolver_.async_resolve(query,
+                          std::bind(&HTTPClient<ssl_socket_t>::OnResolve, this,
+                                    std::placeholders::_1,
+                                    std::placeholders::_2));
+}
+
+template<>
+URLLoaderImpl::HTTPClient<nonssl_socket_t>::HTTPClient(URLLoaderImpl* loader,
+                         asio::io_service& io_service,
+                         const std::string& server, const std::string& port,
+                         const std::string& path)
+  : loader_(loader),
+    resolver_(io_service),
+    socket_(io_service) {
+  CreateRequest(server, path);
+
+  tcp::resolver::query query(server, port);
+  resolver_.async_resolve(query,
+                          std::bind(&HTTPClient<nonssl_socket_t>::OnResolve, this,
+                                    std::placeholders::_1,
+                                    std::placeholders::_2));
+}
+
+template<typename T>
+void URLLoaderImpl::HTTPClient<T>::CreateRequest(const std::string& server,
+                                                 const std::string& path)
+{
+  // We specify the "Connection: close" header so that the server will
+  // close the socket after transmitting the response. This will allow us
+  // to treat all data up until the EOF as the content.
+  std::ostream request_stream(&request_buf_);
+  request_stream << "GET " << path << " HTTP/1.0\r\n";
+  request_stream << "Host: " << server << "\r\n";
+  request_stream << "Accept: */*\r\n";
+  request_stream << "Connection: close\r\n\r\n";
+}
+
+template<>
+void URLLoaderImpl::HTTPClient<ssl_socket_t>::OnResolve(const asio::error_code& err,
+                              tcp::resolver::iterator endpoint_iterator)
+{
+  if (!err) {
+    socket_.set_verify_mode(asio::ssl::verify_peer);
+    socket_.set_verify_callback(std::bind(&HTTPClient<ssl_socket_t>::OnVerifyCertificate,
+                                          this, std::placeholders::_1,
+                                          std::placeholders::_2));
+    asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
+                        std::bind(&HTTPClient<ssl_socket_t>::OnConnect, this,
+                                  std::placeholders::_1));
+  } else {
+    std::cout << "Error: Resolve(SSL): " << err.message() << "\n";
+  }
+}
+
+template<>
+void URLLoaderImpl::HTTPClient<nonssl_socket_t>::OnResolve(
+                                              const asio::error_code& err,
+                              tcp::resolver::iterator endpoint_iterator)
+{
+  if (!err) {
+      asio::async_connect(socket_, endpoint_iterator,
+                          std::bind(&HTTPClient<nonssl_socket_t>::OnConnect, this,
+                                    std::placeholders::_1));
+  } else {
+    std::cout << "Error: Resolve(NonSSL): " << err.message() << "\n";
+  }
+}
+
+template<typename T>
+bool URLLoaderImpl::HTTPClient<T>::OnVerifyCertificate(bool preverified,
+                                                   asio::ssl::verify_context& ctx)
+{
+  // TODO(toshik): RFC 2818 describes the steps involved in doing this for
+  // HTTPS.
+  char subject_name[256];
+  X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
+  X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
+  std::cout << "Verifying " << subject_name << "\n";
+
+#ifdef NETWORK_SERVICE_HTTPS_CERT_HACK
+  preverified = true;
+#endif
+  return preverified;
+}
+
+template<>
+void URLLoaderImpl::HTTPClient<ssl_socket_t>::OnConnect(const asio::error_code& err)
+{
+  if (!err) {
+    socket_.async_handshake(asio::ssl::stream_base::client,
+                            std::bind(&HTTPClient<ssl_socket_t>::OnHandShake, this,
+                                      std::placeholders::_1));
+  } else {
+    std::cout << "Error: Connect(SSL): " << err.message() << "\n";
+  }
+}
+
+template<>
+void URLLoaderImpl::HTTPClient<nonssl_socket_t>::OnConnect(const asio::error_code& err)
+{
+  if (!err) {
+    asio::async_write(socket_, request_buf_,
+                      std::bind(&HTTPClient<nonssl_socket_t>::OnWriteRequest, this,
+                                std::placeholders::_1));
+  } else {
+    std::cout << "Error: Connect(NonSSL): " << err.message() << "\n";
+  }
+}
+
+template<typename T>
+void URLLoaderImpl::HTTPClient<T>::OnHandShake(const asio::error_code& err)
+{
+  if (!err) {
+    asio::async_write(socket_, request_buf_,
+                      std::bind(&HTTPClient<T>::OnWriteRequest, this,
+                                std::placeholders::_1));
+  } else {
+    std::cout << "Error: HandShake: " << err.message() << "\n";
+  }
+}
+
+template<typename T>
+void URLLoaderImpl::HTTPClient<T>::OnWriteRequest(const asio::error_code& err)
+{
+  if (!err) {
+    // TODO(toshik): The response_ streambuf will automatically grow
+    // The growth may be limited by passing a maximum size to the
+    // streambuf constructor.
+    asio::async_read_until(socket_, response_buf_, "\r\n",
+                           std::bind(&HTTPClient<T>::OnReadStatusLine, this,
+                                     std::placeholders::_1));
+  } else {
+    std::cout << "Error: WriteRequest: " << err.message() << "\n";
+  }
+}
+
+template<typename T>
+void URLLoaderImpl::HTTPClient<T>::OnReadStatusLine(const asio::error_code& err)
+{
+  if (!err) {
+    std::istream response_stream(&response_buf_);
+    response_stream >> http_version_;
+    response_stream >> status_code_;
+    std::string status_message;
+    std::getline(response_stream, status_message_);
+    if (!response_stream || http_version_.substr(0, 5) != "HTTP/") {
+      std::cout << "Error: ReadStatusLine: Invalid response\n";
+      return;
+    }
+    if (status_code_ != 200 && status_code_ != 301 && status_code_ != 302) {
+      // TODO(toshik): handle more status codes
+      std::cout << "Error: ReadStatusLine: Status code ";
+      std::cout << status_code_ << "\n";
+      return;
+    }
+
+    asio::async_read_until(socket_, response_buf_, "\r\n\r\n",
+                           std::bind(&HTTPClient<T>::OnReadHeaders, this,
+                                     std::placeholders::_1));
+  } else {
+    std::cout << "Error: ReadStatusLine: " << err << "\n";
+  }
+}
+
+template<typename T>
+MojoResult URLLoaderImpl::HTTPClient<T>::SendBody()
+{
+  if (response_buf_.size() > 0) {
+    uint32_t size = response_buf_.size();
+
+    void *buf;
+    uint32_t num_bytes;
+    MojoResult result = BeginWriteDataRaw(response_body_stream_.get(),
+                                          &buf, &num_bytes,
+                                          MOJO_WRITE_DATA_FLAG_NONE);
+    if (result != MOJO_RESULT_OK) {
+      std::cout << "Warning: SendBody: BeginWriteDataRAW: result="
+                << result << std::endl;
+      // TODO(toshik): how to handle this?
+      response_body_stream_.reset();
+      response_buf_.consume(size);
+      return result;
+    }
+
+    if (num_bytes < size) {
+      std::cout << "Error: SendBody: Not enough buf (" << num_bytes << " < "
+                << size << ")" << std::endl;
+      size = num_bytes;
+      // TODO(toshik): need to handle buffer-full
+    }
+
+    std::istream response_stream(&response_buf_);
+    response_stream.read((char*)buf, size);
+
+    EndWriteDataRaw(response_body_stream_.get(), size);
+
+    response_buf_.consume(size);
+  }
+
+  return MOJO_RESULT_OK;
+}
+
+template<typename T>
+void URLLoaderImpl::HTTPClient<T>::ParseHeaderField(const std::string& header,
+                                                       std::string& name,
+                                                       std::string& value)
+{
+  std::string::const_iterator name_end = std::find(header.begin(),
+                                                      header.end(), ':');
+  name = std::string(header.begin(), name_end);
+
+  std::string::const_iterator value_begin =
+    std::find_if(name_end + 1, header.end(), [](int c) { return c != ' '; });
+  std::string::const_iterator value_end =
+    std::find_if(name_end + 1, header.end(), [](int c) { return c == '\r'; });
+  value = std::string(value_begin, value_end);
+}
+
+template<typename T>
+void URLLoaderImpl::HTTPClient<T>::OnReadHeaders(const asio::error_code& err)
+{
+  if (!err) {
+    std::istream response_stream(&response_buf_);
+    std::string header;
+
+    if (status_code_ == 301 || status_code_ == 302) {
+      redirect_location_.clear();
+
+      while (std::getline(response_stream, header) && header != "\r") {
+        HttpHeaderPtr hdr = HttpHeader::New();
+        std::string name, value;
+        ParseHeaderField(header, name, value);
+        if (name == "Location") {
+          redirect_location_ = value;
+          std::cout << "Redirecting to " << redirect_location_ << "\n";
+        }
+      }
+    } else {
+      URLResponsePtr response = URLResponse::New();
+
+      response->status_code = status_code_;
+      response->status_line =
+        http_version_ + " " + std::to_string(status_code_) + status_message_;
+
+      while (std::getline(response_stream, header) && header != "\r") {
+        HttpHeaderPtr hdr = HttpHeader::New();
+        std::string name, value;
+        ParseHeaderField(header, name, value);
+        hdr->name = name;
+        hdr->value = value;
+        response->headers.push_back(hdr.Pass());
+      }
+
+      DataPipe data_pipe;
+      response_body_stream_ = data_pipe.producer_handle.Pass();
+      response->body = data_pipe.consumer_handle.Pass();
+
+      loader_->SendResponse(response.Pass());
+
+      if (SendBody() != MOJO_RESULT_OK)
+        return;
+
+      asio::async_read(socket_, response_buf_,
+                       asio::transfer_at_least(1),
+                       std::bind(&HTTPClient<T>::OnReadBody, this,
+                                 std::placeholders::_1));
+    }
+  } else {
+    std::cout << "Error: ReadHeaders: " << err << "\n";
+  }
+}
+
+template<typename T>
+void URLLoaderImpl::HTTPClient<T>::OnReadBody(const asio::error_code& err)
+{
+  if (!err) {
+    if (SendBody() != MOJO_RESULT_OK)
+      return;
+
+    asio::async_read(socket_, response_buf_,
+                     asio::transfer_at_least(1),
+                     std::bind(&HTTPClient<T>::OnReadBody, this,
+                               std::placeholders::_1));
+  } else {
+    // std::cout << "Error: " << err << std::endl;
+    // TODO(toshi): handle EOF error
+    response_body_stream_.reset();
+  }
+}
+
+} // namespace mojo
+
+#if defined(ASIO_NO_EXCEPTIONS)
+// ASIO doesn't provide this if exception is not enabled
+template <typename Exception>
+void asio::detail::throw_exception(const Exception& e)
+{
+}
+#endif
+
+#endif /* MOJO_SERVICES_NETWORK_HTTP_CLIENT_H_ */
diff --git a/mojo/services/network/interfaces/BUILD.gn b/mojo/services/network/interfaces/BUILD.gn
new file mode 100644
index 0000000..f757f70
--- /dev/null
+++ b/mojo/services/network/interfaces/BUILD.gn
@@ -0,0 +1,17 @@
+# 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.
+
+import("//build/module_args/mojo.gni")
+import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "network_service.mojom",
+    "url_loader.mojom",
+  ]
+
+  mojo_sdk_deps = [ "mojo/public/interfaces/network" ]
+
+  import_dirs = [ get_path_info("../../../", "abspath") ]
+}
diff --git a/mojo/services/network/interfaces/network_service.mojom b/mojo/services/network/interfaces/network_service.mojom
new file mode 100644
index 0000000..b378cbc
--- /dev/null
+++ b/mojo/services/network/interfaces/network_service.mojom
@@ -0,0 +1,13 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo;
+
+import "mojo/services/network/interfaces/url_loader.mojom";
+
+[ServiceName="mojo::NetworkService"]
+interface NetworkService {
+  CreateURLLoader(URLLoader& loader);
+};
diff --git a/mojo/services/network/interfaces/url_loader.mojom b/mojo/services/network/interfaces/url_loader.mojom
new file mode 100644
index 0000000..32ea0e6
--- /dev/null
+++ b/mojo/services/network/interfaces/url_loader.mojom
@@ -0,0 +1,39 @@
+// 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.
+
+[DartPackage="mojo_services"]
+module mojo;
+
+import "mojo/public/interfaces/network/network_error.mojom";
+import "mojo/public/interfaces/network/url_request.mojom";
+import "mojo/public/interfaces/network/url_response.mojom";
+
+struct URLLoaderStatus {
+  // If the loader has failed due to a network level error, this field will be
+  // set.
+  NetworkError? error;
+
+  // Set to true if the URLLoader is still working. Set to false once an error
+  // is encountered or the response body is completely copied to the response
+  // body stream.
+  bool is_loading;
+
+  // TODO(darin): Add further details about the stages of loading (e.g.,
+  // "resolving host") that happen prior to receiving bytes.
+};
+
+interface URLLoader {
+  // Loads the given |request|, asynchronously producing |response|. Consult
+  // |response| to determine if the request resulted in an error, was
+  // redirected, or has a response body to be consumed.
+  Start(URLRequest request) => (URLResponse response);
+
+  // If the request passed to |Start| had |auto_follow_redirects| set to false,
+  // then upon receiving an URLResponse with a non-NULL |redirect_url| field,
+  // |FollowRedirect| may be called to load the URL indicated by the redirect.
+  FollowRedirect() => (URLResponse response);
+
+  // Query status about the URLLoader.
+  QueryStatus() => (URLLoaderStatus status);
+};
diff --git a/mojo/services/network/main.cc b/mojo/services/network/main.cc
new file mode 100644
index 0000000..d528b17
--- /dev/null
+++ b/mojo/services/network/main.cc
@@ -0,0 +1,14 @@
+// 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.
+
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_runner.h"
+
+#include "network_service_delegate.h"
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+  mojo::ApplicationRunner runner(new NetworkServiceDelegate);
+  return runner.Run(shell_handle);
+}
diff --git a/mojo/services/network/network_error.h b/mojo/services/network/network_error.h
new file mode 100644
index 0000000..6dcf49d
--- /dev/null
+++ b/mojo/services/network/network_error.h
@@ -0,0 +1,15 @@
+// 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.
+
+#ifndef MOJO_SERVICES_NETWORK_NETWORK_ERROR_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_ERROR_H_
+
+// TODO(toshik): copied from net/base/net_error_list.h
+
+enum {
+  ERR_INVALID_ARGUMENT = -4,
+  ERR_UNEXPECTED = -9,
+};
+
+#endif /* MOJO_SERVICES_NETWORK_NETWORK_ERROR_H_ */
diff --git a/mojo/services/network/network_service_delegate.cc b/mojo/services/network/network_service_delegate.cc
new file mode 100644
index 0000000..83ecb09
--- /dev/null
+++ b/mojo/services/network/network_service_delegate.cc
@@ -0,0 +1,29 @@
+// 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.
+
+#include "mojo/services/network/network_service_delegate.h"
+
+#include "mojo/public/cpp/application/application_connection.h"
+
+NetworkServiceDelegate::NetworkServiceDelegate() {}
+
+NetworkServiceDelegate::~NetworkServiceDelegate() {}
+
+void NetworkServiceDelegate::Initialize(mojo::ApplicationImpl* app) {
+}
+
+bool NetworkServiceDelegate::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  connection->AddService(this);
+  return true;
+}
+
+void NetworkServiceDelegate::Quit() {
+}
+
+void NetworkServiceDelegate::Create(
+    mojo::ApplicationConnection* connection,
+    mojo::InterfaceRequest<mojo::NetworkService> request) {
+  new mojo::NetworkServiceImpl(request.Pass(), connection);
+}
diff --git a/mojo/services/network/network_service_delegate.h b/mojo/services/network/network_service_delegate.h
new file mode 100644
index 0000000..c17bfe4
--- /dev/null
+++ b/mojo/services/network/network_service_delegate.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef MOJO_SERVICES_NETWORK_NETWORK_SERVICE_DELEGATE_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_SERVICE_DELEGATE_H_
+
+#include "mojo/services/network/network_service_impl.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/application/interface_factory.h"
+
+class NetworkServiceDelegate
+    : public mojo::ApplicationDelegate,
+      public mojo::InterfaceFactory<mojo::NetworkService> {
+ public:
+  NetworkServiceDelegate();
+  ~NetworkServiceDelegate() override;
+
+ private:
+  // mojo::ApplicationDelegate implementation.
+  void Initialize(mojo::ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+  void Quit() override;
+
+  // mojo::InterfaceFactory<mojo::NetworkService> implementation.
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::NetworkService> request) override;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkServiceDelegate);
+};
+
+#endif  // MOJO_SERVICES_NETWORK_NETWORK_SERVICE_DELEGATE_H_
diff --git a/mojo/services/network/network_service_impl.cc b/mojo/services/network/network_service_impl.cc
new file mode 100644
index 0000000..d48fe46
--- /dev/null
+++ b/mojo/services/network/network_service_impl.cc
@@ -0,0 +1,24 @@
+// 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.
+
+#include "mojo/services/network/network_service_impl.h"
+
+#include "mojo/services/network/url_loader_impl.h"
+#include "mojo/public/cpp/application/application_connection.h"
+
+namespace mojo {
+
+NetworkServiceImpl::NetworkServiceImpl(InterfaceRequest<NetworkService> request,
+                                       ApplicationConnection* connection)
+    : binding_(this, request.Pass()) {
+}
+
+NetworkServiceImpl::~NetworkServiceImpl() {
+}
+
+void NetworkServiceImpl::CreateURLLoader(InterfaceRequest<URLLoader> loader) {
+  new URLLoaderImpl(loader.Pass());
+}
+
+}  // namespace mojo
diff --git a/mojo/services/network/network_service_impl.h b/mojo/services/network/network_service_impl.h
new file mode 100644
index 0000000..c071979
--- /dev/null
+++ b/mojo/services/network/network_service_impl.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
+
+//#include "base/compiler_specific.h"
+#include "base/macros.h"
+
+#include "mojo/services/network/interfaces/network_service.mojom.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+namespace mojo {
+class ApplicationConnection;
+
+class NetworkServiceImpl : public NetworkService {
+ public:
+  NetworkServiceImpl(InterfaceRequest<NetworkService> request,
+                     ApplicationConnection* connection);
+  ~NetworkServiceImpl() override;
+
+  // NetworkService methods:
+  void CreateURLLoader(InterfaceRequest<URLLoader> loader) override;
+
+ private:
+  StrongBinding<NetworkService> binding_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
diff --git a/mojo/services/network/url_loader_impl.cc b/mojo/services/network/url_loader_impl.cc
new file mode 100644
index 0000000..bbba168
--- /dev/null
+++ b/mojo/services/network/url_loader_impl.cc
@@ -0,0 +1,172 @@
+// 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.
+
+#include "mojo/services/network/url_loader_impl.h"
+#include "mojo/services/network/http_client.h"
+#include "mojo/services/network/network_error.h"
+
+#include <memory>
+
+#include <iostream>
+#include <istream>
+#include <ostream>
+#include <string>
+
+namespace mojo {
+
+NetworkErrorPtr MakeNetworkError(int error_code) {
+  NetworkErrorPtr error = NetworkError::New();
+  error->code = error_code;
+  return error.Pass();
+}
+
+URLLoaderImpl::URLLoaderImpl(InterfaceRequest<URLLoader> request)
+  : binding_(this, request.Pass()), responded_(false)
+{
+  binding_.set_connection_error_handler([this]() { OnConnectionError(); });
+}
+
+URLLoaderImpl::~URLLoaderImpl() {
+}
+
+void URLLoaderImpl::Cleanup() {
+  delete this;
+}
+
+void URLLoaderImpl::Start(URLRequestPtr request,
+                          const Callback<void(URLResponsePtr)>& callback) {
+  callback_ = callback;
+  StartInternal(request.Pass());
+}
+
+void URLLoaderImpl::FollowRedirect(
+    const Callback<void(URLResponsePtr)>& callback) {
+
+  callback_ = callback;
+}
+
+void URLLoaderImpl::QueryStatus(
+    const Callback<void(URLLoaderStatusPtr)>& callback) {
+  URLLoaderStatusPtr status(URLLoaderStatus::New());
+
+  /* TODO(toshik): fill status */
+  callback.Run(status.Pass());
+}
+
+void URLLoaderImpl::OnConnectionError() {
+  /* TODO(toshik) */
+  binding_.Close();
+}
+
+void URLLoaderImpl::SendError(int error_code) {
+  URLResponsePtr response(URLResponse::New());
+  response->error = MakeNetworkError(error_code);
+  SendResponse(response.Pass());
+}
+
+void URLLoaderImpl::FollowRedirectInternal() {
+  /* TODO(toshik) */
+}
+
+void URLLoaderImpl::SendResponse(URLResponsePtr response) {
+  Callback<void(URLResponsePtr)> callback;
+  std::swap(callback_, callback);
+  callback.Run(response.Pass());
+  responded_ = true;
+}
+
+bool URLLoaderImpl::ParseURL(const std::string& url, std::string& proto,
+                             std::string& host, std::string& port,
+                             std::string& path) {
+  std::string delim("://");
+  std::string::const_iterator proto_end =
+    std::search(url.begin(), url.end(), delim.begin(), delim.end());
+  if (proto_end == url.end()) {
+    return false;
+  }
+  proto.assign(url.begin(), proto_end);
+
+  std::string::const_iterator host_start = proto_end + delim.length();
+  std::string::const_iterator path_start = std::find(host_start, url.end(), '/');
+  std::string::const_iterator host_end = std::find(host_start, path_start, ':');
+  host.assign(host_start, host_end);
+
+  if (host_end != path_start)
+    port.assign(host_end + 1, path_start);
+  else
+    port = proto;
+
+  if (path_start != url.end())
+    path.assign(path_start, url.end());
+  else
+    path.assign("/");
+
+  if (proto.length() == 0 || host.length() == 0 || port.length() == 0 ||
+      path.length() == 0)
+    return false;
+
+  return true;
+}
+
+void URLLoaderImpl::StartInternal(URLRequestPtr request) {
+  std::string url(request->url);
+
+  asio::io_service io_service;
+  bool redirect = false;
+  int error_code = ERR_UNEXPECTED;
+
+  do {
+    std::string proto, host, port, path;
+
+    if (!ParseURL(url, proto, host, port, path)) {
+      std::cout << "url parse error" << std::endl;
+      error_code = ERR_INVALID_ARGUMENT;
+      break;
+    }
+
+    if (redirect) {
+      io_service.reset();
+      redirect = false;
+    }
+
+    if (proto == "https") {
+#ifdef NETWORK_SERVICE_USE_HTTPS
+      asio::ssl::context ctx(asio::ssl::context::sslv23);
+      ctx.set_default_verify_paths();
+
+      HTTPClient<asio::ssl::stream<tcp::socket>>
+        c(this, io_service, ctx, host, port, path);
+      io_service.run();
+
+      if (c.status_code_ == 301 || c.status_code_ == 302) {
+        redirect = true;
+        url = c.redirect_location_;
+      }
+#else
+      std::cout << "https is not built-in. "
+        "please build with NETWORK_SERVICE_USE_HTTPS" << std::endl;
+      error_code = ERR_INVALID_ARGUMENT;
+      break;
+#endif
+    } else if (proto == "http") {
+      HTTPClient<tcp::socket> c(this, io_service, host, port, path);
+      io_service.run();
+
+      if (c.status_code_ == 301 || c.status_code_ == 302) {
+        redirect = true;
+        url = c.redirect_location_;
+        std::cout << "Redirecting to: " << url << std::endl;
+      }
+    } else {
+      // unknown protocol
+      error_code = ERR_INVALID_ARGUMENT;
+      break;
+    }
+  } while (redirect);
+
+  if (!responded_)
+    SendError(error_code);
+}
+
+}  // namespace mojo
diff --git a/mojo/services/network/url_loader_impl.h b/mojo/services/network/url_loader_impl.h
new file mode 100644
index 0000000..b3c76c6
--- /dev/null
+++ b/mojo/services/network/url_loader_impl.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
+#define MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+//#include "mojo/common/handle_watcher.h"
+#include "mojo/services/network/interfaces/url_loader.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace mojo {
+
+  //class NetToMojoPendingBuffer;
+
+class URLLoaderImpl : public URLLoader {
+
+ public:
+  URLLoaderImpl(InterfaceRequest<URLLoader> request);
+  ~URLLoaderImpl() override;
+
+  // Called when the associated NetworkContext is going away.
+  void Cleanup();
+
+ private:
+  template<typename T> class HTTPClient;
+
+  // URLLoader methods:
+  void Start(URLRequestPtr request,
+             const Callback<void(URLResponsePtr)>& callback) override;
+  void FollowRedirect(const Callback<void(URLResponsePtr)>& callback) override;
+  void QueryStatus(const Callback<void(URLLoaderStatusPtr)>& callback) override;
+
+  void OnConnectionError();
+  void SendError(int error_code);
+  void FollowRedirectInternal();
+  void SendResponse(URLResponsePtr response);
+  bool ParseURL(const std::string& url, std::string& scheme, std::string& host,
+                std::string& port, std::string& path);
+  void StartInternal(URLRequestPtr request);
+
+  Callback<void(URLResponsePtr)> callback_;
+  // bool auto_follow_redirects_;
+  URLLoaderStatusPtr last_status_;
+  Binding<URLLoader> binding_;
+
+  bool responded_;
+};
+
+}  // namespace mojo
+
+#endif  // MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
diff --git a/mojo/services/public/build/config/BUILD.gn b/mojo/services/public/build/config/BUILD.gn
new file mode 100644
index 0000000..c6b13b6
--- /dev/null
+++ b/mojo/services/public/build/config/BUILD.gn
@@ -0,0 +1,22 @@
+# 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.
+
+# The absolute path to the directory containing the Mojo services produced
+# out of Chromium.
+mojo_services_root = get_path_info("../../..", "abspath")
+
+# NOTE: This config name must be in sync with the name of the config used by
+# services that developed out of the Mojo repo so that Chromium's services'
+# BUILD.gn files can work seamlessly in Chromium and when pulled into Mojo or a
+# client repo.
+config("mojo_services") {
+  include_dirs = [
+    # Include paths in Chromium Mojo services' client-side code are specified
+    # relative to the directory holding the services' client-side code.
+    mojo_services_root,
+
+    # The same goes for files generated from mojoms.
+    root_gen_dir + mojo_services_root,
+  ]
+}
diff --git a/mojo/tools/get_test_list.py b/mojo/tools/get_test_list.py
new file mode 120000
index 0000000..b163995
--- /dev/null
+++ b/mojo/tools/get_test_list.py
@@ -0,0 +1 @@
+../../../src/mojo/tools/get_test_list.py
\ No newline at end of file
diff --git a/mojo/tools/mojob.py b/mojo/tools/mojob.py
new file mode 100755
index 0000000..2df00f5
--- /dev/null
+++ b/mojo/tools/mojob.py
@@ -0,0 +1,367 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""A simple script to make building/testing Mojo components easier."""
+
+import argparse
+from copy import deepcopy
+import logging
+from multiprocessing import cpu_count
+import os
+import subprocess
+import sys
+
+from get_test_list import GetTestList
+from mopy.config import Config
+from mopy.paths import Paths
+from mopy.gn import GNArgsForConfig, ParseGNConfig, CommandLineForGNArgs
+from mopy.log import InitLogging
+
+
+_logger = logging.getLogger()
+_verbose_count = 0
+
+
+def _args_to_config(args):
+  # Default to host OS.
+  target_os = None
+  if args.android:
+    target_os = Config.OS_ANDROID
+  elif args.ios:
+    target_os = Config.OS_IOS
+  elif args.fnl:
+    target_os = Config.OS_FNL
+
+  target_cpu = args.target_cpu
+
+  additional_args = {}
+
+  if 'clang' in args:
+    additional_args['is_clang'] = args.clang
+
+  if 'asan' in args and args.asan:
+    additional_args['sanitizer'] = Config.SANITIZER_ASAN
+
+  # Additional non-standard config entries:
+
+  if 'goma' in args:
+    goma_dir = os.environ.get('GOMA_DIR')
+    goma_home_dir = os.path.join(os.getenv('HOME', ''), 'goma')
+    if args.goma and goma_dir:
+      additional_args['use_goma'] = True
+      additional_args['goma_dir'] = goma_dir
+    elif args.goma and os.path.exists(goma_home_dir):
+      additional_args['use_goma'] = True
+      additional_args['goma_dir'] = goma_home_dir
+    else:
+      additional_args['use_goma'] = False
+      additional_args['goma_dir'] = None
+
+  if 'nacl' in args:
+    additional_args['use_nacl'] = args.nacl
+
+#  if not ('asan' in args and args.asan):
+#    go_dir = os.path.join(Paths().src_root, 'third_party', 'go', 'tool')
+#    if args.android:
+#      additional_args['mojo_use_go'] = True
+#      additional_args['go_build_tool'] = os.path.join(
+#          go_dir, 'android_arm', 'bin', 'go')
+#    elif target_os is None and Config.GetHostOS() == Config.OS_LINUX:
+#      additional_args['mojo_use_go'] = True
+#      additional_args['go_build_tool'] = os.path.join(
+#          go_dir, 'linux_amd64', 'bin', 'go')
+
+  if 'dry_run' in args:
+    additional_args['dry_run'] = args.dry_run
+
+  if 'builder_name' in args:
+    additional_args['builder_name'] = args.builder_name
+  if 'build_number' in args:
+    additional_args['build_number'] = args.build_number
+  if 'master_name' in args:
+    additional_args['master_name'] = args.master_name
+  if 'test_results_server' in args:
+    additional_args['test_results_server'] = args.test_results_server
+
+  if 'gn_args' in args:
+    additional_args['gn_args'] = args.gn_args
+
+  is_debug = args.debug and not args.official
+
+  if 'target_sysroot' in args and args.target_sysroot:
+    additional_args['target_sysroot'] = os.path.abspath(args.target_sysroot)
+
+  if 'toolchain_prefix' in args and args.toolchain_prefix:
+    additional_args['toolchain_prefix'] = args.toolchain_prefix
+
+  if 'package_name' in args:
+    additional_args['package_name'] = args.package_name
+
+  return Config(target_os=target_os, target_cpu=target_cpu,
+                is_debug=is_debug, is_official_build=args.official,
+                dcheck_always_on=args.dcheck_always_on,
+                is_simulator=args.simulator, **additional_args)
+
+
+def _get_out_dir(config):
+  """Gets the build output directory (e.g., out/Debug), relative to src, for the
+  given config."""
+
+  paths = Paths(config)
+  return paths.SrcRelPath(paths.build_dir)
+
+
+def _sync(config):  # pylint: disable=W0613
+  """Runs gclient sync for the given config."""
+
+  _logger.debug('_sync()')
+  return subprocess.call(['gclient', 'sync'])
+
+
+def _gn(config):
+  """Runs gn gen for the given config."""
+
+  _logger.debug('_gn()')
+
+  command = ['gn', 'gen', '--check']
+
+  gn_args = CommandLineForGNArgs(GNArgsForConfig(config))
+  out_dir = _get_out_dir(config)
+  command.append(out_dir)
+  command.append('--args=%s' % ' '.join(gn_args))
+
+  print 'Running %s %s ...' % (command[0],
+                               ' '.join('\'%s\'' % x for x in command[1:]))
+  return subprocess.call(command)
+
+
+def _build(config):
+  """Builds for the given config."""
+
+  _logger.debug('_build()')
+
+  out_dir = _get_out_dir(config)
+  gn_args = ParseGNConfig(out_dir)
+  print 'Building in %s ...' % out_dir
+  if gn_args.get('use_goma'):
+    # Use the configured goma directory.
+    local_goma_dir = gn_args.get('goma_dir')
+    print 'Ensuring goma (in %s) started ...' % local_goma_dir
+    command = ['python',
+               os.path.join(local_goma_dir, 'goma_ctl.py'),
+               'ensure_start']
+    exit_code = subprocess.call(command)
+    if exit_code:
+      return exit_code
+
+    # Goma allows us to run many more jobs in parallel, say 32 per core/thread
+    # (= 1024 on a 16-core, 32-thread Z620). Limit the load average to 4 per
+    # core/thread (= 128 on said Z620).
+    jobs = cpu_count() * 32
+    limit = cpu_count() * 4
+    return subprocess.call(['ninja', '-j', str(jobs), '-l', str(limit),
+                            '-C', out_dir])
+  else:
+    return subprocess.call(['ninja', '-C', out_dir])
+
+
+def _run_tests(config, test_types):
+  """Runs the tests of the given type(s) for the given config."""
+
+  assert isinstance(test_types, list)
+  config = deepcopy(config)
+  config.values['test_types'] = test_types
+
+  test_list = GetTestList(config, verbose_count=_verbose_count)
+  dry_run = config.values.get('dry_run')
+  final_exit_code = 0
+  failure_list = []
+  for entry in test_list:
+    print 'Running: %s' % entry['name']
+    print 'Command: %s' % ' '.join(entry['command'])
+    if dry_run:
+      continue
+
+    _logger.info('Starting: %s' % ' '.join(entry['command']))
+    exit_code = subprocess.call(entry['command'])
+    _logger.info('Completed: %s' % ' '.join(entry['command']))
+    if exit_code:
+      if not final_exit_code:
+        final_exit_code = exit_code
+      failure_list.append(entry['name'])
+
+  print 72 * '='
+  print 'SUMMARY:',
+  if dry_run:
+    print 'Dry run: no tests run'
+  elif not failure_list:
+    assert not final_exit_code
+    print 'All tests passed'
+  else:
+    assert final_exit_code
+    print 'The following had failures:', ', '.join(failure_list)
+
+  return final_exit_code
+
+
+def _test(config):
+  _logger.debug('_test()')
+  return _run_tests(config, [Config.TEST_TYPE_DEFAULT])
+
+
+def _perftest(config):
+  _logger.debug('_perftest()')
+  return _run_tests(config, [Config.TEST_TYPE_PERF])
+
+
+def _pytest(config):
+  _logger.debug('_pytest()')
+  return _run_tests(config, ['python'])
+
+def _analyzedart(config):
+  _logger.debug('_analyzedart()')
+  command = ['python']
+  build_dir = _get_out_dir(config)
+  command.append(os.path.join("mojo",
+                              "public",
+                              "tools",
+                              "dart_pkg_static_analysis.py"))
+  command.append("--dart-pkg-dir=" + os.path.join(build_dir, "gen", "dart-pkg"))
+  command.append("--dart-sdk=" + os.path.join("third_party",
+                                              "dart-sdk", "dart-sdk"))
+  command.append("--package-root=" + os.path.join(build_dir,
+                                                  "gen",
+                                                  "dart-pkg",
+                                                  "packages"))
+  if config.values.get('package_name'):
+    command.append(config.values.get('package_name'))
+  print 'Running %s %s ...' % (command[0],
+                               ' '.join('\'%s\'' % x for x in command[1:]))
+  return subprocess.call(command)
+
+def main():
+  os.chdir(Paths().src_root)
+
+  parser = argparse.ArgumentParser(description='A script to make building'
+      '/testing Mojo components easier.')
+
+  parent_parser = argparse.ArgumentParser(add_help=False)
+
+  parent_parser.add_argument('--verbose',
+                             help='Be verbose (multiple times for more)',
+                             default=0, dest='verbose_count', action='count')
+
+  parent_parser.add_argument('--asan', help='Use Address Sanitizer',
+                             action='store_true')
+  parent_parser.add_argument('--dcheck_always_on',
+                             help='DCHECK and MOJO_DCHECK are fatal even in '
+                             'release builds',
+                             action='store_true')
+
+  debug_group = parent_parser.add_mutually_exclusive_group()
+  debug_group.add_argument('--debug', help='Debug build (default)',
+                           default=True, action='store_true')
+  debug_group.add_argument('--release', help='Release build', default=False,
+                           dest='debug', action='store_false')
+  # The official build is a release build suitable for distribution, with a
+  # different package name.
+  debug_group.add_argument('--official', help='Official build', default=False,
+                           dest='official', action='store_true')
+
+  os_group = parent_parser.add_mutually_exclusive_group()
+  os_group.add_argument('--android', help='Build for Android',
+                        action='store_true')
+  os_group.add_argument('--ios', help='Build for iOS',
+                        action='store_true')
+  os_group.add_argument('--fnl', help='Build for FNL',
+                        action='store_true')
+
+  parent_parser.add_argument('--simulator',
+                             help='Build for a simulator of the target',
+                             action='store_true')
+
+  parent_parser.add_argument('--target-cpu',
+                             help='CPU architecture to build for.',
+                             choices=['x64', 'x86', 'arm'])
+
+  parent_parser.add_argument('--target-sysroot',
+                             help='Location of sysroot for target',
+                             default='',
+                             dest='target_sysroot')
+
+  parent_parser.add_argument('--toolchain-prefix',
+                             help='Toolchain prefix',
+                             default='',
+                             dest='toolchain_prefix')
+
+  subparsers = parser.add_subparsers()
+
+  sync_parser = subparsers.add_parser('sync', parents=[parent_parser],
+      help='Sync using gclient (does not run gn).')
+  sync_parser.set_defaults(func=_sync)
+
+  gn_parser = subparsers.add_parser('gn', parents=[parent_parser],
+                                    help='Run gn for mojo (does not sync).')
+  gn_parser.set_defaults(func=_gn)
+  gn_parser.add_argument('--args', help='Specify extra args',
+                         default=None, dest='gn_args')
+  # Note: no default, if nothing is specified on the command line GN decides.
+  gn_parser.add_argument('--nacl', help='Add in NaCl', action='store_true',
+                         default=argparse.SUPPRESS)
+  gn_parser.add_argument('--no-nacl', help='Remove NaCl', action='store_false',
+                         default=argparse.SUPPRESS, dest='nacl')
+
+  clang_group = gn_parser.add_mutually_exclusive_group()
+  clang_group.add_argument('--clang', help='Use Clang (default)', default=None,
+                           action='store_true')
+  clang_group.add_argument('--gcc', help='Use GCC',
+                           dest='clang', action='store_false')
+  goma_group = gn_parser.add_mutually_exclusive_group()
+  goma_group.add_argument('--goma',
+                          help='Use Goma (if $GOMA_DIR is set or $HOME/goma '
+                               'exists; default)',
+                          default=True,
+                          action='store_true')
+  goma_group.add_argument('--no-goma', help='Don\'t use Goma', default=False,
+                          dest='goma', action='store_false')
+
+  build_parser = subparsers.add_parser('build', parents=[parent_parser],
+                                       help='Build')
+  build_parser.set_defaults(func=_build)
+
+  test_parser = subparsers.add_parser('test', parents=[parent_parser],
+                                      help='Run unit tests (does not build).')
+  test_parser.set_defaults(func=_test)
+  test_parser.add_argument('--dry-run',
+                           help='Print instead of executing commands',
+                           default=False, action='store_true')
+
+  perftest_parser = subparsers.add_parser('perftest', parents=[parent_parser],
+      help='Run perf tests (does not build).')
+  perftest_parser.set_defaults(func=_perftest)
+
+  pytest_parser = subparsers.add_parser('pytest', parents=[parent_parser],
+      help='Run Python unit tests (does not build).')
+  pytest_parser.set_defaults(func=_pytest)
+
+  analyze_dart_parser = subparsers.add_parser('analyze-dart',
+      parents=[parent_parser],
+      help='Run the dart analyzer (does not build.')
+  analyze_dart_parser.add_argument('package_name', nargs='?', default=None)
+  analyze_dart_parser.set_defaults(func=_analyzedart)
+
+  args = parser.parse_args()
+  global _verbose_count
+  _verbose_count = args.verbose_count
+  InitLogging(_verbose_count)
+
+  if args.simulator and not args.ios:
+    sys.exit("Currently, the simulator target is only configured for iOS")
+
+  return args.func(_args_to_config(args))
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/mojo/tools/mopy b/mojo/tools/mopy
new file mode 120000
index 0000000..d8fea8a
--- /dev/null
+++ b/mojo/tools/mopy
@@ -0,0 +1 @@
+../../../src/mojo/tools/mopy
\ No newline at end of file
diff --git a/testing b/testing
new file mode 120000
index 0000000..ed614bb
--- /dev/null
+++ b/testing
@@ -0,0 +1 @@
+../src/testing
\ No newline at end of file
diff --git a/third_party/asio b/third_party/asio
new file mode 120000
index 0000000..993e6cf
--- /dev/null
+++ b/third_party/asio
@@ -0,0 +1 @@
+../../src/third_party/asio
\ No newline at end of file
diff --git a/third_party/binutils b/third_party/binutils
new file mode 120000
index 0000000..1b1c09c
--- /dev/null
+++ b/third_party/binutils
@@ -0,0 +1 @@
+../../src/third_party/binutils
\ No newline at end of file
diff --git a/third_party/boringssl b/third_party/boringssl
new file mode 120000
index 0000000..c554834
--- /dev/null
+++ b/third_party/boringssl
@@ -0,0 +1 @@
+../../src/third_party/boringssl
\ No newline at end of file
diff --git a/third_party/icu b/third_party/icu
new file mode 120000
index 0000000..faff30e
--- /dev/null
+++ b/third_party/icu
@@ -0,0 +1 @@
+../../src/third_party/icu
\ No newline at end of file
diff --git a/third_party/libevent b/third_party/libevent
new file mode 120000
index 0000000..ab2bbd1
--- /dev/null
+++ b/third_party/libevent
@@ -0,0 +1 @@
+../../src/third_party/libevent
\ No newline at end of file
diff --git a/third_party/libudev b/third_party/libudev
new file mode 120000
index 0000000..0a2f671
--- /dev/null
+++ b/third_party/libudev
@@ -0,0 +1 @@
+../../src/third_party/libudev
\ No newline at end of file
diff --git a/third_party/libxml b/third_party/libxml
new file mode 120000
index 0000000..7649c4e
--- /dev/null
+++ b/third_party/libxml
@@ -0,0 +1 @@
+../../src/third_party/libxml
\ No newline at end of file
diff --git a/third_party/llvm b/third_party/llvm
new file mode 120000
index 0000000..84e0479
--- /dev/null
+++ b/third_party/llvm
@@ -0,0 +1 @@
+../../src/third_party/llvm
\ No newline at end of file
diff --git a/third_party/llvm-build b/third_party/llvm-build
new file mode 120000
index 0000000..30c94a7
--- /dev/null
+++ b/third_party/llvm-build
@@ -0,0 +1 @@
+../../src/third_party/llvm-build
\ No newline at end of file
diff --git a/third_party/modp_b64 b/third_party/modp_b64
new file mode 120000
index 0000000..9288218
--- /dev/null
+++ b/third_party/modp_b64
@@ -0,0 +1 @@
+../../src/third_party/modp_b64
\ No newline at end of file
diff --git a/third_party/mojo/MOJO_VERSION b/third_party/mojo/MOJO_VERSION
new file mode 100644
index 0000000..90c3ebb
--- /dev/null
+++ b/third_party/mojo/MOJO_VERSION
@@ -0,0 +1 @@
+b88737ed62969ce3203085748f0d53ff4f09ba5b
diff --git a/third_party/mojo/src/mojo/public b/third_party/mojo/src/mojo/public
new file mode 120000
index 0000000..fe8c801
--- /dev/null
+++ b/third_party/mojo/src/mojo/public
@@ -0,0 +1 @@
+../../../../../src/mojo/public
\ No newline at end of file
diff --git a/third_party/pyelftools b/third_party/pyelftools
new file mode 120000
index 0000000..f3bb12b
--- /dev/null
+++ b/third_party/pyelftools
@@ -0,0 +1 @@
+../../src/third_party/pyelftools
\ No newline at end of file
diff --git a/third_party/tcmalloc b/third_party/tcmalloc
new file mode 120000
index 0000000..00c634c
--- /dev/null
+++ b/third_party/tcmalloc
@@ -0,0 +1 @@
+../../src/third_party/tcmalloc
\ No newline at end of file
diff --git a/third_party/zlib b/third_party/zlib
new file mode 120000
index 0000000..adaa983
--- /dev/null
+++ b/third_party/zlib
@@ -0,0 +1 @@
+../../src/third_party/zlib
\ No newline at end of file
diff --git a/tools b/tools
new file mode 120000
index 0000000..2298617
--- /dev/null
+++ b/tools
@@ -0,0 +1 @@
+../src/tools
\ No newline at end of file