Merge "TBR advance appengine to HEAD"
diff --git a/go/src/google.golang.org/appengine/.travis.yml b/go/src/google.golang.org/appengine/.travis.yml
index bfa8658..435bf94 100644
--- a/go/src/google.golang.org/appengine/.travis.yml
+++ b/go/src/google.golang.org/appengine/.travis.yml
@@ -1,14 +1,17 @@
 language: go
+sudo: false
 
 go:
   - 1.4
 
 install:
-  - export GOPATH="$HOME/gopath"
-  - mkdir -p "$GOPATH/src/google.golang.org"
-  - mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/google.golang.org/appengine"
   - go get -v -t -d google.golang.org/appengine/...
+  - mkdir sdk
+  - curl -o sdk.zip "https://storage.googleapis.com/appengine-sdks/featured/go_appengine_sdk_linux_amd64-1.9.24.zip"
+  - unzip sdk.zip -d sdk
 
 script:
+  - go version
   - go test -v google.golang.org/appengine/...
   - go test -v -race google.golang.org/appengine/...
+  - sdk/go_appengine/goapp test -v google.golang.org/appengine/...
\ No newline at end of file
diff --git a/go/src/google.golang.org/appengine/README.google b/go/src/google.golang.org/appengine/README.google
index 014d762..451fbee 100644
--- a/go/src/google.golang.org/appengine/README.google
+++ b/go/src/google.golang.org/appengine/README.google
@@ -1,5 +1,5 @@
-URL: https://github.com/golang/appengine/archive/5331974dffbe7636afb62f4529b4f204886b29f0.zip
-Version: 5331974dffbe7636afb62f4529b4f204886b29f0
+URL: https://github.com/golang/appengine/archive/d2b1961e346073b2ce8710267c92e4ff4cc0d099.zip
+Version: d2b1961e346073b2ce8710267c92e4ff4cc0d099
 License: Apache 2.0
 License File: LICENSE
 
diff --git a/go/src/google.golang.org/appengine/README.md b/go/src/google.golang.org/appengine/README.md
index 4b22bde..096aaa7 100644
--- a/go/src/google.golang.org/appengine/README.md
+++ b/go/src/google.golang.org/appengine/README.md
@@ -56,13 +56,16 @@
 * `appengine.Timeout` has been removed. Use `context.WithTimeout` instead.
 * `appengine.Datacenter` now takes a `context.Context` argument.
 * `datastore.PropertyLoadSaver` has been simplified to use slices in place of channels.
+* `delay.Call` now returns an error.
 * `search.FieldLoadSaver` now handles document metadata.
+* `urlfetch.Transport` no longer has a Deadline field; set a deadline on the
+  `context.Context` instead.
 * `taskqueue.QueueStats` no longer takes a maxTasks argument. That argument has been
   deprecated and unused for a long time.
-* `appengine/aetest`, `appengine/blobstore`, `appengine/cloudsql`
-  and `appengine/runtime` have not been ported yet.
+* `appengine/aetest`, `appengine/cloudsql` and `appengine/runtime` have not been ported yet.
 * `appengine.BackendHostname` and `appengine.BackendInstance` were for the deprecated backends feature.
   Use `appengine.ModuleHostname`and `appengine.ModuleName` instead.
 * `appengine.IsCapabilityDisabled` and `appengine/capability` are obsolete.
-* Most of `appengine/file` is deprecated. Use [Google Cloud Storage](https://godoc.org/google.golang.org/cloud/storage) instead.
-* `appengine/socket` is deprecated. Use the standard `net` package instead.
+* Most of `appengine/file` and parts of `appengine/blobstore` are deprecated.
+  Use [Google Cloud Storage](https://godoc.org/google.golang.org/cloud/storage) instead.
+* `appengine/socket` is not required on Managed VMs. Use the standard `net` package instead.
diff --git a/go/src/google.golang.org/appengine/appengine.go b/go/src/google.golang.org/appengine/appengine.go
index ccd1f82..e455413 100644
--- a/go/src/google.golang.org/appengine/appengine.go
+++ b/go/src/google.golang.org/appengine/appengine.go
@@ -19,20 +19,24 @@
 // IsDevAppServer reports whether the App Engine app is running in the
 // development App Server.
 func IsDevAppServer() bool {
-	// TODO(dsymonds): Detect this.
-	return false
+	return internal.IsDevAppServer()
 }
 
 // NewContext returns a context for an in-flight HTTP request.
 // This function is cheap.
 func NewContext(req *http.Request) context.Context {
-	return internal.NewContext(req)
+	return WithContext(context.Background(), req)
+}
+
+// WithContext returns a copy of the parent context
+// and associates it with an in-flight HTTP request.
+// This function is cheap.
+func WithContext(parent context.Context, req *http.Request) context.Context {
+	return internal.WithContext(parent, req)
 }
 
 // TODO(dsymonds): Add a Call function here? Otherwise other packages can't access internal.Call.
 
-// TODO(dsymonds): Add BackgroundContext function?
-
 // BlobKey is a key for a blobstore blob.
 //
 // Conceptually, this type belongs in the blobstore package, but it lives in
diff --git a/go/src/google.golang.org/appengine/appengine_test.go b/go/src/google.golang.org/appengine/appengine_test.go
index e1cfb82..f1cf0a1 100644
--- a/go/src/google.golang.org/appengine/appengine_test.go
+++ b/go/src/google.golang.org/appengine/appengine_test.go
@@ -1,3 +1,7 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package appengine
 
 import (
diff --git a/go/src/google.golang.org/appengine/appengine_vm.go b/go/src/google.golang.org/appengine/appengine_vm.go
new file mode 100644
index 0000000..7648196
--- /dev/null
+++ b/go/src/google.golang.org/appengine/appengine_vm.go
@@ -0,0 +1,32 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+
+package appengine
+
+import (
+	"golang.org/x/net/context"
+
+	"google.golang.org/appengine/internal"
+)
+
+// The comment below must not be changed.
+// It is used by go-app-builder to recognise that this package has
+// the Main function to use in the synthetic main.
+//   The gophers party all night; the rabbits provide the beats.
+
+// Main installs the health checker and creates a server listening on port
+// "PORT" if set in the environment or on port 8080.
+// It uses the default http handler and never returns.
+func Main() {
+	internal.Main()
+}
+
+// BackgroundContext returns a context not associated with a request.
+// This should only be used when not servicing a request.
+// This only works on Managed VMs.
+func BackgroundContext() context.Context {
+	return internal.BackgroundContext()
+}
diff --git a/go/src/google.golang.org/appengine/blobstore/blobstore.go b/go/src/google.golang.org/appengine/blobstore/blobstore.go
new file mode 100644
index 0000000..1c8087b
--- /dev/null
+++ b/go/src/google.golang.org/appengine/blobstore/blobstore.go
@@ -0,0 +1,276 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// Package blobstore provides a client for App Engine's persistent blob
+// storage service.
+package blobstore // import "google.golang.org/appengine/blobstore"
+
+import (
+	"bufio"
+	"encoding/base64"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"mime"
+	"mime/multipart"
+	"net/http"
+	"net/textproto"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/golang/protobuf/proto"
+	"golang.org/x/net/context"
+
+	"google.golang.org/appengine"
+	"google.golang.org/appengine/datastore"
+	"google.golang.org/appengine/internal"
+
+	basepb "google.golang.org/appengine/internal/base"
+	blobpb "google.golang.org/appengine/internal/blobstore"
+)
+
+const (
+	blobInfoKind      = "__BlobInfo__"
+	blobFileIndexKind = "__BlobFileIndex__"
+	zeroKey           = appengine.BlobKey("")
+)
+
+// BlobInfo is the blob metadata that is stored in the datastore.
+// Filename may be empty.
+type BlobInfo struct {
+	BlobKey      appengine.BlobKey
+	ContentType  string    `datastore:"content_type"`
+	CreationTime time.Time `datastore:"creation"`
+	Filename     string    `datastore:"filename"`
+	Size         int64     `datastore:"size"`
+	MD5          string    `datastore:"md5_hash"`
+
+	// ObjectName is the Google Cloud Storage name for this blob.
+	ObjectName string `datastore:"gs_object_name"`
+}
+
+// isErrFieldMismatch returns whether err is a datastore.ErrFieldMismatch.
+//
+// The blobstore stores blob metadata in the datastore. When loading that
+// metadata, it may contain fields that we don't care about. datastore.Get will
+// return datastore.ErrFieldMismatch in that case, so we ignore that specific
+// error.
+func isErrFieldMismatch(err error) bool {
+	_, ok := err.(*datastore.ErrFieldMismatch)
+	return ok
+}
+
+// Stat returns the BlobInfo for a provided blobKey. If no blob was found for
+// that key, Stat returns datastore.ErrNoSuchEntity.
+func Stat(c context.Context, blobKey appengine.BlobKey) (*BlobInfo, error) {
+	c, _ = appengine.Namespace(c, "") // Blobstore is always in the empty string namespace
+	dskey := datastore.NewKey(c, blobInfoKind, string(blobKey), 0, nil)
+	bi := &BlobInfo{
+		BlobKey: blobKey,
+	}
+	if err := datastore.Get(c, dskey, bi); err != nil && !isErrFieldMismatch(err) {
+		return nil, err
+	}
+	return bi, nil
+}
+
+// Send sets the headers on response to instruct App Engine to send a blob as
+// the response body. This is more efficient than reading and writing it out
+// manually and isn't subject to normal response size limits.
+func Send(response http.ResponseWriter, blobKey appengine.BlobKey) {
+	hdr := response.Header()
+	hdr.Set("X-AppEngine-BlobKey", string(blobKey))
+
+	if hdr.Get("Content-Type") == "" {
+		// This value is known to dev_appserver to mean automatic.
+		// In production this is remapped to the empty value which
+		// means automatic.
+		hdr.Set("Content-Type", "application/vnd.google.appengine.auto")
+	}
+}
+
+// UploadURL creates an upload URL for the form that the user will
+// fill out, passing the application path to load when the POST of the
+// form is completed. These URLs expire and should not be reused. The
+// opts parameter may be nil.
+func UploadURL(c context.Context, successPath string, opts *UploadURLOptions) (*url.URL, error) {
+	req := &blobpb.CreateUploadURLRequest{
+		SuccessPath: proto.String(successPath),
+	}
+	if opts != nil {
+		if n := opts.MaxUploadBytes; n != 0 {
+			req.MaxUploadSizeBytes = &n
+		}
+		if n := opts.MaxUploadBytesPerBlob; n != 0 {
+			req.MaxUploadSizePerBlobBytes = &n
+		}
+		if s := opts.StorageBucket; s != "" {
+			req.GsBucketName = &s
+		}
+	}
+	res := &blobpb.CreateUploadURLResponse{}
+	if err := internal.Call(c, "blobstore", "CreateUploadURL", req, res); err != nil {
+		return nil, err
+	}
+	return url.Parse(*res.Url)
+}
+
+// UploadURLOptions are the options to create an upload URL.
+type UploadURLOptions struct {
+	MaxUploadBytes        int64 // optional
+	MaxUploadBytesPerBlob int64 // optional
+
+	// StorageBucket specifies the Google Cloud Storage bucket in which
+	// to store the blob.
+	// This is required if you use Cloud Storage instead of Blobstore.
+	// Your application must have permission to write to the bucket.
+	// You may optionally specify a bucket name and path in the format
+	// "bucket_name/path", in which case the included path will be the
+	// prefix of the uploaded object's name.
+	StorageBucket string
+}
+
+// Delete deletes a blob.
+func Delete(c context.Context, blobKey appengine.BlobKey) error {
+	return DeleteMulti(c, []appengine.BlobKey{blobKey})
+}
+
+// DeleteMulti deletes multiple blobs.
+func DeleteMulti(c context.Context, blobKey []appengine.BlobKey) error {
+	s := make([]string, len(blobKey))
+	for i, b := range blobKey {
+		s[i] = string(b)
+	}
+	req := &blobpb.DeleteBlobRequest{
+		BlobKey: s,
+	}
+	res := &basepb.VoidProto{}
+	if err := internal.Call(c, "blobstore", "DeleteBlob", req, res); err != nil {
+		return err
+	}
+	return nil
+}
+
+func errorf(format string, args ...interface{}) error {
+	return fmt.Errorf("blobstore: "+format, args...)
+}
+
+// ParseUpload parses the synthetic POST request that your app gets from
+// App Engine after a user's successful upload of blobs. Given the request,
+// ParseUpload returns a map of the blobs received (keyed by HTML form
+// element name) and other non-blob POST parameters.
+func ParseUpload(req *http.Request) (blobs map[string][]*BlobInfo, other url.Values, err error) {
+	_, params, err := mime.ParseMediaType(req.Header.Get("Content-Type"))
+	if err != nil {
+		return nil, nil, err
+	}
+	boundary := params["boundary"]
+	if boundary == "" {
+		return nil, nil, errorf("did not find MIME multipart boundary")
+	}
+
+	blobs = make(map[string][]*BlobInfo)
+	other = make(url.Values)
+
+	mreader := multipart.NewReader(io.MultiReader(req.Body, strings.NewReader("\r\n\r\n")), boundary)
+	for {
+		part, perr := mreader.NextPart()
+		if perr == io.EOF {
+			break
+		}
+		if perr != nil {
+			return nil, nil, errorf("error reading next mime part with boundary %q (len=%d): %v",
+				boundary, len(boundary), perr)
+		}
+
+		bi := &BlobInfo{}
+		ctype, params, err := mime.ParseMediaType(part.Header.Get("Content-Disposition"))
+		if err != nil {
+			return nil, nil, err
+		}
+		bi.Filename = params["filename"]
+		formKey := params["name"]
+
+		ctype, params, err = mime.ParseMediaType(part.Header.Get("Content-Type"))
+		if err != nil {
+			return nil, nil, err
+		}
+		bi.BlobKey = appengine.BlobKey(params["blob-key"])
+		if ctype != "message/external-body" || bi.BlobKey == "" {
+			if formKey != "" {
+				slurp, serr := ioutil.ReadAll(part)
+				if serr != nil {
+					return nil, nil, errorf("error reading %q MIME part", formKey)
+				}
+				other[formKey] = append(other[formKey], string(slurp))
+			}
+			continue
+		}
+
+		// App Engine sends a MIME header as the body of each MIME part.
+		tp := textproto.NewReader(bufio.NewReader(part))
+		header, mimeerr := tp.ReadMIMEHeader()
+		if mimeerr != nil {
+			return nil, nil, mimeerr
+		}
+		bi.Size, err = strconv.ParseInt(header.Get("Content-Length"), 10, 64)
+		if err != nil {
+			return nil, nil, err
+		}
+		bi.ContentType = header.Get("Content-Type")
+
+		// Parse the time from the MIME header like:
+		// X-AppEngine-Upload-Creation: 2011-03-15 21:38:34.712136
+		createDate := header.Get("X-AppEngine-Upload-Creation")
+		if createDate == "" {
+			return nil, nil, errorf("expected to find an X-AppEngine-Upload-Creation header")
+		}
+		bi.CreationTime, err = time.Parse("2006-01-02 15:04:05.000000", createDate)
+		if err != nil {
+			return nil, nil, errorf("error parsing X-AppEngine-Upload-Creation: %s", err)
+		}
+
+		if hdr := header.Get("Content-MD5"); hdr != "" {
+			md5, err := base64.URLEncoding.DecodeString(hdr)
+			if err != nil {
+				return nil, nil, errorf("bad Content-MD5 %q: %v", hdr, err)
+			}
+			bi.MD5 = string(md5)
+		}
+
+		// If the GCS object name was provided, record it.
+		bi.ObjectName = header.Get("X-AppEngine-Cloud-Storage-Object")
+
+		blobs[formKey] = append(blobs[formKey], bi)
+	}
+	return
+}
+
+// Reader is a blob reader.
+type Reader interface {
+	io.Reader
+	io.ReaderAt
+	io.Seeker
+}
+
+// NewReader returns a reader for a blob. It always succeeds; if the blob does
+// not exist then an error will be reported upon first read.
+func NewReader(c context.Context, blobKey appengine.BlobKey) Reader {
+	return openBlob(c, blobKey)
+}
+
+// BlobKeyForFile returns a BlobKey for a Google Storage file.
+// The filename should be of the form "/gs/bucket_name/object_name".
+func BlobKeyForFile(c context.Context, filename string) (appengine.BlobKey, error) {
+	req := &blobpb.CreateEncodedGoogleStorageKeyRequest{
+		Filename: &filename,
+	}
+	res := &blobpb.CreateEncodedGoogleStorageKeyResponse{}
+	if err := internal.Call(c, "blobstore", "CreateEncodedGoogleStorageKey", req, res); err != nil {
+		return "", err
+	}
+	return appengine.BlobKey(*res.BlobKey), nil
+}
diff --git a/go/src/google.golang.org/appengine/blobstore/blobstore_test.go b/go/src/google.golang.org/appengine/blobstore/blobstore_test.go
new file mode 100644
index 0000000..c2be7ef
--- /dev/null
+++ b/go/src/google.golang.org/appengine/blobstore/blobstore_test.go
@@ -0,0 +1,183 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package blobstore
+
+import (
+	"io"
+	"os"
+	"strconv"
+	"strings"
+	"testing"
+
+	"google.golang.org/appengine"
+	"google.golang.org/appengine/internal/aetesting"
+
+	pb "google.golang.org/appengine/internal/blobstore"
+)
+
+const rbs = readBufferSize
+
+func min(x, y int) int {
+	if x < y {
+		return x
+	}
+	return y
+}
+
+func fakeFetchData(req *pb.FetchDataRequest, res *pb.FetchDataResponse) error {
+	i0 := int(*req.StartIndex)
+	i1 := int(*req.EndIndex + 1) // Blobstore's end-indices are inclusive; Go's are exclusive.
+	bk := *req.BlobKey
+	if i := strings.Index(bk, "."); i != -1 {
+		// Strip everything past the ".".
+		bk = bk[:i]
+	}
+	switch bk {
+	case "a14p":
+		const s = "abcdefghijklmnop"
+		i0 := min(len(s), i0)
+		i1 := min(len(s), i1)
+		res.Data = []byte(s[i0:i1])
+	case "longBlob":
+		res.Data = make([]byte, i1-i0)
+		for i := range res.Data {
+			res.Data[i] = 'A' + uint8(i0/rbs)
+			i0++
+		}
+	}
+	return nil
+}
+
+// step is one step of a readerTest.
+// It consists of a Reader method to call, the method arguments
+// (lenp, offset, whence) and the expected results.
+type step struct {
+	method  string
+	lenp    int
+	offset  int64
+	whence  int
+	want    string
+	wantErr error
+}
+
+var readerTest = []struct {
+	blobKey string
+	step    []step
+}{
+	{"noSuchBlobKey", []step{
+		{"Read", 8, 0, 0, "", io.EOF},
+	}},
+	{"a14p.0", []step{
+		// Test basic reads.
+		{"Read", 1, 0, 0, "a", nil},
+		{"Read", 3, 0, 0, "bcd", nil},
+		{"Read", 1, 0, 0, "e", nil},
+		{"Read", 2, 0, 0, "fg", nil},
+		// Test Seek.
+		{"Seek", 0, 2, os.SEEK_SET, "2", nil},
+		{"Read", 5, 0, 0, "cdefg", nil},
+		{"Seek", 0, 2, os.SEEK_CUR, "9", nil},
+		{"Read", 1, 0, 0, "j", nil},
+		// Test reads up to and past EOF.
+		{"Read", 5, 0, 0, "klmno", nil},
+		{"Read", 5, 0, 0, "p", nil},
+		{"Read", 5, 0, 0, "", io.EOF},
+		// Test ReadAt.
+		{"ReadAt", 4, 0, 0, "abcd", nil},
+		{"ReadAt", 4, 3, 0, "defg", nil},
+		{"ReadAt", 4, 12, 0, "mnop", nil},
+		{"ReadAt", 4, 13, 0, "nop", io.EOF},
+		{"ReadAt", 4, 99, 0, "", io.EOF},
+	}},
+	{"a14p.1", []step{
+		// Test Seek before any reads.
+		{"Seek", 0, 2, os.SEEK_SET, "2", nil},
+		{"Read", 1, 0, 0, "c", nil},
+		// Test that ReadAt doesn't affect the Read offset.
+		{"ReadAt", 3, 9, 0, "jkl", nil},
+		{"Read", 3, 0, 0, "def", nil},
+	}},
+	{"a14p.2", []step{
+		// Test ReadAt before any reads or seeks.
+		{"ReadAt", 2, 14, 0, "op", nil},
+	}},
+	{"longBlob.0", []step{
+		// Test basic read.
+		{"Read", 1, 0, 0, "A", nil},
+		// Test that Read returns early when the buffer is exhausted.
+		{"Seek", 0, rbs - 2, os.SEEK_SET, strconv.Itoa(rbs - 2), nil},
+		{"Read", 5, 0, 0, "AA", nil},
+		{"Read", 3, 0, 0, "BBB", nil},
+		// Test that what we just read is still in the buffer.
+		{"Seek", 0, rbs - 2, os.SEEK_SET, strconv.Itoa(rbs - 2), nil},
+		{"Read", 5, 0, 0, "AABBB", nil},
+		// Test ReadAt.
+		{"ReadAt", 3, rbs - 4, 0, "AAA", nil},
+		{"ReadAt", 6, rbs - 4, 0, "AAAABB", nil},
+		{"ReadAt", 8, rbs - 4, 0, "AAAABBBB", nil},
+		{"ReadAt", 5, rbs - 4, 0, "AAAAB", nil},
+		{"ReadAt", 2, rbs - 4, 0, "AA", nil},
+		// Test seeking backwards from the Read offset.
+		{"Seek", 0, 2*rbs - 8, os.SEEK_SET, strconv.Itoa(2*rbs - 8), nil},
+		{"Read", 1, 0, 0, "B", nil},
+		{"Read", 1, 0, 0, "B", nil},
+		{"Read", 1, 0, 0, "B", nil},
+		{"Read", 1, 0, 0, "B", nil},
+		{"Read", 8, 0, 0, "BBBBCCCC", nil},
+	}},
+	{"longBlob.1", []step{
+		// Test ReadAt with a slice larger than the buffer size.
+		{"LargeReadAt", 2*rbs - 2, 0, 0, strconv.Itoa(2*rbs - 2), nil},
+		{"LargeReadAt", 2*rbs - 1, 0, 0, strconv.Itoa(2*rbs - 1), nil},
+		{"LargeReadAt", 2*rbs + 0, 0, 0, strconv.Itoa(2*rbs + 0), nil},
+		{"LargeReadAt", 2*rbs + 1, 0, 0, strconv.Itoa(2*rbs + 1), nil},
+		{"LargeReadAt", 2*rbs + 2, 0, 0, strconv.Itoa(2*rbs + 2), nil},
+		{"LargeReadAt", 2*rbs - 2, 1, 0, strconv.Itoa(2*rbs - 2), nil},
+		{"LargeReadAt", 2*rbs - 1, 1, 0, strconv.Itoa(2*rbs - 1), nil},
+		{"LargeReadAt", 2*rbs + 0, 1, 0, strconv.Itoa(2*rbs + 0), nil},
+		{"LargeReadAt", 2*rbs + 1, 1, 0, strconv.Itoa(2*rbs + 1), nil},
+		{"LargeReadAt", 2*rbs + 2, 1, 0, strconv.Itoa(2*rbs + 2), nil},
+	}},
+}
+
+func TestReader(t *testing.T) {
+	for _, rt := range readerTest {
+		c := aetesting.FakeSingleContext(t, "blobstore", "FetchData", fakeFetchData)
+		r := NewReader(c, appengine.BlobKey(rt.blobKey))
+		for i, step := range rt.step {
+			var (
+				got    string
+				gotErr error
+				n      int
+				offset int64
+			)
+			switch step.method {
+			case "LargeReadAt":
+				p := make([]byte, step.lenp)
+				n, gotErr = r.ReadAt(p, step.offset)
+				got = strconv.Itoa(n)
+			case "Read":
+				p := make([]byte, step.lenp)
+				n, gotErr = r.Read(p)
+				got = string(p[:n])
+			case "ReadAt":
+				p := make([]byte, step.lenp)
+				n, gotErr = r.ReadAt(p, step.offset)
+				got = string(p[:n])
+			case "Seek":
+				offset, gotErr = r.Seek(step.offset, step.whence)
+				got = strconv.FormatInt(offset, 10)
+			default:
+				t.Fatalf("unknown method: %s", step.method)
+			}
+			if gotErr != step.wantErr {
+				t.Fatalf("%s step %d: got error %v want %v", rt.blobKey, i, gotErr, step.wantErr)
+			}
+			if got != step.want {
+				t.Fatalf("%s step %d: got %q want %q", rt.blobKey, i, got, step.want)
+			}
+		}
+	}
+}
diff --git a/go/src/google.golang.org/appengine/blobstore/read.go b/go/src/google.golang.org/appengine/blobstore/read.go
new file mode 100644
index 0000000..578b1f5
--- /dev/null
+++ b/go/src/google.golang.org/appengine/blobstore/read.go
@@ -0,0 +1,160 @@
+// Copyright 2012 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package blobstore
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"sync"
+
+	"github.com/golang/protobuf/proto"
+	"golang.org/x/net/context"
+
+	"google.golang.org/appengine"
+	"google.golang.org/appengine/internal"
+
+	blobpb "google.golang.org/appengine/internal/blobstore"
+)
+
+// openBlob returns a reader for a blob. It always succeeds; if the blob does
+// not exist then an error will be reported upon first read.
+func openBlob(c context.Context, blobKey appengine.BlobKey) Reader {
+	return &reader{
+		c:       c,
+		blobKey: blobKey,
+	}
+}
+
+const readBufferSize = 256 * 1024
+
+// reader is a blob reader. It implements the Reader interface.
+type reader struct {
+	c context.Context
+
+	// Either blobKey or filename is set:
+	blobKey  appengine.BlobKey
+	filename string
+
+	closeFunc func() // is nil if unavailable or already closed.
+
+	// buf is the read buffer. r is how much of buf has been read.
+	// off is the offset of buf[0] relative to the start of the blob.
+	// An invariant is 0 <= r && r <= len(buf).
+	// Reads that don't require an RPC call will increment r but not off.
+	// Seeks may modify r without discarding the buffer, but only if the
+	// invariant can be maintained.
+	mu  sync.Mutex
+	buf []byte
+	r   int
+	off int64
+}
+
+func (r *reader) Close() error {
+	if f := r.closeFunc; f != nil {
+		f()
+	}
+	r.closeFunc = nil
+	return nil
+}
+
+func (r *reader) Read(p []byte) (int, error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+	r.mu.Lock()
+	defer r.mu.Unlock()
+	if r.r == len(r.buf) {
+		if err := r.fetch(r.off + int64(r.r)); err != nil {
+			return 0, err
+		}
+	}
+	n := copy(p, r.buf[r.r:])
+	r.r += n
+	return n, nil
+}
+
+func (r *reader) ReadAt(p []byte, off int64) (int, error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
+	r.mu.Lock()
+	defer r.mu.Unlock()
+	// Convert relative offsets to absolute offsets.
+	ab0 := r.off + int64(r.r)
+	ab1 := r.off + int64(len(r.buf))
+	ap0 := off
+	ap1 := off + int64(len(p))
+	// Check if we can satisfy the read entirely out of the existing buffer.
+	if r.off <= ap0 && ap1 <= ab1 {
+		// Convert off from an absolute offset to a relative offset.
+		rp0 := int(ap0 - r.off)
+		return copy(p, r.buf[rp0:]), nil
+	}
+	// Restore the original Read/Seek offset after ReadAt completes.
+	defer r.seek(ab0)
+	// Repeatedly fetch and copy until we have filled p.
+	n := 0
+	for len(p) > 0 {
+		if err := r.fetch(off + int64(n)); err != nil {
+			return n, err
+		}
+		r.r = copy(p, r.buf)
+		n += r.r
+		p = p[r.r:]
+	}
+	return n, nil
+}
+
+func (r *reader) Seek(offset int64, whence int) (ret int64, err error) {
+	r.mu.Lock()
+	defer r.mu.Unlock()
+	switch whence {
+	case os.SEEK_SET:
+		ret = offset
+	case os.SEEK_CUR:
+		ret = r.off + int64(r.r) + offset
+	case os.SEEK_END:
+		return 0, errors.New("seeking relative to the end of a blob isn't supported")
+	default:
+		return 0, fmt.Errorf("invalid Seek whence value: %d", whence)
+	}
+	if ret < 0 {
+		return 0, errors.New("negative Seek offset")
+	}
+	return r.seek(ret)
+}
+
+// fetch fetches readBufferSize bytes starting at the given offset. On success,
+// the data is saved as r.buf.
+func (r *reader) fetch(off int64) error {
+	req := &blobpb.FetchDataRequest{
+		BlobKey:    proto.String(string(r.blobKey)),
+		StartIndex: proto.Int64(off),
+		EndIndex:   proto.Int64(off + readBufferSize - 1), // EndIndex is inclusive.
+	}
+	res := &blobpb.FetchDataResponse{}
+	if err := internal.Call(r.c, "blobstore", "FetchData", req, res); err != nil {
+		return err
+	}
+	if len(res.Data) == 0 {
+		return io.EOF
+	}
+	r.buf, r.r, r.off = res.Data, 0, off
+	return nil
+}
+
+// seek seeks to the given offset with an effective whence equal to SEEK_SET.
+// It discards the read buffer if the invariant cannot be maintained.
+func (r *reader) seek(off int64) (int64, error) {
+	delta := off - r.off
+	if delta >= 0 && delta < int64(len(r.buf)) {
+		r.r = int(delta)
+		return off, nil
+	}
+	r.buf, r.r, r.off = nil, 0, off
+	return off, nil
+}
diff --git a/go/src/google.golang.org/appengine/channel/channel_test.go b/go/src/google.golang.org/appengine/channel/channel_test.go
index d047337..c7498eb 100644
--- a/go/src/google.golang.org/appengine/channel/channel_test.go
+++ b/go/src/google.golang.org/appengine/channel/channel_test.go
@@ -1,3 +1,7 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package channel
 
 import (
diff --git a/go/src/google.golang.org/appengine/cmd/aebundler/aebundler.go b/go/src/google.golang.org/appengine/cmd/aebundler/aebundler.go
new file mode 100644
index 0000000..9080ce2
--- /dev/null
+++ b/go/src/google.golang.org/appengine/cmd/aebundler/aebundler.go
@@ -0,0 +1,342 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// Program aebundler turns a Go app into a fully self-contained tar file.
+// The app and its subdirectories (if any) are placed under "."
+// and the dependencies from $GOPATH are placed under ./_gopath/src.
+// A main func is synthesized if one does not exist.
+//
+// A sample Dockerfile to be used with this bundler could look like this:
+//     FROM gcr.io/google_appengine/go-compat
+//     ADD . /app
+//     RUN GOPATH=/app/_gopath go build -tags appenginevm -o /app/_ah/exe
+package main
+
+import (
+	"archive/tar"
+	"flag"
+	"fmt"
+	"go/ast"
+	"go/build"
+	"go/parser"
+	"go/token"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var (
+	output  = flag.String("o", "", "name of output tar file or '-' for stdout")
+	rootDir = flag.String("root", ".", "directory name of application root")
+	vm      = flag.Bool("vm", true, "bundle a Managed VM app")
+
+	skipFiles = map[string]bool{
+		".git":        true,
+		".gitconfig":  true,
+		".hg":         true,
+		".travis.yml": true,
+	}
+)
+
+const (
+	newMain = `package main
+import "google.golang.org/appengine"
+func main() {
+	appengine.Main()
+}
+`
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+	fmt.Fprintf(os.Stderr, "\t%s -o <file.tar|->\tBundle app to named tar file or stdout\n", os.Args[0])
+	fmt.Fprintf(os.Stderr, "\noptional arguments:\n")
+	flag.PrintDefaults()
+}
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+
+	var tags []string
+	if *vm {
+		tags = append(tags, "appenginevm")
+	} else {
+		tags = append(tags, "appengine")
+	}
+
+	tarFile := *output
+	if tarFile == "" {
+		usage()
+		errorf("Required -o flag not specified.")
+	}
+
+	app, err := analyze(tags)
+	if err != nil {
+		errorf("Error analyzing app: %v", err)
+	}
+	if err := app.bundle(tarFile); err != nil {
+		errorf("Unable to bundle app: %v", err)
+	}
+}
+
+// errorf prints the error message and exits.
+func errorf(format string, a ...interface{}) {
+	fmt.Fprintf(os.Stderr, "aebundler: "+format+"\n", a...)
+	os.Exit(1)
+}
+
+type app struct {
+	hasMain  bool
+	appFiles []string
+	imports  map[string]string
+}
+
+// analyze checks the app for building with the given build tags and returns hasMain,
+// app files, and a map of full directory import names to original import names.
+func analyze(tags []string) (*app, error) {
+	ctxt := buildContext(tags)
+	hasMain, appFiles, err := checkMain(ctxt)
+	if err != nil {
+		return nil, err
+	}
+	gopath := filepath.SplitList(ctxt.GOPATH)
+	im, err := imports(ctxt, *rootDir, gopath)
+	return &app{
+		hasMain:  hasMain,
+		appFiles: appFiles,
+		imports:  im,
+	}, err
+}
+
+// buildContext returns the context for building the source.
+func buildContext(tags []string) *build.Context {
+	return &build.Context{
+		GOARCH:    build.Default.GOARCH,
+		GOOS:      build.Default.GOOS,
+		GOROOT:    build.Default.GOROOT,
+		GOPATH:    build.Default.GOPATH,
+		Compiler:  build.Default.Compiler,
+		BuildTags: append(build.Default.BuildTags, tags...),
+	}
+}
+
+// bundle bundles the app into the named tarFile ("-"==stdout).
+func (s *app) bundle(tarFile string) (err error) {
+	var out io.Writer
+	if tarFile == "-" {
+		out = os.Stdout
+	} else {
+		f, err := os.Create(tarFile)
+		if err != nil {
+			return err
+		}
+		defer func() {
+			if cerr := f.Close(); err == nil {
+				err = cerr
+			}
+		}()
+		out = f
+	}
+	tw := tar.NewWriter(out)
+
+	for srcDir, importName := range s.imports {
+		dstDir := "_gopath/src/" + importName
+		if err = copyTree(tw, dstDir, srcDir); err != nil {
+			return fmt.Errorf("unable to copy directory %v to %v: %v", srcDir, dstDir, err)
+		}
+	}
+	if err := copyTree(tw, ".", *rootDir); err != nil {
+		return fmt.Errorf("unable to copy root directory to /app: %v", err)
+	}
+	if !s.hasMain {
+		if err := synthesizeMain(tw, s.appFiles); err != nil {
+			return fmt.Errorf("unable to synthesize new main func: %v", err)
+		}
+	}
+
+	if err := tw.Close(); err != nil {
+		return fmt.Errorf("unable to close tar file %v: %v", tarFile, err)
+	}
+	return nil
+}
+
+// synthesizeMain generates a new main func and writes it to the tarball.
+func synthesizeMain(tw *tar.Writer, appFiles []string) error {
+	appMap := make(map[string]bool)
+	for _, f := range appFiles {
+		appMap[f] = true
+	}
+	var f string
+	for i := 0; i < 100; i++ {
+		f = fmt.Sprintf("app_main%d.go", i)
+		if !appMap[filepath.Join(*rootDir, f)] {
+			break
+		}
+	}
+	if appMap[filepath.Join(*rootDir, f)] {
+		return fmt.Errorf("unable to find unique name for %v", f)
+	}
+	hdr := &tar.Header{
+		Name: f,
+		Mode: 0644,
+		Size: int64(len(newMain)),
+	}
+	if err := tw.WriteHeader(hdr); err != nil {
+		return fmt.Errorf("unable to write header for %v: %v", f, err)
+	}
+	if _, err := tw.Write([]byte(newMain)); err != nil {
+		return fmt.Errorf("unable to write %v to tar file: %v", f, err)
+	}
+	return nil
+}
+
+// imports returns a map of all import directories (recursively) used by the app.
+// The return value maps full directory names to original import names.
+func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) {
+	pkg, err := ctxt.ImportDir(srcDir, 0)
+	if err != nil {
+		return nil, fmt.Errorf("unable to analyze source: %v", err)
+	}
+
+	// Resolve all non-standard-library imports
+	result := make(map[string]string)
+	for _, v := range pkg.Imports {
+		if !strings.Contains(v, ".") {
+			continue
+		}
+		src, err := findInGopath(v, gopath)
+		if err != nil {
+			return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err)
+		}
+		result[src] = v
+		im, err := imports(ctxt, src, gopath)
+		if err != nil {
+			return nil, fmt.Errorf("unable to parse package %v: %v", src, err)
+		}
+		for k, v := range im {
+			result[k] = v
+		}
+	}
+	return result, nil
+}
+
+// findInGopath searches the gopath for the named import directory.
+func findInGopath(dir string, gopath []string) (string, error) {
+	for _, v := range gopath {
+		dst := filepath.Join(v, "src", dir)
+		if _, err := os.Stat(dst); err == nil {
+			return dst, nil
+		}
+	}
+	return "", fmt.Errorf("unable to find package %v in gopath %v", dir, gopath)
+}
+
+// copyTree copies srcDir to tar file dstDir, ignoring skipFiles.
+func copyTree(tw *tar.Writer, dstDir, srcDir string) error {
+	entries, err := ioutil.ReadDir(srcDir)
+	if err != nil {
+		return fmt.Errorf("unable to read dir %v: %v", srcDir, err)
+	}
+	for _, entry := range entries {
+		n := entry.Name()
+		if skipFiles[n] {
+			continue
+		}
+		s := filepath.Join(srcDir, n)
+		d := filepath.Join(dstDir, n)
+		if entry.IsDir() {
+			if err := copyTree(tw, d, s); err != nil {
+				return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
+			}
+			continue
+		}
+		if err := copyFile(tw, d, s); err != nil {
+			return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
+		}
+	}
+	return nil
+}
+
+// copyFile copies src to tar file dst.
+func copyFile(tw *tar.Writer, dst, src string) error {
+	s, err := os.Open(src)
+	if err != nil {
+		return fmt.Errorf("unable to open %v: %v", src, err)
+	}
+	defer s.Close()
+	fi, err := s.Stat()
+	if err != nil {
+		return fmt.Errorf("unable to stat %v: %v", src, err)
+	}
+
+	hdr, err := tar.FileInfoHeader(fi, dst)
+	if err != nil {
+		return fmt.Errorf("unable to create tar header for %v: %v", dst, err)
+	}
+	hdr.Name = dst
+	if err := tw.WriteHeader(hdr); err != nil {
+		return fmt.Errorf("unable to write header for %v: %v", dst, err)
+	}
+	_, err = io.Copy(tw, s)
+	if err != nil {
+		return fmt.Errorf("unable to copy %v to %v: %v", src, dst, err)
+	}
+	return nil
+}
+
+// checkMain verifies that there is a single "main" function.
+// It also returns a list of all Go source files in the app.
+func checkMain(ctxt *build.Context) (bool, []string, error) {
+	pkg, err := ctxt.ImportDir(*rootDir, 0)
+	if err != nil {
+		return false, nil, fmt.Errorf("unable to analyze source: %v", err)
+	}
+	if !pkg.IsCommand() {
+		errorf("Your app's package needs to be changed from %q to \"main\".\n", pkg.Name)
+	}
+	// Search for a "func main"
+	var hasMain bool
+	var appFiles []string
+	for _, f := range pkg.GoFiles {
+		n := filepath.Join(*rootDir, f)
+		appFiles = append(appFiles, n)
+		if hasMain, err = readFile(n); err != nil {
+			return false, nil, fmt.Errorf("error parsing %q: %v", n, err)
+		}
+	}
+	return hasMain, appFiles, nil
+}
+
+// isMain returns whether the given function declaration is a main function.
+// Such a function must be called "main", not have a receiver, and have no arguments or return types.
+func isMain(f *ast.FuncDecl) bool {
+	ft := f.Type
+	return f.Name.Name == "main" && f.Recv == nil && ft.Params.NumFields() == 0 && ft.Results.NumFields() == 0
+}
+
+// readFile reads and parses the Go source code file and returns whether it has a main function.
+func readFile(filename string) (hasMain bool, err error) {
+	var src []byte
+	src, err = ioutil.ReadFile(filename)
+	if err != nil {
+		return
+	}
+	fset := token.NewFileSet()
+	file, err := parser.ParseFile(fset, filename, src, 0)
+	for _, decl := range file.Decls {
+		funcDecl, ok := decl.(*ast.FuncDecl)
+		if !ok {
+			continue
+		}
+		if !isMain(funcDecl) {
+			continue
+		}
+		hasMain = true
+		break
+	}
+	return
+}
diff --git a/go/src/google.golang.org/appengine/cmd/aedeploy/aedeploy.go b/go/src/google.golang.org/appengine/cmd/aedeploy/aedeploy.go
new file mode 100644
index 0000000..11de9a3
--- /dev/null
+++ b/go/src/google.golang.org/appengine/cmd/aedeploy/aedeploy.go
@@ -0,0 +1,260 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// Program aedeploy assists with deploying Go Managed VM apps to production.
+// A temporary directory is created; the app, its subdirectories, and all its
+// dependencies from $GOPATH are copied into the directory; then the app
+// is deployed to production with the provided command.
+//
+// The app must be in "package main".
+//
+// This command must be issued from within the root directory of the app
+// (where the app.yaml file is located).
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"go/build"
+	"io"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+var (
+	skipFiles = map[string]bool{
+		".git":        true,
+		".gitconfig":  true,
+		".hg":         true,
+		".travis.yml": true,
+	}
+
+	gopathCache = map[string]string{}
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
+	fmt.Fprintf(os.Stderr, "\t%s gcloud --verbosity debug preview app deploy --version myversion ./app.yaml\tDeploy app to production\n", os.Args[0])
+}
+
+func main() {
+	flag.Usage = usage
+	flag.Parse()
+	if flag.NArg() < 1 {
+		usage()
+		os.Exit(1)
+	}
+
+	if err := aedeploy(); err != nil {
+		fmt.Fprintf(os.Stderr, os.Args[0]+": Error: %v\n", err)
+		os.Exit(1)
+	}
+}
+
+func aedeploy() error {
+	tags := []string{"appenginevm"}
+	app, err := analyze(tags)
+	if err != nil {
+		return err
+	}
+
+	tmpDir, err := app.bundle()
+	if tmpDir != "" {
+		defer os.RemoveAll(tmpDir)
+	}
+	if err != nil {
+		return err
+	}
+
+	if err := os.Chdir(tmpDir); err != nil {
+		return fmt.Errorf("unable to chdir to %v: %v", tmpDir, err)
+	}
+	return deploy()
+}
+
+// deploy calls the provided command to deploy the app from the temporary directory.
+func deploy() error {
+	cmd := exec.Command(flag.Arg(0), flag.Args()[1:]...)
+	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
+	if err := cmd.Run(); err != nil {
+		return fmt.Errorf("unable to run %q: %v", strings.Join(flag.Args(), " "), err)
+	}
+	return nil
+}
+
+type app struct {
+	appFiles []string
+	imports  map[string]string
+}
+
+// analyze checks the app for building with the given build tags and returns
+// app files, and a map of full directory import names to original import names.
+func analyze(tags []string) (*app, error) {
+	ctxt := buildContext(tags)
+	appFiles, err := appFiles(ctxt)
+	if err != nil {
+		return nil, err
+	}
+	gopath := filepath.SplitList(ctxt.GOPATH)
+	im, err := imports(ctxt, ".", gopath)
+	return &app{
+		appFiles: appFiles,
+		imports:  im,
+	}, err
+}
+
+// buildContext returns the context for building the source.
+func buildContext(tags []string) *build.Context {
+	return &build.Context{
+		GOARCH:    "amd64",
+		GOOS:      "linux",
+		GOROOT:    build.Default.GOROOT,
+		GOPATH:    build.Default.GOPATH,
+		Compiler:  build.Default.Compiler,
+		BuildTags: append(build.Default.BuildTags, tags...),
+	}
+}
+
+// bundle bundles the app into a temporary directory.
+func (s *app) bundle() (tmpdir string, err error) {
+	workDir, err := ioutil.TempDir("", os.Args[0])
+	if err != nil {
+		return "", fmt.Errorf("unable to create tmpdir: %v", err)
+	}
+
+	for srcDir, importName := range s.imports {
+		dstDir := "_gopath/src/" + importName
+		if err := copyTree(workDir, dstDir, srcDir); err != nil {
+			return workDir, fmt.Errorf("unable to copy directory %v to %v: %v", srcDir, dstDir, err)
+		}
+	}
+	if err := copyTree(workDir, ".", "."); err != nil {
+		return workDir, fmt.Errorf("unable to copy root directory to /app: %v", err)
+	}
+	return workDir, nil
+}
+
+// imports returns a map of all import directories (recursively) used by the app.
+// The return value maps full directory names to original import names.
+func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) {
+	pkg, err := ctxt.ImportDir(srcDir, 0)
+	if err != nil {
+		return nil, err
+	}
+
+	// Resolve all non-standard-library imports
+	result := make(map[string]string)
+	for _, v := range pkg.Imports {
+		if !strings.Contains(v, ".") {
+			continue
+		}
+		src, err := findInGopath(v, gopath)
+		if err != nil {
+			return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err)
+		}
+		if _, ok := result[src]; ok { // Already processed
+			continue
+		}
+		result[src] = v
+		im, err := imports(ctxt, src, gopath)
+		if err != nil {
+			return nil, fmt.Errorf("unable to parse package %v: %v", src, err)
+		}
+		for k, v := range im {
+			result[k] = v
+		}
+	}
+	return result, nil
+}
+
+// findInGopath searches the gopath for the named import directory.
+func findInGopath(dir string, gopath []string) (string, error) {
+	if v, ok := gopathCache[dir]; ok {
+		return v, nil
+	}
+	for _, v := range gopath {
+		dst := filepath.Join(v, "src", dir)
+		if _, err := os.Stat(dst); err == nil {
+			gopathCache[dir] = dst
+			return dst, nil
+		}
+	}
+	return "", fmt.Errorf("unable to find package %v in gopath %v", dir, gopath)
+}
+
+// copyTree copies srcDir to dstDir relative to dstRoot, ignoring skipFiles.
+func copyTree(dstRoot, dstDir, srcDir string) error {
+	d := filepath.Join(dstRoot, dstDir)
+	if err := os.MkdirAll(d, 0755); err != nil {
+		return fmt.Errorf("unable to create directory %v: %v", d, err)
+	}
+
+	entries, err := ioutil.ReadDir(srcDir)
+	if err != nil {
+		return fmt.Errorf("unable to read dir %v: %v", srcDir, err)
+	}
+	for _, entry := range entries {
+		n := entry.Name()
+		if skipFiles[n] {
+			continue
+		}
+		s := filepath.Join(srcDir, n)
+		d := filepath.Join(dstDir, n)
+		if entry.IsDir() {
+			if err := copyTree(dstRoot, d, s); err != nil {
+				return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
+			}
+			continue
+		}
+		if err := copyFile(dstRoot, d, s); err != nil {
+			return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
+		}
+	}
+	return nil
+}
+
+// copyFile copies src to dst relative to dstRoot.
+func copyFile(dstRoot, dst, src string) error {
+	s, err := os.Open(src)
+	if err != nil {
+		return fmt.Errorf("unable to open %v: %v", src, err)
+	}
+	defer s.Close()
+
+	dst = filepath.Join(dstRoot, dst)
+	d, err := os.Create(dst)
+	if err != nil {
+		return fmt.Errorf("unable to create %v: %v", dst)
+	}
+	_, err = io.Copy(d, s)
+	if err != nil {
+		d.Close() // ignore error, copy already failed.
+		return fmt.Errorf("unable to copy %v to %v: %v", src, dst, err)
+	}
+	if err := d.Close(); err != nil {
+		return fmt.Errorf("unable to close %v: %v", dst, err)
+	}
+	return nil
+}
+
+// appFiles returns a list of all Go source files in the app.
+func appFiles(ctxt *build.Context) ([]string, error) {
+	pkg, err := ctxt.ImportDir(".", 0)
+	if err != nil {
+		return nil, err
+	}
+	if !pkg.IsCommand() {
+		return nil, fmt.Errorf(`the root of your app needs to be package "main" (currently %q). Please see https://cloud.google.com/appengine/docs/go/managed-vms for more details on structuring your app.`, pkg.Name)
+	}
+	var appFiles []string
+	for _, f := range pkg.GoFiles {
+		n := filepath.Join(".", f)
+		appFiles = append(appFiles, n)
+	}
+	return appFiles, nil
+}
diff --git a/go/src/google.golang.org/appengine/datastore/doc.go b/go/src/google.golang.org/appengine/datastore/doc.go
index 38164e9..8296c36 100644
--- a/go/src/google.golang.org/appengine/datastore/doc.go
+++ b/go/src/google.golang.org/appengine/datastore/doc.go
@@ -105,9 +105,9 @@
 If the options is "" then the comma may be omitted. There are no other
 recognized options.
 
-Fields (except for []byte) are indexed by default. Strings longer than 500
-characters cannot be indexed; fields used to store long strings should be
-tagged with "noindex". Similarly, ByteStrings longer than 500 bytes cannot be
+Fields (except for []byte) are indexed by default. Strings longer than 1500
+bytes cannot be indexed; fields used to store long strings should be
+tagged with "noindex". Similarly, ByteStrings longer than 1500 bytes cannot be
 indexed.
 
 Example code:
@@ -187,9 +187,9 @@
 		Sum int `datastore:"-"`
 	}
 
-	func (x *CustomPropsExample) Load(c <-chan Property) error {
+	func (x *CustomPropsExample) Load(ps []datastore.Property) error {
 		// Load I and J as usual.
-		if err := datastore.LoadStruct(x, c); err != nil {
+		if err := datastore.LoadStruct(x, ps); err != nil {
 			return err
 		}
 		// Derive the Sum field.
@@ -197,24 +197,24 @@
 		return nil
 	}
 
-	func (x *CustomPropsExample) Save(c chan<- Property) error {
-		defer close(c)
+	func (x *CustomPropsExample) Save() ([]datastore.Property, error) {
 		// Validate the Sum field.
 		if x.Sum != x.I + x.J {
 			return errors.New("CustomPropsExample has inconsistent sum")
 		}
 		// Save I and J as usual. The code below is equivalent to calling
-		// "return datastore.SaveStruct(x, c)", but is done manually for
+		// "return datastore.SaveStruct(x)", but is done manually for
 		// demonstration purposes.
-		c <- datastore.Property{
-			Name:  "I",
-			Value: int64(x.I),
+		return []datastore.Property{
+			{
+				Name:  "I",
+				Value: int64(x.I),
+			},
+			{
+				Name:  "J",
+				Value: int64(x.J),
+			},
 		}
-		c <- datastore.Property{
-			Name:  "J",
-			Value: int64(x.J),
-		}
-		return nil
 	}
 
 The *PropertyList type implements PropertyLoadSaver, and can therefore hold an
diff --git a/go/src/google.golang.org/appengine/datastore/prop.go b/go/src/google.golang.org/appengine/datastore/prop.go
index b0a1e0f..3caef9a 100644
--- a/go/src/google.golang.org/appengine/datastore/prop.go
+++ b/go/src/google.golang.org/appengine/datastore/prop.go
@@ -13,7 +13,7 @@
 )
 
 // Entities with more than this many indexed properties will not be saved.
-const maxIndexedProperties = 5000
+const maxIndexedProperties = 20000
 
 // []byte fields more than 1 megabyte long will not be loaded or saved.
 const maxBlobLen = 1 << 20
@@ -63,7 +63,7 @@
 	Multiple bool
 }
 
-// ByteString is a short byte slice (up to 500 bytes) that can be indexed.
+// ByteString is a short byte slice (up to 1500 bytes) that can be indexed.
 type ByteString []byte
 
 // PropertyLoadSaver can be converted from and to a slice of Properties.
diff --git a/go/src/google.golang.org/appengine/datastore/transaction.go b/go/src/google.golang.org/appengine/datastore/transaction.go
index fa63f52..a7f3f2b 100644
--- a/go/src/google.golang.org/appengine/datastore/transaction.go
+++ b/go/src/google.golang.org/appengine/datastore/transaction.go
@@ -39,7 +39,8 @@
 // returning nil if it succeeds. If the commit fails due to a conflicting
 // transaction, RunInTransaction retries f, each time with a new transaction
 // context. It gives up and returns ErrConcurrentTransaction after three
-// failed attempts.
+// failed attempts. The number of attempts can be configured by specifying
+// TransactionOptions.Attempts.
 //
 // If f returns non-nil, then any datastore changes will not be applied and
 // RunInTransaction returns that same error. The function f is not retried.
@@ -48,13 +49,20 @@
 // must be careful not to assume that any of f's changes have been committed
 // until RunInTransaction returns nil.
 //
+// Since f may be called multiple times, f should usually be idempotent.
+// datastore.Get is not idempotent when unmarshaling slice fields.
+//
 // Nested transactions are not supported; c may not be a transaction context.
 func RunInTransaction(c context.Context, f func(tc context.Context) error, opts *TransactionOptions) error {
 	xg := false
 	if opts != nil {
 		xg = opts.XG
 	}
-	for i := 0; i < 3; i++ {
+	attempts := 3
+	if opts != nil && opts.Attempts > 0 {
+		attempts = opts.Attempts
+	}
+	for i := 0; i < attempts; i++ {
 		if err := internal.RunTransactionOnce(c, f, xg); err != internal.ErrConcurrentTransaction {
 			return err
 		}
@@ -73,4 +81,7 @@
 	// It is valid to set XG to true even if the transaction is within a
 	// single entity group.
 	XG bool
+	// Attempts controls the number of retries to perform when commits fail
+	// due to a conflicting transaction. If omitted, it defaults to 3.
+	Attempts int
 }
diff --git a/go/src/google.golang.org/appengine/delay/delay.go b/go/src/google.golang.org/appengine/delay/delay.go
index 347b839..6d628fc 100644
--- a/go/src/google.golang.org/appengine/delay/delay.go
+++ b/go/src/google.golang.org/appengine/delay/delay.go
@@ -134,20 +134,17 @@
 }
 
 // Call invokes a delayed function.
-//   f.Call(c, ...)
+//   err := f.Call(c, ...)
 // is equivalent to
 //   t, _ := f.Task(...)
-//   taskqueue.Add(c, t, "")
-func (f *Function) Call(c context.Context, args ...interface{}) {
+//   err := taskqueue.Add(c, t, "")
+func (f *Function) Call(c context.Context, args ...interface{}) error {
 	t, err := f.Task(args...)
 	if err != nil {
-		log.Errorf(c, "%v", err)
-		return
+		return err
 	}
-	if _, err := taskqueueAdder(c, t, queue); err != nil {
-		log.Errorf(c, "delay: taskqueue.Add failed: %v", err)
-		return
-	}
+	_, err = taskqueueAdder(c, t, queue)
+	return err
 }
 
 // Task creates a Task that will invoke the function.
diff --git a/go/src/google.golang.org/appengine/delay/delay_test.go b/go/src/google.golang.org/appengine/delay/delay_test.go
index 1a1c74c..ef68d20 100644
--- a/go/src/google.golang.org/appengine/delay/delay_test.go
+++ b/go/src/google.golang.org/appengine/delay/delay_test.go
@@ -109,13 +109,8 @@
 func TestInvalidFunction(t *testing.T) {
 	c := newFakeContext()
 
-	invalidFunc.Call(c.ctx)
-
-	wantLogging := [][]interface{}{
-		{"ERROR", "%v", fmt.Errorf("delay: func is invalid: %s", errFirstArg)},
-	}
-	if !reflect.DeepEqual(c.logging, wantLogging) {
-		t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
+	if got, want := invalidFunc.Call(c.ctx), fmt.Errorf("delay: func is invalid: %s", errFirstArg); got.Error() != want.Error() {
+		t.Errorf("Incorrect error: got %q, want %q", got, want)
 	}
 }
 
@@ -137,12 +132,8 @@
 		t.Errorf("Got %d calls to taskqueueAdder, want 3", calls)
 	}
 
-	varFunc.Call(c.ctx, "%d %s", 12, "a string is bad")
-	wantLogging := [][]interface{}{
-		{"ERROR", "%v", errors.New("delay: argument 3 has wrong type: string is not assignable to int")},
-	}
-	if !reflect.DeepEqual(c.logging, wantLogging) {
-		t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
+	if got, want := varFunc.Call(c.ctx, "%d %s", 12, "a string is bad"), errors.New("delay: argument 3 has wrong type: string is not assignable to int"); got.Error() != want.Error() {
+		t.Errorf("Incorrect error: got %q, want %q", got, want)
 	}
 }
 
@@ -151,17 +142,28 @@
 
 	c := newFakeContext()
 
-	regFunc.Call(c.ctx)
-	regFunc.Call(c.ctx, "lala", 53)
-	regFunc.Call(c.ctx, 53)
-
-	wantLogging := [][]interface{}{
-		{"ERROR", "%v", errors.New("delay: too few arguments to func: 1 < 2")},
-		{"ERROR", "%v", errors.New("delay: too many arguments to func: 3 > 2")},
-		{"ERROR", "%v", errors.New("delay: argument 1 has wrong type: int is not assignable to string")},
+	tests := []struct {
+		args    []interface{} // all except context
+		wantErr string
+	}{
+		{
+			args:    nil,
+			wantErr: "delay: too few arguments to func: 1 < 2",
+		},
+		{
+			args:    []interface{}{"lala", 53},
+			wantErr: "delay: too many arguments to func: 3 > 2",
+		},
+		{
+			args:    []interface{}{53},
+			wantErr: "delay: argument 1 has wrong type: int is not assignable to string",
+		},
 	}
-	if !reflect.DeepEqual(c.logging, wantLogging) {
-		t.Errorf("Incorrect logging: got %+v, want %+v", c.logging, wantLogging)
+	for i, tc := range tests {
+		got := regFunc.Call(c.ctx, tc.args...)
+		if got.Error() != tc.wantErr {
+			t.Errorf("Call %v: got %q, want %q", i, got, tc.wantErr)
+		}
 	}
 }
 
diff --git a/go/src/google.golang.org/appengine/demos/guestbook/app.yaml b/go/src/google.golang.org/appengine/demos/guestbook/app.yaml
index 7837139..95c088f 100644
--- a/go/src/google.golang.org/appengine/demos/guestbook/app.yaml
+++ b/go/src/google.golang.org/appengine/demos/guestbook/app.yaml
@@ -1,6 +1,4 @@
 # Demo application for Managed VMs.
-application: vm-guestbook
-version: 1
 runtime: go
 vm: true
 api_version: go1
diff --git a/go/src/google.golang.org/appengine/demos/guestbook/guestbook.go b/go/src/google.golang.org/appengine/demos/guestbook/guestbook.go
index 9069484..0c6e897 100644
--- a/go/src/google.golang.org/appengine/demos/guestbook/guestbook.go
+++ b/go/src/google.golang.org/appengine/demos/guestbook/guestbook.go
@@ -2,7 +2,10 @@
 // Use of this source code is governed by the Apache 2.0
 // license that can be found in the LICENSE file.
 
-package guestbook
+// This example only works on Managed VMs.
+// +build !appengine
+
+package main
 
 import (
 	"html/template"
@@ -25,15 +28,16 @@
 	Date    time.Time
 }
 
-func init() {
+func main() {
 	http.HandleFunc("/", handleMainPage)
 	http.HandleFunc("/sign", handleSign)
+	appengine.Main()
 }
 
 // guestbookKey returns the key used for all guestbook entries.
-func guestbookKey(c context.Context) *datastore.Key {
+func guestbookKey(ctx context.Context) *datastore.Key {
 	// The string "default_guestbook" here could be varied to have multiple guestbooks.
-	return datastore.NewKey(c, "Guestbook", "default_guestbook", 0, nil)
+	return datastore.NewKey(ctx, "Guestbook", "default_guestbook", 0, nil)
 }
 
 var tpl = template.Must(template.ParseGlob("templates/*.html"))
@@ -48,24 +52,24 @@
 		return
 	}
 
-	c := appengine.NewContext(r)
+	ctx := appengine.NewContext(r)
 	tic := time.Now()
-	q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(c)).Order("-Date").Limit(10)
+	q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(ctx)).Order("-Date").Limit(10)
 	var gg []*Greeting
-	if _, err := q.GetAll(c, &gg); err != nil {
+	if _, err := q.GetAll(ctx, &gg); err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
-		log.Errorf(c, "GetAll: %v", err)
+		log.Errorf(ctx, "GetAll: %v", err)
 		return
 	}
-	log.Infof(c, "Datastore lookup took %s", time.Since(tic).String())
-	log.Infof(c, "Rendering %d greetings", len(gg))
+	log.Infof(ctx, "Datastore lookup took %s", time.Since(tic).String())
+	log.Infof(ctx, "Rendering %d greetings", len(gg))
 
 	var email, logout, login string
-	if u := user.Current(c); u != nil {
-		logout, _ = user.LogoutURL(c, "/")
+	if u := user.Current(ctx); u != nil {
+		logout, _ = user.LogoutURL(ctx, "/")
 		email = u.Email
 	} else {
-		login, _ = user.LoginURL(c, "/")
+		login, _ = user.LoginURL(ctx, "/")
 	}
 	data := struct {
 		Greetings            []*Greeting
@@ -78,7 +82,7 @@
 	}
 	w.Header().Set("Content-Type", "text/html; charset=utf-8")
 	if err := tpl.ExecuteTemplate(w, "guestbook.html", data); err != nil {
-		log.Errorf(c, "%v", err)
+		log.Errorf(ctx, "%v", err)
 	}
 }
 
@@ -87,16 +91,16 @@
 		http.Error(w, "POST requests only", http.StatusMethodNotAllowed)
 		return
 	}
-	c := appengine.NewContext(r)
+	ctx := appengine.NewContext(r)
 	g := &Greeting{
 		Content: r.FormValue("content"),
 		Date:    time.Now(),
 	}
-	if u := user.Current(c); u != nil {
+	if u := user.Current(ctx); u != nil {
 		g.Author = u.String()
 	}
-	key := datastore.NewIncompleteKey(c, "Greeting", guestbookKey(c))
-	if _, err := datastore.Put(c, key, g); err != nil {
+	key := datastore.NewIncompleteKey(ctx, "Greeting", guestbookKey(ctx))
+	if _, err := datastore.Put(ctx, key, g); err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
diff --git a/go/src/google.golang.org/appengine/demos/helloworld/app.yaml b/go/src/google.golang.org/appengine/demos/helloworld/app.yaml
index bac034b..e0b2eb7 100644
--- a/go/src/google.golang.org/appengine/demos/helloworld/app.yaml
+++ b/go/src/google.golang.org/appengine/demos/helloworld/app.yaml
@@ -1,5 +1,3 @@
-application: helloworld
-version: 1
 runtime: go
 api_version: go1
 vm: true
diff --git a/go/src/google.golang.org/appengine/demos/helloworld/helloworld.go b/go/src/google.golang.org/appengine/demos/helloworld/helloworld.go
index 2324929..fcd9b0b 100644
--- a/go/src/google.golang.org/appengine/demos/helloworld/helloworld.go
+++ b/go/src/google.golang.org/appengine/demos/helloworld/helloworld.go
@@ -2,7 +2,10 @@
 // Use of this source code is governed by the Apache 2.0
 // license that can be found in the LICENSE file.
 
-package helloworld
+// This example only works on Managed VMs.
+// +build !appengine
+
+package main
 
 import (
 	"html/template"
@@ -15,8 +18,9 @@
 
 var initTime = time.Now()
 
-func init() {
+func main() {
 	http.HandleFunc("/", handle)
+	appengine.Main()
 }
 
 func handle(w http.ResponseWriter, r *http.Request) {
@@ -25,8 +29,8 @@
 		return
 	}
 
-	c := appengine.NewContext(r)
-	log.Infof(c, "Serving the front page.")
+	ctx := appengine.NewContext(r)
+	log.Infof(ctx, "Serving the front page.")
 
 	tmpl.Execute(w, time.Since(initTime))
 }
diff --git a/go/src/google.golang.org/appengine/identity.go b/go/src/google.golang.org/appengine/identity.go
index 336e3ff..b8dcf8f 100644
--- a/go/src/google.golang.org/appengine/identity.go
+++ b/go/src/google.golang.org/appengine/identity.go
@@ -126,15 +126,14 @@
 }
 
 // SignBytes signs bytes using a private key unique to your application.
-func SignBytes(c context.Context, bytes []byte) (string, []byte, error) {
+func SignBytes(c context.Context, bytes []byte) (keyName string, signature []byte, err error) {
 	req := &pb.SignForAppRequest{BytesToSign: bytes}
 	res := &pb.SignForAppResponse{}
 
-	err := internal.Call(c, "app_identity_service", "SignForApp", req, res)
-	if err != nil {
+	if err := internal.Call(c, "app_identity_service", "SignForApp", req, res); err != nil {
 		return "", nil, err
 	}
-	return res.GetKeyName(), res.GetSignatureBytes(), err
+	return res.GetKeyName(), res.GetSignatureBytes(), nil
 }
 
 func init() {
diff --git a/go/src/google.golang.org/appengine/internal/api.go b/go/src/google.golang.org/appengine/internal/api.go
index 40a6587..84b18a9 100644
--- a/go/src/google.golang.org/appengine/internal/api.go
+++ b/go/src/google.golang.org/appengine/internal/api.go
@@ -139,6 +139,7 @@
 	defer func() {
 		if x := recover(); x != nil {
 			logf(c, 4, "%s", renderPanic(x)) // 4 == critical
+			c.outCode = 500
 		}
 	}()
 
@@ -215,19 +216,26 @@
 	return c
 }
 
-func toContext(c *context) netcontext.Context {
-	ctx := netcontext.WithValue(netcontext.Background(), &contextKey, c)
+func withContext(parent netcontext.Context, c *context) netcontext.Context {
+	ctx := netcontext.WithValue(parent, &contextKey, c)
 	if ns := c.req.Header.Get(curNamespaceHeader); ns != "" {
 		ctx = WithNamespace(ctx, ns)
 	}
 	return ctx
 }
 
-func IncomingHeaders(ctx netcontext.Context) http.Header {
-	return fromContext(ctx).req.Header
+func toContext(c *context) netcontext.Context {
+	return withContext(netcontext.Background(), c)
 }
 
-func NewContext(req *http.Request) netcontext.Context {
+func IncomingHeaders(ctx netcontext.Context) http.Header {
+	if c := fromContext(ctx); c != nil {
+		return c.req.Header
+	}
+	return nil
+}
+
+func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context {
 	ctxs.Lock()
 	c := ctxs.m[req]
 	ctxs.Unlock()
@@ -238,7 +246,7 @@
 		// so that stack traces will be more sensible.
 		log.Panic("appengine: NewContext passed an unknown http.Request")
 	}
-	return toContext(c)
+	return withContext(parent, c)
 }
 
 func BackgroundContext() netcontext.Context {
@@ -253,7 +261,7 @@
 	appID := partitionlessAppID()
 	escAppID := strings.Replace(strings.Replace(appID, ":", "_", -1), ".", "_", -1)
 	majVersion := VersionID(nil)
-	if i := strings.Index(majVersion, "_"); i >= 0 {
+	if i := strings.Index(majVersion, "."); i > 0 {
 		majVersion = majVersion[:i]
 	}
 	ticket := fmt.Sprintf("%s/%s.%s.%s", escAppID, ModuleName(nil), majVersion, InstanceID())
@@ -377,7 +385,7 @@
 }
 
 func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error {
-	if f, ok := ctx.Value(&callOverrideKey).(callOverrideFunc); ok {
+	if f, ctx, ok := callOverrideFromContext(ctx); ok {
 		return f(ctx, service, method, in, out)
 	}
 
diff --git a/go/src/google.golang.org/appengine/internal/api_classic.go b/go/src/google.golang.org/appengine/internal/api_classic.go
index 32dbd3a..986191d 100644
--- a/go/src/google.golang.org/appengine/internal/api_classic.go
+++ b/go/src/google.golang.org/appengine/internal/api_classic.go
@@ -1,3 +1,7 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 // +build appengine
 
 package internal
@@ -27,8 +31,8 @@
 	return fromContext(ctx)
 }
 
-func toContext(c appengine.Context) netcontext.Context {
-	ctx := netcontext.WithValue(netcontext.Background(), &contextKey, c)
+func withContext(parent netcontext.Context, c appengine.Context) netcontext.Context {
+	ctx := netcontext.WithValue(parent, &contextKey, c)
 
 	s := &basepb.StringProto{}
 	c.Call("__go__", "GetNamespace", &basepb.VoidProto{}, s, nil)
@@ -39,13 +43,22 @@
 	return ctx
 }
 
-func NewContext(req *http.Request) netcontext.Context {
+func IncomingHeaders(ctx netcontext.Context) http.Header {
+	if c := fromContext(ctx); c != nil {
+		if req, ok := c.Request().(*http.Request); ok {
+			return req.Header
+		}
+	}
+	return nil
+}
+
+func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context {
 	c := appengine.NewContext(req)
-	return toContext(c)
+	return withContext(parent, c)
 }
 
 func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error {
-	if f, ok := ctx.Value(&callOverrideKey).(callOverrideFunc); ok {
+	if f, ctx, ok := callOverrideFromContext(ctx); ok {
 		return f(ctx, service, method, in, out)
 	}
 
@@ -70,7 +83,22 @@
 		}
 	}
 
-	return c.Call(service, method, in, out, opts)
+	err := c.Call(service, method, in, out, opts)
+	switch v := err.(type) {
+	case *appengine_internal.APIError:
+		return &APIError{
+			Service: v.Service,
+			Detail:  v.Detail,
+			Code:    v.Code,
+		}
+	case *appengine_internal.CallError:
+		return &CallError{
+			Detail:  v.Detail,
+			Code:    v.Code,
+			Timeout: v.Timeout,
+		}
+	}
+	return err
 }
 
 func handleHTTP(w http.ResponseWriter, r *http.Request) {
diff --git a/go/src/google.golang.org/appengine/internal/api_common.go b/go/src/google.golang.org/appengine/internal/api_common.go
index c499124..4f34b58 100644
--- a/go/src/google.golang.org/appengine/internal/api_common.go
+++ b/go/src/google.golang.org/appengine/internal/api_common.go
@@ -1,3 +1,7 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package internal
 
 import (
@@ -7,10 +11,29 @@
 
 type callOverrideFunc func(ctx netcontext.Context, service, method string, in, out proto.Message) error
 
-var callOverrideKey = "holds a callOverrideFunc"
+var callOverrideKey = "holds []callOverrideFunc"
 
 func WithCallOverride(ctx netcontext.Context, f callOverrideFunc) netcontext.Context {
-	return netcontext.WithValue(ctx, &callOverrideKey, f)
+	// We avoid appending to any existing call override
+	// so we don't risk overwriting a popped stack below.
+	var cofs []callOverrideFunc
+	if uf, ok := ctx.Value(&callOverrideKey).([]callOverrideFunc); ok {
+		cofs = append(cofs, uf...)
+	}
+	cofs = append(cofs, f)
+	return netcontext.WithValue(ctx, &callOverrideKey, cofs)
+}
+
+func callOverrideFromContext(ctx netcontext.Context) (callOverrideFunc, netcontext.Context, bool) {
+	cofs, _ := ctx.Value(&callOverrideKey).([]callOverrideFunc)
+	if len(cofs) == 0 {
+		return nil, nil, false
+	}
+	// We found a list of overrides; grab the last, and reconstitute a
+	// context that will hide it.
+	f := cofs[len(cofs)-1]
+	ctx = netcontext.WithValue(ctx, &callOverrideKey, cofs[:len(cofs)-1])
+	return f, ctx, true
 }
 
 type logOverrideFunc func(level int64, format string, args ...interface{})
diff --git a/go/src/google.golang.org/appengine/internal/api_race_test.go b/go/src/google.golang.org/appengine/internal/api_race_test.go
index d6977f1..6cfe906 100644
--- a/go/src/google.golang.org/appengine/internal/api_race_test.go
+++ b/go/src/google.golang.org/appengine/internal/api_race_test.go
@@ -1,3 +1,7 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 // +build race
 
 package internal
diff --git a/go/src/google.golang.org/appengine/internal/api_test.go b/go/src/google.golang.org/appengine/internal/api_test.go
index a8a6ed9..b090739 100644
--- a/go/src/google.golang.org/appengine/internal/api_test.go
+++ b/go/src/google.golang.org/appengine/internal/api_test.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by the Apache 2.0
 // license that can be found in the LICENSE file.
 
+// +build !appengine
+
 package internal
 
 import (
@@ -231,7 +233,7 @@
 	defer cleanup()
 
 	http.HandleFunc("/quick_log", func(w http.ResponseWriter, r *http.Request) {
-		c := NewContext(r)
+		c := WithContext(netcontext.Background(), r)
 		Logf(c, 1, "It's a lovely day.")
 		w.WriteHeader(200)
 		w.Write(make([]byte, 100<<10)) // write 100 KB to force HTTP flush
@@ -307,6 +309,22 @@
 	}
 }
 
+func TestPanickingHandler(t *testing.T) {
+	http.HandleFunc("/panic", func(http.ResponseWriter, *http.Request) {
+		panic("whoops!")
+	})
+	r := &http.Request{
+		Method: "GET",
+		URL:    &url.URL{Scheme: "http", Path: "/panic"},
+		Body:   ioutil.NopCloser(bytes.NewReader(nil)),
+	}
+	rec := httptest.NewRecorder()
+	handleHTTP(rec, r)
+	if rec.Code != 500 {
+		t.Errorf("Panicking handler returned HTTP %d, want HTTP %d", rec.Code, 500)
+	}
+}
+
 var raceDetector = false
 
 func TestAPICallAllocations(t *testing.T) {
@@ -342,7 +360,7 @@
 	}
 
 	// Lots of room for improvement...
-	const min, max float64 = 80, 95
+	const min, max float64 = 75, 95
 	if avg < min || max < avg {
 		t.Errorf("Allocations per API call = %g, want in [%g,%g]", avg, min, max)
 	}
@@ -407,3 +425,34 @@
 	// Wait for stdin to be closed.
 	io.Copy(ioutil.Discard, os.Stdin)
 }
+
+func TestBackgroundContext(t *testing.T) {
+	environ := []struct {
+		key, value string
+	}{
+		{"GAE_LONG_APP_ID", "my-app-id"},
+		{"GAE_MINOR_VERSION", "067924799508853122"},
+		{"GAE_MODULE_INSTANCE", "0"},
+		{"GAE_MODULE_NAME", "default"},
+		{"GAE_MODULE_VERSION", "20150612t184001"},
+	}
+	for _, v := range environ {
+		old := os.Getenv(v.key)
+		os.Setenv(v.key, v.value)
+		v.value = old
+	}
+	defer func() { // Restore old environment after the test completes.
+		for _, v := range environ {
+			if v.value == "" {
+				os.Unsetenv(v.key)
+				continue
+			}
+			os.Setenv(v.key, v.value)
+		}
+	}()
+
+	ctx, key := fromContext(BackgroundContext()), "X-Magic-Ticket-Header"
+	if g, w := ctx.req.Header.Get(key), "my-app-id/default.20150612t184001.0"; g != w {
+		t.Errorf("%v = %q, want %q", key, g, w)
+	}
+}
diff --git a/go/src/google.golang.org/appengine/internal/app_identity/app_identity_service.pb.go b/go/src/google.golang.org/appengine/internal/app_identity/app_identity_service.pb.go
index 532dad7..87d9701 100644
--- a/go/src/google.golang.org/appengine/internal/app_identity/app_identity_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/app_identity/app_identity_service.pb.go
@@ -25,10 +25,12 @@
 package app_identity
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type AppIdentityServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/base/api_base.pb.go b/go/src/google.golang.org/appengine/internal/base/api_base.pb.go
index 9ecc29b..36a1956 100644
--- a/go/src/google.golang.org/appengine/internal/base/api_base.pb.go
+++ b/go/src/google.golang.org/appengine/internal/base/api_base.pb.go
@@ -20,10 +20,12 @@
 package base
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type StringProto struct {
@@ -129,6 +131,3 @@
 func (m *VoidProto) Reset()         { *m = VoidProto{} }
 func (m *VoidProto) String() string { return proto.CompactTextString(m) }
 func (*VoidProto) ProtoMessage()    {}
-
-func init() {
-}
diff --git a/go/src/google.golang.org/appengine/internal/blobstore/blobstore_service.pb.go b/go/src/google.golang.org/appengine/internal/blobstore/blobstore_service.pb.go
new file mode 100644
index 0000000..8705ec3
--- /dev/null
+++ b/go/src/google.golang.org/appengine/internal/blobstore/blobstore_service.pb.go
@@ -0,0 +1,347 @@
+// Code generated by protoc-gen-go.
+// source: google.golang.org/appengine/internal/blobstore/blobstore_service.proto
+// DO NOT EDIT!
+
+/*
+Package blobstore is a generated protocol buffer package.
+
+It is generated from these files:
+	google.golang.org/appengine/internal/blobstore/blobstore_service.proto
+
+It has these top-level messages:
+	BlobstoreServiceError
+	CreateUploadURLRequest
+	CreateUploadURLResponse
+	DeleteBlobRequest
+	FetchDataRequest
+	FetchDataResponse
+	CloneBlobRequest
+	CloneBlobResponse
+	DecodeBlobKeyRequest
+	DecodeBlobKeyResponse
+	CreateEncodedGoogleStorageKeyRequest
+	CreateEncodedGoogleStorageKeyResponse
+*/
+package blobstore
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+type BlobstoreServiceError_ErrorCode int32
+
+const (
+	BlobstoreServiceError_OK                        BlobstoreServiceError_ErrorCode = 0
+	BlobstoreServiceError_INTERNAL_ERROR            BlobstoreServiceError_ErrorCode = 1
+	BlobstoreServiceError_URL_TOO_LONG              BlobstoreServiceError_ErrorCode = 2
+	BlobstoreServiceError_PERMISSION_DENIED         BlobstoreServiceError_ErrorCode = 3
+	BlobstoreServiceError_BLOB_NOT_FOUND            BlobstoreServiceError_ErrorCode = 4
+	BlobstoreServiceError_DATA_INDEX_OUT_OF_RANGE   BlobstoreServiceError_ErrorCode = 5
+	BlobstoreServiceError_BLOB_FETCH_SIZE_TOO_LARGE BlobstoreServiceError_ErrorCode = 6
+	BlobstoreServiceError_ARGUMENT_OUT_OF_RANGE     BlobstoreServiceError_ErrorCode = 8
+	BlobstoreServiceError_INVALID_BLOB_KEY          BlobstoreServiceError_ErrorCode = 9
+)
+
+var BlobstoreServiceError_ErrorCode_name = map[int32]string{
+	0: "OK",
+	1: "INTERNAL_ERROR",
+	2: "URL_TOO_LONG",
+	3: "PERMISSION_DENIED",
+	4: "BLOB_NOT_FOUND",
+	5: "DATA_INDEX_OUT_OF_RANGE",
+	6: "BLOB_FETCH_SIZE_TOO_LARGE",
+	8: "ARGUMENT_OUT_OF_RANGE",
+	9: "INVALID_BLOB_KEY",
+}
+var BlobstoreServiceError_ErrorCode_value = map[string]int32{
+	"OK":                        0,
+	"INTERNAL_ERROR":            1,
+	"URL_TOO_LONG":              2,
+	"PERMISSION_DENIED":         3,
+	"BLOB_NOT_FOUND":            4,
+	"DATA_INDEX_OUT_OF_RANGE":   5,
+	"BLOB_FETCH_SIZE_TOO_LARGE": 6,
+	"ARGUMENT_OUT_OF_RANGE":     8,
+	"INVALID_BLOB_KEY":          9,
+}
+
+func (x BlobstoreServiceError_ErrorCode) Enum() *BlobstoreServiceError_ErrorCode {
+	p := new(BlobstoreServiceError_ErrorCode)
+	*p = x
+	return p
+}
+func (x BlobstoreServiceError_ErrorCode) String() string {
+	return proto.EnumName(BlobstoreServiceError_ErrorCode_name, int32(x))
+}
+func (x *BlobstoreServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(BlobstoreServiceError_ErrorCode_value, data, "BlobstoreServiceError_ErrorCode")
+	if err != nil {
+		return err
+	}
+	*x = BlobstoreServiceError_ErrorCode(value)
+	return nil
+}
+
+type BlobstoreServiceError struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *BlobstoreServiceError) Reset()         { *m = BlobstoreServiceError{} }
+func (m *BlobstoreServiceError) String() string { return proto.CompactTextString(m) }
+func (*BlobstoreServiceError) ProtoMessage()    {}
+
+type CreateUploadURLRequest struct {
+	SuccessPath               *string `protobuf:"bytes,1,req,name=success_path" json:"success_path,omitempty"`
+	MaxUploadSizeBytes        *int64  `protobuf:"varint,2,opt,name=max_upload_size_bytes" json:"max_upload_size_bytes,omitempty"`
+	MaxUploadSizePerBlobBytes *int64  `protobuf:"varint,3,opt,name=max_upload_size_per_blob_bytes" json:"max_upload_size_per_blob_bytes,omitempty"`
+	GsBucketName              *string `protobuf:"bytes,4,opt,name=gs_bucket_name" json:"gs_bucket_name,omitempty"`
+	UrlExpiryTimeSeconds      *int32  `protobuf:"varint,5,opt,name=url_expiry_time_seconds" json:"url_expiry_time_seconds,omitempty"`
+	XXX_unrecognized          []byte  `json:"-"`
+}
+
+func (m *CreateUploadURLRequest) Reset()         { *m = CreateUploadURLRequest{} }
+func (m *CreateUploadURLRequest) String() string { return proto.CompactTextString(m) }
+func (*CreateUploadURLRequest) ProtoMessage()    {}
+
+func (m *CreateUploadURLRequest) GetSuccessPath() string {
+	if m != nil && m.SuccessPath != nil {
+		return *m.SuccessPath
+	}
+	return ""
+}
+
+func (m *CreateUploadURLRequest) GetMaxUploadSizeBytes() int64 {
+	if m != nil && m.MaxUploadSizeBytes != nil {
+		return *m.MaxUploadSizeBytes
+	}
+	return 0
+}
+
+func (m *CreateUploadURLRequest) GetMaxUploadSizePerBlobBytes() int64 {
+	if m != nil && m.MaxUploadSizePerBlobBytes != nil {
+		return *m.MaxUploadSizePerBlobBytes
+	}
+	return 0
+}
+
+func (m *CreateUploadURLRequest) GetGsBucketName() string {
+	if m != nil && m.GsBucketName != nil {
+		return *m.GsBucketName
+	}
+	return ""
+}
+
+func (m *CreateUploadURLRequest) GetUrlExpiryTimeSeconds() int32 {
+	if m != nil && m.UrlExpiryTimeSeconds != nil {
+		return *m.UrlExpiryTimeSeconds
+	}
+	return 0
+}
+
+type CreateUploadURLResponse struct {
+	Url              *string `protobuf:"bytes,1,req,name=url" json:"url,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *CreateUploadURLResponse) Reset()         { *m = CreateUploadURLResponse{} }
+func (m *CreateUploadURLResponse) String() string { return proto.CompactTextString(m) }
+func (*CreateUploadURLResponse) ProtoMessage()    {}
+
+func (m *CreateUploadURLResponse) GetUrl() string {
+	if m != nil && m.Url != nil {
+		return *m.Url
+	}
+	return ""
+}
+
+type DeleteBlobRequest struct {
+	BlobKey          []string `protobuf:"bytes,1,rep,name=blob_key" json:"blob_key,omitempty"`
+	Token            *string  `protobuf:"bytes,2,opt,name=token" json:"token,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *DeleteBlobRequest) Reset()         { *m = DeleteBlobRequest{} }
+func (m *DeleteBlobRequest) String() string { return proto.CompactTextString(m) }
+func (*DeleteBlobRequest) ProtoMessage()    {}
+
+func (m *DeleteBlobRequest) GetBlobKey() []string {
+	if m != nil {
+		return m.BlobKey
+	}
+	return nil
+}
+
+func (m *DeleteBlobRequest) GetToken() string {
+	if m != nil && m.Token != nil {
+		return *m.Token
+	}
+	return ""
+}
+
+type FetchDataRequest struct {
+	BlobKey          *string `protobuf:"bytes,1,req,name=blob_key" json:"blob_key,omitempty"`
+	StartIndex       *int64  `protobuf:"varint,2,req,name=start_index" json:"start_index,omitempty"`
+	EndIndex         *int64  `protobuf:"varint,3,req,name=end_index" json:"end_index,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *FetchDataRequest) Reset()         { *m = FetchDataRequest{} }
+func (m *FetchDataRequest) String() string { return proto.CompactTextString(m) }
+func (*FetchDataRequest) ProtoMessage()    {}
+
+func (m *FetchDataRequest) GetBlobKey() string {
+	if m != nil && m.BlobKey != nil {
+		return *m.BlobKey
+	}
+	return ""
+}
+
+func (m *FetchDataRequest) GetStartIndex() int64 {
+	if m != nil && m.StartIndex != nil {
+		return *m.StartIndex
+	}
+	return 0
+}
+
+func (m *FetchDataRequest) GetEndIndex() int64 {
+	if m != nil && m.EndIndex != nil {
+		return *m.EndIndex
+	}
+	return 0
+}
+
+type FetchDataResponse struct {
+	Data             []byte `protobuf:"bytes,1000,req,name=data" json:"data,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *FetchDataResponse) Reset()         { *m = FetchDataResponse{} }
+func (m *FetchDataResponse) String() string { return proto.CompactTextString(m) }
+func (*FetchDataResponse) ProtoMessage()    {}
+
+func (m *FetchDataResponse) GetData() []byte {
+	if m != nil {
+		return m.Data
+	}
+	return nil
+}
+
+type CloneBlobRequest struct {
+	BlobKey          []byte `protobuf:"bytes,1,req,name=blob_key" json:"blob_key,omitempty"`
+	MimeType         []byte `protobuf:"bytes,2,req,name=mime_type" json:"mime_type,omitempty"`
+	TargetAppId      []byte `protobuf:"bytes,3,req,name=target_app_id" json:"target_app_id,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *CloneBlobRequest) Reset()         { *m = CloneBlobRequest{} }
+func (m *CloneBlobRequest) String() string { return proto.CompactTextString(m) }
+func (*CloneBlobRequest) ProtoMessage()    {}
+
+func (m *CloneBlobRequest) GetBlobKey() []byte {
+	if m != nil {
+		return m.BlobKey
+	}
+	return nil
+}
+
+func (m *CloneBlobRequest) GetMimeType() []byte {
+	if m != nil {
+		return m.MimeType
+	}
+	return nil
+}
+
+func (m *CloneBlobRequest) GetTargetAppId() []byte {
+	if m != nil {
+		return m.TargetAppId
+	}
+	return nil
+}
+
+type CloneBlobResponse struct {
+	BlobKey          []byte `protobuf:"bytes,1,req,name=blob_key" json:"blob_key,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *CloneBlobResponse) Reset()         { *m = CloneBlobResponse{} }
+func (m *CloneBlobResponse) String() string { return proto.CompactTextString(m) }
+func (*CloneBlobResponse) ProtoMessage()    {}
+
+func (m *CloneBlobResponse) GetBlobKey() []byte {
+	if m != nil {
+		return m.BlobKey
+	}
+	return nil
+}
+
+type DecodeBlobKeyRequest struct {
+	BlobKey          []string `protobuf:"bytes,1,rep,name=blob_key" json:"blob_key,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *DecodeBlobKeyRequest) Reset()         { *m = DecodeBlobKeyRequest{} }
+func (m *DecodeBlobKeyRequest) String() string { return proto.CompactTextString(m) }
+func (*DecodeBlobKeyRequest) ProtoMessage()    {}
+
+func (m *DecodeBlobKeyRequest) GetBlobKey() []string {
+	if m != nil {
+		return m.BlobKey
+	}
+	return nil
+}
+
+type DecodeBlobKeyResponse struct {
+	Decoded          []string `protobuf:"bytes,1,rep,name=decoded" json:"decoded,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *DecodeBlobKeyResponse) Reset()         { *m = DecodeBlobKeyResponse{} }
+func (m *DecodeBlobKeyResponse) String() string { return proto.CompactTextString(m) }
+func (*DecodeBlobKeyResponse) ProtoMessage()    {}
+
+func (m *DecodeBlobKeyResponse) GetDecoded() []string {
+	if m != nil {
+		return m.Decoded
+	}
+	return nil
+}
+
+type CreateEncodedGoogleStorageKeyRequest struct {
+	Filename         *string `protobuf:"bytes,1,req,name=filename" json:"filename,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *CreateEncodedGoogleStorageKeyRequest) Reset()         { *m = CreateEncodedGoogleStorageKeyRequest{} }
+func (m *CreateEncodedGoogleStorageKeyRequest) String() string { return proto.CompactTextString(m) }
+func (*CreateEncodedGoogleStorageKeyRequest) ProtoMessage()    {}
+
+func (m *CreateEncodedGoogleStorageKeyRequest) GetFilename() string {
+	if m != nil && m.Filename != nil {
+		return *m.Filename
+	}
+	return ""
+}
+
+type CreateEncodedGoogleStorageKeyResponse struct {
+	BlobKey          *string `protobuf:"bytes,1,req,name=blob_key" json:"blob_key,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *CreateEncodedGoogleStorageKeyResponse) Reset()         { *m = CreateEncodedGoogleStorageKeyResponse{} }
+func (m *CreateEncodedGoogleStorageKeyResponse) String() string { return proto.CompactTextString(m) }
+func (*CreateEncodedGoogleStorageKeyResponse) ProtoMessage()    {}
+
+func (m *CreateEncodedGoogleStorageKeyResponse) GetBlobKey() string {
+	if m != nil && m.BlobKey != nil {
+		return *m.BlobKey
+	}
+	return ""
+}
+
+func init() {
+}
diff --git a/go/src/google.golang.org/appengine/internal/blobstore/blobstore_service.proto b/go/src/google.golang.org/appengine/internal/blobstore/blobstore_service.proto
new file mode 100644
index 0000000..33b2650
--- /dev/null
+++ b/go/src/google.golang.org/appengine/internal/blobstore/blobstore_service.proto
@@ -0,0 +1,71 @@
+syntax = "proto2";
+option go_package = "blobstore";
+
+package appengine;
+
+message BlobstoreServiceError {
+  enum ErrorCode {
+    OK = 0;
+    INTERNAL_ERROR = 1;
+    URL_TOO_LONG = 2;
+    PERMISSION_DENIED = 3;
+    BLOB_NOT_FOUND = 4;
+    DATA_INDEX_OUT_OF_RANGE = 5;
+    BLOB_FETCH_SIZE_TOO_LARGE = 6;
+    ARGUMENT_OUT_OF_RANGE = 8;
+    INVALID_BLOB_KEY = 9;
+  }
+}
+
+message CreateUploadURLRequest {
+  required string success_path = 1;
+  optional int64 max_upload_size_bytes = 2;
+  optional int64 max_upload_size_per_blob_bytes = 3;
+  optional string gs_bucket_name = 4;
+  optional int32 url_expiry_time_seconds = 5;
+}
+
+message CreateUploadURLResponse {
+  required string url = 1;
+}
+
+message DeleteBlobRequest {
+  repeated string blob_key = 1;
+  optional string token = 2;
+}
+
+message FetchDataRequest {
+  required string blob_key = 1;
+  required int64 start_index = 2;
+  required int64 end_index = 3;
+}
+
+message FetchDataResponse {
+  required bytes data = 1000 [ctype = CORD];
+}
+
+message CloneBlobRequest {
+  required bytes blob_key = 1;
+  required bytes mime_type = 2;
+  required bytes target_app_id = 3;
+}
+
+message CloneBlobResponse {
+  required bytes blob_key = 1;
+}
+
+message DecodeBlobKeyRequest {
+  repeated string blob_key = 1;
+}
+
+message DecodeBlobKeyResponse {
+  repeated string decoded = 1;
+}
+
+message CreateEncodedGoogleStorageKeyRequest {
+  required string filename = 1;
+}
+
+message CreateEncodedGoogleStorageKeyResponse {
+  required string blob_key = 1;
+}
diff --git a/go/src/google.golang.org/appengine/internal/channel/channel_service.pb.go b/go/src/google.golang.org/appengine/internal/channel/channel_service.pb.go
index f2334eb..7b8d00c 100644
--- a/go/src/google.golang.org/appengine/internal/channel/channel_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/channel/channel_service.pb.go
@@ -17,10 +17,12 @@
 package channel
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type ChannelServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go b/go/src/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go
index cd78da9..5c64e0f 100644
--- a/go/src/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go
+++ b/go/src/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go
@@ -51,10 +51,12 @@
 package datastore
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type Property_Meaning int32
diff --git a/go/src/google.golang.org/appengine/internal/identity_classic.go b/go/src/google.golang.org/appengine/internal/identity_classic.go
index 08e060d..e6b9227 100644
--- a/go/src/google.golang.org/appengine/internal/identity_classic.go
+++ b/go/src/google.golang.org/appengine/internal/identity_classic.go
@@ -1,3 +1,7 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 // +build appengine
 
 package internal
@@ -18,5 +22,6 @@
 func ModuleName(ctx netcontext.Context) string { return appengine.ModuleName(fromContext(ctx)) }
 func VersionID(ctx netcontext.Context) string  { return appengine.VersionID(fromContext(ctx)) }
 func InstanceID() string                       { return appengine.InstanceID() }
+func IsDevAppServer() bool                     { return appengine.IsDevAppServer() }
 
 func fullyQualifiedAppID(ctx netcontext.Context) string { return fromContext(ctx).FullyQualifiedAppID() }
diff --git a/go/src/google.golang.org/appengine/internal/identity_vm.go b/go/src/google.golang.org/appengine/internal/identity_vm.go
index a1dfff9..f011197 100644
--- a/go/src/google.golang.org/appengine/internal/identity_vm.go
+++ b/go/src/google.golang.org/appengine/internal/identity_vm.go
@@ -56,10 +56,10 @@
 }
 
 func VersionID(_ netcontext.Context) string {
-	if s := os.Getenv("GAE_MODULE_VERSION"); s != "" {
-		return s
+	if s1, s2 := os.Getenv("GAE_MODULE_VERSION"), os.Getenv("GAE_MINOR_VERSION"); s1 != "" && s2 != "" {
+		return s1 + "." + s2
 	}
-	return string(mustGetMetadata("instance/attributes/gae_backend_version"))
+	return string(mustGetMetadata("instance/attributes/gae_backend_version")) + "." + string(mustGetMetadata("instance/attributes/gae_minor_version"))
 }
 
 func InstanceID() string {
@@ -91,3 +91,7 @@
 	}
 	return appID
 }
+
+func IsDevAppServer() bool {
+	return os.Getenv("RUN_WITH_DEVAPPSERVER") != ""
+}
diff --git a/go/src/google.golang.org/appengine/internal/image/images_service.pb.go b/go/src/google.golang.org/appengine/internal/image/images_service.pb.go
index 27cf17d..ba7c722 100644
--- a/go/src/google.golang.org/appengine/internal/image/images_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/image/images_service.pb.go
@@ -32,10 +32,12 @@
 package image
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type ImagesServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/internal.go b/go/src/google.golang.org/appengine/internal/internal.go
index 8b3135b..914145b 100644
--- a/go/src/google.golang.org/appengine/internal/internal.go
+++ b/go/src/google.golang.org/appengine/internal/internal.go
@@ -109,11 +109,6 @@
 	return e.Timeout
 }
 
-// The comment below must not be changed.
-// It is used by go-app-builder to recognise that this package has
-// the internal.Main function to use in the synthetic main.
-//   The gophers party all night; the rabbits provide the beats.
-
 // Main is designed so that the complete generated main package is:
 //
 //      package main
diff --git a/go/src/google.golang.org/appengine/internal/internal_test.go b/go/src/google.golang.org/appengine/internal/internal_test.go
index 226028d..dc684f5 100644
--- a/go/src/google.golang.org/appengine/internal/internal_test.go
+++ b/go/src/google.golang.org/appengine/internal/internal_test.go
@@ -1,3 +1,7 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package internal
 
 import (
diff --git a/go/src/google.golang.org/appengine/internal/log/log_service.pb.go b/go/src/google.golang.org/appengine/internal/log/log_service.pb.go
index ebb2bab..20c595b 100644
--- a/go/src/google.golang.org/appengine/internal/log/log_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/log/log_service.pb.go
@@ -27,10 +27,12 @@
 package log
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type LogServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/mail/mail_service.pb.go b/go/src/google.golang.org/appengine/internal/mail/mail_service.pb.go
index 06a5d51..a6978c3 100644
--- a/go/src/google.golang.org/appengine/internal/mail/mail_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/mail/mail_service.pb.go
@@ -17,10 +17,12 @@
 package mail
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type MailServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/memcache/memcache_service.pb.go b/go/src/google.golang.org/appengine/internal/memcache/memcache_service.pb.go
index d0d1f5f..b1eca0f 100644
--- a/go/src/google.golang.org/appengine/internal/memcache/memcache_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/memcache/memcache_service.pb.go
@@ -32,10 +32,12 @@
 package memcache
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type MemcacheServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/modules/modules_service.pb.go b/go/src/google.golang.org/appengine/internal/modules/modules_service.pb.go
index 341e7d1..a0145ed 100644
--- a/go/src/google.golang.org/appengine/internal/modules/modules_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/modules/modules_service.pb.go
@@ -30,10 +30,12 @@
 package modules
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type ModulesServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/net.go b/go/src/google.golang.org/appengine/internal/net.go
index 12ddfbf..3b94cf0 100644
--- a/go/src/google.golang.org/appengine/internal/net.go
+++ b/go/src/google.golang.org/appengine/internal/net.go
@@ -43,21 +43,14 @@
 }
 
 type limitConn struct {
-	mu sync.Mutex // only for closing the net.Conn
+	close sync.Once
 	net.Conn
 }
 
 func (lc *limitConn) Close() error {
-	lc.mu.Lock()
-	defer lc.mu.Unlock()
-
-	if lc.Conn == nil {
-		// Silently ignore double close.
-		return nil
-	}
-	limitRelease()
-	err := lc.Conn.Close()
-	lc.Conn = nil
-	runtime.SetFinalizer(lc, nil)
-	return err
+	defer lc.close.Do(func() {
+		limitRelease()
+		runtime.SetFinalizer(lc, nil)
+	})
+	return lc.Conn.Close()
 }
diff --git a/go/src/google.golang.org/appengine/internal/net_test.go b/go/src/google.golang.org/appengine/internal/net_test.go
index 3c0c152..24da8bb 100644
--- a/go/src/google.golang.org/appengine/internal/net_test.go
+++ b/go/src/google.golang.org/appengine/internal/net_test.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by the Apache 2.0
 // license that can be found in the LICENSE file.
 
+// +build !appengine
+
 package internal
 
 import (
diff --git a/go/src/google.golang.org/appengine/internal/regen.sh b/go/src/google.golang.org/appengine/internal/regen.sh
index 51c6178..f98e761 100755
--- a/go/src/google.golang.org/appengine/internal/regen.sh
+++ b/go/src/google.golang.org/appengine/internal/regen.sh
@@ -29,11 +29,6 @@
 done
 
 for f in $(find $PKG/internal -name '*.pb.go'); do
-  # Fix up import lines.
-  # This should be fixed upstream.
-  # https://code.google.com/p/goprotobuf/issues/detail?id=32
-  sed -i '/^import.*\.pb"$/s,/[^/]*\.pb"$,",' $f
-
   # Remove proto.RegisterEnum calls.
   # These cause duplicate registration panics when these packages
   # are used on classic App Engine. proto.RegisterEnum only affects
diff --git a/go/src/google.golang.org/appengine/internal/remote_api/remote_api.pb.go b/go/src/google.golang.org/appengine/internal/remote_api/remote_api.pb.go
index aceca21..526bd39 100644
--- a/go/src/google.golang.org/appengine/internal/remote_api/remote_api.pb.go
+++ b/go/src/google.golang.org/appengine/internal/remote_api/remote_api.pb.go
@@ -17,10 +17,12 @@
 package remote_api
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type RpcError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/search/search.pb.go b/go/src/google.golang.org/appengine/internal/search/search.pb.go
index 16cd51e..10d2398 100644
--- a/go/src/google.golang.org/appengine/internal/search/search.pb.go
+++ b/go/src/google.golang.org/appengine/internal/search/search.pb.go
@@ -15,8 +15,10 @@
 	FieldValue
 	Field
 	FieldTypes
+	IndexShardSettings
 	FacetValue
 	Facet
+	DocumentMetadata
 	Document
 	SearchServiceError
 	RequestStatus
@@ -44,7 +46,7 @@
 	FacetRequestParam
 	FacetAutoDetectParam
 	FacetRequest
-	FacetRefine
+	FacetRefinement
 	SearchParams
 	SearchRequest
 	FacetResultValue
@@ -55,10 +57,12 @@
 package search
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type Scope_Type int32
@@ -194,18 +198,15 @@
 
 const (
 	FacetValue_ATOM   FacetValue_ContentType = 2
-	FacetValue_DATE   FacetValue_ContentType = 3
 	FacetValue_NUMBER FacetValue_ContentType = 4
 )
 
 var FacetValue_ContentType_name = map[int32]string{
 	2: "ATOM",
-	3: "DATE",
 	4: "NUMBER",
 }
 var FacetValue_ContentType_value = map[string]int32{
 	"ATOM":   2,
-	"DATE":   3,
 	"NUMBER": 4,
 }
 
@@ -736,6 +737,48 @@
 	return nil
 }
 
+type IndexShardSettings struct {
+	PrevNumShards            []int32 `protobuf:"varint,1,rep,name=prev_num_shards" json:"prev_num_shards,omitempty"`
+	NumShards                *int32  `protobuf:"varint,2,req,name=num_shards,def=1" json:"num_shards,omitempty"`
+	PrevNumShardsSearchFalse []int32 `protobuf:"varint,3,rep,name=prev_num_shards_search_false" json:"prev_num_shards_search_false,omitempty"`
+	LocalReplica             *string `protobuf:"bytes,4,opt,name=local_replica,def=" json:"local_replica,omitempty"`
+	XXX_unrecognized         []byte  `json:"-"`
+}
+
+func (m *IndexShardSettings) Reset()         { *m = IndexShardSettings{} }
+func (m *IndexShardSettings) String() string { return proto.CompactTextString(m) }
+func (*IndexShardSettings) ProtoMessage()    {}
+
+const Default_IndexShardSettings_NumShards int32 = 1
+
+func (m *IndexShardSettings) GetPrevNumShards() []int32 {
+	if m != nil {
+		return m.PrevNumShards
+	}
+	return nil
+}
+
+func (m *IndexShardSettings) GetNumShards() int32 {
+	if m != nil && m.NumShards != nil {
+		return *m.NumShards
+	}
+	return Default_IndexShardSettings_NumShards
+}
+
+func (m *IndexShardSettings) GetPrevNumShardsSearchFalse() []int32 {
+	if m != nil {
+		return m.PrevNumShardsSearchFalse
+	}
+	return nil
+}
+
+func (m *IndexShardSettings) GetLocalReplica() string {
+	if m != nil && m.LocalReplica != nil {
+		return *m.LocalReplica
+	}
+	return ""
+}
+
 type FacetValue struct {
 	Type             *FacetValue_ContentType `protobuf:"varint,1,opt,name=type,enum=search.FacetValue_ContentType,def=2" json:"type,omitempty"`
 	StringValue      *string                 `protobuf:"bytes,3,opt,name=string_value" json:"string_value,omitempty"`
@@ -786,16 +829,38 @@
 	return nil
 }
 
+type DocumentMetadata struct {
+	Version            *int64 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"`
+	CommittedStVersion *int64 `protobuf:"varint,2,opt,name=committed_st_version" json:"committed_st_version,omitempty"`
+	XXX_unrecognized   []byte `json:"-"`
+}
+
+func (m *DocumentMetadata) Reset()         { *m = DocumentMetadata{} }
+func (m *DocumentMetadata) String() string { return proto.CompactTextString(m) }
+func (*DocumentMetadata) ProtoMessage()    {}
+
+func (m *DocumentMetadata) GetVersion() int64 {
+	if m != nil && m.Version != nil {
+		return *m.Version
+	}
+	return 0
+}
+
+func (m *DocumentMetadata) GetCommittedStVersion() int64 {
+	if m != nil && m.CommittedStVersion != nil {
+		return *m.CommittedStVersion
+	}
+	return 0
+}
+
 type Document struct {
-	Id               *string            `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
-	Language         *string            `protobuf:"bytes,2,opt,name=language,def=en" json:"language,omitempty"`
-	Field            []*Field           `protobuf:"bytes,3,rep,name=field" json:"field,omitempty"`
-	OrderId          *int32             `protobuf:"varint,4,opt,name=order_id" json:"order_id,omitempty"`
-	Storage          *Document_Storage  `protobuf:"varint,5,opt,name=storage,enum=search.Document_Storage,def=0" json:"storage,omitempty"`
-	Acl              *AccessControlList `protobuf:"bytes,6,opt,name=acl" json:"acl,omitempty"`
-	Version          *int64             `protobuf:"varint,7,opt,name=version" json:"version,omitempty"`
-	Facet            []*Facet           `protobuf:"bytes,8,rep,name=facet" json:"facet,omitempty"`
-	XXX_unrecognized []byte             `json:"-"`
+	Id               *string           `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
+	Language         *string           `protobuf:"bytes,2,opt,name=language,def=en" json:"language,omitempty"`
+	Field            []*Field          `protobuf:"bytes,3,rep,name=field" json:"field,omitempty"`
+	OrderId          *int32            `protobuf:"varint,4,opt,name=order_id" json:"order_id,omitempty"`
+	Storage          *Document_Storage `protobuf:"varint,5,opt,name=storage,enum=search.Document_Storage,def=0" json:"storage,omitempty"`
+	Facet            []*Facet          `protobuf:"bytes,8,rep,name=facet" json:"facet,omitempty"`
+	XXX_unrecognized []byte            `json:"-"`
 }
 
 func (m *Document) Reset()         { *m = Document{} }
@@ -840,20 +905,6 @@
 	return Default_Document_Storage
 }
 
-func (m *Document) GetAcl() *AccessControlList {
-	if m != nil {
-		return m.Acl
-	}
-	return nil
-}
-
-func (m *Document) GetVersion() int64 {
-	if m != nil && m.Version != nil {
-		return *m.Version
-	}
-	return 0
-}
-
 func (m *Document) GetFacet() []*Facet {
 	if m != nil {
 		return m.Facet
@@ -872,6 +923,7 @@
 type RequestStatus struct {
 	Code             *SearchServiceError_ErrorCode `protobuf:"varint,1,req,name=code,enum=search.SearchServiceError_ErrorCode" json:"code,omitempty"`
 	ErrorDetail      *string                       `protobuf:"bytes,2,opt,name=error_detail" json:"error_detail,omitempty"`
+	CanonicalCode    *int32                        `protobuf:"varint,3,opt,name=canonical_code" json:"canonical_code,omitempty"`
 	XXX_unrecognized []byte                        `json:"-"`
 }
 
@@ -893,6 +945,13 @@
 	return ""
 }
 
+func (m *RequestStatus) GetCanonicalCode() int32 {
+	if m != nil && m.CanonicalCode != nil {
+		return *m.CanonicalCode
+	}
+	return 0
+}
+
 type IndexSpec struct {
 	Name             *string                `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
 	Consistency      *IndexSpec_Consistency `protobuf:"varint,2,opt,name=consistency,enum=search.IndexSpec_Consistency,def=1" json:"consistency,omitempty"`
@@ -1652,10 +1711,9 @@
 }
 
 type FacetRequest struct {
-	Name             *string                 `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
-	Type             *FacetValue_ContentType `protobuf:"varint,2,req,name=type,enum=search.FacetValue_ContentType" json:"type,omitempty"`
-	Params           *FacetRequestParam      `protobuf:"bytes,3,opt,name=params" json:"params,omitempty"`
-	XXX_unrecognized []byte                  `json:"-"`
+	Name             *string            `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+	Params           *FacetRequestParam `protobuf:"bytes,2,opt,name=params" json:"params,omitempty"`
+	XXX_unrecognized []byte             `json:"-"`
 }
 
 func (m *FacetRequest) Reset()         { *m = FacetRequest{} }
@@ -1669,13 +1727,6 @@
 	return ""
 }
 
-func (m *FacetRequest) GetType() FacetValue_ContentType {
-	if m != nil && m.Type != nil {
-		return *m.Type
-	}
-	return FacetValue_ATOM
-}
-
 func (m *FacetRequest) GetParams() *FacetRequestParam {
 	if m != nil {
 		return m.Params
@@ -1683,48 +1734,56 @@
 	return nil
 }
 
-type FacetRefine struct {
-	Name             *string                 `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
-	Type             *FacetValue_ContentType `protobuf:"varint,2,req,name=type,enum=search.FacetValue_ContentType" json:"type,omitempty"`
-	Value            *string                 `protobuf:"bytes,3,opt,name=value" json:"value,omitempty"`
-	Start            *string                 `protobuf:"bytes,4,opt,name=start" json:"start,omitempty"`
-	End              *string                 `protobuf:"bytes,5,opt,name=end" json:"end,omitempty"`
-	XXX_unrecognized []byte                  `json:"-"`
+type FacetRefinement struct {
+	Name             *string                `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+	Value            *string                `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
+	Range            *FacetRefinement_Range `protobuf:"bytes,3,opt,name=range" json:"range,omitempty"`
+	XXX_unrecognized []byte                 `json:"-"`
 }
 
-func (m *FacetRefine) Reset()         { *m = FacetRefine{} }
-func (m *FacetRefine) String() string { return proto.CompactTextString(m) }
-func (*FacetRefine) ProtoMessage()    {}
+func (m *FacetRefinement) Reset()         { *m = FacetRefinement{} }
+func (m *FacetRefinement) String() string { return proto.CompactTextString(m) }
+func (*FacetRefinement) ProtoMessage()    {}
 
-func (m *FacetRefine) GetName() string {
+func (m *FacetRefinement) GetName() string {
 	if m != nil && m.Name != nil {
 		return *m.Name
 	}
 	return ""
 }
 
-func (m *FacetRefine) GetType() FacetValue_ContentType {
-	if m != nil && m.Type != nil {
-		return *m.Type
-	}
-	return FacetValue_ATOM
-}
-
-func (m *FacetRefine) GetValue() string {
+func (m *FacetRefinement) GetValue() string {
 	if m != nil && m.Value != nil {
 		return *m.Value
 	}
 	return ""
 }
 
-func (m *FacetRefine) GetStart() string {
+func (m *FacetRefinement) GetRange() *FacetRefinement_Range {
+	if m != nil {
+		return m.Range
+	}
+	return nil
+}
+
+type FacetRefinement_Range struct {
+	Start            *string `protobuf:"bytes,1,opt,name=start" json:"start,omitempty"`
+	End              *string `protobuf:"bytes,2,opt,name=end" json:"end,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *FacetRefinement_Range) Reset()         { *m = FacetRefinement_Range{} }
+func (m *FacetRefinement_Range) String() string { return proto.CompactTextString(m) }
+func (*FacetRefinement_Range) ProtoMessage()    {}
+
+func (m *FacetRefinement_Range) GetStart() string {
 	if m != nil && m.Start != nil {
 		return *m.Start
 	}
 	return ""
 }
 
-func (m *FacetRefine) GetEnd() string {
+func (m *FacetRefinement_Range) GetEnd() string {
 	if m != nil && m.End != nil {
 		return *m.End
 	}
@@ -1746,8 +1805,9 @@
 	ParsingMode            *SearchParams_ParsingMode `protobuf:"varint,13,opt,name=parsing_mode,enum=search.SearchParams_ParsingMode,def=0" json:"parsing_mode,omitempty"`
 	AutoDiscoverFacetCount *int32                    `protobuf:"varint,15,opt,name=auto_discover_facet_count,def=0" json:"auto_discover_facet_count,omitempty"`
 	IncludeFacet           []*FacetRequest           `protobuf:"bytes,16,rep,name=include_facet" json:"include_facet,omitempty"`
-	FacetRefine            []*FacetRefine            `protobuf:"bytes,17,rep,name=facet_refine" json:"facet_refine,omitempty"`
+	FacetRefinement        []*FacetRefinement        `protobuf:"bytes,17,rep,name=facet_refinement" json:"facet_refinement,omitempty"`
 	FacetAutoDetectParam   *FacetAutoDetectParam     `protobuf:"bytes,18,opt,name=facet_auto_detect_param" json:"facet_auto_detect_param,omitempty"`
+	FacetDepth             *int32                    `protobuf:"varint,19,opt,name=facet_depth,def=1000" json:"facet_depth,omitempty"`
 	XXX_unrecognized       []byte                    `json:"-"`
 }
 
@@ -1759,6 +1819,7 @@
 const Default_SearchParams_Limit int32 = 20
 const Default_SearchParams_ParsingMode SearchParams_ParsingMode = SearchParams_STRICT
 const Default_SearchParams_AutoDiscoverFacetCount int32 = 0
+const Default_SearchParams_FacetDepth int32 = 1000
 
 func (m *SearchParams) GetIndexSpec() *IndexSpec {
 	if m != nil {
@@ -1858,9 +1919,9 @@
 	return nil
 }
 
-func (m *SearchParams) GetFacetRefine() []*FacetRefine {
+func (m *SearchParams) GetFacetRefinement() []*FacetRefinement {
 	if m != nil {
-		return m.FacetRefine
+		return m.FacetRefinement
 	}
 	return nil
 }
@@ -1872,6 +1933,13 @@
 	return nil
 }
 
+func (m *SearchParams) GetFacetDepth() int32 {
+	if m != nil && m.FacetDepth != nil {
+		return *m.FacetDepth
+	}
+	return Default_SearchParams_FacetDepth
+}
+
 type SearchRequest struct {
 	Params           *SearchParams `protobuf:"bytes,1,req,name=params" json:"params,omitempty"`
 	AppId            []byte        `protobuf:"bytes,3,opt,name=app_id" json:"app_id,omitempty"`
@@ -1897,9 +1965,10 @@
 }
 
 type FacetResultValue struct {
-	Name             *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
-	Count            *int32  `protobuf:"varint,2,req,name=count" json:"count,omitempty"`
-	XXX_unrecognized []byte  `json:"-"`
+	Name             *string          `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+	Count            *int32           `protobuf:"varint,2,req,name=count" json:"count,omitempty"`
+	Refinement       *FacetRefinement `protobuf:"bytes,3,req,name=refinement" json:"refinement,omitempty"`
+	XXX_unrecognized []byte           `json:"-"`
 }
 
 func (m *FacetResultValue) Reset()         { *m = FacetResultValue{} }
@@ -1920,11 +1989,17 @@
 	return 0
 }
 
+func (m *FacetResultValue) GetRefinement() *FacetRefinement {
+	if m != nil {
+		return m.Refinement
+	}
+	return nil
+}
+
 type FacetResult struct {
-	Name             *string                 `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
-	Type             *FacetValue_ContentType `protobuf:"varint,2,req,name=type,enum=search.FacetValue_ContentType" json:"type,omitempty"`
-	Value            []*FacetResultValue     `protobuf:"bytes,3,rep,name=value" json:"value,omitempty"`
-	XXX_unrecognized []byte                  `json:"-"`
+	Name             *string             `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+	Value            []*FacetResultValue `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte              `json:"-"`
 }
 
 func (m *FacetResult) Reset()         { *m = FacetResult{} }
@@ -1938,13 +2013,6 @@
 	return ""
 }
 
-func (m *FacetResult) GetType() FacetValue_ContentType {
-	if m != nil && m.Type != nil {
-		return *m.Type
-	}
-	return FacetValue_ATOM
-}
-
 func (m *FacetResult) GetValue() []*FacetResultValue {
 	if m != nil {
 		return m.Value
diff --git a/go/src/google.golang.org/appengine/internal/search/search.proto b/go/src/google.golang.org/appengine/internal/search/search.proto
index 6299841..219f4c3 100644
--- a/go/src/google.golang.org/appengine/internal/search/search.proto
+++ b/go/src/google.golang.org/appengine/internal/search/search.proto
@@ -67,10 +67,16 @@
   repeated FieldValue.ContentType type = 2;
 }
 
+message IndexShardSettings {
+  repeated int32 prev_num_shards = 1;
+  required int32 num_shards = 2 [default=1];
+  repeated int32 prev_num_shards_search_false = 3;
+  optional string local_replica = 4 [default = ""];
+}
+
 message FacetValue {
   enum ContentType {
     ATOM = 2;
-    DATE = 3;
     NUMBER = 4;
   }
 
@@ -83,6 +89,10 @@
   required FacetValue value = 2;
 }
 
+message DocumentMetadata  {
+  optional int64 version = 1;
+  optional int64 committed_st_version = 2;
+}
 
 message Document {
   optional string id = 1;
@@ -95,8 +105,6 @@
   }
 
   optional Storage storage = 5 [default = DISK];
-  optional AccessControlList acl = 6;
-  optional int64 version = 7;
   repeated Facet facet = 8;
 }
 
@@ -115,6 +123,7 @@
 message RequestStatus {
   required SearchServiceError.ErrorCode code = 1;
   optional string error_detail = 2;
+  optional int32 canonical_code = 3;
 }
 
 message IndexSpec {
@@ -297,16 +306,18 @@
 
 message FacetRequest {
   required string name = 1;
-  required FacetValue.ContentType type = 2;
-  optional FacetRequestParam params = 3;
+  optional FacetRequestParam params = 2;
 }
 
-message FacetRefine {
+message FacetRefinement {
   required string name = 1;
-  required FacetValue.ContentType type = 2;
-  optional string value = 3;
-  optional string start = 4;
-  optional string end = 5;
+  optional string value = 2;
+
+  message Range {
+    optional string start = 1;
+    optional string end = 2;
+  }
+  optional Range range = 3;
 }
 
 message SearchParams {
@@ -337,8 +348,9 @@
 
   optional int32 auto_discover_facet_count = 15 [default = 0];
   repeated FacetRequest include_facet = 16;
-  repeated FacetRefine facet_refine = 17;
+  repeated FacetRefinement facet_refinement = 17;
   optional FacetAutoDetectParam facet_auto_detect_param = 18;
+  optional int32 facet_depth = 19 [default=1000];
 }
 
 message SearchRequest {
@@ -350,12 +362,12 @@
 message FacetResultValue {
   required string name = 1;
   required int32 count = 2;
+  required FacetRefinement refinement = 3;
 }
 
 message FacetResult {
   required string name = 1;
-  required FacetValue.ContentType type = 2;
-  repeated FacetResultValue value = 3;
+  repeated FacetResultValue value = 2;
 }
 
 message SearchResult {
diff --git a/go/src/google.golang.org/appengine/internal/socket/socket_service.pb.go b/go/src/google.golang.org/appengine/internal/socket/socket_service.pb.go
new file mode 100644
index 0000000..60628ec
--- /dev/null
+++ b/go/src/google.golang.org/appengine/internal/socket/socket_service.pb.go
@@ -0,0 +1,1858 @@
+// Code generated by protoc-gen-go.
+// source: google.golang.org/appengine/internal/socket/socket_service.proto
+// DO NOT EDIT!
+
+/*
+Package socket is a generated protocol buffer package.
+
+It is generated from these files:
+	google.golang.org/appengine/internal/socket/socket_service.proto
+
+It has these top-level messages:
+	RemoteSocketServiceError
+	AddressPort
+	CreateSocketRequest
+	CreateSocketReply
+	BindRequest
+	BindReply
+	GetSocketNameRequest
+	GetSocketNameReply
+	GetPeerNameRequest
+	GetPeerNameReply
+	SocketOption
+	SetSocketOptionsRequest
+	SetSocketOptionsReply
+	GetSocketOptionsRequest
+	GetSocketOptionsReply
+	ConnectRequest
+	ConnectReply
+	ListenRequest
+	ListenReply
+	AcceptRequest
+	AcceptReply
+	ShutDownRequest
+	ShutDownReply
+	CloseRequest
+	CloseReply
+	SendRequest
+	SendReply
+	ReceiveRequest
+	ReceiveReply
+	PollEvent
+	PollRequest
+	PollReply
+	ResolveRequest
+	ResolveReply
+*/
+package socket
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+type RemoteSocketServiceError_ErrorCode int32
+
+const (
+	RemoteSocketServiceError_SYSTEM_ERROR      RemoteSocketServiceError_ErrorCode = 1
+	RemoteSocketServiceError_GAI_ERROR         RemoteSocketServiceError_ErrorCode = 2
+	RemoteSocketServiceError_FAILURE           RemoteSocketServiceError_ErrorCode = 4
+	RemoteSocketServiceError_PERMISSION_DENIED RemoteSocketServiceError_ErrorCode = 5
+	RemoteSocketServiceError_INVALID_REQUEST   RemoteSocketServiceError_ErrorCode = 6
+	RemoteSocketServiceError_SOCKET_CLOSED     RemoteSocketServiceError_ErrorCode = 7
+)
+
+var RemoteSocketServiceError_ErrorCode_name = map[int32]string{
+	1: "SYSTEM_ERROR",
+	2: "GAI_ERROR",
+	4: "FAILURE",
+	5: "PERMISSION_DENIED",
+	6: "INVALID_REQUEST",
+	7: "SOCKET_CLOSED",
+}
+var RemoteSocketServiceError_ErrorCode_value = map[string]int32{
+	"SYSTEM_ERROR":      1,
+	"GAI_ERROR":         2,
+	"FAILURE":           4,
+	"PERMISSION_DENIED": 5,
+	"INVALID_REQUEST":   6,
+	"SOCKET_CLOSED":     7,
+}
+
+func (x RemoteSocketServiceError_ErrorCode) Enum() *RemoteSocketServiceError_ErrorCode {
+	p := new(RemoteSocketServiceError_ErrorCode)
+	*p = x
+	return p
+}
+func (x RemoteSocketServiceError_ErrorCode) String() string {
+	return proto.EnumName(RemoteSocketServiceError_ErrorCode_name, int32(x))
+}
+func (x *RemoteSocketServiceError_ErrorCode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(RemoteSocketServiceError_ErrorCode_value, data, "RemoteSocketServiceError_ErrorCode")
+	if err != nil {
+		return err
+	}
+	*x = RemoteSocketServiceError_ErrorCode(value)
+	return nil
+}
+
+type RemoteSocketServiceError_SystemError int32
+
+const (
+	RemoteSocketServiceError_SYS_SUCCESS         RemoteSocketServiceError_SystemError = 0
+	RemoteSocketServiceError_SYS_EPERM           RemoteSocketServiceError_SystemError = 1
+	RemoteSocketServiceError_SYS_ENOENT          RemoteSocketServiceError_SystemError = 2
+	RemoteSocketServiceError_SYS_ESRCH           RemoteSocketServiceError_SystemError = 3
+	RemoteSocketServiceError_SYS_EINTR           RemoteSocketServiceError_SystemError = 4
+	RemoteSocketServiceError_SYS_EIO             RemoteSocketServiceError_SystemError = 5
+	RemoteSocketServiceError_SYS_ENXIO           RemoteSocketServiceError_SystemError = 6
+	RemoteSocketServiceError_SYS_E2BIG           RemoteSocketServiceError_SystemError = 7
+	RemoteSocketServiceError_SYS_ENOEXEC         RemoteSocketServiceError_SystemError = 8
+	RemoteSocketServiceError_SYS_EBADF           RemoteSocketServiceError_SystemError = 9
+	RemoteSocketServiceError_SYS_ECHILD          RemoteSocketServiceError_SystemError = 10
+	RemoteSocketServiceError_SYS_EAGAIN          RemoteSocketServiceError_SystemError = 11
+	RemoteSocketServiceError_SYS_EWOULDBLOCK     RemoteSocketServiceError_SystemError = 11
+	RemoteSocketServiceError_SYS_ENOMEM          RemoteSocketServiceError_SystemError = 12
+	RemoteSocketServiceError_SYS_EACCES          RemoteSocketServiceError_SystemError = 13
+	RemoteSocketServiceError_SYS_EFAULT          RemoteSocketServiceError_SystemError = 14
+	RemoteSocketServiceError_SYS_ENOTBLK         RemoteSocketServiceError_SystemError = 15
+	RemoteSocketServiceError_SYS_EBUSY           RemoteSocketServiceError_SystemError = 16
+	RemoteSocketServiceError_SYS_EEXIST          RemoteSocketServiceError_SystemError = 17
+	RemoteSocketServiceError_SYS_EXDEV           RemoteSocketServiceError_SystemError = 18
+	RemoteSocketServiceError_SYS_ENODEV          RemoteSocketServiceError_SystemError = 19
+	RemoteSocketServiceError_SYS_ENOTDIR         RemoteSocketServiceError_SystemError = 20
+	RemoteSocketServiceError_SYS_EISDIR          RemoteSocketServiceError_SystemError = 21
+	RemoteSocketServiceError_SYS_EINVAL          RemoteSocketServiceError_SystemError = 22
+	RemoteSocketServiceError_SYS_ENFILE          RemoteSocketServiceError_SystemError = 23
+	RemoteSocketServiceError_SYS_EMFILE          RemoteSocketServiceError_SystemError = 24
+	RemoteSocketServiceError_SYS_ENOTTY          RemoteSocketServiceError_SystemError = 25
+	RemoteSocketServiceError_SYS_ETXTBSY         RemoteSocketServiceError_SystemError = 26
+	RemoteSocketServiceError_SYS_EFBIG           RemoteSocketServiceError_SystemError = 27
+	RemoteSocketServiceError_SYS_ENOSPC          RemoteSocketServiceError_SystemError = 28
+	RemoteSocketServiceError_SYS_ESPIPE          RemoteSocketServiceError_SystemError = 29
+	RemoteSocketServiceError_SYS_EROFS           RemoteSocketServiceError_SystemError = 30
+	RemoteSocketServiceError_SYS_EMLINK          RemoteSocketServiceError_SystemError = 31
+	RemoteSocketServiceError_SYS_EPIPE           RemoteSocketServiceError_SystemError = 32
+	RemoteSocketServiceError_SYS_EDOM            RemoteSocketServiceError_SystemError = 33
+	RemoteSocketServiceError_SYS_ERANGE          RemoteSocketServiceError_SystemError = 34
+	RemoteSocketServiceError_SYS_EDEADLK         RemoteSocketServiceError_SystemError = 35
+	RemoteSocketServiceError_SYS_EDEADLOCK       RemoteSocketServiceError_SystemError = 35
+	RemoteSocketServiceError_SYS_ENAMETOOLONG    RemoteSocketServiceError_SystemError = 36
+	RemoteSocketServiceError_SYS_ENOLCK          RemoteSocketServiceError_SystemError = 37
+	RemoteSocketServiceError_SYS_ENOSYS          RemoteSocketServiceError_SystemError = 38
+	RemoteSocketServiceError_SYS_ENOTEMPTY       RemoteSocketServiceError_SystemError = 39
+	RemoteSocketServiceError_SYS_ELOOP           RemoteSocketServiceError_SystemError = 40
+	RemoteSocketServiceError_SYS_ENOMSG          RemoteSocketServiceError_SystemError = 42
+	RemoteSocketServiceError_SYS_EIDRM           RemoteSocketServiceError_SystemError = 43
+	RemoteSocketServiceError_SYS_ECHRNG          RemoteSocketServiceError_SystemError = 44
+	RemoteSocketServiceError_SYS_EL2NSYNC        RemoteSocketServiceError_SystemError = 45
+	RemoteSocketServiceError_SYS_EL3HLT          RemoteSocketServiceError_SystemError = 46
+	RemoteSocketServiceError_SYS_EL3RST          RemoteSocketServiceError_SystemError = 47
+	RemoteSocketServiceError_SYS_ELNRNG          RemoteSocketServiceError_SystemError = 48
+	RemoteSocketServiceError_SYS_EUNATCH         RemoteSocketServiceError_SystemError = 49
+	RemoteSocketServiceError_SYS_ENOCSI          RemoteSocketServiceError_SystemError = 50
+	RemoteSocketServiceError_SYS_EL2HLT          RemoteSocketServiceError_SystemError = 51
+	RemoteSocketServiceError_SYS_EBADE           RemoteSocketServiceError_SystemError = 52
+	RemoteSocketServiceError_SYS_EBADR           RemoteSocketServiceError_SystemError = 53
+	RemoteSocketServiceError_SYS_EXFULL          RemoteSocketServiceError_SystemError = 54
+	RemoteSocketServiceError_SYS_ENOANO          RemoteSocketServiceError_SystemError = 55
+	RemoteSocketServiceError_SYS_EBADRQC         RemoteSocketServiceError_SystemError = 56
+	RemoteSocketServiceError_SYS_EBADSLT         RemoteSocketServiceError_SystemError = 57
+	RemoteSocketServiceError_SYS_EBFONT          RemoteSocketServiceError_SystemError = 59
+	RemoteSocketServiceError_SYS_ENOSTR          RemoteSocketServiceError_SystemError = 60
+	RemoteSocketServiceError_SYS_ENODATA         RemoteSocketServiceError_SystemError = 61
+	RemoteSocketServiceError_SYS_ETIME           RemoteSocketServiceError_SystemError = 62
+	RemoteSocketServiceError_SYS_ENOSR           RemoteSocketServiceError_SystemError = 63
+	RemoteSocketServiceError_SYS_ENONET          RemoteSocketServiceError_SystemError = 64
+	RemoteSocketServiceError_SYS_ENOPKG          RemoteSocketServiceError_SystemError = 65
+	RemoteSocketServiceError_SYS_EREMOTE         RemoteSocketServiceError_SystemError = 66
+	RemoteSocketServiceError_SYS_ENOLINK         RemoteSocketServiceError_SystemError = 67
+	RemoteSocketServiceError_SYS_EADV            RemoteSocketServiceError_SystemError = 68
+	RemoteSocketServiceError_SYS_ESRMNT          RemoteSocketServiceError_SystemError = 69
+	RemoteSocketServiceError_SYS_ECOMM           RemoteSocketServiceError_SystemError = 70
+	RemoteSocketServiceError_SYS_EPROTO          RemoteSocketServiceError_SystemError = 71
+	RemoteSocketServiceError_SYS_EMULTIHOP       RemoteSocketServiceError_SystemError = 72
+	RemoteSocketServiceError_SYS_EDOTDOT         RemoteSocketServiceError_SystemError = 73
+	RemoteSocketServiceError_SYS_EBADMSG         RemoteSocketServiceError_SystemError = 74
+	RemoteSocketServiceError_SYS_EOVERFLOW       RemoteSocketServiceError_SystemError = 75
+	RemoteSocketServiceError_SYS_ENOTUNIQ        RemoteSocketServiceError_SystemError = 76
+	RemoteSocketServiceError_SYS_EBADFD          RemoteSocketServiceError_SystemError = 77
+	RemoteSocketServiceError_SYS_EREMCHG         RemoteSocketServiceError_SystemError = 78
+	RemoteSocketServiceError_SYS_ELIBACC         RemoteSocketServiceError_SystemError = 79
+	RemoteSocketServiceError_SYS_ELIBBAD         RemoteSocketServiceError_SystemError = 80
+	RemoteSocketServiceError_SYS_ELIBSCN         RemoteSocketServiceError_SystemError = 81
+	RemoteSocketServiceError_SYS_ELIBMAX         RemoteSocketServiceError_SystemError = 82
+	RemoteSocketServiceError_SYS_ELIBEXEC        RemoteSocketServiceError_SystemError = 83
+	RemoteSocketServiceError_SYS_EILSEQ          RemoteSocketServiceError_SystemError = 84
+	RemoteSocketServiceError_SYS_ERESTART        RemoteSocketServiceError_SystemError = 85
+	RemoteSocketServiceError_SYS_ESTRPIPE        RemoteSocketServiceError_SystemError = 86
+	RemoteSocketServiceError_SYS_EUSERS          RemoteSocketServiceError_SystemError = 87
+	RemoteSocketServiceError_SYS_ENOTSOCK        RemoteSocketServiceError_SystemError = 88
+	RemoteSocketServiceError_SYS_EDESTADDRREQ    RemoteSocketServiceError_SystemError = 89
+	RemoteSocketServiceError_SYS_EMSGSIZE        RemoteSocketServiceError_SystemError = 90
+	RemoteSocketServiceError_SYS_EPROTOTYPE      RemoteSocketServiceError_SystemError = 91
+	RemoteSocketServiceError_SYS_ENOPROTOOPT     RemoteSocketServiceError_SystemError = 92
+	RemoteSocketServiceError_SYS_EPROTONOSUPPORT RemoteSocketServiceError_SystemError = 93
+	RemoteSocketServiceError_SYS_ESOCKTNOSUPPORT RemoteSocketServiceError_SystemError = 94
+	RemoteSocketServiceError_SYS_EOPNOTSUPP      RemoteSocketServiceError_SystemError = 95
+	RemoteSocketServiceError_SYS_ENOTSUP         RemoteSocketServiceError_SystemError = 95
+	RemoteSocketServiceError_SYS_EPFNOSUPPORT    RemoteSocketServiceError_SystemError = 96
+	RemoteSocketServiceError_SYS_EAFNOSUPPORT    RemoteSocketServiceError_SystemError = 97
+	RemoteSocketServiceError_SYS_EADDRINUSE      RemoteSocketServiceError_SystemError = 98
+	RemoteSocketServiceError_SYS_EADDRNOTAVAIL   RemoteSocketServiceError_SystemError = 99
+	RemoteSocketServiceError_SYS_ENETDOWN        RemoteSocketServiceError_SystemError = 100
+	RemoteSocketServiceError_SYS_ENETUNREACH     RemoteSocketServiceError_SystemError = 101
+	RemoteSocketServiceError_SYS_ENETRESET       RemoteSocketServiceError_SystemError = 102
+	RemoteSocketServiceError_SYS_ECONNABORTED    RemoteSocketServiceError_SystemError = 103
+	RemoteSocketServiceError_SYS_ECONNRESET      RemoteSocketServiceError_SystemError = 104
+	RemoteSocketServiceError_SYS_ENOBUFS         RemoteSocketServiceError_SystemError = 105
+	RemoteSocketServiceError_SYS_EISCONN         RemoteSocketServiceError_SystemError = 106
+	RemoteSocketServiceError_SYS_ENOTCONN        RemoteSocketServiceError_SystemError = 107
+	RemoteSocketServiceError_SYS_ESHUTDOWN       RemoteSocketServiceError_SystemError = 108
+	RemoteSocketServiceError_SYS_ETOOMANYREFS    RemoteSocketServiceError_SystemError = 109
+	RemoteSocketServiceError_SYS_ETIMEDOUT       RemoteSocketServiceError_SystemError = 110
+	RemoteSocketServiceError_SYS_ECONNREFUSED    RemoteSocketServiceError_SystemError = 111
+	RemoteSocketServiceError_SYS_EHOSTDOWN       RemoteSocketServiceError_SystemError = 112
+	RemoteSocketServiceError_SYS_EHOSTUNREACH    RemoteSocketServiceError_SystemError = 113
+	RemoteSocketServiceError_SYS_EALREADY        RemoteSocketServiceError_SystemError = 114
+	RemoteSocketServiceError_SYS_EINPROGRESS     RemoteSocketServiceError_SystemError = 115
+	RemoteSocketServiceError_SYS_ESTALE          RemoteSocketServiceError_SystemError = 116
+	RemoteSocketServiceError_SYS_EUCLEAN         RemoteSocketServiceError_SystemError = 117
+	RemoteSocketServiceError_SYS_ENOTNAM         RemoteSocketServiceError_SystemError = 118
+	RemoteSocketServiceError_SYS_ENAVAIL         RemoteSocketServiceError_SystemError = 119
+	RemoteSocketServiceError_SYS_EISNAM          RemoteSocketServiceError_SystemError = 120
+	RemoteSocketServiceError_SYS_EREMOTEIO       RemoteSocketServiceError_SystemError = 121
+	RemoteSocketServiceError_SYS_EDQUOT          RemoteSocketServiceError_SystemError = 122
+	RemoteSocketServiceError_SYS_ENOMEDIUM       RemoteSocketServiceError_SystemError = 123
+	RemoteSocketServiceError_SYS_EMEDIUMTYPE     RemoteSocketServiceError_SystemError = 124
+	RemoteSocketServiceError_SYS_ECANCELED       RemoteSocketServiceError_SystemError = 125
+	RemoteSocketServiceError_SYS_ENOKEY          RemoteSocketServiceError_SystemError = 126
+	RemoteSocketServiceError_SYS_EKEYEXPIRED     RemoteSocketServiceError_SystemError = 127
+	RemoteSocketServiceError_SYS_EKEYREVOKED     RemoteSocketServiceError_SystemError = 128
+	RemoteSocketServiceError_SYS_EKEYREJECTED    RemoteSocketServiceError_SystemError = 129
+	RemoteSocketServiceError_SYS_EOWNERDEAD      RemoteSocketServiceError_SystemError = 130
+	RemoteSocketServiceError_SYS_ENOTRECOVERABLE RemoteSocketServiceError_SystemError = 131
+	RemoteSocketServiceError_SYS_ERFKILL         RemoteSocketServiceError_SystemError = 132
+)
+
+var RemoteSocketServiceError_SystemError_name = map[int32]string{
+	0:  "SYS_SUCCESS",
+	1:  "SYS_EPERM",
+	2:  "SYS_ENOENT",
+	3:  "SYS_ESRCH",
+	4:  "SYS_EINTR",
+	5:  "SYS_EIO",
+	6:  "SYS_ENXIO",
+	7:  "SYS_E2BIG",
+	8:  "SYS_ENOEXEC",
+	9:  "SYS_EBADF",
+	10: "SYS_ECHILD",
+	11: "SYS_EAGAIN",
+	// Duplicate value: 11: "SYS_EWOULDBLOCK",
+	12: "SYS_ENOMEM",
+	13: "SYS_EACCES",
+	14: "SYS_EFAULT",
+	15: "SYS_ENOTBLK",
+	16: "SYS_EBUSY",
+	17: "SYS_EEXIST",
+	18: "SYS_EXDEV",
+	19: "SYS_ENODEV",
+	20: "SYS_ENOTDIR",
+	21: "SYS_EISDIR",
+	22: "SYS_EINVAL",
+	23: "SYS_ENFILE",
+	24: "SYS_EMFILE",
+	25: "SYS_ENOTTY",
+	26: "SYS_ETXTBSY",
+	27: "SYS_EFBIG",
+	28: "SYS_ENOSPC",
+	29: "SYS_ESPIPE",
+	30: "SYS_EROFS",
+	31: "SYS_EMLINK",
+	32: "SYS_EPIPE",
+	33: "SYS_EDOM",
+	34: "SYS_ERANGE",
+	35: "SYS_EDEADLK",
+	// Duplicate value: 35: "SYS_EDEADLOCK",
+	36: "SYS_ENAMETOOLONG",
+	37: "SYS_ENOLCK",
+	38: "SYS_ENOSYS",
+	39: "SYS_ENOTEMPTY",
+	40: "SYS_ELOOP",
+	42: "SYS_ENOMSG",
+	43: "SYS_EIDRM",
+	44: "SYS_ECHRNG",
+	45: "SYS_EL2NSYNC",
+	46: "SYS_EL3HLT",
+	47: "SYS_EL3RST",
+	48: "SYS_ELNRNG",
+	49: "SYS_EUNATCH",
+	50: "SYS_ENOCSI",
+	51: "SYS_EL2HLT",
+	52: "SYS_EBADE",
+	53: "SYS_EBADR",
+	54: "SYS_EXFULL",
+	55: "SYS_ENOANO",
+	56: "SYS_EBADRQC",
+	57: "SYS_EBADSLT",
+	59: "SYS_EBFONT",
+	60: "SYS_ENOSTR",
+	61: "SYS_ENODATA",
+	62: "SYS_ETIME",
+	63: "SYS_ENOSR",
+	64: "SYS_ENONET",
+	65: "SYS_ENOPKG",
+	66: "SYS_EREMOTE",
+	67: "SYS_ENOLINK",
+	68: "SYS_EADV",
+	69: "SYS_ESRMNT",
+	70: "SYS_ECOMM",
+	71: "SYS_EPROTO",
+	72: "SYS_EMULTIHOP",
+	73: "SYS_EDOTDOT",
+	74: "SYS_EBADMSG",
+	75: "SYS_EOVERFLOW",
+	76: "SYS_ENOTUNIQ",
+	77: "SYS_EBADFD",
+	78: "SYS_EREMCHG",
+	79: "SYS_ELIBACC",
+	80: "SYS_ELIBBAD",
+	81: "SYS_ELIBSCN",
+	82: "SYS_ELIBMAX",
+	83: "SYS_ELIBEXEC",
+	84: "SYS_EILSEQ",
+	85: "SYS_ERESTART",
+	86: "SYS_ESTRPIPE",
+	87: "SYS_EUSERS",
+	88: "SYS_ENOTSOCK",
+	89: "SYS_EDESTADDRREQ",
+	90: "SYS_EMSGSIZE",
+	91: "SYS_EPROTOTYPE",
+	92: "SYS_ENOPROTOOPT",
+	93: "SYS_EPROTONOSUPPORT",
+	94: "SYS_ESOCKTNOSUPPORT",
+	95: "SYS_EOPNOTSUPP",
+	// Duplicate value: 95: "SYS_ENOTSUP",
+	96:  "SYS_EPFNOSUPPORT",
+	97:  "SYS_EAFNOSUPPORT",
+	98:  "SYS_EADDRINUSE",
+	99:  "SYS_EADDRNOTAVAIL",
+	100: "SYS_ENETDOWN",
+	101: "SYS_ENETUNREACH",
+	102: "SYS_ENETRESET",
+	103: "SYS_ECONNABORTED",
+	104: "SYS_ECONNRESET",
+	105: "SYS_ENOBUFS",
+	106: "SYS_EISCONN",
+	107: "SYS_ENOTCONN",
+	108: "SYS_ESHUTDOWN",
+	109: "SYS_ETOOMANYREFS",
+	110: "SYS_ETIMEDOUT",
+	111: "SYS_ECONNREFUSED",
+	112: "SYS_EHOSTDOWN",
+	113: "SYS_EHOSTUNREACH",
+	114: "SYS_EALREADY",
+	115: "SYS_EINPROGRESS",
+	116: "SYS_ESTALE",
+	117: "SYS_EUCLEAN",
+	118: "SYS_ENOTNAM",
+	119: "SYS_ENAVAIL",
+	120: "SYS_EISNAM",
+	121: "SYS_EREMOTEIO",
+	122: "SYS_EDQUOT",
+	123: "SYS_ENOMEDIUM",
+	124: "SYS_EMEDIUMTYPE",
+	125: "SYS_ECANCELED",
+	126: "SYS_ENOKEY",
+	127: "SYS_EKEYEXPIRED",
+	128: "SYS_EKEYREVOKED",
+	129: "SYS_EKEYREJECTED",
+	130: "SYS_EOWNERDEAD",
+	131: "SYS_ENOTRECOVERABLE",
+	132: "SYS_ERFKILL",
+}
+var RemoteSocketServiceError_SystemError_value = map[string]int32{
+	"SYS_SUCCESS":         0,
+	"SYS_EPERM":           1,
+	"SYS_ENOENT":          2,
+	"SYS_ESRCH":           3,
+	"SYS_EINTR":           4,
+	"SYS_EIO":             5,
+	"SYS_ENXIO":           6,
+	"SYS_E2BIG":           7,
+	"SYS_ENOEXEC":         8,
+	"SYS_EBADF":           9,
+	"SYS_ECHILD":          10,
+	"SYS_EAGAIN":          11,
+	"SYS_EWOULDBLOCK":     11,
+	"SYS_ENOMEM":          12,
+	"SYS_EACCES":          13,
+	"SYS_EFAULT":          14,
+	"SYS_ENOTBLK":         15,
+	"SYS_EBUSY":           16,
+	"SYS_EEXIST":          17,
+	"SYS_EXDEV":           18,
+	"SYS_ENODEV":          19,
+	"SYS_ENOTDIR":         20,
+	"SYS_EISDIR":          21,
+	"SYS_EINVAL":          22,
+	"SYS_ENFILE":          23,
+	"SYS_EMFILE":          24,
+	"SYS_ENOTTY":          25,
+	"SYS_ETXTBSY":         26,
+	"SYS_EFBIG":           27,
+	"SYS_ENOSPC":          28,
+	"SYS_ESPIPE":          29,
+	"SYS_EROFS":           30,
+	"SYS_EMLINK":          31,
+	"SYS_EPIPE":           32,
+	"SYS_EDOM":            33,
+	"SYS_ERANGE":          34,
+	"SYS_EDEADLK":         35,
+	"SYS_EDEADLOCK":       35,
+	"SYS_ENAMETOOLONG":    36,
+	"SYS_ENOLCK":          37,
+	"SYS_ENOSYS":          38,
+	"SYS_ENOTEMPTY":       39,
+	"SYS_ELOOP":           40,
+	"SYS_ENOMSG":          42,
+	"SYS_EIDRM":           43,
+	"SYS_ECHRNG":          44,
+	"SYS_EL2NSYNC":        45,
+	"SYS_EL3HLT":          46,
+	"SYS_EL3RST":          47,
+	"SYS_ELNRNG":          48,
+	"SYS_EUNATCH":         49,
+	"SYS_ENOCSI":          50,
+	"SYS_EL2HLT":          51,
+	"SYS_EBADE":           52,
+	"SYS_EBADR":           53,
+	"SYS_EXFULL":          54,
+	"SYS_ENOANO":          55,
+	"SYS_EBADRQC":         56,
+	"SYS_EBADSLT":         57,
+	"SYS_EBFONT":          59,
+	"SYS_ENOSTR":          60,
+	"SYS_ENODATA":         61,
+	"SYS_ETIME":           62,
+	"SYS_ENOSR":           63,
+	"SYS_ENONET":          64,
+	"SYS_ENOPKG":          65,
+	"SYS_EREMOTE":         66,
+	"SYS_ENOLINK":         67,
+	"SYS_EADV":            68,
+	"SYS_ESRMNT":          69,
+	"SYS_ECOMM":           70,
+	"SYS_EPROTO":          71,
+	"SYS_EMULTIHOP":       72,
+	"SYS_EDOTDOT":         73,
+	"SYS_EBADMSG":         74,
+	"SYS_EOVERFLOW":       75,
+	"SYS_ENOTUNIQ":        76,
+	"SYS_EBADFD":          77,
+	"SYS_EREMCHG":         78,
+	"SYS_ELIBACC":         79,
+	"SYS_ELIBBAD":         80,
+	"SYS_ELIBSCN":         81,
+	"SYS_ELIBMAX":         82,
+	"SYS_ELIBEXEC":        83,
+	"SYS_EILSEQ":          84,
+	"SYS_ERESTART":        85,
+	"SYS_ESTRPIPE":        86,
+	"SYS_EUSERS":          87,
+	"SYS_ENOTSOCK":        88,
+	"SYS_EDESTADDRREQ":    89,
+	"SYS_EMSGSIZE":        90,
+	"SYS_EPROTOTYPE":      91,
+	"SYS_ENOPROTOOPT":     92,
+	"SYS_EPROTONOSUPPORT": 93,
+	"SYS_ESOCKTNOSUPPORT": 94,
+	"SYS_EOPNOTSUPP":      95,
+	"SYS_ENOTSUP":         95,
+	"SYS_EPFNOSUPPORT":    96,
+	"SYS_EAFNOSUPPORT":    97,
+	"SYS_EADDRINUSE":      98,
+	"SYS_EADDRNOTAVAIL":   99,
+	"SYS_ENETDOWN":        100,
+	"SYS_ENETUNREACH":     101,
+	"SYS_ENETRESET":       102,
+	"SYS_ECONNABORTED":    103,
+	"SYS_ECONNRESET":      104,
+	"SYS_ENOBUFS":         105,
+	"SYS_EISCONN":         106,
+	"SYS_ENOTCONN":        107,
+	"SYS_ESHUTDOWN":       108,
+	"SYS_ETOOMANYREFS":    109,
+	"SYS_ETIMEDOUT":       110,
+	"SYS_ECONNREFUSED":    111,
+	"SYS_EHOSTDOWN":       112,
+	"SYS_EHOSTUNREACH":    113,
+	"SYS_EALREADY":        114,
+	"SYS_EINPROGRESS":     115,
+	"SYS_ESTALE":          116,
+	"SYS_EUCLEAN":         117,
+	"SYS_ENOTNAM":         118,
+	"SYS_ENAVAIL":         119,
+	"SYS_EISNAM":          120,
+	"SYS_EREMOTEIO":       121,
+	"SYS_EDQUOT":          122,
+	"SYS_ENOMEDIUM":       123,
+	"SYS_EMEDIUMTYPE":     124,
+	"SYS_ECANCELED":       125,
+	"SYS_ENOKEY":          126,
+	"SYS_EKEYEXPIRED":     127,
+	"SYS_EKEYREVOKED":     128,
+	"SYS_EKEYREJECTED":    129,
+	"SYS_EOWNERDEAD":      130,
+	"SYS_ENOTRECOVERABLE": 131,
+	"SYS_ERFKILL":         132,
+}
+
+func (x RemoteSocketServiceError_SystemError) Enum() *RemoteSocketServiceError_SystemError {
+	p := new(RemoteSocketServiceError_SystemError)
+	*p = x
+	return p
+}
+func (x RemoteSocketServiceError_SystemError) String() string {
+	return proto.EnumName(RemoteSocketServiceError_SystemError_name, int32(x))
+}
+func (x *RemoteSocketServiceError_SystemError) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(RemoteSocketServiceError_SystemError_value, data, "RemoteSocketServiceError_SystemError")
+	if err != nil {
+		return err
+	}
+	*x = RemoteSocketServiceError_SystemError(value)
+	return nil
+}
+
+type CreateSocketRequest_SocketFamily int32
+
+const (
+	CreateSocketRequest_IPv4 CreateSocketRequest_SocketFamily = 1
+	CreateSocketRequest_IPv6 CreateSocketRequest_SocketFamily = 2
+)
+
+var CreateSocketRequest_SocketFamily_name = map[int32]string{
+	1: "IPv4",
+	2: "IPv6",
+}
+var CreateSocketRequest_SocketFamily_value = map[string]int32{
+	"IPv4": 1,
+	"IPv6": 2,
+}
+
+func (x CreateSocketRequest_SocketFamily) Enum() *CreateSocketRequest_SocketFamily {
+	p := new(CreateSocketRequest_SocketFamily)
+	*p = x
+	return p
+}
+func (x CreateSocketRequest_SocketFamily) String() string {
+	return proto.EnumName(CreateSocketRequest_SocketFamily_name, int32(x))
+}
+func (x *CreateSocketRequest_SocketFamily) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(CreateSocketRequest_SocketFamily_value, data, "CreateSocketRequest_SocketFamily")
+	if err != nil {
+		return err
+	}
+	*x = CreateSocketRequest_SocketFamily(value)
+	return nil
+}
+
+type CreateSocketRequest_SocketProtocol int32
+
+const (
+	CreateSocketRequest_TCP CreateSocketRequest_SocketProtocol = 1
+	CreateSocketRequest_UDP CreateSocketRequest_SocketProtocol = 2
+)
+
+var CreateSocketRequest_SocketProtocol_name = map[int32]string{
+	1: "TCP",
+	2: "UDP",
+}
+var CreateSocketRequest_SocketProtocol_value = map[string]int32{
+	"TCP": 1,
+	"UDP": 2,
+}
+
+func (x CreateSocketRequest_SocketProtocol) Enum() *CreateSocketRequest_SocketProtocol {
+	p := new(CreateSocketRequest_SocketProtocol)
+	*p = x
+	return p
+}
+func (x CreateSocketRequest_SocketProtocol) String() string {
+	return proto.EnumName(CreateSocketRequest_SocketProtocol_name, int32(x))
+}
+func (x *CreateSocketRequest_SocketProtocol) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(CreateSocketRequest_SocketProtocol_value, data, "CreateSocketRequest_SocketProtocol")
+	if err != nil {
+		return err
+	}
+	*x = CreateSocketRequest_SocketProtocol(value)
+	return nil
+}
+
+type SocketOption_SocketOptionLevel int32
+
+const (
+	SocketOption_SOCKET_SOL_IP     SocketOption_SocketOptionLevel = 0
+	SocketOption_SOCKET_SOL_SOCKET SocketOption_SocketOptionLevel = 1
+	SocketOption_SOCKET_SOL_TCP    SocketOption_SocketOptionLevel = 6
+	SocketOption_SOCKET_SOL_UDP    SocketOption_SocketOptionLevel = 17
+)
+
+var SocketOption_SocketOptionLevel_name = map[int32]string{
+	0:  "SOCKET_SOL_IP",
+	1:  "SOCKET_SOL_SOCKET",
+	6:  "SOCKET_SOL_TCP",
+	17: "SOCKET_SOL_UDP",
+}
+var SocketOption_SocketOptionLevel_value = map[string]int32{
+	"SOCKET_SOL_IP":     0,
+	"SOCKET_SOL_SOCKET": 1,
+	"SOCKET_SOL_TCP":    6,
+	"SOCKET_SOL_UDP":    17,
+}
+
+func (x SocketOption_SocketOptionLevel) Enum() *SocketOption_SocketOptionLevel {
+	p := new(SocketOption_SocketOptionLevel)
+	*p = x
+	return p
+}
+func (x SocketOption_SocketOptionLevel) String() string {
+	return proto.EnumName(SocketOption_SocketOptionLevel_name, int32(x))
+}
+func (x *SocketOption_SocketOptionLevel) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(SocketOption_SocketOptionLevel_value, data, "SocketOption_SocketOptionLevel")
+	if err != nil {
+		return err
+	}
+	*x = SocketOption_SocketOptionLevel(value)
+	return nil
+}
+
+type SocketOption_SocketOptionName int32
+
+const (
+	SocketOption_SOCKET_SO_DEBUG         SocketOption_SocketOptionName = 1
+	SocketOption_SOCKET_SO_REUSEADDR     SocketOption_SocketOptionName = 2
+	SocketOption_SOCKET_SO_TYPE          SocketOption_SocketOptionName = 3
+	SocketOption_SOCKET_SO_ERROR         SocketOption_SocketOptionName = 4
+	SocketOption_SOCKET_SO_DONTROUTE     SocketOption_SocketOptionName = 5
+	SocketOption_SOCKET_SO_BROADCAST     SocketOption_SocketOptionName = 6
+	SocketOption_SOCKET_SO_SNDBUF        SocketOption_SocketOptionName = 7
+	SocketOption_SOCKET_SO_RCVBUF        SocketOption_SocketOptionName = 8
+	SocketOption_SOCKET_SO_KEEPALIVE     SocketOption_SocketOptionName = 9
+	SocketOption_SOCKET_SO_OOBINLINE     SocketOption_SocketOptionName = 10
+	SocketOption_SOCKET_SO_LINGER        SocketOption_SocketOptionName = 13
+	SocketOption_SOCKET_SO_RCVTIMEO      SocketOption_SocketOptionName = 20
+	SocketOption_SOCKET_SO_SNDTIMEO      SocketOption_SocketOptionName = 21
+	SocketOption_SOCKET_IP_TOS           SocketOption_SocketOptionName = 1
+	SocketOption_SOCKET_IP_TTL           SocketOption_SocketOptionName = 2
+	SocketOption_SOCKET_IP_HDRINCL       SocketOption_SocketOptionName = 3
+	SocketOption_SOCKET_IP_OPTIONS       SocketOption_SocketOptionName = 4
+	SocketOption_SOCKET_TCP_NODELAY      SocketOption_SocketOptionName = 1
+	SocketOption_SOCKET_TCP_MAXSEG       SocketOption_SocketOptionName = 2
+	SocketOption_SOCKET_TCP_CORK         SocketOption_SocketOptionName = 3
+	SocketOption_SOCKET_TCP_KEEPIDLE     SocketOption_SocketOptionName = 4
+	SocketOption_SOCKET_TCP_KEEPINTVL    SocketOption_SocketOptionName = 5
+	SocketOption_SOCKET_TCP_KEEPCNT      SocketOption_SocketOptionName = 6
+	SocketOption_SOCKET_TCP_SYNCNT       SocketOption_SocketOptionName = 7
+	SocketOption_SOCKET_TCP_LINGER2      SocketOption_SocketOptionName = 8
+	SocketOption_SOCKET_TCP_DEFER_ACCEPT SocketOption_SocketOptionName = 9
+	SocketOption_SOCKET_TCP_WINDOW_CLAMP SocketOption_SocketOptionName = 10
+	SocketOption_SOCKET_TCP_INFO         SocketOption_SocketOptionName = 11
+	SocketOption_SOCKET_TCP_QUICKACK     SocketOption_SocketOptionName = 12
+)
+
+var SocketOption_SocketOptionName_name = map[int32]string{
+	1:  "SOCKET_SO_DEBUG",
+	2:  "SOCKET_SO_REUSEADDR",
+	3:  "SOCKET_SO_TYPE",
+	4:  "SOCKET_SO_ERROR",
+	5:  "SOCKET_SO_DONTROUTE",
+	6:  "SOCKET_SO_BROADCAST",
+	7:  "SOCKET_SO_SNDBUF",
+	8:  "SOCKET_SO_RCVBUF",
+	9:  "SOCKET_SO_KEEPALIVE",
+	10: "SOCKET_SO_OOBINLINE",
+	13: "SOCKET_SO_LINGER",
+	20: "SOCKET_SO_RCVTIMEO",
+	21: "SOCKET_SO_SNDTIMEO",
+	// Duplicate value: 1: "SOCKET_IP_TOS",
+	// Duplicate value: 2: "SOCKET_IP_TTL",
+	// Duplicate value: 3: "SOCKET_IP_HDRINCL",
+	// Duplicate value: 4: "SOCKET_IP_OPTIONS",
+	// Duplicate value: 1: "SOCKET_TCP_NODELAY",
+	// Duplicate value: 2: "SOCKET_TCP_MAXSEG",
+	// Duplicate value: 3: "SOCKET_TCP_CORK",
+	// Duplicate value: 4: "SOCKET_TCP_KEEPIDLE",
+	// Duplicate value: 5: "SOCKET_TCP_KEEPINTVL",
+	// Duplicate value: 6: "SOCKET_TCP_KEEPCNT",
+	// Duplicate value: 7: "SOCKET_TCP_SYNCNT",
+	// Duplicate value: 8: "SOCKET_TCP_LINGER2",
+	// Duplicate value: 9: "SOCKET_TCP_DEFER_ACCEPT",
+	// Duplicate value: 10: "SOCKET_TCP_WINDOW_CLAMP",
+	11: "SOCKET_TCP_INFO",
+	12: "SOCKET_TCP_QUICKACK",
+}
+var SocketOption_SocketOptionName_value = map[string]int32{
+	"SOCKET_SO_DEBUG":         1,
+	"SOCKET_SO_REUSEADDR":     2,
+	"SOCKET_SO_TYPE":          3,
+	"SOCKET_SO_ERROR":         4,
+	"SOCKET_SO_DONTROUTE":     5,
+	"SOCKET_SO_BROADCAST":     6,
+	"SOCKET_SO_SNDBUF":        7,
+	"SOCKET_SO_RCVBUF":        8,
+	"SOCKET_SO_KEEPALIVE":     9,
+	"SOCKET_SO_OOBINLINE":     10,
+	"SOCKET_SO_LINGER":        13,
+	"SOCKET_SO_RCVTIMEO":      20,
+	"SOCKET_SO_SNDTIMEO":      21,
+	"SOCKET_IP_TOS":           1,
+	"SOCKET_IP_TTL":           2,
+	"SOCKET_IP_HDRINCL":       3,
+	"SOCKET_IP_OPTIONS":       4,
+	"SOCKET_TCP_NODELAY":      1,
+	"SOCKET_TCP_MAXSEG":       2,
+	"SOCKET_TCP_CORK":         3,
+	"SOCKET_TCP_KEEPIDLE":     4,
+	"SOCKET_TCP_KEEPINTVL":    5,
+	"SOCKET_TCP_KEEPCNT":      6,
+	"SOCKET_TCP_SYNCNT":       7,
+	"SOCKET_TCP_LINGER2":      8,
+	"SOCKET_TCP_DEFER_ACCEPT": 9,
+	"SOCKET_TCP_WINDOW_CLAMP": 10,
+	"SOCKET_TCP_INFO":         11,
+	"SOCKET_TCP_QUICKACK":     12,
+}
+
+func (x SocketOption_SocketOptionName) Enum() *SocketOption_SocketOptionName {
+	p := new(SocketOption_SocketOptionName)
+	*p = x
+	return p
+}
+func (x SocketOption_SocketOptionName) String() string {
+	return proto.EnumName(SocketOption_SocketOptionName_name, int32(x))
+}
+func (x *SocketOption_SocketOptionName) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(SocketOption_SocketOptionName_value, data, "SocketOption_SocketOptionName")
+	if err != nil {
+		return err
+	}
+	*x = SocketOption_SocketOptionName(value)
+	return nil
+}
+
+type ShutDownRequest_How int32
+
+const (
+	ShutDownRequest_SOCKET_SHUT_RD   ShutDownRequest_How = 1
+	ShutDownRequest_SOCKET_SHUT_WR   ShutDownRequest_How = 2
+	ShutDownRequest_SOCKET_SHUT_RDWR ShutDownRequest_How = 3
+)
+
+var ShutDownRequest_How_name = map[int32]string{
+	1: "SOCKET_SHUT_RD",
+	2: "SOCKET_SHUT_WR",
+	3: "SOCKET_SHUT_RDWR",
+}
+var ShutDownRequest_How_value = map[string]int32{
+	"SOCKET_SHUT_RD":   1,
+	"SOCKET_SHUT_WR":   2,
+	"SOCKET_SHUT_RDWR": 3,
+}
+
+func (x ShutDownRequest_How) Enum() *ShutDownRequest_How {
+	p := new(ShutDownRequest_How)
+	*p = x
+	return p
+}
+func (x ShutDownRequest_How) String() string {
+	return proto.EnumName(ShutDownRequest_How_name, int32(x))
+}
+func (x *ShutDownRequest_How) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(ShutDownRequest_How_value, data, "ShutDownRequest_How")
+	if err != nil {
+		return err
+	}
+	*x = ShutDownRequest_How(value)
+	return nil
+}
+
+type ReceiveRequest_Flags int32
+
+const (
+	ReceiveRequest_MSG_OOB  ReceiveRequest_Flags = 1
+	ReceiveRequest_MSG_PEEK ReceiveRequest_Flags = 2
+)
+
+var ReceiveRequest_Flags_name = map[int32]string{
+	1: "MSG_OOB",
+	2: "MSG_PEEK",
+}
+var ReceiveRequest_Flags_value = map[string]int32{
+	"MSG_OOB":  1,
+	"MSG_PEEK": 2,
+}
+
+func (x ReceiveRequest_Flags) Enum() *ReceiveRequest_Flags {
+	p := new(ReceiveRequest_Flags)
+	*p = x
+	return p
+}
+func (x ReceiveRequest_Flags) String() string {
+	return proto.EnumName(ReceiveRequest_Flags_name, int32(x))
+}
+func (x *ReceiveRequest_Flags) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(ReceiveRequest_Flags_value, data, "ReceiveRequest_Flags")
+	if err != nil {
+		return err
+	}
+	*x = ReceiveRequest_Flags(value)
+	return nil
+}
+
+type PollEvent_PollEventFlag int32
+
+const (
+	PollEvent_SOCKET_POLLNONE   PollEvent_PollEventFlag = 0
+	PollEvent_SOCKET_POLLIN     PollEvent_PollEventFlag = 1
+	PollEvent_SOCKET_POLLPRI    PollEvent_PollEventFlag = 2
+	PollEvent_SOCKET_POLLOUT    PollEvent_PollEventFlag = 4
+	PollEvent_SOCKET_POLLERR    PollEvent_PollEventFlag = 8
+	PollEvent_SOCKET_POLLHUP    PollEvent_PollEventFlag = 16
+	PollEvent_SOCKET_POLLNVAL   PollEvent_PollEventFlag = 32
+	PollEvent_SOCKET_POLLRDNORM PollEvent_PollEventFlag = 64
+	PollEvent_SOCKET_POLLRDBAND PollEvent_PollEventFlag = 128
+	PollEvent_SOCKET_POLLWRNORM PollEvent_PollEventFlag = 256
+	PollEvent_SOCKET_POLLWRBAND PollEvent_PollEventFlag = 512
+	PollEvent_SOCKET_POLLMSG    PollEvent_PollEventFlag = 1024
+	PollEvent_SOCKET_POLLREMOVE PollEvent_PollEventFlag = 4096
+	PollEvent_SOCKET_POLLRDHUP  PollEvent_PollEventFlag = 8192
+)
+
+var PollEvent_PollEventFlag_name = map[int32]string{
+	0:    "SOCKET_POLLNONE",
+	1:    "SOCKET_POLLIN",
+	2:    "SOCKET_POLLPRI",
+	4:    "SOCKET_POLLOUT",
+	8:    "SOCKET_POLLERR",
+	16:   "SOCKET_POLLHUP",
+	32:   "SOCKET_POLLNVAL",
+	64:   "SOCKET_POLLRDNORM",
+	128:  "SOCKET_POLLRDBAND",
+	256:  "SOCKET_POLLWRNORM",
+	512:  "SOCKET_POLLWRBAND",
+	1024: "SOCKET_POLLMSG",
+	4096: "SOCKET_POLLREMOVE",
+	8192: "SOCKET_POLLRDHUP",
+}
+var PollEvent_PollEventFlag_value = map[string]int32{
+	"SOCKET_POLLNONE":   0,
+	"SOCKET_POLLIN":     1,
+	"SOCKET_POLLPRI":    2,
+	"SOCKET_POLLOUT":    4,
+	"SOCKET_POLLERR":    8,
+	"SOCKET_POLLHUP":    16,
+	"SOCKET_POLLNVAL":   32,
+	"SOCKET_POLLRDNORM": 64,
+	"SOCKET_POLLRDBAND": 128,
+	"SOCKET_POLLWRNORM": 256,
+	"SOCKET_POLLWRBAND": 512,
+	"SOCKET_POLLMSG":    1024,
+	"SOCKET_POLLREMOVE": 4096,
+	"SOCKET_POLLRDHUP":  8192,
+}
+
+func (x PollEvent_PollEventFlag) Enum() *PollEvent_PollEventFlag {
+	p := new(PollEvent_PollEventFlag)
+	*p = x
+	return p
+}
+func (x PollEvent_PollEventFlag) String() string {
+	return proto.EnumName(PollEvent_PollEventFlag_name, int32(x))
+}
+func (x *PollEvent_PollEventFlag) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(PollEvent_PollEventFlag_value, data, "PollEvent_PollEventFlag")
+	if err != nil {
+		return err
+	}
+	*x = PollEvent_PollEventFlag(value)
+	return nil
+}
+
+type ResolveReply_ErrorCode int32
+
+const (
+	ResolveReply_SOCKET_EAI_ADDRFAMILY ResolveReply_ErrorCode = 1
+	ResolveReply_SOCKET_EAI_AGAIN      ResolveReply_ErrorCode = 2
+	ResolveReply_SOCKET_EAI_BADFLAGS   ResolveReply_ErrorCode = 3
+	ResolveReply_SOCKET_EAI_FAIL       ResolveReply_ErrorCode = 4
+	ResolveReply_SOCKET_EAI_FAMILY     ResolveReply_ErrorCode = 5
+	ResolveReply_SOCKET_EAI_MEMORY     ResolveReply_ErrorCode = 6
+	ResolveReply_SOCKET_EAI_NODATA     ResolveReply_ErrorCode = 7
+	ResolveReply_SOCKET_EAI_NONAME     ResolveReply_ErrorCode = 8
+	ResolveReply_SOCKET_EAI_SERVICE    ResolveReply_ErrorCode = 9
+	ResolveReply_SOCKET_EAI_SOCKTYPE   ResolveReply_ErrorCode = 10
+	ResolveReply_SOCKET_EAI_SYSTEM     ResolveReply_ErrorCode = 11
+	ResolveReply_SOCKET_EAI_BADHINTS   ResolveReply_ErrorCode = 12
+	ResolveReply_SOCKET_EAI_PROTOCOL   ResolveReply_ErrorCode = 13
+	ResolveReply_SOCKET_EAI_OVERFLOW   ResolveReply_ErrorCode = 14
+	ResolveReply_SOCKET_EAI_MAX        ResolveReply_ErrorCode = 15
+)
+
+var ResolveReply_ErrorCode_name = map[int32]string{
+	1:  "SOCKET_EAI_ADDRFAMILY",
+	2:  "SOCKET_EAI_AGAIN",
+	3:  "SOCKET_EAI_BADFLAGS",
+	4:  "SOCKET_EAI_FAIL",
+	5:  "SOCKET_EAI_FAMILY",
+	6:  "SOCKET_EAI_MEMORY",
+	7:  "SOCKET_EAI_NODATA",
+	8:  "SOCKET_EAI_NONAME",
+	9:  "SOCKET_EAI_SERVICE",
+	10: "SOCKET_EAI_SOCKTYPE",
+	11: "SOCKET_EAI_SYSTEM",
+	12: "SOCKET_EAI_BADHINTS",
+	13: "SOCKET_EAI_PROTOCOL",
+	14: "SOCKET_EAI_OVERFLOW",
+	15: "SOCKET_EAI_MAX",
+}
+var ResolveReply_ErrorCode_value = map[string]int32{
+	"SOCKET_EAI_ADDRFAMILY": 1,
+	"SOCKET_EAI_AGAIN":      2,
+	"SOCKET_EAI_BADFLAGS":   3,
+	"SOCKET_EAI_FAIL":       4,
+	"SOCKET_EAI_FAMILY":     5,
+	"SOCKET_EAI_MEMORY":     6,
+	"SOCKET_EAI_NODATA":     7,
+	"SOCKET_EAI_NONAME":     8,
+	"SOCKET_EAI_SERVICE":    9,
+	"SOCKET_EAI_SOCKTYPE":   10,
+	"SOCKET_EAI_SYSTEM":     11,
+	"SOCKET_EAI_BADHINTS":   12,
+	"SOCKET_EAI_PROTOCOL":   13,
+	"SOCKET_EAI_OVERFLOW":   14,
+	"SOCKET_EAI_MAX":        15,
+}
+
+func (x ResolveReply_ErrorCode) Enum() *ResolveReply_ErrorCode {
+	p := new(ResolveReply_ErrorCode)
+	*p = x
+	return p
+}
+func (x ResolveReply_ErrorCode) String() string {
+	return proto.EnumName(ResolveReply_ErrorCode_name, int32(x))
+}
+func (x *ResolveReply_ErrorCode) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(ResolveReply_ErrorCode_value, data, "ResolveReply_ErrorCode")
+	if err != nil {
+		return err
+	}
+	*x = ResolveReply_ErrorCode(value)
+	return nil
+}
+
+type RemoteSocketServiceError struct {
+	SystemError      *int32  `protobuf:"varint,1,opt,name=system_error,def=0" json:"system_error,omitempty"`
+	ErrorDetail      *string `protobuf:"bytes,2,opt,name=error_detail" json:"error_detail,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *RemoteSocketServiceError) Reset()         { *m = RemoteSocketServiceError{} }
+func (m *RemoteSocketServiceError) String() string { return proto.CompactTextString(m) }
+func (*RemoteSocketServiceError) ProtoMessage()    {}
+
+const Default_RemoteSocketServiceError_SystemError int32 = 0
+
+func (m *RemoteSocketServiceError) GetSystemError() int32 {
+	if m != nil && m.SystemError != nil {
+		return *m.SystemError
+	}
+	return Default_RemoteSocketServiceError_SystemError
+}
+
+func (m *RemoteSocketServiceError) GetErrorDetail() string {
+	if m != nil && m.ErrorDetail != nil {
+		return *m.ErrorDetail
+	}
+	return ""
+}
+
+type AddressPort struct {
+	Port             *int32  `protobuf:"varint,1,req,name=port" json:"port,omitempty"`
+	PackedAddress    []byte  `protobuf:"bytes,2,opt,name=packed_address" json:"packed_address,omitempty"`
+	HostnameHint     *string `protobuf:"bytes,3,opt,name=hostname_hint" json:"hostname_hint,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *AddressPort) Reset()         { *m = AddressPort{} }
+func (m *AddressPort) String() string { return proto.CompactTextString(m) }
+func (*AddressPort) ProtoMessage()    {}
+
+func (m *AddressPort) GetPort() int32 {
+	if m != nil && m.Port != nil {
+		return *m.Port
+	}
+	return 0
+}
+
+func (m *AddressPort) GetPackedAddress() []byte {
+	if m != nil {
+		return m.PackedAddress
+	}
+	return nil
+}
+
+func (m *AddressPort) GetHostnameHint() string {
+	if m != nil && m.HostnameHint != nil {
+		return *m.HostnameHint
+	}
+	return ""
+}
+
+type CreateSocketRequest struct {
+	Family           *CreateSocketRequest_SocketFamily   `protobuf:"varint,1,req,name=family,enum=appengine.CreateSocketRequest_SocketFamily" json:"family,omitempty"`
+	Protocol         *CreateSocketRequest_SocketProtocol `protobuf:"varint,2,req,name=protocol,enum=appengine.CreateSocketRequest_SocketProtocol" json:"protocol,omitempty"`
+	SocketOptions    []*SocketOption                     `protobuf:"bytes,3,rep,name=socket_options" json:"socket_options,omitempty"`
+	ProxyExternalIp  *AddressPort                        `protobuf:"bytes,4,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"`
+	ListenBacklog    *int32                              `protobuf:"varint,5,opt,name=listen_backlog,def=0" json:"listen_backlog,omitempty"`
+	RemoteIp         *AddressPort                        `protobuf:"bytes,6,opt,name=remote_ip" json:"remote_ip,omitempty"`
+	AppId            *string                             `protobuf:"bytes,9,opt,name=app_id" json:"app_id,omitempty"`
+	ProjectId        *int64                              `protobuf:"varint,10,opt,name=project_id" json:"project_id,omitempty"`
+	XXX_unrecognized []byte                              `json:"-"`
+}
+
+func (m *CreateSocketRequest) Reset()         { *m = CreateSocketRequest{} }
+func (m *CreateSocketRequest) String() string { return proto.CompactTextString(m) }
+func (*CreateSocketRequest) ProtoMessage()    {}
+
+const Default_CreateSocketRequest_ListenBacklog int32 = 0
+
+func (m *CreateSocketRequest) GetFamily() CreateSocketRequest_SocketFamily {
+	if m != nil && m.Family != nil {
+		return *m.Family
+	}
+	return CreateSocketRequest_IPv4
+}
+
+func (m *CreateSocketRequest) GetProtocol() CreateSocketRequest_SocketProtocol {
+	if m != nil && m.Protocol != nil {
+		return *m.Protocol
+	}
+	return CreateSocketRequest_TCP
+}
+
+func (m *CreateSocketRequest) GetSocketOptions() []*SocketOption {
+	if m != nil {
+		return m.SocketOptions
+	}
+	return nil
+}
+
+func (m *CreateSocketRequest) GetProxyExternalIp() *AddressPort {
+	if m != nil {
+		return m.ProxyExternalIp
+	}
+	return nil
+}
+
+func (m *CreateSocketRequest) GetListenBacklog() int32 {
+	if m != nil && m.ListenBacklog != nil {
+		return *m.ListenBacklog
+	}
+	return Default_CreateSocketRequest_ListenBacklog
+}
+
+func (m *CreateSocketRequest) GetRemoteIp() *AddressPort {
+	if m != nil {
+		return m.RemoteIp
+	}
+	return nil
+}
+
+func (m *CreateSocketRequest) GetAppId() string {
+	if m != nil && m.AppId != nil {
+		return *m.AppId
+	}
+	return ""
+}
+
+func (m *CreateSocketRequest) GetProjectId() int64 {
+	if m != nil && m.ProjectId != nil {
+		return *m.ProjectId
+	}
+	return 0
+}
+
+type CreateSocketReply struct {
+	SocketDescriptor *string                   `protobuf:"bytes,1,opt,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	ServerAddress    *AddressPort              `protobuf:"bytes,3,opt,name=server_address" json:"server_address,omitempty"`
+	ProxyExternalIp  *AddressPort              `protobuf:"bytes,4,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"`
+	XXX_extensions   map[int32]proto.Extension `json:"-"`
+	XXX_unrecognized []byte                    `json:"-"`
+}
+
+func (m *CreateSocketReply) Reset()         { *m = CreateSocketReply{} }
+func (m *CreateSocketReply) String() string { return proto.CompactTextString(m) }
+func (*CreateSocketReply) ProtoMessage()    {}
+
+var extRange_CreateSocketReply = []proto.ExtensionRange{
+	{1000, 536870911},
+}
+
+func (*CreateSocketReply) ExtensionRangeArray() []proto.ExtensionRange {
+	return extRange_CreateSocketReply
+}
+func (m *CreateSocketReply) ExtensionMap() map[int32]proto.Extension {
+	if m.XXX_extensions == nil {
+		m.XXX_extensions = make(map[int32]proto.Extension)
+	}
+	return m.XXX_extensions
+}
+
+func (m *CreateSocketReply) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *CreateSocketReply) GetServerAddress() *AddressPort {
+	if m != nil {
+		return m.ServerAddress
+	}
+	return nil
+}
+
+func (m *CreateSocketReply) GetProxyExternalIp() *AddressPort {
+	if m != nil {
+		return m.ProxyExternalIp
+	}
+	return nil
+}
+
+type BindRequest struct {
+	SocketDescriptor *string      `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	ProxyExternalIp  *AddressPort `protobuf:"bytes,2,req,name=proxy_external_ip" json:"proxy_external_ip,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *BindRequest) Reset()         { *m = BindRequest{} }
+func (m *BindRequest) String() string { return proto.CompactTextString(m) }
+func (*BindRequest) ProtoMessage()    {}
+
+func (m *BindRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *BindRequest) GetProxyExternalIp() *AddressPort {
+	if m != nil {
+		return m.ProxyExternalIp
+	}
+	return nil
+}
+
+type BindReply struct {
+	ProxyExternalIp  *AddressPort `protobuf:"bytes,1,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *BindReply) Reset()         { *m = BindReply{} }
+func (m *BindReply) String() string { return proto.CompactTextString(m) }
+func (*BindReply) ProtoMessage()    {}
+
+func (m *BindReply) GetProxyExternalIp() *AddressPort {
+	if m != nil {
+		return m.ProxyExternalIp
+	}
+	return nil
+}
+
+type GetSocketNameRequest struct {
+	SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *GetSocketNameRequest) Reset()         { *m = GetSocketNameRequest{} }
+func (m *GetSocketNameRequest) String() string { return proto.CompactTextString(m) }
+func (*GetSocketNameRequest) ProtoMessage()    {}
+
+func (m *GetSocketNameRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+type GetSocketNameReply struct {
+	ProxyExternalIp  *AddressPort `protobuf:"bytes,2,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *GetSocketNameReply) Reset()         { *m = GetSocketNameReply{} }
+func (m *GetSocketNameReply) String() string { return proto.CompactTextString(m) }
+func (*GetSocketNameReply) ProtoMessage()    {}
+
+func (m *GetSocketNameReply) GetProxyExternalIp() *AddressPort {
+	if m != nil {
+		return m.ProxyExternalIp
+	}
+	return nil
+}
+
+type GetPeerNameRequest struct {
+	SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *GetPeerNameRequest) Reset()         { *m = GetPeerNameRequest{} }
+func (m *GetPeerNameRequest) String() string { return proto.CompactTextString(m) }
+func (*GetPeerNameRequest) ProtoMessage()    {}
+
+func (m *GetPeerNameRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+type GetPeerNameReply struct {
+	PeerIp           *AddressPort `protobuf:"bytes,2,opt,name=peer_ip" json:"peer_ip,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *GetPeerNameReply) Reset()         { *m = GetPeerNameReply{} }
+func (m *GetPeerNameReply) String() string { return proto.CompactTextString(m) }
+func (*GetPeerNameReply) ProtoMessage()    {}
+
+func (m *GetPeerNameReply) GetPeerIp() *AddressPort {
+	if m != nil {
+		return m.PeerIp
+	}
+	return nil
+}
+
+type SocketOption struct {
+	Level            *SocketOption_SocketOptionLevel `protobuf:"varint,1,req,name=level,enum=appengine.SocketOption_SocketOptionLevel" json:"level,omitempty"`
+	Option           *SocketOption_SocketOptionName  `protobuf:"varint,2,req,name=option,enum=appengine.SocketOption_SocketOptionName" json:"option,omitempty"`
+	Value            []byte                          `protobuf:"bytes,3,req,name=value" json:"value,omitempty"`
+	XXX_unrecognized []byte                          `json:"-"`
+}
+
+func (m *SocketOption) Reset()         { *m = SocketOption{} }
+func (m *SocketOption) String() string { return proto.CompactTextString(m) }
+func (*SocketOption) ProtoMessage()    {}
+
+func (m *SocketOption) GetLevel() SocketOption_SocketOptionLevel {
+	if m != nil && m.Level != nil {
+		return *m.Level
+	}
+	return SocketOption_SOCKET_SOL_IP
+}
+
+func (m *SocketOption) GetOption() SocketOption_SocketOptionName {
+	if m != nil && m.Option != nil {
+		return *m.Option
+	}
+	return SocketOption_SOCKET_SO_DEBUG
+}
+
+func (m *SocketOption) GetValue() []byte {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+type SetSocketOptionsRequest struct {
+	SocketDescriptor *string         `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	Options          []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *SetSocketOptionsRequest) Reset()         { *m = SetSocketOptionsRequest{} }
+func (m *SetSocketOptionsRequest) String() string { return proto.CompactTextString(m) }
+func (*SetSocketOptionsRequest) ProtoMessage()    {}
+
+func (m *SetSocketOptionsRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *SetSocketOptionsRequest) GetOptions() []*SocketOption {
+	if m != nil {
+		return m.Options
+	}
+	return nil
+}
+
+type SetSocketOptionsReply struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SetSocketOptionsReply) Reset()         { *m = SetSocketOptionsReply{} }
+func (m *SetSocketOptionsReply) String() string { return proto.CompactTextString(m) }
+func (*SetSocketOptionsReply) ProtoMessage()    {}
+
+type GetSocketOptionsRequest struct {
+	SocketDescriptor *string         `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	Options          []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *GetSocketOptionsRequest) Reset()         { *m = GetSocketOptionsRequest{} }
+func (m *GetSocketOptionsRequest) String() string { return proto.CompactTextString(m) }
+func (*GetSocketOptionsRequest) ProtoMessage()    {}
+
+func (m *GetSocketOptionsRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *GetSocketOptionsRequest) GetOptions() []*SocketOption {
+	if m != nil {
+		return m.Options
+	}
+	return nil
+}
+
+type GetSocketOptionsReply struct {
+	Options          []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"`
+	XXX_unrecognized []byte          `json:"-"`
+}
+
+func (m *GetSocketOptionsReply) Reset()         { *m = GetSocketOptionsReply{} }
+func (m *GetSocketOptionsReply) String() string { return proto.CompactTextString(m) }
+func (*GetSocketOptionsReply) ProtoMessage()    {}
+
+func (m *GetSocketOptionsReply) GetOptions() []*SocketOption {
+	if m != nil {
+		return m.Options
+	}
+	return nil
+}
+
+type ConnectRequest struct {
+	SocketDescriptor *string      `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	RemoteIp         *AddressPort `protobuf:"bytes,2,req,name=remote_ip" json:"remote_ip,omitempty"`
+	TimeoutSeconds   *float64     `protobuf:"fixed64,3,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *ConnectRequest) Reset()         { *m = ConnectRequest{} }
+func (m *ConnectRequest) String() string { return proto.CompactTextString(m) }
+func (*ConnectRequest) ProtoMessage()    {}
+
+const Default_ConnectRequest_TimeoutSeconds float64 = -1
+
+func (m *ConnectRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *ConnectRequest) GetRemoteIp() *AddressPort {
+	if m != nil {
+		return m.RemoteIp
+	}
+	return nil
+}
+
+func (m *ConnectRequest) GetTimeoutSeconds() float64 {
+	if m != nil && m.TimeoutSeconds != nil {
+		return *m.TimeoutSeconds
+	}
+	return Default_ConnectRequest_TimeoutSeconds
+}
+
+type ConnectReply struct {
+	ProxyExternalIp  *AddressPort              `protobuf:"bytes,1,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"`
+	XXX_extensions   map[int32]proto.Extension `json:"-"`
+	XXX_unrecognized []byte                    `json:"-"`
+}
+
+func (m *ConnectReply) Reset()         { *m = ConnectReply{} }
+func (m *ConnectReply) String() string { return proto.CompactTextString(m) }
+func (*ConnectReply) ProtoMessage()    {}
+
+var extRange_ConnectReply = []proto.ExtensionRange{
+	{1000, 536870911},
+}
+
+func (*ConnectReply) ExtensionRangeArray() []proto.ExtensionRange {
+	return extRange_ConnectReply
+}
+func (m *ConnectReply) ExtensionMap() map[int32]proto.Extension {
+	if m.XXX_extensions == nil {
+		m.XXX_extensions = make(map[int32]proto.Extension)
+	}
+	return m.XXX_extensions
+}
+
+func (m *ConnectReply) GetProxyExternalIp() *AddressPort {
+	if m != nil {
+		return m.ProxyExternalIp
+	}
+	return nil
+}
+
+type ListenRequest struct {
+	SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	Backlog          *int32  `protobuf:"varint,2,req,name=backlog" json:"backlog,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *ListenRequest) Reset()         { *m = ListenRequest{} }
+func (m *ListenRequest) String() string { return proto.CompactTextString(m) }
+func (*ListenRequest) ProtoMessage()    {}
+
+func (m *ListenRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *ListenRequest) GetBacklog() int32 {
+	if m != nil && m.Backlog != nil {
+		return *m.Backlog
+	}
+	return 0
+}
+
+type ListenReply struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *ListenReply) Reset()         { *m = ListenReply{} }
+func (m *ListenReply) String() string { return proto.CompactTextString(m) }
+func (*ListenReply) ProtoMessage()    {}
+
+type AcceptRequest struct {
+	SocketDescriptor *string  `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	TimeoutSeconds   *float64 `protobuf:"fixed64,2,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *AcceptRequest) Reset()         { *m = AcceptRequest{} }
+func (m *AcceptRequest) String() string { return proto.CompactTextString(m) }
+func (*AcceptRequest) ProtoMessage()    {}
+
+const Default_AcceptRequest_TimeoutSeconds float64 = -1
+
+func (m *AcceptRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *AcceptRequest) GetTimeoutSeconds() float64 {
+	if m != nil && m.TimeoutSeconds != nil {
+		return *m.TimeoutSeconds
+	}
+	return Default_AcceptRequest_TimeoutSeconds
+}
+
+type AcceptReply struct {
+	NewSocketDescriptor []byte       `protobuf:"bytes,2,opt,name=new_socket_descriptor" json:"new_socket_descriptor,omitempty"`
+	RemoteAddress       *AddressPort `protobuf:"bytes,3,opt,name=remote_address" json:"remote_address,omitempty"`
+	XXX_unrecognized    []byte       `json:"-"`
+}
+
+func (m *AcceptReply) Reset()         { *m = AcceptReply{} }
+func (m *AcceptReply) String() string { return proto.CompactTextString(m) }
+func (*AcceptReply) ProtoMessage()    {}
+
+func (m *AcceptReply) GetNewSocketDescriptor() []byte {
+	if m != nil {
+		return m.NewSocketDescriptor
+	}
+	return nil
+}
+
+func (m *AcceptReply) GetRemoteAddress() *AddressPort {
+	if m != nil {
+		return m.RemoteAddress
+	}
+	return nil
+}
+
+type ShutDownRequest struct {
+	SocketDescriptor *string              `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	How              *ShutDownRequest_How `protobuf:"varint,2,req,name=how,enum=appengine.ShutDownRequest_How" json:"how,omitempty"`
+	SendOffset       *int64               `protobuf:"varint,3,req,name=send_offset" json:"send_offset,omitempty"`
+	XXX_unrecognized []byte               `json:"-"`
+}
+
+func (m *ShutDownRequest) Reset()         { *m = ShutDownRequest{} }
+func (m *ShutDownRequest) String() string { return proto.CompactTextString(m) }
+func (*ShutDownRequest) ProtoMessage()    {}
+
+func (m *ShutDownRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *ShutDownRequest) GetHow() ShutDownRequest_How {
+	if m != nil && m.How != nil {
+		return *m.How
+	}
+	return ShutDownRequest_SOCKET_SHUT_RD
+}
+
+func (m *ShutDownRequest) GetSendOffset() int64 {
+	if m != nil && m.SendOffset != nil {
+		return *m.SendOffset
+	}
+	return 0
+}
+
+type ShutDownReply struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *ShutDownReply) Reset()         { *m = ShutDownReply{} }
+func (m *ShutDownReply) String() string { return proto.CompactTextString(m) }
+func (*ShutDownReply) ProtoMessage()    {}
+
+type CloseRequest struct {
+	SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	SendOffset       *int64  `protobuf:"varint,2,opt,name=send_offset,def=-1" json:"send_offset,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *CloseRequest) Reset()         { *m = CloseRequest{} }
+func (m *CloseRequest) String() string { return proto.CompactTextString(m) }
+func (*CloseRequest) ProtoMessage()    {}
+
+const Default_CloseRequest_SendOffset int64 = -1
+
+func (m *CloseRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *CloseRequest) GetSendOffset() int64 {
+	if m != nil && m.SendOffset != nil {
+		return *m.SendOffset
+	}
+	return Default_CloseRequest_SendOffset
+}
+
+type CloseReply struct {
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *CloseReply) Reset()         { *m = CloseReply{} }
+func (m *CloseReply) String() string { return proto.CompactTextString(m) }
+func (*CloseReply) ProtoMessage()    {}
+
+type SendRequest struct {
+	SocketDescriptor *string      `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	Data             []byte       `protobuf:"bytes,2,req,name=data" json:"data,omitempty"`
+	StreamOffset     *int64       `protobuf:"varint,3,req,name=stream_offset" json:"stream_offset,omitempty"`
+	Flags            *int32       `protobuf:"varint,4,opt,name=flags,def=0" json:"flags,omitempty"`
+	SendTo           *AddressPort `protobuf:"bytes,5,opt,name=send_to" json:"send_to,omitempty"`
+	TimeoutSeconds   *float64     `protobuf:"fixed64,6,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *SendRequest) Reset()         { *m = SendRequest{} }
+func (m *SendRequest) String() string { return proto.CompactTextString(m) }
+func (*SendRequest) ProtoMessage()    {}
+
+const Default_SendRequest_Flags int32 = 0
+const Default_SendRequest_TimeoutSeconds float64 = -1
+
+func (m *SendRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *SendRequest) GetData() []byte {
+	if m != nil {
+		return m.Data
+	}
+	return nil
+}
+
+func (m *SendRequest) GetStreamOffset() int64 {
+	if m != nil && m.StreamOffset != nil {
+		return *m.StreamOffset
+	}
+	return 0
+}
+
+func (m *SendRequest) GetFlags() int32 {
+	if m != nil && m.Flags != nil {
+		return *m.Flags
+	}
+	return Default_SendRequest_Flags
+}
+
+func (m *SendRequest) GetSendTo() *AddressPort {
+	if m != nil {
+		return m.SendTo
+	}
+	return nil
+}
+
+func (m *SendRequest) GetTimeoutSeconds() float64 {
+	if m != nil && m.TimeoutSeconds != nil {
+		return *m.TimeoutSeconds
+	}
+	return Default_SendRequest_TimeoutSeconds
+}
+
+type SendReply struct {
+	DataSent         *int32 `protobuf:"varint,1,opt,name=data_sent" json:"data_sent,omitempty"`
+	XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SendReply) Reset()         { *m = SendReply{} }
+func (m *SendReply) String() string { return proto.CompactTextString(m) }
+func (*SendReply) ProtoMessage()    {}
+
+func (m *SendReply) GetDataSent() int32 {
+	if m != nil && m.DataSent != nil {
+		return *m.DataSent
+	}
+	return 0
+}
+
+type ReceiveRequest struct {
+	SocketDescriptor *string  `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	DataSize         *int32   `protobuf:"varint,2,req,name=data_size" json:"data_size,omitempty"`
+	Flags            *int32   `protobuf:"varint,3,opt,name=flags,def=0" json:"flags,omitempty"`
+	TimeoutSeconds   *float64 `protobuf:"fixed64,5,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *ReceiveRequest) Reset()         { *m = ReceiveRequest{} }
+func (m *ReceiveRequest) String() string { return proto.CompactTextString(m) }
+func (*ReceiveRequest) ProtoMessage()    {}
+
+const Default_ReceiveRequest_Flags int32 = 0
+const Default_ReceiveRequest_TimeoutSeconds float64 = -1
+
+func (m *ReceiveRequest) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *ReceiveRequest) GetDataSize() int32 {
+	if m != nil && m.DataSize != nil {
+		return *m.DataSize
+	}
+	return 0
+}
+
+func (m *ReceiveRequest) GetFlags() int32 {
+	if m != nil && m.Flags != nil {
+		return *m.Flags
+	}
+	return Default_ReceiveRequest_Flags
+}
+
+func (m *ReceiveRequest) GetTimeoutSeconds() float64 {
+	if m != nil && m.TimeoutSeconds != nil {
+		return *m.TimeoutSeconds
+	}
+	return Default_ReceiveRequest_TimeoutSeconds
+}
+
+type ReceiveReply struct {
+	StreamOffset     *int64       `protobuf:"varint,2,opt,name=stream_offset" json:"stream_offset,omitempty"`
+	Data             []byte       `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"`
+	ReceivedFrom     *AddressPort `protobuf:"bytes,4,opt,name=received_from" json:"received_from,omitempty"`
+	BufferSize       *int32       `protobuf:"varint,5,opt,name=buffer_size" json:"buffer_size,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *ReceiveReply) Reset()         { *m = ReceiveReply{} }
+func (m *ReceiveReply) String() string { return proto.CompactTextString(m) }
+func (*ReceiveReply) ProtoMessage()    {}
+
+func (m *ReceiveReply) GetStreamOffset() int64 {
+	if m != nil && m.StreamOffset != nil {
+		return *m.StreamOffset
+	}
+	return 0
+}
+
+func (m *ReceiveReply) GetData() []byte {
+	if m != nil {
+		return m.Data
+	}
+	return nil
+}
+
+func (m *ReceiveReply) GetReceivedFrom() *AddressPort {
+	if m != nil {
+		return m.ReceivedFrom
+	}
+	return nil
+}
+
+func (m *ReceiveReply) GetBufferSize() int32 {
+	if m != nil && m.BufferSize != nil {
+		return *m.BufferSize
+	}
+	return 0
+}
+
+type PollEvent struct {
+	SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"`
+	RequestedEvents  *int32  `protobuf:"varint,2,req,name=requested_events" json:"requested_events,omitempty"`
+	ObservedEvents   *int32  `protobuf:"varint,3,req,name=observed_events" json:"observed_events,omitempty"`
+	XXX_unrecognized []byte  `json:"-"`
+}
+
+func (m *PollEvent) Reset()         { *m = PollEvent{} }
+func (m *PollEvent) String() string { return proto.CompactTextString(m) }
+func (*PollEvent) ProtoMessage()    {}
+
+func (m *PollEvent) GetSocketDescriptor() string {
+	if m != nil && m.SocketDescriptor != nil {
+		return *m.SocketDescriptor
+	}
+	return ""
+}
+
+func (m *PollEvent) GetRequestedEvents() int32 {
+	if m != nil && m.RequestedEvents != nil {
+		return *m.RequestedEvents
+	}
+	return 0
+}
+
+func (m *PollEvent) GetObservedEvents() int32 {
+	if m != nil && m.ObservedEvents != nil {
+		return *m.ObservedEvents
+	}
+	return 0
+}
+
+type PollRequest struct {
+	Events           []*PollEvent `protobuf:"bytes,1,rep,name=events" json:"events,omitempty"`
+	TimeoutSeconds   *float64     `protobuf:"fixed64,2,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *PollRequest) Reset()         { *m = PollRequest{} }
+func (m *PollRequest) String() string { return proto.CompactTextString(m) }
+func (*PollRequest) ProtoMessage()    {}
+
+const Default_PollRequest_TimeoutSeconds float64 = -1
+
+func (m *PollRequest) GetEvents() []*PollEvent {
+	if m != nil {
+		return m.Events
+	}
+	return nil
+}
+
+func (m *PollRequest) GetTimeoutSeconds() float64 {
+	if m != nil && m.TimeoutSeconds != nil {
+		return *m.TimeoutSeconds
+	}
+	return Default_PollRequest_TimeoutSeconds
+}
+
+type PollReply struct {
+	Events           []*PollEvent `protobuf:"bytes,2,rep,name=events" json:"events,omitempty"`
+	XXX_unrecognized []byte       `json:"-"`
+}
+
+func (m *PollReply) Reset()         { *m = PollReply{} }
+func (m *PollReply) String() string { return proto.CompactTextString(m) }
+func (*PollReply) ProtoMessage()    {}
+
+func (m *PollReply) GetEvents() []*PollEvent {
+	if m != nil {
+		return m.Events
+	}
+	return nil
+}
+
+type ResolveRequest struct {
+	Name             *string                            `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+	AddressFamilies  []CreateSocketRequest_SocketFamily `protobuf:"varint,2,rep,name=address_families,enum=appengine.CreateSocketRequest_SocketFamily" json:"address_families,omitempty"`
+	XXX_unrecognized []byte                             `json:"-"`
+}
+
+func (m *ResolveRequest) Reset()         { *m = ResolveRequest{} }
+func (m *ResolveRequest) String() string { return proto.CompactTextString(m) }
+func (*ResolveRequest) ProtoMessage()    {}
+
+func (m *ResolveRequest) GetName() string {
+	if m != nil && m.Name != nil {
+		return *m.Name
+	}
+	return ""
+}
+
+func (m *ResolveRequest) GetAddressFamilies() []CreateSocketRequest_SocketFamily {
+	if m != nil {
+		return m.AddressFamilies
+	}
+	return nil
+}
+
+type ResolveReply struct {
+	PackedAddress    [][]byte `protobuf:"bytes,2,rep,name=packed_address" json:"packed_address,omitempty"`
+	CanonicalName    *string  `protobuf:"bytes,3,opt,name=canonical_name" json:"canonical_name,omitempty"`
+	Aliases          []string `protobuf:"bytes,4,rep,name=aliases" json:"aliases,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *ResolveReply) Reset()         { *m = ResolveReply{} }
+func (m *ResolveReply) String() string { return proto.CompactTextString(m) }
+func (*ResolveReply) ProtoMessage()    {}
+
+func (m *ResolveReply) GetPackedAddress() [][]byte {
+	if m != nil {
+		return m.PackedAddress
+	}
+	return nil
+}
+
+func (m *ResolveReply) GetCanonicalName() string {
+	if m != nil && m.CanonicalName != nil {
+		return *m.CanonicalName
+	}
+	return ""
+}
+
+func (m *ResolveReply) GetAliases() []string {
+	if m != nil {
+		return m.Aliases
+	}
+	return nil
+}
+
+func init() {
+}
diff --git a/go/src/google.golang.org/appengine/internal/socket/socket_service.proto b/go/src/google.golang.org/appengine/internal/socket/socket_service.proto
new file mode 100644
index 0000000..2fcc795
--- /dev/null
+++ b/go/src/google.golang.org/appengine/internal/socket/socket_service.proto
@@ -0,0 +1,460 @@
+syntax = "proto2";
+option go_package = "socket";
+
+package appengine;
+
+message RemoteSocketServiceError {
+  enum ErrorCode {
+    SYSTEM_ERROR = 1;
+    GAI_ERROR = 2;
+    FAILURE = 4;
+    PERMISSION_DENIED = 5;
+    INVALID_REQUEST = 6;
+    SOCKET_CLOSED = 7;
+  }
+
+  enum SystemError {
+    option allow_alias = true;
+
+    SYS_SUCCESS = 0;
+    SYS_EPERM = 1;
+    SYS_ENOENT = 2;
+    SYS_ESRCH = 3;
+    SYS_EINTR = 4;
+    SYS_EIO = 5;
+    SYS_ENXIO = 6;
+    SYS_E2BIG = 7;
+    SYS_ENOEXEC = 8;
+    SYS_EBADF = 9;
+    SYS_ECHILD = 10;
+    SYS_EAGAIN = 11;
+    SYS_EWOULDBLOCK = 11;
+    SYS_ENOMEM = 12;
+    SYS_EACCES = 13;
+    SYS_EFAULT = 14;
+    SYS_ENOTBLK = 15;
+    SYS_EBUSY = 16;
+    SYS_EEXIST = 17;
+    SYS_EXDEV = 18;
+    SYS_ENODEV = 19;
+    SYS_ENOTDIR = 20;
+    SYS_EISDIR = 21;
+    SYS_EINVAL = 22;
+    SYS_ENFILE = 23;
+    SYS_EMFILE = 24;
+    SYS_ENOTTY = 25;
+    SYS_ETXTBSY = 26;
+    SYS_EFBIG = 27;
+    SYS_ENOSPC = 28;
+    SYS_ESPIPE = 29;
+    SYS_EROFS = 30;
+    SYS_EMLINK = 31;
+    SYS_EPIPE = 32;
+    SYS_EDOM = 33;
+    SYS_ERANGE = 34;
+    SYS_EDEADLK = 35;
+    SYS_EDEADLOCK = 35;
+    SYS_ENAMETOOLONG = 36;
+    SYS_ENOLCK = 37;
+    SYS_ENOSYS = 38;
+    SYS_ENOTEMPTY = 39;
+    SYS_ELOOP = 40;
+    SYS_ENOMSG = 42;
+    SYS_EIDRM = 43;
+    SYS_ECHRNG = 44;
+    SYS_EL2NSYNC = 45;
+    SYS_EL3HLT = 46;
+    SYS_EL3RST = 47;
+    SYS_ELNRNG = 48;
+    SYS_EUNATCH = 49;
+    SYS_ENOCSI = 50;
+    SYS_EL2HLT = 51;
+    SYS_EBADE = 52;
+    SYS_EBADR = 53;
+    SYS_EXFULL = 54;
+    SYS_ENOANO = 55;
+    SYS_EBADRQC = 56;
+    SYS_EBADSLT = 57;
+    SYS_EBFONT = 59;
+    SYS_ENOSTR = 60;
+    SYS_ENODATA = 61;
+    SYS_ETIME = 62;
+    SYS_ENOSR = 63;
+    SYS_ENONET = 64;
+    SYS_ENOPKG = 65;
+    SYS_EREMOTE = 66;
+    SYS_ENOLINK = 67;
+    SYS_EADV = 68;
+    SYS_ESRMNT = 69;
+    SYS_ECOMM = 70;
+    SYS_EPROTO = 71;
+    SYS_EMULTIHOP = 72;
+    SYS_EDOTDOT = 73;
+    SYS_EBADMSG = 74;
+    SYS_EOVERFLOW = 75;
+    SYS_ENOTUNIQ = 76;
+    SYS_EBADFD = 77;
+    SYS_EREMCHG = 78;
+    SYS_ELIBACC = 79;
+    SYS_ELIBBAD = 80;
+    SYS_ELIBSCN = 81;
+    SYS_ELIBMAX = 82;
+    SYS_ELIBEXEC = 83;
+    SYS_EILSEQ = 84;
+    SYS_ERESTART = 85;
+    SYS_ESTRPIPE = 86;
+    SYS_EUSERS = 87;
+    SYS_ENOTSOCK = 88;
+    SYS_EDESTADDRREQ = 89;
+    SYS_EMSGSIZE = 90;
+    SYS_EPROTOTYPE = 91;
+    SYS_ENOPROTOOPT = 92;
+    SYS_EPROTONOSUPPORT = 93;
+    SYS_ESOCKTNOSUPPORT = 94;
+    SYS_EOPNOTSUPP = 95;
+    SYS_ENOTSUP = 95;
+    SYS_EPFNOSUPPORT = 96;
+    SYS_EAFNOSUPPORT = 97;
+    SYS_EADDRINUSE = 98;
+    SYS_EADDRNOTAVAIL = 99;
+    SYS_ENETDOWN = 100;
+    SYS_ENETUNREACH = 101;
+    SYS_ENETRESET = 102;
+    SYS_ECONNABORTED = 103;
+    SYS_ECONNRESET = 104;
+    SYS_ENOBUFS = 105;
+    SYS_EISCONN = 106;
+    SYS_ENOTCONN = 107;
+    SYS_ESHUTDOWN = 108;
+    SYS_ETOOMANYREFS = 109;
+    SYS_ETIMEDOUT = 110;
+    SYS_ECONNREFUSED = 111;
+    SYS_EHOSTDOWN = 112;
+    SYS_EHOSTUNREACH = 113;
+    SYS_EALREADY = 114;
+    SYS_EINPROGRESS = 115;
+    SYS_ESTALE = 116;
+    SYS_EUCLEAN = 117;
+    SYS_ENOTNAM = 118;
+    SYS_ENAVAIL = 119;
+    SYS_EISNAM = 120;
+    SYS_EREMOTEIO = 121;
+    SYS_EDQUOT = 122;
+    SYS_ENOMEDIUM = 123;
+    SYS_EMEDIUMTYPE = 124;
+    SYS_ECANCELED = 125;
+    SYS_ENOKEY = 126;
+    SYS_EKEYEXPIRED = 127;
+    SYS_EKEYREVOKED = 128;
+    SYS_EKEYREJECTED = 129;
+    SYS_EOWNERDEAD = 130;
+    SYS_ENOTRECOVERABLE = 131;
+    SYS_ERFKILL = 132;
+  }
+
+  optional int32 system_error = 1 [default=0];
+  optional string error_detail = 2;
+}
+
+message AddressPort {
+  required int32 port = 1;
+  optional bytes packed_address = 2;
+
+  optional string hostname_hint = 3;
+}
+
+
+
+message CreateSocketRequest {
+  enum SocketFamily {
+    IPv4 = 1;
+    IPv6 = 2;
+  }
+
+  enum SocketProtocol {
+    TCP = 1;
+    UDP = 2;
+  }
+
+  required SocketFamily family = 1;
+  required SocketProtocol protocol = 2;
+
+  repeated SocketOption socket_options = 3;
+
+  optional AddressPort proxy_external_ip = 4;
+
+  optional int32 listen_backlog = 5 [default=0];
+
+  optional AddressPort remote_ip = 6;
+
+  optional string app_id = 9;
+
+  optional int64 project_id = 10;
+}
+
+message CreateSocketReply {
+  optional string socket_descriptor = 1;
+
+  optional AddressPort server_address = 3;
+
+  optional AddressPort proxy_external_ip = 4;
+
+  extensions 1000 to max;
+}
+
+
+
+message BindRequest {
+  required string socket_descriptor = 1;
+  required AddressPort proxy_external_ip = 2;
+}
+
+message BindReply {
+  optional AddressPort proxy_external_ip = 1;
+}
+
+
+
+message GetSocketNameRequest {
+  required string socket_descriptor = 1;
+}
+
+message GetSocketNameReply {
+  optional AddressPort proxy_external_ip = 2;
+}
+
+
+
+message GetPeerNameRequest {
+  required string socket_descriptor = 1;
+}
+
+message GetPeerNameReply {
+  optional AddressPort peer_ip = 2;
+}
+
+
+message SocketOption {
+
+  enum SocketOptionLevel {
+    SOCKET_SOL_IP = 0;
+    SOCKET_SOL_SOCKET = 1;
+    SOCKET_SOL_TCP = 6;
+    SOCKET_SOL_UDP = 17;
+  }
+
+  enum SocketOptionName {
+    option allow_alias = true;
+
+    SOCKET_SO_DEBUG = 1;
+    SOCKET_SO_REUSEADDR = 2;
+    SOCKET_SO_TYPE = 3;
+    SOCKET_SO_ERROR = 4;
+    SOCKET_SO_DONTROUTE = 5;
+    SOCKET_SO_BROADCAST = 6;
+    SOCKET_SO_SNDBUF = 7;
+    SOCKET_SO_RCVBUF = 8;
+    SOCKET_SO_KEEPALIVE = 9;
+    SOCKET_SO_OOBINLINE = 10;
+    SOCKET_SO_LINGER = 13;
+    SOCKET_SO_RCVTIMEO = 20;
+    SOCKET_SO_SNDTIMEO = 21;
+
+    SOCKET_IP_TOS = 1;
+    SOCKET_IP_TTL = 2;
+    SOCKET_IP_HDRINCL = 3;
+    SOCKET_IP_OPTIONS = 4;
+
+    SOCKET_TCP_NODELAY = 1;
+    SOCKET_TCP_MAXSEG = 2;
+    SOCKET_TCP_CORK = 3;
+    SOCKET_TCP_KEEPIDLE = 4;
+    SOCKET_TCP_KEEPINTVL = 5;
+    SOCKET_TCP_KEEPCNT = 6;
+    SOCKET_TCP_SYNCNT = 7;
+    SOCKET_TCP_LINGER2 = 8;
+    SOCKET_TCP_DEFER_ACCEPT = 9;
+    SOCKET_TCP_WINDOW_CLAMP = 10;
+    SOCKET_TCP_INFO = 11;
+    SOCKET_TCP_QUICKACK = 12;
+  }
+
+  required SocketOptionLevel level = 1;
+  required SocketOptionName option = 2;
+  required bytes value = 3;
+}
+
+
+message SetSocketOptionsRequest {
+  required string socket_descriptor = 1;
+  repeated SocketOption options = 2;
+}
+
+message SetSocketOptionsReply {
+}
+
+message GetSocketOptionsRequest {
+  required string socket_descriptor = 1;
+  repeated SocketOption options = 2;
+}
+
+message GetSocketOptionsReply {
+  repeated SocketOption options = 2;
+}
+
+
+message ConnectRequest {
+  required string socket_descriptor = 1;
+  required AddressPort remote_ip = 2;
+  optional double timeout_seconds = 3 [default=-1];
+}
+
+message ConnectReply {
+  optional AddressPort proxy_external_ip = 1;
+
+  extensions 1000 to max;
+}
+
+
+message ListenRequest {
+  required string socket_descriptor = 1;
+  required int32 backlog = 2;
+}
+
+message ListenReply {
+}
+
+
+message AcceptRequest {
+  required string socket_descriptor = 1;
+  optional double timeout_seconds = 2 [default=-1];
+}
+
+message AcceptReply {
+  optional bytes new_socket_descriptor = 2;
+  optional AddressPort remote_address = 3;
+}
+
+
+
+message ShutDownRequest {
+  enum How {
+    SOCKET_SHUT_RD = 1;
+    SOCKET_SHUT_WR = 2;
+    SOCKET_SHUT_RDWR = 3;
+  }
+  required string socket_descriptor = 1;
+  required How how = 2;
+  required int64 send_offset = 3;
+}
+
+message ShutDownReply {
+}
+
+
+
+message CloseRequest {
+  required string socket_descriptor = 1;
+  optional int64 send_offset = 2 [default=-1];
+}
+
+message CloseReply {
+}
+
+
+
+message SendRequest {
+  required string socket_descriptor = 1;
+  required bytes data = 2 [ctype=CORD];
+  required int64 stream_offset = 3;
+  optional int32 flags = 4 [default=0];
+  optional AddressPort send_to = 5;
+  optional double timeout_seconds = 6 [default=-1];
+}
+
+message SendReply {
+  optional int32 data_sent = 1;
+}
+
+
+message ReceiveRequest {
+  enum Flags {
+    MSG_OOB = 1;
+    MSG_PEEK = 2;
+  }
+  required string socket_descriptor = 1;
+  required int32 data_size = 2;
+  optional int32 flags = 3 [default=0];
+  optional double timeout_seconds = 5 [default=-1];
+}
+
+message ReceiveReply {
+  optional int64 stream_offset = 2;
+  optional bytes data = 3 [ctype=CORD];
+  optional AddressPort received_from = 4;
+  optional int32 buffer_size = 5;
+}
+
+
+
+message PollEvent {
+
+  enum PollEventFlag {
+    SOCKET_POLLNONE = 0;
+    SOCKET_POLLIN = 1;
+    SOCKET_POLLPRI = 2;
+    SOCKET_POLLOUT = 4;
+    SOCKET_POLLERR = 8;
+    SOCKET_POLLHUP = 16;
+    SOCKET_POLLNVAL = 32;
+    SOCKET_POLLRDNORM = 64;
+    SOCKET_POLLRDBAND = 128;
+    SOCKET_POLLWRNORM = 256;
+    SOCKET_POLLWRBAND = 512;
+    SOCKET_POLLMSG = 1024;
+    SOCKET_POLLREMOVE = 4096;
+    SOCKET_POLLRDHUP = 8192;
+  };
+
+  required string socket_descriptor = 1;
+  required int32 requested_events = 2;
+  required int32 observed_events = 3;
+}
+
+message PollRequest {
+  repeated PollEvent events = 1;
+  optional double timeout_seconds = 2 [default=-1];
+}
+
+message PollReply {
+  repeated PollEvent events = 2;
+}
+
+message ResolveRequest {
+  required string name = 1;
+  repeated CreateSocketRequest.SocketFamily address_families = 2;
+}
+
+message ResolveReply {
+  enum ErrorCode {
+    SOCKET_EAI_ADDRFAMILY = 1;
+    SOCKET_EAI_AGAIN = 2;
+    SOCKET_EAI_BADFLAGS = 3;
+    SOCKET_EAI_FAIL = 4;
+    SOCKET_EAI_FAMILY = 5;
+    SOCKET_EAI_MEMORY = 6;
+    SOCKET_EAI_NODATA = 7;
+    SOCKET_EAI_NONAME = 8;
+    SOCKET_EAI_SERVICE = 9;
+    SOCKET_EAI_SOCKTYPE = 10;
+    SOCKET_EAI_SYSTEM = 11;
+    SOCKET_EAI_BADHINTS = 12;
+    SOCKET_EAI_PROTOCOL = 13;
+    SOCKET_EAI_OVERFLOW = 14;
+    SOCKET_EAI_MAX = 15;
+  };
+
+  repeated bytes packed_address = 2;
+  optional string canonical_name = 3;
+  repeated string aliases = 4;
+}
diff --git a/go/src/google.golang.org/appengine/internal/taskqueue/taskqueue_service.pb.go b/go/src/google.golang.org/appengine/internal/taskqueue/taskqueue_service.pb.go
index 9180ba6..6c9b90a 100644
--- a/go/src/google.golang.org/appengine/internal/taskqueue/taskqueue_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/taskqueue/taskqueue_service.pb.go
@@ -52,11 +52,13 @@
 package taskqueue
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 import appengine "google.golang.org/appengine/internal/datastore"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type TaskQueueServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/transaction.go b/go/src/google.golang.org/appengine/internal/transaction.go
index d8e063e..28a6d18 100644
--- a/go/src/google.golang.org/appengine/internal/transaction.go
+++ b/go/src/google.golang.org/appengine/internal/transaction.go
@@ -1,3 +1,7 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package internal
 
 // This file implements hooks for applying datastore transactions.
diff --git a/go/src/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go b/go/src/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go
index e559522..5eff3c3 100644
--- a/go/src/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go
@@ -16,10 +16,12 @@
 package urlfetch
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type URLFetchServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/user/user_service.pb.go b/go/src/google.golang.org/appengine/internal/user/user_service.pb.go
index 67627d9..6b52ffc 100644
--- a/go/src/google.golang.org/appengine/internal/user/user_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/user/user_service.pb.go
@@ -22,10 +22,12 @@
 package user
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type UserServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/internal/xmpp/xmpp_service.pb.go b/go/src/google.golang.org/appengine/internal/xmpp/xmpp_service.pb.go
index be1e986..6d5b0ae 100644
--- a/go/src/google.golang.org/appengine/internal/xmpp/xmpp_service.pb.go
+++ b/go/src/google.golang.org/appengine/internal/xmpp/xmpp_service.pb.go
@@ -24,10 +24,12 @@
 package xmpp
 
 import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
 import math "math"
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
+var _ = fmt.Errorf
 var _ = math.Inf
 
 type XmppServiceError_ErrorCode int32
diff --git a/go/src/google.golang.org/appengine/log/log_test.go b/go/src/google.golang.org/appengine/log/log_test.go
index 5d7e985..726468e 100644
--- a/go/src/google.golang.org/appengine/log/log_test.go
+++ b/go/src/google.golang.org/appengine/log/log_test.go
@@ -1,3 +1,7 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package log
 
 import (
diff --git a/go/src/google.golang.org/appengine/memcache/memcache.go b/go/src/google.golang.org/appengine/memcache/memcache.go
index f91331f..d8eed4b 100644
--- a/go/src/google.golang.org/appengine/memcache/memcache.go
+++ b/go/src/google.golang.org/appengine/memcache/memcache.go
@@ -196,7 +196,7 @@
 // by delta and returns the new value. The value must fit in a uint64.
 // Overflow wraps around, and underflow is capped to zero. The
 // provided delta may be negative. If the key doesn't exist in
-// memcacheg, the provided initial value is used to atomically
+// memcache, the provided initial value is used to atomically
 // populate it before the delta is applied.
 // The key must be at most 250 bytes in length.
 func Increment(c context.Context, key string, delta int64, initialValue uint64) (newValue uint64, err error) {
diff --git a/go/src/google.golang.org/appengine/memcache/memcache_test.go b/go/src/google.golang.org/appengine/memcache/memcache_test.go
index 70e3830..5f93954 100644
--- a/go/src/google.golang.org/appengine/memcache/memcache_test.go
+++ b/go/src/google.golang.org/appengine/memcache/memcache_test.go
@@ -1,3 +1,7 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package memcache
 
 import (
diff --git a/go/src/google.golang.org/appengine/namespace.go b/go/src/google.golang.org/appengine/namespace.go
index 9c00e90..65b0a00 100644
--- a/go/src/google.golang.org/appengine/namespace.go
+++ b/go/src/google.golang.org/appengine/namespace.go
@@ -20,7 +20,6 @@
 		return nil, fmt.Errorf("appengine: namespace %q does not match /%s/", namespace, validNamespace)
 	}
 	n := &namespacedContext{
-		ctx:       c,
 		namespace: namespace,
 	}
 	return internal.WithNamespace(internal.WithCallOverride(c, n.call), namespace), nil
@@ -31,14 +30,13 @@
 
 // namespacedContext wraps a Context to support namespaces.
 type namespacedContext struct {
-	ctx       context.Context
 	namespace string
 }
 
-func (n *namespacedContext) call(_ context.Context, service, method string, in, out proto.Message) error {
+func (n *namespacedContext) call(ctx context.Context, service, method string, in, out proto.Message) error {
 	// Apply any namespace mods.
 	if mod, ok := internal.NamespaceMods[service]; ok {
 		mod(in, n.namespace)
 	}
-	return internal.Call(n.ctx, service, method, in, out)
+	return internal.Call(ctx, service, method, in, out)
 }
diff --git a/go/src/google.golang.org/appengine/namespace_test.go b/go/src/google.golang.org/appengine/namespace_test.go
index 92fa534..dba45a3 100644
--- a/go/src/google.golang.org/appengine/namespace_test.go
+++ b/go/src/google.golang.org/appengine/namespace_test.go
@@ -1,7 +1,19 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package appengine
 
 import (
+	"strings"
 	"testing"
+
+	"github.com/golang/protobuf/proto"
+	"golang.org/x/net/context"
+
+	"google.golang.org/appengine/internal"
+	"google.golang.org/appengine/internal/aetesting"
+	basepb "google.golang.org/appengine/internal/base"
 )
 
 func TestNamespaceValidity(t *testing.T) {
@@ -23,7 +35,7 @@
 		{" ", false},
 	}
 	for _, tc := range testCases {
-		_, err := Namespace(nil, tc.namespace)
+		_, err := Namespace(context.Background(), tc.namespace)
 		if err == nil && !tc.ok {
 			t.Errorf("Namespace %q should be rejected, but wasn't", tc.namespace)
 		} else if err != nil && tc.ok {
@@ -31,3 +43,49 @@
 		}
 	}
 }
+
+func TestNamespaceApplication(t *testing.T) {
+	internal.NamespaceMods["srv"] = func(m proto.Message, namespace string) {
+		sm := m.(*basepb.StringProto)
+		if strings.Contains(sm.GetValue(), "-") {
+			// be idempotent
+			return
+		}
+		sm.Value = proto.String(sm.GetValue() + "-" + namespace)
+	}
+	ctx := aetesting.FakeSingleContext(t, "srv", "mth", func(in, out *basepb.StringProto) error {
+		out.Value = proto.String(in.GetValue())
+		return nil
+	})
+	call := func(ctx context.Context, in string) (out string, ok bool) {
+		inm := &basepb.StringProto{Value: &in}
+		outm := &basepb.StringProto{}
+		if err := internal.Call(ctx, "srv", "mth", inm, outm); err != nil {
+			t.Errorf("RPC(in=%q) failed: %v", in, err)
+			return "", false
+		}
+		return outm.GetValue(), true
+	}
+
+	// Check without a namespace.
+	got, ok := call(ctx, "foo")
+	if !ok {
+		t.FailNow()
+	}
+	if got != "foo" {
+		t.Errorf("Un-namespaced RPC returned %q, want %q", got, "foo")
+	}
+
+	// Now check by applying a namespace.
+	nsCtx, err := Namespace(ctx, "myns")
+	if err != nil {
+		t.Fatal(err)
+	}
+	got, ok = call(nsCtx, "bar")
+	if !ok {
+		t.FailNow()
+	}
+	if got != "bar-myns" {
+		t.Errorf("Namespaced RPC returned %q, want %q", got, "bar-myns")
+	}
+}
diff --git a/go/src/google.golang.org/appengine/remote_api/client_test.go b/go/src/google.golang.org/appengine/remote_api/client_test.go
index 4a49cbe..2e892a0 100644
--- a/go/src/google.golang.org/appengine/remote_api/client_test.go
+++ b/go/src/google.golang.org/appengine/remote_api/client_test.go
@@ -1,3 +1,7 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 package remote_api
 
 import (
diff --git a/go/src/google.golang.org/appengine/search/doc.go b/go/src/google.golang.org/appengine/search/doc.go
new file mode 100644
index 0000000..ef56415
--- /dev/null
+++ b/go/src/google.golang.org/appengine/search/doc.go
@@ -0,0 +1,205 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+/*
+Package search provides a client for App Engine's search service.
+
+
+Basic Operations
+
+Indexes contain documents. Each index is identified by its name: a
+human-readable ASCII string.
+
+Within an index, documents are associated with an ID, which is also
+a human-readable ASCII string. A document's contents are a mapping from
+case-sensitive field names to values. Valid types for field values are:
+  - string,
+  - search.Atom,
+  - search.HTML,
+  - time.Time (stored with millisecond precision),
+  - float64 (value between -2,147,483,647 and 2,147,483,647 inclusive),
+  - appengine.GeoPoint.
+
+The Get and Put methods on an Index load and save a document.
+A document's contents are typically represented by a struct pointer.
+
+Example code:
+
+	type Doc struct {
+		Author   string
+		Comment  string
+		Creation time.Time
+	}
+
+	index, err := search.Open("comments")
+	if err != nil {
+		return err
+	}
+	newID, err := index.Put(ctx, "", &Doc{
+		Author:   "gopher",
+		Comment:  "the truth of the matter",
+		Creation: time.Now(),
+	})
+	if err != nil {
+		return err
+	}
+
+A single document can be retrieved by its ID. Pass a destination struct
+to Get to hold the resulting document.
+
+	var doc Doc
+	err := index.Get(ctx, id, &doc)
+	if err != nil {
+		return err
+	}
+
+
+Search and Listing Documents
+
+Indexes have two methods for retrieving multiple documents at once: Search and
+List.
+
+Searching an index for a query will result in an iterator. As with an iterator
+from package datastore, pass a destination struct to Next to decode the next
+result. Next will return Done when the iterator is exhausted.
+
+	for t := index.Search(ctx, "Comment:truth", nil); ; {
+		var doc Doc
+		id, err := t.Next(&doc)
+		if err == search.Done {
+			break
+		}
+		if err != nil {
+			return err
+		}
+		fmt.Fprintf(w, "%s -> %#v\n", id, doc)
+	}
+
+Search takes a string query to determine which documents to return. The query
+can be simple, such as a single word to match, or complex. The query
+language is described at
+https://cloud.google.com/appengine/docs/go/search/query_strings
+
+Search also takes an optional SearchOptions struct which gives much more
+control over how results are calculated and returned.
+
+Call List to iterate over all documents in an index.
+
+	for t := index.List(ctx, nil); ; {
+		var doc Doc
+		id, err := t.Next(&doc)
+		if err == search.Done {
+			break
+		}
+		if err != nil {
+			return err
+		}
+		fmt.Fprintf(w, "%s -> %#v\n", id, doc)
+	}
+
+
+Fields and Facets
+
+A document's contents can be represented by a variety of types. These are
+typically struct pointers, but they can also be represented by any type
+implementing the FieldLoadSaver interface. The FieldLoadSaver allows metadata
+to be set for the document with the DocumentMetadata type. Struct pointers are
+more strongly typed and are easier to use; FieldLoadSavers are more flexible.
+
+A document's contents can be expressed in two ways: fields and facets.
+
+Fields are the most common way of providing content for documents. Fields can
+store data in multiple types and can be matched in searches using query
+strings.
+
+Facets provide a way to attach categorical information to a document. The only
+valid types for facets are search.Atom and float64. Facets allow search
+results to contain summaries of the categories matched in a search, and to
+restrict searches to only match against specific categories.
+
+By default, for struct pointers, all of the struct fields are used as document
+fields, and the field name used is the same as on the struct (and hence must
+start with an upper case letter). Struct fields may have a
+`search:"name,options"` tag. The name must start with a letter and be
+composed only of word characters. If options is "facet" then the struct
+field will be used as a document facet. If options is "" then the comma
+may be omitted. There are no other recognized options.
+
+Example code:
+
+	// A and B are renamed to a and b.
+	// A, C and I are facets.
+	// D's tag is equivalent to having no tag at all (E).
+	// I has tag information for both the search and json packages.
+	type TaggedStruct struct {
+		A float64 `search:"a,facet"`
+		B float64 `search:"b"`
+		C float64 `search:",facet"`
+		D float64 `search:""`
+		E float64
+		I float64 `search:",facet" json:"i"`
+	}
+
+
+The FieldLoadSaver Interface
+
+A document's contents can also be represented by any type that implements the
+FieldLoadSaver interface. This type may be a struct pointer, but it
+does not have to be. The search package will call Load when loading the
+document's contents, and Save when saving them. In addition to a slice of
+Fields, the Load and Save methods also use the DocumentMetadata type to
+provide additional information about a document (such as its Rank, or set of
+Facets). Possible uses for this interface include deriving non-stored fields,
+verifying fields or setting specific languages for string and HTML fields.
+
+Example code:
+
+	type CustomFieldsExample struct {
+		// Item's title and which language it is in.
+		Title string
+		Lang  string
+		// Mass, in grams.
+		Mass int
+	}
+
+	func (x *CustomFieldsExample) Load(fields []search.Field, meta *search.DocumentMetadata) error {
+		// Load the title field, failing if any other field is found.
+		for _, f := range fields {
+			if f.Name != "title" {
+				return fmt.Errorf("unknown field %q", f.Name)
+			}
+			s, ok := f.Value.(string)
+			if !ok {
+				return fmt.Errorf("unsupported type %T for field %q", f.Value, f.Name)
+			}
+			x.Title = s
+			x.Lang = f.Language
+		}
+		// Load the mass facet, failing if any other facet is found.
+		for _, f := range meta.Facets {
+			if f.Name != "mass" {
+				return fmt.Errorf("unknown facet %q", f.Name)
+			}
+			m, ok := f.Value.(float64)
+			if !ok {
+				return fmt.Errorf("unsupported type %T for facet %q", f.Value, f.Name)
+			}
+			x.Mass = int(m)
+		}
+		return nil
+	}
+
+	func (x *CustomFieldsExample) Save() ([]search.Field, *search.DocumentMetadata, error) {
+		fields := []search.Field{
+			{Name: "title", Value: x.Title, Language: x.Lang},
+		}
+		meta := &search.DocumentMetdata{
+			Facets: {
+				{Name: "mass", Value: float64(x.Mass)},
+			},
+		}
+		return fields, meta, nil
+	}
+*/
+package search
diff --git a/go/src/google.golang.org/appengine/search/field.go b/go/src/google.golang.org/appengine/search/field.go
index b4c31c0..e2f273d 100644
--- a/go/src/google.golang.org/appengine/search/field.go
+++ b/go/src/google.golang.org/appengine/search/field.go
@@ -4,15 +4,10 @@
 
 package search
 
-import (
-	"fmt"
-	"reflect"
-)
-
 // Field is a name/value pair. A search index's document can be loaded and
 // saved as a sequence of Fields.
 type Field struct {
-	// Name is the field name.
+	// Name is the field name. A valid field name matches /[A-Za-z][A-Za-z0-9_]*/.
 	Name string
 	// Value is the field value. The valid types are:
 	//  - string,
@@ -32,12 +27,34 @@
 	Derived bool
 }
 
+// Facet is a name/value pair which is used to add categorical information to a
+// document.
+type Facet struct {
+	// Name is the facet name. A valid facet name matches /[A-Za-z][A-Za-z0-9_]*/.
+	// A facet name cannot be longer than 500 characters.
+	Name string
+	// Value is the facet value.
+	//
+	// When being used in documents (for example, in
+	// DocumentMetadata.Facets), the valid types are:
+	//  - search.Atom,
+	//  - float64.
+	//
+	// When being used in SearchOptions.Refinements or being returned
+	// in FacetResult, the valid types are:
+	//  - search.Atom,
+	//  - search.Range.
+	Value interface{}
+}
+
 // DocumentMetadata is a struct containing information describing a given document.
 type DocumentMetadata struct {
 	// Rank is an integer specifying the order the document will be returned in
 	// search results. If zero, the rank will be set to the number of seconds since
 	// 2011-01-01 00:00:00 UTC when being Put into an index.
 	Rank int
+	// Facets is the set of facets for this document.
+	Facets []Facet
 }
 
 // FieldLoadSaver can be converted from and to a slice of Fields
@@ -63,82 +80,3 @@
 }
 
 var _ FieldLoadSaver = (*FieldList)(nil)
-
-// structFLS adapts a struct to be a FieldLoadSaver.
-type structFLS struct {
-	reflect.Value
-}
-
-func (s structFLS) Load(fields []Field, _ *DocumentMetadata) (err error) {
-	for _, field := range fields {
-		f := s.FieldByName(field.Name)
-		if !f.IsValid() {
-			err = &ErrFieldMismatch{
-				FieldName: field.Name,
-				Reason:    "no such struct field",
-			}
-			continue
-		}
-		if !f.CanSet() {
-			err = &ErrFieldMismatch{
-				FieldName: field.Name,
-				Reason:    "cannot set struct field",
-			}
-			continue
-		}
-		v := reflect.ValueOf(field.Value)
-		if ft, vt := f.Type(), v.Type(); ft != vt {
-			err = &ErrFieldMismatch{
-				FieldName: field.Name,
-				Reason:    fmt.Sprintf("type mismatch: %v for %v data", ft, vt),
-			}
-			continue
-		}
-		f.Set(v)
-	}
-	return err
-}
-
-func (s structFLS) Save() ([]Field, *DocumentMetadata, error) {
-	fields := make([]Field, 0, s.NumField())
-	for i := 0; i < s.NumField(); i++ {
-		f := s.Field(i)
-		if !f.CanSet() {
-			continue
-		}
-		fields = append(fields, Field{
-			Name:  s.Type().Field(i).Name,
-			Value: f.Interface(),
-		})
-	}
-	return fields, nil, nil
-}
-
-// newStructFLS returns a FieldLoadSaver for the struct pointer p.
-func newStructFLS(p interface{}) (FieldLoadSaver, error) {
-	v := reflect.ValueOf(p)
-	if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct {
-		return nil, ErrInvalidDocumentType
-	}
-	return structFLS{v.Elem()}, nil
-}
-
-// LoadStruct loads the fields from f to dst. dst must be a struct pointer.
-func LoadStruct(dst interface{}, f []Field) error {
-	x, err := newStructFLS(dst)
-	if err != nil {
-		return err
-	}
-	return x.Load(f, nil)
-}
-
-// SaveStruct returns the fields from src as a slice of Field.
-// src must be a struct pointer.
-func SaveStruct(src interface{}) ([]Field, error) {
-	x, err := newStructFLS(src)
-	if err != nil {
-		return nil, err
-	}
-	fs, _, err := x.Save()
-	return fs, err
-}
diff --git a/go/src/google.golang.org/appengine/search/search.go b/go/src/google.golang.org/appengine/search/search.go
index fede7dd..cbba874 100644
--- a/go/src/google.golang.org/appengine/search/search.go
+++ b/go/src/google.golang.org/appengine/search/search.go
@@ -2,89 +2,6 @@
 // Use of this source code is governed by the Apache 2.0
 // license that can be found in the LICENSE file.
 
-/*
-Package search provides a client for App Engine's search service.
-
-Indexes contains documents, and a document's contents are a mapping from case-
-sensitive field names to values. In Go, documents are represented by struct
-pointers, and the valid types for a struct's fields are:
-  - string,
-  - search.Atom,
-  - search.HTML,
-  - time.Time (stored with millisecond precision),
-  - float64 (value between -2,147,483,647 and 2,147,483,647 inclusive),
-  - appengine.GeoPoint.
-
-Documents can also be represented by any type implementing the FieldLoadSaver
-interface.
-
-Example code:
-
-	type Doc struct {
-		Author   string
-		Comment  string
-		Creation time.Time
-	}
-
-	index, err := search.Open("comments")
-	if err != nil {
-		return err
-	}
-	newID, err := index.Put(c, "", &Doc{
-		Author:   "gopher",
-		Comment:  "the truth of the matter",
-		Creation: time.Now(),
-	})
-	if err != nil {
-		return err
-	}
-
-Searching an index for a query will result in an iterator. As with an iterator
-from package datastore, pass a destination struct to Next to decode the next
-result. Next will return Done when the iterator is exhausted.
-
-	for t := index.Search(c, "Comment:truth", nil); ; {
-		var doc Doc
-		id, err := t.Next(&doc)
-		if err == search.Done {
-			break
-		}
-		if err != nil {
-			return err
-		}
-		fmt.Fprintf(w, "%s -> %#v\n", id, doc)
-	}
-
-Call List to iterate over documents.
-
-	for t := index.List(c, nil); ; {
-		var doc Doc
-		id, err := t.Next(&doc)
-		if err == search.Done {
-			break
-		}
-		if err != nil {
-			return err
-		}
-		fmt.Fprintf(w, "%s -> %#v\n", id, doc)
-	}
-
-A single document can also be retrieved by its ID. Pass a destination struct
-to Get to hold the resulting document.
-
-	var doc Doc
-	err := index.Get(c, id, &doc)
-	if err != nil {
-		return err
-	}
-
-Queries are expressed as strings, plus some optional parameters. The query
-language is described at
-https://cloud.google.com/appengine/docs/go/search/query_strings
-
-Note that in Go, field names come from the struct field definition and begin
-with an upper case letter.
-*/
 package search // import "google.golang.org/appengine/search"
 
 // TODO: let Put specify the document language: "en", "fr", etc. Also: order_id?? storage??
@@ -96,6 +13,7 @@
 import (
 	"errors"
 	"fmt"
+	"math"
 	"reflect"
 	"regexp"
 	"strconv"
@@ -120,18 +38,6 @@
 	ErrNoSuchDocument = errors.New("search: no such document")
 )
 
-// ErrFieldMismatch is returned when a field is to be loaded into a different
-// than the one it was stored from, or when a field is missing or unexported in
-// the destination struct.
-type ErrFieldMismatch struct {
-	FieldName string
-	Reason    string
-}
-
-func (e *ErrFieldMismatch) Error() string {
-	return fmt.Sprintf("search: cannot load field %q: %s", e.FieldName, e.Reason)
-}
-
 // Atom is a document field whose contents are indexed as a single indivisible
 // string.
 type Atom string
@@ -155,11 +61,12 @@
 }
 
 var (
-	fieldNameRE = regexp.MustCompile(`^[A-Z][A-Za-z0-9_]*$`)
+	fieldNameRE = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)
 	languageRE  = regexp.MustCompile(`^[a-z]{2}$`)
 )
 
-// validFieldName is the Go equivalent of Python's _CheckFieldName.
+// validFieldName is the Go equivalent of Python's _CheckFieldName. It checks
+// the validity of both field and facet names.
 func validFieldName(s string) bool {
 	return len(s) <= 500 && fieldNameRE.MatchString(s)
 }
@@ -213,22 +120,10 @@
 // src must be a non-nil struct pointer or implement the FieldLoadSaver
 // interface.
 func (x *Index) Put(c context.Context, id string, src interface{}) (string, error) {
-	fields, meta, err := saveDoc(src)
+	d, err := saveDoc(src)
 	if err != nil {
 		return "", err
 	}
-	d := &pb.Document{
-		Field:   fields,
-		OrderId: proto.Int32(int32(time.Since(orderIDEpoch).Seconds())),
-	}
-	if meta != nil {
-		if meta.Rank != 0 {
-			if !validDocRank(meta.Rank) {
-				return "", fmt.Errorf("search: invalid rank %d, must be [0, 2^31)", meta.Rank)
-			}
-			*d.OrderId = int32(meta.Rank)
-		}
-	}
 	if id != "" {
 		if !validIndexNameOrDocID(id) {
 			return "", fmt.Errorf("search: invalid ID %q", id)
@@ -291,10 +186,7 @@
 	if len(res.Document) != 1 || res.Document[0].GetId() != id {
 		return ErrNoSuchDocument
 	}
-	metadata := &DocumentMetadata{
-		Rank: int(res.Document[0].GetOrderId()),
-	}
-	return loadDoc(dst, res.Document[0].Field, nil, metadata)
+	return loadDoc(dst, res.Document[0], nil)
 }
 
 // Delete deletes a document from the index.
@@ -327,13 +219,10 @@
 		count:         -1,
 		listInclusive: true,
 		more:          moreList,
-		limit:         -1,
 	}
 	if opts != nil {
 		t.listStartID = opts.StartID
-		if opts.Limit > 0 {
-			t.limit = opts.Limit
-		}
+		t.limit = opts.Limit
 		t.idsOnly = opts.IDsOnly
 	}
 	return t
@@ -365,7 +254,7 @@
 	}
 	t.listRes = res.Document
 	t.listStartID, t.listInclusive, t.more = "", false, nil
-	if len(res.Document) != 0 {
+	if len(res.Document) != 0 && t.limit <= 0 {
 		if id := res.Document[len(res.Document)-1].GetId(); id != "" {
 			t.listStartID, t.more = id, moreList
 		}
@@ -396,26 +285,37 @@
 		index:       x,
 		searchQuery: query,
 		more:        moreSearch,
-		limit:       -1,
 	}
 	if opts != nil {
-		if opts.Limit > 0 {
-			t.limit = opts.Limit
+		if opts.Cursor != "" {
+			if opts.Offset != 0 {
+				return errIter("at most one of Cursor and Offset may be specified")
+			}
+			t.searchCursor = proto.String(string(opts.Cursor))
 		}
+		t.limit = opts.Limit
 		t.fields = opts.Fields
 		t.idsOnly = opts.IDsOnly
 		t.sort = opts.Sort
 		t.exprs = opts.Expressions
+		t.refinements = opts.Refinements
+		t.facetOpts = opts.Facets
+		t.searchOffset = opts.Offset
 	}
 	return t
 }
 
 func moreSearch(t *Iterator) error {
+	// We use per-result (rather than single/per-page) cursors since this
+	// lets us return a Cursor for every iterator document. The two cursor
+	// types are largely interchangeable: a page cursor is the same as the
+	// last per-result cursor in a given search response.
 	req := &pb.SearchRequest{
 		Params: &pb.SearchParams{
 			IndexSpec:  &t.index.spec,
 			Query:      &t.searchQuery,
-			CursorType: pb.SearchParams_SINGLE.Enum(),
+			Cursor:     t.searchCursor,
+			CursorType: pb.SearchParams_PER_RESULT.Enum(),
 			FieldSpec: &pb.FieldSpec{
 				Name: t.fields,
 			},
@@ -424,6 +324,10 @@
 	if t.limit > 0 {
 		req.Params.Limit = proto.Int32(int32(t.limit))
 	}
+	if t.searchOffset > 0 {
+		req.Params.Offset = proto.Int32(int32(t.searchOffset))
+		t.searchOffset = 0
+	}
 	if t.idsOnly {
 		req.Params.KeysOnly = &t.idsOnly
 	}
@@ -432,16 +336,25 @@
 			return err
 		}
 	}
+	if t.refinements != nil {
+		if err := refinementsToProto(t.refinements, req.Params); err != nil {
+			return err
+		}
+	}
 	for _, e := range t.exprs {
 		req.Params.FieldSpec.Expression = append(req.Params.FieldSpec.Expression, &pb.FieldSpec_Expression{
 			Name:       proto.String(e.Name),
 			Expression: proto.String(e.Expr),
 		})
 	}
-
-	if t.searchCursor != nil {
-		req.Params.Cursor = t.searchCursor
+	for _, f := range t.facetOpts {
+		if err := f.setParams(req.Params); err != nil {
+			return fmt.Errorf("bad FacetSearchOption: %v", err)
+		}
 	}
+	// Don't repeat facet search.
+	t.facetOpts = nil
+
 	res := &pb.SearchResponse{}
 	if err := internal.Call(t.c, "search", "Search", req, res); err != nil {
 		return err
@@ -450,11 +363,14 @@
 		return fmt.Errorf("search: %s: %s", res.Status.GetCode(), res.Status.GetErrorDetail())
 	}
 	t.searchRes = res.Result
+	if len(res.FacetResult) > 0 {
+		t.facetRes = res.FacetResult
+	}
 	t.count = int(*res.MatchedCount)
-	if res.Cursor != nil {
-		t.searchCursor, t.more = res.Cursor, moreSearch
+	if t.limit > 0 {
+		t.more = nil
 	} else {
-		t.searchCursor, t.more = nil, nil
+		t.more = moreSearch
 	}
 	return nil
 }
@@ -481,9 +397,30 @@
 	// document.
 	Expressions []FieldExpression
 
-	// TODO: cursor, offset, maybe others.
+	// Facets controls what facet information is returned for these search results.
+	// If no options are specified, no facet results will be returned.
+	Facets []FacetSearchOption
+
+	// Refinements filters the returned documents by requiring them to contain facets
+	// with specific values. Refinements are applied in conjunction for facets with
+	// different names, and in disjunction otherwise.
+	Refinements []Facet
+
+	// Cursor causes the results to commence with the first document after
+	// the document associated with the cursor.
+	Cursor Cursor
+
+	// Offset specifies the number of documents to skip over before returning results.
+	// When specified, Cursor must be nil.
+	Offset int
 }
 
+// Cursor represents an iterator's position.
+//
+// The string value of a cursor is web-safe. It can be saved and restored
+// for later use.
+type Cursor string
+
 // FieldExpression defines a custom expression to evaluate for each result.
 type FieldExpression struct {
 	// Name is the name to use for the computed field.
@@ -495,6 +432,130 @@
 	Expr string
 }
 
+// FacetSearchOption controls what facet information is returned in search results.
+type FacetSearchOption interface {
+	setParams(*pb.SearchParams) error
+}
+
+// AutoFacetDiscovery returns a FacetSearchOption which enables automatic facet
+// discovery for the search. Automatic facet discovery looks for the facets
+// which appear the most often in the aggregate in the matched documents.
+//
+// The maximum number of facets returned is controlled by facetLimit, and the
+// maximum number of values per facet by facetLimit. A limit of zero indicates
+// a default limit should be used.
+func AutoFacetDiscovery(facetLimit, valueLimit int) FacetSearchOption {
+	return &autoFacetOpt{facetLimit, valueLimit}
+}
+
+type autoFacetOpt struct {
+	facetLimit, valueLimit int
+}
+
+const defaultAutoFacetLimit = 10 // As per python runtime search.py.
+
+func (o *autoFacetOpt) setParams(params *pb.SearchParams) error {
+	lim := int32(o.facetLimit)
+	if lim == 0 {
+		lim = defaultAutoFacetLimit
+	}
+	params.AutoDiscoverFacetCount = &lim
+	if o.valueLimit > 0 {
+		params.FacetAutoDetectParam = &pb.FacetAutoDetectParam{
+			ValueLimit: proto.Int32(int32(o.valueLimit)),
+		}
+	}
+	return nil
+}
+
+// FacetDiscovery returns a FacetSearchOption which selects a facet to be
+// returned with the search results. By default, the most frequently
+// occurring values for that facet will be returned. However, you can also
+// specify a list of particular Atoms or specific Ranges to return.
+func FacetDiscovery(name string, value ...interface{}) FacetSearchOption {
+	return &facetOpt{name, value}
+}
+
+type facetOpt struct {
+	name   string
+	values []interface{}
+}
+
+func (o *facetOpt) setParams(params *pb.SearchParams) error {
+	req := &pb.FacetRequest{Name: &o.name}
+	params.IncludeFacet = append(params.IncludeFacet, req)
+	if len(o.values) == 0 {
+		return nil
+	}
+	vtype := reflect.TypeOf(o.values[0])
+	reqParam := &pb.FacetRequestParam{}
+	for _, v := range o.values {
+		if reflect.TypeOf(v) != vtype {
+			return errors.New("values must all be Atom, or must all be Range")
+		}
+		switch v := v.(type) {
+		case Atom:
+			reqParam.ValueConstraint = append(reqParam.ValueConstraint, string(v))
+		case Range:
+			rng, err := rangeToProto(v)
+			if err != nil {
+				return fmt.Errorf("invalid range: %v", err)
+			}
+			reqParam.Range = append(reqParam.Range, rng)
+		default:
+			return fmt.Errorf("unsupported value type %T", v)
+		}
+	}
+	req.Params = reqParam
+	return nil
+}
+
+// FacetDocumentDepth returns a FacetSearchOption which controls the number of
+// documents to be evaluated with preparing facet results.
+func FacetDocumentDepth(depth int) FacetSearchOption {
+	return facetDepthOpt(depth)
+}
+
+type facetDepthOpt int
+
+func (o facetDepthOpt) setParams(params *pb.SearchParams) error {
+	params.FacetDepth = proto.Int32(int32(o))
+	return nil
+}
+
+// FacetResult represents the number of times a particular facet and value
+// appeared in the documents matching a search request.
+type FacetResult struct {
+	Facet
+
+	// Count is the number of times this specific facet and value appeared in the
+	// matching documents.
+	Count int
+}
+
+// Range represents a numeric range with inclusive start and exclusive end.
+// Start may be specified as math.Inf(-1) to indicate there is no minimum
+// value, and End may similarly be specified as math.Inf(1); at least one of
+// Start or End must be a finite number.
+type Range struct {
+	Start, End float64
+}
+
+var (
+	negInf = math.Inf(-1)
+	posInf = math.Inf(1)
+)
+
+// AtLeast returns a Range matching any value greater than, or equal to, min.
+func AtLeast(min float64) Range {
+	return Range{Start: min, End: posInf}
+}
+
+// LessThan returns a Range matching any value less than max.
+func LessThan(max float64) Range {
+	return Range{Start: negInf, End: max}
+}
+
 // SortOptions control the ordering and scoring of search results.
 type SortOptions struct {
 	// Expressions is a slice of expressions representing a multi-dimensional
@@ -586,6 +647,59 @@
 	return nil
 }
 
+func refinementsToProto(refinements []Facet, params *pb.SearchParams) error {
+	for _, r := range refinements {
+		ref := &pb.FacetRefinement{
+			Name: proto.String(r.Name),
+		}
+		switch v := r.Value.(type) {
+		case Atom:
+			ref.Value = proto.String(string(v))
+		case Range:
+			rng, err := rangeToProto(v)
+			if err != nil {
+				return fmt.Errorf("search: refinement for facet %q: %v", r.Name, err)
+			}
+			// Unfortunately there are two identical messages for identify Facet ranges.
+			ref.Range = &pb.FacetRefinement_Range{Start: rng.Start, End: rng.End}
+		default:
+			return fmt.Errorf("search: unsupported refinement for facet %q of type %T", r.Name, v)
+		}
+		params.FacetRefinement = append(params.FacetRefinement, ref)
+	}
+	return nil
+}
+
+func rangeToProto(r Range) (*pb.FacetRange, error) {
+	rng := &pb.FacetRange{}
+	if r.Start != negInf {
+		if !validFloat(r.Start) {
+			return nil, errors.New("invalid value for Start")
+		}
+		rng.Start = proto.String(strconv.FormatFloat(r.Start, 'e', -1, 64))
+	} else if r.End == posInf {
+		return nil, errors.New("either Start or End must be finite")
+	}
+	if r.End != posInf {
+		if !validFloat(r.End) {
+			return nil, errors.New("invalid value for End")
+		}
+		rng.End = proto.String(strconv.FormatFloat(r.End, 'e', -1, 64))
+	}
+	return rng, nil
+}
+
+func protoToRange(rng *pb.FacetRefinement_Range) Range {
+	r := Range{Start: negInf, End: posInf}
+	if x, err := strconv.ParseFloat(rng.GetStart(), 64); err != nil {
+		r.Start = x
+	}
+	if x, err := strconv.ParseFloat(rng.GetEnd(), 64); err != nil {
+		r.End = x
+	}
+	return r
+}
+
 // Iterator is the result of searching an index for a query or listing an
 // index.
 type Iterator struct {
@@ -598,20 +712,31 @@
 	listInclusive bool
 
 	searchRes    []*pb.SearchResult
+	facetRes     []*pb.FacetResult
 	searchQuery  string
 	searchCursor *string
+	searchOffset int
 	sort         *SortOptions
 
-	fields []string
-	exprs  []FieldExpression
+	fields      []string
+	exprs       []FieldExpression
+	refinements []Facet
+	facetOpts   []FacetSearchOption
 
 	more func(*Iterator) error
 
 	count   int
-	limit   int // items left to return; -1 for unlimited.
+	limit   int // items left to return; 0 for unlimited.
 	idsOnly bool
 }
 
+// errIter returns an iterator that only returns the given error.
+func errIter(err string) *Iterator {
+	return &Iterator{
+		err: errors.New(err),
+	}
+}
+
 // Done is returned when a query iteration has completed.
 var Done = errors.New("search: query has no more results")
 
@@ -619,6 +744,13 @@
 // query. It is only valid to call for iterators returned by Search.
 func (t *Iterator) Count() int { return t.count }
 
+// fetchMore retrieves more results, if there are no errors or pending results.
+func (t *Iterator) fetchMore() {
+	if t.err == nil && len(t.listRes)+len(t.searchRes) == 0 && t.more != nil {
+		t.err = t.more(t)
+	}
+}
+
 // Next returns the ID of the next result. When there are no more results,
 // Done is returned as the error.
 //
@@ -627,9 +759,7 @@
 // will be filled with the indexed fields. dst is ignored if this iterator was
 // created with an IDsOnly option.
 func (t *Iterator) Next(dst interface{}) (string, error) {
-	if t.err == nil && len(t.listRes)+len(t.searchRes) == 0 && t.more != nil {
-		t.err = t.more(t)
-	}
+	t.fetchMore()
 	if t.err != nil {
 		return "", t.err
 	}
@@ -643,6 +773,7 @@
 	case len(t.searchRes) != 0:
 		doc = t.searchRes[0].Document
 		exprs = t.searchRes[0].Expression
+		t.searchCursor = t.searchRes[0].Cursor
 		t.searchRes = t.searchRes[1:]
 	default:
 		return "", Done
@@ -651,24 +782,57 @@
 		return "", errors.New("search: internal error: no document returned")
 	}
 	if !t.idsOnly && dst != nil {
-		metadata := &DocumentMetadata{
-			Rank: int(doc.GetOrderId()),
-		}
-		if err := loadDoc(dst, doc.Field, exprs, metadata); err != nil {
+		if err := loadDoc(dst, doc, exprs); err != nil {
 			return "", err
 		}
 	}
-	if t.limit > 0 {
-		t.limit--
-		if t.limit == 0 {
-			t.more = nil // prevent further fetches
-		}
-	}
 	return doc.GetId(), nil
 }
 
-// saveDoc converts from a struct pointer or FieldLoadSaver to protobufs.
-func saveDoc(src interface{}) ([]*pb.Field, *DocumentMetadata, error) {
+// Cursor returns the cursor associated with the current document (that is,
+// the document most recently returned by a call to Next).
+//
+// Passing this cursor in a future call to Search will cause those results
+// to commence with the first document after the current document.
+func (t *Iterator) Cursor() Cursor {
+	if t.searchCursor == nil {
+		return ""
+	}
+	return Cursor(*t.searchCursor)
+}
+
+// Facets returns the facets found within the search results, if any facets
+// were requested in the SearchOptions.
+func (t *Iterator) Facets() ([][]FacetResult, error) {
+	t.fetchMore()
+	if t.err != nil && t.err != Done {
+		return nil, t.err
+	}
+
+	var facets [][]FacetResult
+	for _, f := range t.facetRes {
+		fres := make([]FacetResult, 0, len(f.Value))
+		for _, v := range f.Value {
+			ref := v.Refinement
+			facet := FacetResult{
+				Facet: Facet{Name: ref.GetName()},
+				Count: int(v.GetCount()),
+			}
+			if ref.Value != nil {
+				facet.Value = Atom(*ref.Value)
+			} else {
+				facet.Value = protoToRange(ref.Range)
+			}
+			fres = append(fres, facet)
+		}
+		facets = append(facets, fres)
+	}
+	return facets, nil
+}
+
+// saveDoc converts from a struct pointer or
+// FieldLoadSaver/FieldMetadataLoadSaver to the Document protobuf.
+func saveDoc(src interface{}) (*pb.Document, error) {
 	var err error
 	var fields []Field
 	var meta *DocumentMetadata
@@ -679,10 +843,33 @@
 		fields, err = SaveStruct(src)
 	}
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
-	f, err := fieldsToProto(fields)
-	return f, meta, err
+
+	fieldsProto, err := fieldsToProto(fields)
+	if err != nil {
+		return nil, err
+	}
+	d := &pb.Document{
+		Field:   fieldsProto,
+		OrderId: proto.Int32(int32(time.Since(orderIDEpoch).Seconds())),
+	}
+	if meta != nil {
+		if meta.Rank != 0 {
+			if !validDocRank(meta.Rank) {
+				return nil, fmt.Errorf("search: invalid rank %d, must be [0, 2^31)", meta.Rank)
+			}
+			*d.OrderId = int32(meta.Rank)
+		}
+		if len(meta.Facets) > 0 {
+			facets, err := facetsToProto(meta.Facets)
+			if err != nil {
+				return nil, err
+			}
+			d.Facet = facets
+		}
+	}
+	return d, nil
 }
 
 func fieldsToProto(src []Field) ([]*pb.Field, error) {
@@ -757,12 +944,48 @@
 	return dst, nil
 }
 
-// loadDoc converts from protobufs and document metadata to a struct pointer or
-// FieldLoadSaver/FieldMetadataLoadSaver. Two slices of fields may be provided:
-// src represents the document's stored fields; exprs is the derived expressions
-// requested by the developer. The latter may be empty.
-func loadDoc(dst interface{}, src, exprs []*pb.Field, meta *DocumentMetadata) (err error) {
-	fields, err := protoToFields(src)
+func facetsToProto(src []Facet) ([]*pb.Facet, error) {
+	dst := make([]*pb.Facet, 0, len(src))
+	for _, f := range src {
+		if !validFieldName(f.Name) {
+			return nil, fmt.Errorf("search: invalid facet name %q", f.Name)
+		}
+		facetValue := &pb.FacetValue{}
+		switch x := f.Value.(type) {
+		case Atom:
+			if !utf8.ValidString(string(x)) {
+				return nil, fmt.Errorf("search: %q facet is invalid UTF-8: %q", f.Name, x)
+			}
+			facetValue.Type = pb.FacetValue_ATOM.Enum()
+			facetValue.StringValue = proto.String(string(x))
+		case float64:
+			if !validFloat(x) {
+				return nil, fmt.Errorf("search: numeric facet %q with invalid value %f", f.Name, x)
+			}
+			facetValue.Type = pb.FacetValue_NUMBER.Enum()
+			facetValue.StringValue = proto.String(strconv.FormatFloat(x, 'e', -1, 64))
+		default:
+			return nil, fmt.Errorf("search: unsupported facet type: %v", reflect.TypeOf(f.Value))
+		}
+		dst = append(dst, &pb.Facet{
+			Name:  proto.String(f.Name),
+			Value: facetValue,
+		})
+	}
+	return dst, nil
+}
+
+// loadDoc converts from protobufs to a struct pointer or
+// FieldLoadSaver/FieldMetadataLoadSaver. The src param provides the document's
+// stored fields and facets, and any document metadata.  An additional slice of
+// fields, exprs, may optionally be provided to contain any derived expressions
+// requested by the developer.
+func loadDoc(dst interface{}, src *pb.Document, exprs []*pb.Field) (err error) {
+	fields, err := protoToFields(src.Field)
+	if err != nil {
+		return err
+	}
+	facets, err := protoToFacets(src.Facet)
 	if err != nil {
 		return err
 	}
@@ -777,11 +1000,15 @@
 		}
 		fields = append(fields, exprFields...)
 	}
+	meta := &DocumentMetadata{
+		Rank:   int(src.GetOrderId()),
+		Facets: facets,
+	}
 	switch x := dst.(type) {
 	case FieldLoadSaver:
 		return x.Load(fields, meta)
 	default:
-		return LoadStruct(dst, fields)
+		return loadStructWithMeta(dst, fields, meta)
 	}
 }
 
@@ -830,6 +1057,34 @@
 	return dst, nil
 }
 
+func protoToFacets(facets []*pb.Facet) ([]Facet, error) {
+	if len(facets) == 0 {
+		return nil, nil
+	}
+	dst := make([]Facet, 0, len(facets))
+	for _, facet := range facets {
+		facetValue := facet.GetValue()
+		f := Facet{
+			Name: facet.GetName(),
+		}
+		switch facetValue.GetType() {
+		case pb.FacetValue_ATOM:
+			f.Value = Atom(facetValue.GetStringValue())
+		case pb.FacetValue_NUMBER:
+			sv := facetValue.GetStringValue()
+			x, err := strconv.ParseFloat(sv, 64)
+			if err != nil {
+				return nil, err
+			}
+			f.Value = x
+		default:
+			return nil, fmt.Errorf("search: internal error: unknown data type %s", facetValue.GetType())
+		}
+		dst = append(dst, f)
+	}
+	return dst, nil
+}
+
 func namespaceMod(m proto.Message, namespace string) {
 	set := func(s **string) {
 		if *s == nil {
diff --git a/go/src/google.golang.org/appengine/search/search_test.go b/go/src/google.golang.org/appengine/search/search_test.go
index 6178c51..fd00654 100644
--- a/go/src/google.golang.org/appengine/search/search_test.go
+++ b/go/src/google.golang.org/appengine/search/search_test.go
@@ -140,7 +140,7 @@
 
 func TestLoadDoc(t *testing.T) {
 	got, want := TestDoc{}, searchDoc
-	if err := loadDoc(&got, protoFields, nil, nil); err != nil {
+	if err := loadDoc(&got, &pb.Document{Field: protoFields}, nil); err != nil {
 		t.Fatalf("loadDoc: %v", err)
 	}
 	if got != want {
@@ -149,12 +149,12 @@
 }
 
 func TestSaveDoc(t *testing.T) {
-	got, _, err := saveDoc(&searchDoc)
+	got, err := saveDoc(&searchDoc)
 	if err != nil {
 		t.Fatalf("saveDoc: %v", err)
 	}
 	want := protoFields
-	if !reflect.DeepEqual(got, want) {
+	if !reflect.DeepEqual(got.Field, want) {
 		t.Errorf("\ngot  %v\nwant %v", got, want)
 	}
 }
@@ -162,7 +162,7 @@
 func TestLoadFieldList(t *testing.T) {
 	var got FieldList
 	want := searchFieldsWithLang
-	if err := loadDoc(&got, protoFields, nil, nil); err != nil {
+	if err := loadDoc(&got, &pb.Document{Field: protoFields}, nil); err != nil {
 		t.Fatalf("loadDoc: %v", err)
 	}
 	if !reflect.DeepEqual(got, want) {
@@ -176,11 +176,11 @@
 		{Name: "Bar", Value: "私は日本人だ", Language: "jp"},
 	}
 	var got FieldList
-	protoFields, _, err := saveDoc(fl)
+	doc, err := saveDoc(fl)
 	if err != nil {
 		t.Fatalf("saveDoc: %v", err)
 	}
-	if err := loadDoc(&got, protoFields, nil, nil); err != nil {
+	if err := loadDoc(&got, doc, nil); err != nil {
 		t.Fatalf("loadDoc: %v", err)
 	}
 	if want := fl; !reflect.DeepEqual(&got, want) {
@@ -189,12 +189,12 @@
 }
 
 func TestSaveFieldList(t *testing.T) {
-	got, _, err := saveDoc(&searchFields)
+	got, err := saveDoc(&searchFields)
 	if err != nil {
 		t.Fatalf("saveDoc: %v", err)
 	}
 	want := protoFields
-	if !reflect.DeepEqual(got, want) {
+	if !reflect.DeepEqual(got.Field, want) {
 		t.Errorf("\ngot  %v\nwant %v", got, want)
 	}
 }
@@ -205,7 +205,8 @@
 		f.Derived = (i >= 2) // First 2 elements are "fields", next are "expressions".
 		want = append(want, f)
 	}
-	if err := loadDoc(&got, protoFields[:2], protoFields[2:], nil); err != nil {
+	doc, expr := &pb.Document{Field: protoFields[:2]}, protoFields[2:]
+	if err := loadDoc(&got, doc, expr); err != nil {
 		t.Fatalf("loadDoc: %v", err)
 	}
 	if !reflect.DeepEqual(got, want) {
@@ -219,27 +220,32 @@
 		Meta:   searchMeta,
 		Fields: searchFieldsWithLang,
 	}
-	if err := loadDoc(&got, protoFields, nil, searchMeta); err != nil {
+	doc := &pb.Document{
+		Field:   protoFields,
+		OrderId: proto.Int32(42),
+	}
+	if err := loadDoc(&got, doc, nil); err != nil {
 		t.Fatalf("loadDoc: %v", err)
 	}
 	if !reflect.DeepEqual(got, want) {
-		t.Errorf("got  %v\nwant %v", got, want)
+		t.Errorf("\ngot  %v\nwant %v", got, want)
 	}
 }
 
 func TestSaveMeta(t *testing.T) {
-	got, gotMeta, err := saveDoc(&FieldListWithMeta{
+	got, err := saveDoc(&FieldListWithMeta{
 		Meta:   searchMeta,
 		Fields: searchFields,
 	})
 	if err != nil {
 		t.Fatalf("saveDoc: %v", err)
 	}
-	if want := protoFields; !reflect.DeepEqual(got, want) {
-		t.Errorf("\ngot  %v\nwant %v", got, want)
+	want := &pb.Document{
+		Field:   protoFields,
+		OrderId: proto.Int32(42),
 	}
-	if want := searchMeta; !reflect.DeepEqual(gotMeta, want) {
-		t.Errorf("\ngot  %v\nwant %v", gotMeta, want)
+	if !proto.Equal(got, want) {
+		t.Errorf("\ngot  %v\nwant %v", got, want)
 	}
 }
 
@@ -251,7 +257,7 @@
 		{"Normal", true},
 		{"Also_OK_123", true},
 		{"Not so great", false},
-		{"lower_case", false},
+		{"lower_case", true},
 		{"Exclaim!", false},
 		{"Hello세상아 안녕", false},
 		{"", false},
@@ -261,7 +267,7 @@
 	}
 
 	for _, tc := range testCases {
-		_, _, err := saveDoc(&FieldList{
+		_, err := saveDoc(&FieldList{
 			Field{Name: tc.name, Value: "val"},
 		})
 		if err != nil && !strings.Contains(err.Error(), "invalid field name") {
@@ -288,7 +294,7 @@
 	}
 
 	for _, tt := range testCases {
-		_, _, err := saveDoc(&FieldList{tt.field})
+		_, err := saveDoc(&FieldList{tt.field})
 		if err == nil != tt.valid {
 			t.Errorf("Field %v, got error %v, wanted valid %t", tt.field, err, tt.valid)
 		}
@@ -325,7 +331,7 @@
 		},
 	}
 	for _, tc := range testCases {
-		_, _, err := saveDoc(&tc.fields)
+		_, err := saveDoc(&tc.fields)
 		if (err == nil) != (tc.errMsg == "") || (err != nil && !strings.Contains(err.Error(), tc.errMsg)) {
 			t.Errorf("%s: got err %v, wanted %q", tc.desc, err, tc.errMsg)
 		}
@@ -368,7 +374,7 @@
 		},
 	}
 	for _, tc := range testCases {
-		err := loadDoc(tc.dst, tc.src, nil, nil)
+		err := loadDoc(tc.dst, &pb.Document{Field: tc.src}, nil)
 		if !reflect.DeepEqual(err, tc.err) {
 			t.Errorf("%s, got err %v, wanted %v", tc.desc, err, tc.err)
 		}
@@ -376,40 +382,49 @@
 }
 
 func TestLimit(t *testing.T) {
-	more := func(it *Iterator) error {
-		if it.limit == 0 {
-			return errors.New("Iterator.limit should not be zero in next")
+	index, err := Open("Doc")
+	if err != nil {
+		t.Fatalf("err from Open: %v", err)
+	}
+	c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, res *pb.SearchResponse) error {
+		limit := 20 // Default per page.
+		if req.Params.Limit != nil {
+			limit = int(*req.Params.Limit)
 		}
-		// Page up to 20 items at once.
-		ret := 20
-		if it.limit > 0 && it.limit < ret {
-			ret = it.limit
-		}
-		it.listRes = make([]*pb.Document, ret)
-		for i := range it.listRes {
-			it.listRes[i] = &pb.Document{}
+		res.Status = &pb.RequestStatus{Code: pb.SearchServiceError_OK.Enum()}
+		res.MatchedCount = proto.Int64(int64(limit))
+		for i := 0; i < limit; i++ {
+			res.Result = append(res.Result, &pb.SearchResult{Document: &pb.Document{}})
+			res.Cursor = proto.String("moreresults")
 		}
 		return nil
+	})
+
+	const maxDocs = 500 // Limit maximum number of docs.
+	testCases := []struct {
+		limit, want int
+	}{
+		{limit: 0, want: maxDocs},
+		{limit: 42, want: 42},
+		{limit: 100, want: 100},
+		{limit: 1000, want: maxDocs},
 	}
 
-	it := &Iterator{
-		more:  more,
-		limit: 42,
-	}
-
-	count := 0
-	for {
-		_, err := it.Next(nil)
-		if err == Done {
-			break
+	for _, tt := range testCases {
+		it := index.Search(c, "gopher", &SearchOptions{Limit: tt.limit, IDsOnly: true})
+		count := 0
+		for ; count < maxDocs; count++ {
+			_, err := it.Next(nil)
+			if err == Done {
+				break
+			}
+			if err != nil {
+				t.Fatalf("err after %d: %v", count, err)
+			}
 		}
-		if err != nil {
-			t.Fatalf("err after %d: %v", count, err)
+		if count != tt.want {
+			t.Errorf("got %d results, expected %d", count, tt.want)
 		}
-		count++
-	}
-	if count != 42 {
-		t.Errorf("got %d results, expected 42", count)
 	}
 }
 
@@ -516,7 +531,7 @@
 		t.Fatalf("err from Open: %v", err)
 	}
 
-	noErr := errors.New("") // sentinel error when there isn't one…
+	noErr := errors.New("") // Sentinel err to return to prevent sending request.
 
 	testCases := []struct {
 		desc       string
@@ -648,3 +663,244 @@
 		}
 	}
 }
+
+func TestBasicSearchOpts(t *testing.T) {
+	index, err := Open("Doc")
+	if err != nil {
+		t.Fatalf("err from Open: %v", err)
+	}
+
+	noErr := errors.New("") // Sentinel err to return to prevent sending request.
+
+	testCases := []struct {
+		desc      string
+		facetOpts []FacetSearchOption
+		cursor    Cursor
+		offset    int
+		want      *pb.SearchParams
+		wantErr   string
+	}{
+		{
+			desc: "No options",
+			want: &pb.SearchParams{},
+		},
+		{
+			desc: "Default auto discovery",
+			facetOpts: []FacetSearchOption{
+				AutoFacetDiscovery(0, 0),
+			},
+			want: &pb.SearchParams{
+				AutoDiscoverFacetCount: proto.Int32(10),
+			},
+		},
+		{
+			desc: "Auto discovery",
+			facetOpts: []FacetSearchOption{
+				AutoFacetDiscovery(7, 12),
+			},
+			want: &pb.SearchParams{
+				AutoDiscoverFacetCount: proto.Int32(7),
+				FacetAutoDetectParam: &pb.FacetAutoDetectParam{
+					ValueLimit: proto.Int32(12),
+				},
+			},
+		},
+		{
+			desc: "Param Depth",
+			facetOpts: []FacetSearchOption{
+				AutoFacetDiscovery(7, 12),
+			},
+			want: &pb.SearchParams{
+				AutoDiscoverFacetCount: proto.Int32(7),
+				FacetAutoDetectParam: &pb.FacetAutoDetectParam{
+					ValueLimit: proto.Int32(12),
+				},
+			},
+		},
+		{
+			desc: "Doc depth",
+			facetOpts: []FacetSearchOption{
+				FacetDocumentDepth(123),
+			},
+			want: &pb.SearchParams{
+				FacetDepth: proto.Int32(123),
+			},
+		},
+		{
+			desc: "Facet discovery",
+			facetOpts: []FacetSearchOption{
+				FacetDiscovery("colour"),
+				FacetDiscovery("size", Atom("M"), Atom("L")),
+				FacetDiscovery("price", LessThan(7), Range{7, 14}, AtLeast(14)),
+			},
+			want: &pb.SearchParams{
+				IncludeFacet: []*pb.FacetRequest{
+					{Name: proto.String("colour")},
+					{Name: proto.String("size"), Params: &pb.FacetRequestParam{
+						ValueConstraint: []string{"M", "L"},
+					}},
+					{Name: proto.String("price"), Params: &pb.FacetRequestParam{
+						Range: []*pb.FacetRange{
+							{End: proto.String("7e+00")},
+							{Start: proto.String("7e+00"), End: proto.String("1.4e+01")},
+							{Start: proto.String("1.4e+01")},
+						},
+					}},
+				},
+			},
+		},
+		{
+			desc: "Facet discovery - bad value",
+			facetOpts: []FacetSearchOption{
+				FacetDiscovery("colour", true),
+			},
+			wantErr: "bad FacetSearchOption: unsupported value type bool",
+		},
+		{
+			desc: "Facet discovery - mix value types",
+			facetOpts: []FacetSearchOption{
+				FacetDiscovery("colour", Atom("blue"), AtLeast(7)),
+			},
+			wantErr: "bad FacetSearchOption: values must all be Atom, or must all be Range",
+		},
+		{
+			desc: "Facet discovery - invalid range",
+			facetOpts: []FacetSearchOption{
+				FacetDiscovery("colour", Range{negInf, posInf}),
+			},
+			wantErr: "bad FacetSearchOption: invalid range: either Start or End must be finite",
+		},
+		{
+			desc:   "Cursor",
+			cursor: Cursor("mycursor"),
+			want: &pb.SearchParams{
+				Cursor: proto.String("mycursor"),
+			},
+		},
+		{
+			desc:   "Offset",
+			offset: 121,
+			want: &pb.SearchParams{
+				Offset: proto.Int32(121),
+			},
+		},
+		{
+			desc:    "Cursor and Offset set",
+			cursor:  Cursor("mycursor"),
+			offset:  121,
+			wantErr: "at most one of Cursor and Offset may be specified",
+		},
+	}
+
+	for _, tt := range testCases {
+		c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, _ *pb.SearchResponse) error {
+			if tt.want == nil {
+				t.Errorf("%s: expected call to fail", tt.desc)
+				return nil
+			}
+			// Set default fields.
+			tt.want.Query = proto.String("gopher")
+			tt.want.IndexSpec = &pb.IndexSpec{Name: proto.String("Doc")}
+			tt.want.CursorType = pb.SearchParams_PER_RESULT.Enum()
+			tt.want.FieldSpec = &pb.FieldSpec{}
+			if got := req.Params; !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("%s: params=%v; want %v", tt.desc, got, tt.want)
+			}
+			return noErr // Always return some error to prevent response parsing.
+		})
+
+		it := index.Search(c, "gopher", &SearchOptions{
+			Facets: tt.facetOpts,
+			Cursor: tt.cursor,
+			Offset: tt.offset,
+		})
+		_, err := it.Next(nil)
+		if err == nil {
+			t.Fatalf("%s: err==nil; should not happen", tt.desc)
+		}
+		if err.Error() != tt.wantErr {
+			t.Errorf("%s: got error %q, want %q", tt.desc, err, tt.wantErr)
+		}
+	}
+}
+
+func TestFacetRefinements(t *testing.T) {
+	index, err := Open("Doc")
+	if err != nil {
+		t.Fatalf("err from Open: %v", err)
+	}
+
+	noErr := errors.New("") // Sentinel err to return to prevent sending request.
+
+	testCases := []struct {
+		desc    string
+		refine  []Facet
+		want    []*pb.FacetRefinement
+		wantErr string
+	}{
+		{
+			desc: "No refinements",
+		},
+		{
+			desc: "Basic",
+			refine: []Facet{
+				{Name: "fur", Value: Atom("fluffy")},
+				{Name: "age", Value: LessThan(123)},
+				{Name: "age", Value: AtLeast(0)},
+				{Name: "legs", Value: Range{Start: 3, End: 5}},
+			},
+			want: []*pb.FacetRefinement{
+				{Name: proto.String("fur"), Value: proto.String("fluffy")},
+				{Name: proto.String("age"), Range: &pb.FacetRefinement_Range{End: proto.String("1.23e+02")}},
+				{Name: proto.String("age"), Range: &pb.FacetRefinement_Range{Start: proto.String("0e+00")}},
+				{Name: proto.String("legs"), Range: &pb.FacetRefinement_Range{Start: proto.String("3e+00"), End: proto.String("5e+00")}},
+			},
+		},
+		{
+			desc: "Infinite range",
+			refine: []Facet{
+				{Name: "age", Value: Range{Start: negInf, End: posInf}},
+			},
+			wantErr: `search: refinement for facet "age": either Start or End must be finite`,
+		},
+		{
+			desc: "Bad End value in range",
+			refine: []Facet{
+				{Name: "age", Value: LessThan(2147483648)},
+			},
+			wantErr: `search: refinement for facet "age": invalid value for End`,
+		},
+		{
+			desc: "Bad Start value in range",
+			refine: []Facet{
+				{Name: "age", Value: AtLeast(-2147483649)},
+			},
+			wantErr: `search: refinement for facet "age": invalid value for Start`,
+		},
+		{
+			desc: "Unknown value type",
+			refine: []Facet{
+				{Name: "age", Value: "you can't use strings!"},
+			},
+			wantErr: `search: unsupported refinement for facet "age" of type string`,
+		},
+	}
+
+	for _, tt := range testCases {
+		c := aetesting.FakeSingleContext(t, "search", "Search", func(req *pb.SearchRequest, _ *pb.SearchResponse) error {
+			if got := req.Params.FacetRefinement; !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("%s: params.FacetRefinement=%v; want %v", tt.desc, got, tt.want)
+			}
+			return noErr // Always return some error to prevent response parsing.
+		})
+
+		it := index.Search(c, "gopher", &SearchOptions{Refinements: tt.refine})
+		_, err := it.Next(nil)
+		if err == nil {
+			t.Fatalf("%s: err==nil; should not happen", tt.desc)
+		}
+		if err.Error() != tt.wantErr {
+			t.Errorf("%s: got error %q, want %q", tt.desc, err, tt.wantErr)
+		}
+	}
+}
diff --git a/go/src/google.golang.org/appengine/search/struct.go b/go/src/google.golang.org/appengine/search/struct.go
new file mode 100644
index 0000000..083c97f
--- /dev/null
+++ b/go/src/google.golang.org/appengine/search/struct.go
@@ -0,0 +1,245 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package search
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+	"sync"
+)
+
+// ErrFieldMismatch is returned when a field is to be loaded into a different
+// than the one it was stored from, or when a field is missing or unexported in
+// the destination struct.
+type ErrFieldMismatch struct {
+	FieldName string
+	Reason    string
+}
+
+func (e *ErrFieldMismatch) Error() string {
+	return fmt.Sprintf("search: cannot load field %q: %s", e.FieldName, e.Reason)
+}
+
+// ErrFacetMismatch is returned when a facet is to be loaded into a different
+// type than the one it was stored from, or when a field is missing or
+// unexported in the destination struct. StructType is the type of the struct
+// pointed to by the destination argument passed to Iterator.Next.
+type ErrFacetMismatch struct {
+	StructType reflect.Type
+	FacetName  string
+	Reason     string
+}
+
+func (e *ErrFacetMismatch) Error() string {
+	return fmt.Sprintf("search: cannot load facet %q into a %q: %s", e.FacetName, e.StructType, e.Reason)
+}
+
+// structCodec defines how to convert a given struct to/from a search document.
+type structCodec struct {
+	// byIndex returns the struct tag for the i'th struct field.
+	byIndex []structTag
+
+	// fieldByName returns the index of the struct field for the given field name.
+	fieldByName map[string]int
+
+	// facetByName returns the index of the struct field for the given facet name,
+	facetByName map[string]int
+}
+
+// structTag holds a structured version of each struct field's parsed tag.
+type structTag struct {
+	name  string
+	facet bool
+}
+
+var (
+	codecsMu sync.RWMutex
+	codecs   = map[reflect.Type]*structCodec{}
+)
+
+func loadCodec(t reflect.Type) (*structCodec, error) {
+	codecsMu.RLock()
+	codec, ok := codecs[t]
+	codecsMu.RUnlock()
+	if ok {
+		return codec, nil
+	}
+
+	codecsMu.Lock()
+	defer codecsMu.Unlock()
+	if codec, ok := codecs[t]; ok {
+		return codec, nil
+	}
+
+	codec = &structCodec{
+		fieldByName: make(map[string]int),
+		facetByName: make(map[string]int),
+	}
+
+	for i, I := 0, t.NumField(); i < I; i++ {
+		f := t.Field(i)
+		name, opts := f.Tag.Get("search"), ""
+		if i := strings.Index(name, ","); i != -1 {
+			name, opts = name[:i], name[i+1:]
+		}
+		// TODO(davidday): Support name=="-" as per datastore.
+		if name == "" {
+			name = f.Name
+		} else if !validFieldName(name) {
+			return nil, fmt.Errorf("search: struct tag has invalid field name: %q", name)
+		}
+		facet := opts == "facet"
+		codec.byIndex = append(codec.byIndex, structTag{name: name, facet: facet})
+		if facet {
+			codec.facetByName[name] = i
+		} else {
+			codec.fieldByName[name] = i
+		}
+	}
+
+	codecs[t] = codec
+	return codec, nil
+}
+
+// structFLS adapts a struct to be a FieldLoadSaver.
+type structFLS struct {
+	v     reflect.Value
+	codec *structCodec
+}
+
+func (s structFLS) Load(fields []Field, meta *DocumentMetadata) error {
+	var err error
+	for _, field := range fields {
+		i, ok := s.codec.fieldByName[field.Name]
+		if !ok {
+			// Note the error, but keep going.
+			err = &ErrFieldMismatch{
+				FieldName: field.Name,
+				Reason:    "no such struct field",
+			}
+			continue
+
+		}
+		f := s.v.Field(i)
+		if !f.CanSet() {
+			// Note the error, but keep going.
+			err = &ErrFieldMismatch{
+				FieldName: field.Name,
+				Reason:    "cannot set struct field",
+			}
+			continue
+		}
+		v := reflect.ValueOf(field.Value)
+		if ft, vt := f.Type(), v.Type(); ft != vt {
+			err = &ErrFieldMismatch{
+				FieldName: field.Name,
+				Reason:    fmt.Sprintf("type mismatch: %v for %v data", ft, vt),
+			}
+			continue
+		}
+		f.Set(v)
+	}
+	if meta == nil {
+		return nil
+	}
+	for _, facet := range meta.Facets {
+		i, ok := s.codec.facetByName[facet.Name]
+		if !ok {
+			// Note the error, but keep going.
+			if err == nil {
+				err = &ErrFacetMismatch{
+					StructType: s.v.Type(),
+					FacetName:  facet.Name,
+					Reason:     "no matching field found",
+				}
+			}
+			continue
+		}
+		f := s.v.Field(i)
+		if !f.CanSet() {
+			// Note the error, but keep going.
+			if err == nil {
+				err = &ErrFacetMismatch{
+					StructType: s.v.Type(),
+					FacetName:  facet.Name,
+					Reason:     "unable to set unexported field of struct",
+				}
+			}
+			continue
+		}
+		v := reflect.ValueOf(facet.Value)
+		if ft, vt := f.Type(), v.Type(); ft != vt {
+			if err == nil {
+				err = &ErrFacetMismatch{
+					StructType: s.v.Type(),
+					FacetName:  facet.Name,
+					Reason:     fmt.Sprintf("type mismatch: %v for %d data", ft, vt),
+				}
+				continue
+			}
+		}
+		f.Set(v)
+	}
+	return err
+}
+
+func (s structFLS) Save() ([]Field, *DocumentMetadata, error) {
+	fields := make([]Field, 0, len(s.codec.fieldByName))
+	var facets []Facet
+	for i, tag := range s.codec.byIndex {
+		f := s.v.Field(i)
+		if !f.CanSet() {
+			continue
+		}
+		if tag.facet {
+			facets = append(facets, Facet{Name: tag.name, Value: f.Interface()})
+		} else {
+			fields = append(fields, Field{Name: tag.name, Value: f.Interface()})
+		}
+	}
+	return fields, &DocumentMetadata{Facets: facets}, nil
+}
+
+// newStructFLS returns a FieldLoadSaver for the struct pointer p.
+func newStructFLS(p interface{}) (FieldLoadSaver, error) {
+	v := reflect.ValueOf(p)
+	if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct {
+		return nil, ErrInvalidDocumentType
+	}
+	codec, err := loadCodec(v.Elem().Type())
+	if err != nil {
+		return nil, err
+	}
+	return structFLS{v.Elem(), codec}, nil
+}
+
+func loadStructWithMeta(dst interface{}, f []Field, meta *DocumentMetadata) error {
+	x, err := newStructFLS(dst)
+	if err != nil {
+		return err
+	}
+	return x.Load(f, meta)
+}
+
+func saveStructWithMeta(src interface{}) ([]Field, *DocumentMetadata, error) {
+	x, err := newStructFLS(src)
+	if err != nil {
+		return nil, nil, err
+	}
+	return x.Save()
+}
+
+// LoadStruct loads the fields from f to dst. dst must be a struct pointer.
+func LoadStruct(dst interface{}, f []Field) error {
+	return loadStructWithMeta(dst, f, nil)
+}
+
+// SaveStruct returns the fields from src as a slice of Field.
+// src must be a struct pointer.
+func SaveStruct(src interface{}) ([]Field, error) {
+	f, _, err := saveStructWithMeta(src)
+	return f, err
+}
diff --git a/go/src/google.golang.org/appengine/search/struct_test.go b/go/src/google.golang.org/appengine/search/struct_test.go
new file mode 100644
index 0000000..45b8ab2
--- /dev/null
+++ b/go/src/google.golang.org/appengine/search/struct_test.go
@@ -0,0 +1,172 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package search
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestLoadingStruct(t *testing.T) {
+	testCases := []struct {
+		desc    string
+		fields  []Field
+		meta    *DocumentMetadata
+		want    interface{}
+		wantErr bool
+	}{
+		{
+			desc: "Basic struct",
+			fields: []Field{
+				{Name: "Name", Value: "Gopher"},
+				{Name: "Legs", Value: float64(4)},
+			},
+			want: &struct {
+				Name string
+				Legs float64
+			}{"Gopher", 4},
+		},
+		{
+			desc: "Struct with tags",
+			fields: []Field{
+				{Name: "Name", Value: "Gopher"},
+				{Name: "about", Value: "Likes slide rules."},
+			},
+			meta: &DocumentMetadata{Facets: []Facet{
+				{Name: "Legs", Value: float64(4)},
+				{Name: "Fur", Value: Atom("furry")},
+			}},
+			want: &struct {
+				Name string
+				Info string  `search:"about"`
+				Legs float64 `search:",facet"`
+				Fuzz Atom    `search:"Fur,facet"`
+			}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
+		},
+		{
+			desc: "Bad field from tag",
+			want: &struct {
+				AlphaBeta string `search:"αβ"`
+			}{},
+			wantErr: true,
+		},
+		{
+			desc: "Ignore missing field",
+			fields: []Field{
+				{Name: "Meaning", Value: float64(42)},
+			},
+			want: &struct{}{},
+		},
+		{
+			desc: "Ignore unsettable field",
+			fields: []Field{
+				{Name: "meaning", Value: float64(42)},
+			},
+			want: &struct{ meaning float64 }{}, // field not populated.
+		},
+		{
+			desc: "Error on missing facet",
+			meta: &DocumentMetadata{Facets: []Facet{
+				{Name: "Set", Value: Atom("yes")},
+				{Name: "Missing", Value: Atom("no")},
+			}},
+			want: &struct {
+				Set Atom `search:",facet"`
+			}{Atom("yes")},
+			wantErr: true,
+		},
+		{
+			desc: "Error on unsettable facet",
+			meta: &DocumentMetadata{Facets: []Facet{
+				{Name: "Set", Value: Atom("yes")},
+				{Name: "unset", Value: Atom("no")},
+			}},
+			want: &struct {
+				Set Atom `search:",facet"`
+			}{Atom("yes")},
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range testCases {
+		// Make a pointer to an empty version of what want points to.
+		dst := reflect.New(reflect.TypeOf(tt.want).Elem()).Interface()
+		err := loadStructWithMeta(dst, tt.fields, tt.meta)
+		if err != nil != tt.wantErr {
+			t.Errorf("%s: got err %v; want err %t", tt.desc, err, tt.wantErr)
+			continue
+		}
+		if !reflect.DeepEqual(dst, tt.want) {
+			t.Errorf("%s: doesn't match\ngot:  %v\nwant: %v", tt.desc, dst, tt.want)
+		}
+	}
+}
+
+func TestSavingStruct(t *testing.T) {
+	testCases := []struct {
+		desc       string
+		doc        interface{}
+		wantFields []Field
+		wantFacets []Facet
+	}{
+		{
+			desc: "Basic struct",
+			doc: &struct {
+				Name string
+				Legs float64
+			}{"Gopher", 4},
+			wantFields: []Field{
+				{Name: "Name", Value: "Gopher"},
+				{Name: "Legs", Value: float64(4)},
+			},
+		},
+		{
+			desc: "Struct with tags",
+			doc: &struct {
+				Name string
+				Info string  `search:"about"`
+				Legs float64 `search:",facet"`
+				Fuzz Atom    `search:"Fur,facet"`
+			}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
+			wantFields: []Field{
+				{Name: "Name", Value: "Gopher"},
+				{Name: "about", Value: "Likes slide rules."},
+			},
+			wantFacets: []Facet{
+				{Name: "Legs", Value: float64(4)},
+				{Name: "Fur", Value: Atom("furry")},
+			},
+		},
+		{
+			desc: "Ignore unexported struct fields",
+			doc: &struct {
+				Name string
+				info string
+				Legs float64 `search:",facet"`
+				fuzz Atom    `search:",facet"`
+			}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
+			wantFields: []Field{
+				{Name: "Name", Value: "Gopher"},
+			},
+			wantFacets: []Facet{
+				{Name: "Legs", Value: float64(4)},
+			},
+		},
+	}
+
+	for _, tt := range testCases {
+		fields, meta, err := saveStructWithMeta(tt.doc)
+		if err != nil {
+			t.Errorf("%s: got err %v; want nil", tt.desc, err)
+			continue
+		}
+		if !reflect.DeepEqual(fields, tt.wantFields) {
+			t.Errorf("%s: fields don't match\ngot:  %v\nwant: %v", tt.desc, fields, tt.wantFields)
+		}
+		if facets := meta.Facets; !reflect.DeepEqual(facets, tt.wantFacets) {
+			t.Errorf("%s: facets don't match\ngot:  %v\nwant: %v", tt.desc, facets, tt.wantFacets)
+		}
+	}
+}
diff --git a/go/src/google.golang.org/appengine/socket/doc.go b/go/src/google.golang.org/appengine/socket/doc.go
new file mode 100644
index 0000000..1e23553
--- /dev/null
+++ b/go/src/google.golang.org/appengine/socket/doc.go
@@ -0,0 +1,10 @@
+// Copyright 2012 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// Package socket provides outbound network sockets.
+//
+// This package is only required in the classic App Engine environment.
+// Applications running only in the Managed VM hosting environment should
+// use the standard library's net package.
+package socket
diff --git a/go/src/google.golang.org/appengine/socket/socket_classic.go b/go/src/google.golang.org/appengine/socket/socket_classic.go
new file mode 100644
index 0000000..0ad50e2
--- /dev/null
+++ b/go/src/google.golang.org/appengine/socket/socket_classic.go
@@ -0,0 +1,290 @@
+// Copyright 2012 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build appengine
+
+package socket
+
+import (
+	"fmt"
+	"io"
+	"net"
+	"strconv"
+	"time"
+
+	"github.com/golang/protobuf/proto"
+	"golang.org/x/net/context"
+	"google.golang.org/appengine/internal"
+
+	pb "google.golang.org/appengine/internal/socket"
+)
+
+// Dial connects to the address addr on the network protocol.
+// The address format is host:port, where host may be a hostname or an IP address.
+// Known protocols are "tcp" and "udp".
+// The returned connection satisfies net.Conn, and is valid while ctx is valid;
+// if the connection is to be used after ctx becomes invalid, invoke SetContext
+// with the new context.
+func Dial(ctx context.Context, protocol, addr string) (*Conn, error) {
+	return DialTimeout(ctx, protocol, addr, 0)
+}
+
+var ipFamilies = []pb.CreateSocketRequest_SocketFamily{
+	pb.CreateSocketRequest_IPv4,
+	pb.CreateSocketRequest_IPv6,
+}
+
+// DialTimeout is like Dial but takes a timeout.
+// The timeout includes name resolution, if required.
+func DialTimeout(ctx context.Context, protocol, addr string, timeout time.Duration) (*Conn, error) {
+	dialCtx := ctx // Used for dialing and name resolution, but not stored in the *Conn.
+	if timeout > 0 {
+		var cancel context.CancelFunc
+		dialCtx, cancel = context.WithTimeout(ctx, timeout)
+		defer cancel()
+	}
+
+	host, portStr, err := net.SplitHostPort(addr)
+	if err != nil {
+		return nil, err
+	}
+	port, err := strconv.Atoi(portStr)
+	if err != nil {
+		return nil, fmt.Errorf("socket: bad port %q: %v", portStr, err)
+	}
+
+	var prot pb.CreateSocketRequest_SocketProtocol
+	switch protocol {
+	case "tcp":
+		prot = pb.CreateSocketRequest_TCP
+	case "udp":
+		prot = pb.CreateSocketRequest_UDP
+	default:
+		return nil, fmt.Errorf("socket: unknown protocol %q", protocol)
+	}
+
+	packedAddrs, resolved, err := resolve(dialCtx, ipFamilies, host)
+	if err != nil {
+		return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err)
+	}
+	if len(packedAddrs) == 0 {
+		return nil, fmt.Errorf("no addresses for %q", host)
+	}
+
+	packedAddr := packedAddrs[0] // use first address
+	fam := pb.CreateSocketRequest_IPv4
+	if len(packedAddr) == net.IPv6len {
+		fam = pb.CreateSocketRequest_IPv6
+	}
+
+	req := &pb.CreateSocketRequest{
+		Family:   fam.Enum(),
+		Protocol: prot.Enum(),
+		RemoteIp: &pb.AddressPort{
+			Port:          proto.Int32(int32(port)),
+			PackedAddress: packedAddr,
+		},
+	}
+	if resolved {
+		req.RemoteIp.HostnameHint = &host
+	}
+	res := &pb.CreateSocketReply{}
+	if err := internal.Call(dialCtx, "remote_socket", "CreateSocket", req, res); err != nil {
+		return nil, err
+	}
+
+	return &Conn{
+		ctx:    ctx,
+		desc:   res.GetSocketDescriptor(),
+		prot:   prot,
+		local:  res.ProxyExternalIp,
+		remote: req.RemoteIp,
+	}, nil
+}
+
+// LookupIP returns the given host's IP addresses.
+func LookupIP(ctx context.Context, host string) (addrs []net.IP, err error) {
+	packedAddrs, _, err := resolve(ctx, ipFamilies, host)
+	if err != nil {
+		return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err)
+	}
+	addrs = make([]net.IP, len(packedAddrs))
+	for i, pa := range packedAddrs {
+		addrs[i] = net.IP(pa)
+	}
+	return addrs, nil
+}
+
+func resolve(ctx context.Context, fams []pb.CreateSocketRequest_SocketFamily, host string) ([][]byte, bool, error) {
+	// Check if it's an IP address.
+	if ip := net.ParseIP(host); ip != nil {
+		if ip := ip.To4(); ip != nil {
+			return [][]byte{ip}, false, nil
+		}
+		return [][]byte{ip}, false, nil
+	}
+
+	req := &pb.ResolveRequest{
+		Name:            &host,
+		AddressFamilies: fams,
+	}
+	res := &pb.ResolveReply{}
+	if err := internal.Call(ctx, "remote_socket", "Resolve", req, res); err != nil {
+		// XXX: need to map to pb.ResolveReply_ErrorCode?
+		return nil, false, err
+	}
+	return res.PackedAddress, true, nil
+}
+
+// withDeadline is like context.WithDeadline, except it ignores the zero deadline.
+func withDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) {
+	if deadline.IsZero() {
+		return parent, func() {}
+	}
+	return context.WithDeadline(parent, deadline)
+}
+
+// Conn represents a socket connection.
+// It implements net.Conn.
+type Conn struct {
+	ctx    context.Context
+	desc   string
+	offset int64
+
+	prot          pb.CreateSocketRequest_SocketProtocol
+	local, remote *pb.AddressPort
+
+	readDeadline, writeDeadline time.Time // optional
+}
+
+// SetContext sets the context that is used by this Conn.
+// It is usually used only when using a Conn that was created in a different context,
+// such as when a connection is created during a warmup request but used while
+// servicing a user request.
+func (cn *Conn) SetContext(ctx context.Context) {
+	cn.ctx = ctx
+}
+
+func (cn *Conn) Read(b []byte) (n int, err error) {
+	const maxRead = 1 << 20
+	if len(b) > maxRead {
+		b = b[:maxRead]
+	}
+
+	req := &pb.ReceiveRequest{
+		SocketDescriptor: &cn.desc,
+		DataSize:         proto.Int32(int32(len(b))),
+	}
+	res := &pb.ReceiveReply{}
+	if !cn.readDeadline.IsZero() {
+		req.TimeoutSeconds = proto.Float64(cn.readDeadline.Sub(time.Now()).Seconds())
+	}
+	ctx, cancel := withDeadline(cn.ctx, cn.readDeadline)
+	defer cancel()
+	if err := internal.Call(ctx, "remote_socket", "Receive", req, res); err != nil {
+		return 0, err
+	}
+	if len(res.Data) == 0 {
+		return 0, io.EOF
+	}
+	if len(res.Data) > len(b) {
+		return 0, fmt.Errorf("socket: internal error: read too much data: %d > %d", len(res.Data), len(b))
+	}
+	return copy(b, res.Data), nil
+}
+
+func (cn *Conn) Write(b []byte) (n int, err error) {
+	const lim = 1 << 20 // max per chunk
+
+	for n < len(b) {
+		chunk := b[n:]
+		if len(chunk) > lim {
+			chunk = chunk[:lim]
+		}
+
+		req := &pb.SendRequest{
+			SocketDescriptor: &cn.desc,
+			Data:             chunk,
+			StreamOffset:     &cn.offset,
+		}
+		res := &pb.SendReply{}
+		if !cn.writeDeadline.IsZero() {
+			req.TimeoutSeconds = proto.Float64(cn.writeDeadline.Sub(time.Now()).Seconds())
+		}
+		ctx, cancel := withDeadline(cn.ctx, cn.writeDeadline)
+		defer cancel()
+		if err = internal.Call(ctx, "remote_socket", "Send", req, res); err != nil {
+			// assume zero bytes were sent in this RPC
+			break
+		}
+		n += int(res.GetDataSent())
+		cn.offset += int64(res.GetDataSent())
+	}
+
+	return
+}
+
+func (cn *Conn) Close() error {
+	req := &pb.CloseRequest{
+		SocketDescriptor: &cn.desc,
+	}
+	res := &pb.CloseReply{}
+	if err := internal.Call(cn.ctx, "remote_socket", "Close", req, res); err != nil {
+		return err
+	}
+	cn.desc = "CLOSED"
+	return nil
+}
+
+func addr(prot pb.CreateSocketRequest_SocketProtocol, ap *pb.AddressPort) net.Addr {
+	if ap == nil {
+		return nil
+	}
+	switch prot {
+	case pb.CreateSocketRequest_TCP:
+		return &net.TCPAddr{
+			IP:   net.IP(ap.PackedAddress),
+			Port: int(*ap.Port),
+		}
+	case pb.CreateSocketRequest_UDP:
+		return &net.UDPAddr{
+			IP:   net.IP(ap.PackedAddress),
+			Port: int(*ap.Port),
+		}
+	}
+	panic("unknown protocol " + prot.String())
+}
+
+func (cn *Conn) LocalAddr() net.Addr  { return addr(cn.prot, cn.local) }
+func (cn *Conn) RemoteAddr() net.Addr { return addr(cn.prot, cn.remote) }
+
+func (cn *Conn) SetDeadline(t time.Time) error {
+	cn.readDeadline = t
+	cn.writeDeadline = t
+	return nil
+}
+
+func (cn *Conn) SetReadDeadline(t time.Time) error {
+	cn.readDeadline = t
+	return nil
+}
+
+func (cn *Conn) SetWriteDeadline(t time.Time) error {
+	cn.writeDeadline = t
+	return nil
+}
+
+// KeepAlive signals that the connection is still in use.
+// It may be called to prevent the socket being closed due to inactivity.
+func (cn *Conn) KeepAlive() error {
+	req := &pb.GetSocketNameRequest{
+		SocketDescriptor: &cn.desc,
+	}
+	res := &pb.GetSocketNameReply{}
+	return internal.Call(cn.ctx, "remote_socket", "GetSocketName", req, res)
+}
+
+func init() {
+	internal.RegisterErrorCodeMap("remote_socket", pb.RemoteSocketServiceError_ErrorCode_name)
+}
diff --git a/go/src/google.golang.org/appengine/socket/socket_vm.go b/go/src/google.golang.org/appengine/socket/socket_vm.go
new file mode 100644
index 0000000..ed98ac2
--- /dev/null
+++ b/go/src/google.golang.org/appengine/socket/socket_vm.go
@@ -0,0 +1,64 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+
+package socket
+
+import (
+	"net"
+	"time"
+
+	"golang.org/x/net/context"
+)
+
+// Dial connects to the address addr on the network protocol.
+// The address format is host:port, where host may be a hostname or an IP address.
+// Known protocols are "tcp" and "udp".
+// The returned connection satisfies net.Conn, and is valid while ctx is valid;
+// if the connection is to be used after ctx becomes invalid, invoke SetContext
+// with the new context.
+func Dial(ctx context.Context, protocol, addr string) (*Conn, error) {
+	conn, err := net.Dial(protocol, addr)
+	if err != nil {
+		return nil, err
+	}
+	return &Conn{conn}, nil
+}
+
+// DialTimeout is like Dial but takes a timeout.
+// The timeout includes name resolution, if required.
+func DialTimeout(ctx context.Context, protocol, addr string, timeout time.Duration) (*Conn, error) {
+	conn, err := net.DialTimeout(protocol, addr, timeout)
+	if err != nil {
+		return nil, err
+	}
+	return &Conn{conn}, nil
+}
+
+// LookupIP returns the given host's IP addresses.
+func LookupIP(ctx context.Context, host string) (addrs []net.IP, err error) {
+	return net.LookupIP(host)
+}
+
+// Conn represents a socket connection.
+// It implements net.Conn.
+type Conn struct {
+	net.Conn
+}
+
+// SetContext sets the context that is used by this Conn.
+// It is usually used only when using a Conn that was created in a different context,
+// such as when a connection is created during a warmup request but used while
+// servicing a user request.
+func (cn *Conn) SetContext(ctx context.Context) {
+	// This function is not required on managed VMs.
+}
+
+// KeepAlive signals that the connection is still in use.
+// It may be called to prevent the socket being closed due to inactivity.
+func (cn *Conn) KeepAlive() error {
+	// This function is not required on managed VMs.
+	return nil
+}
diff --git a/go/src/google.golang.org/appengine/taskqueue/ns_classic.go b/go/src/google.golang.org/appengine/taskqueue/ns_classic.go
deleted file mode 100644
index f8aa96e..0000000
--- a/go/src/google.golang.org/appengine/taskqueue/ns_classic.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// +build appengine
-
-package taskqueue
-
-import (
-	basepb "appengine_internal/base"
-
-	"golang.org/x/net/context"
-
-	"google.golang.org/appengine/internal"
-)
-
-func getDefaultNamespace(ctx context.Context) string {
-	c := internal.ClassicContextFromContext(ctx)
-	s := &basepb.StringProto{}
-	c.Call("__go__", "GetDefaultNamespace", &basepb.VoidProto{}, s, nil)
-	return s.GetValue()
-}
diff --git a/go/src/google.golang.org/appengine/taskqueue/ns_vm.go b/go/src/google.golang.org/appengine/taskqueue/ns_vm.go
deleted file mode 100644
index af3d841..0000000
--- a/go/src/google.golang.org/appengine/taskqueue/ns_vm.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// +build !appengine
-
-package taskqueue
-
-import (
-	"golang.org/x/net/context"
-
-	"google.golang.org/appengine/internal"
-)
-
-func getDefaultNamespace(ctx context.Context) string {
-	return internal.IncomingHeaders(ctx).Get(defaultNamespace)
-}
diff --git a/go/src/google.golang.org/appengine/taskqueue/taskqueue.go b/go/src/google.golang.org/appengine/taskqueue/taskqueue.go
index d7c7ed3..9b62fac 100644
--- a/go/src/google.golang.org/appengine/taskqueue/taskqueue.go
+++ b/go/src/google.golang.org/appengine/taskqueue/taskqueue.go
@@ -152,10 +152,18 @@
 	defaultNamespace = http.CanonicalHeaderKey("X-AppEngine-Default-Namespace")
 )
 
+func getDefaultNamespace(ctx context.Context) string {
+	return internal.IncomingHeaders(ctx).Get(defaultNamespace)
+}
+
 func newAddReq(c context.Context, task *Task, queueName string) (*pb.TaskQueueAddRequest, error) {
 	if queueName == "" {
 		queueName = "default"
 	}
+	path := task.Path
+	if path == "" {
+		path = "/_ah/queue/" + queueName
+	}
 	eta := task.ETA
 	if eta.IsZero() {
 		eta = time.Now().Add(task.Delay)
@@ -182,7 +190,7 @@
 		} else {
 			return nil, fmt.Errorf("taskqueue: bad method %q", method)
 		}
-		req.Url = []byte(task.Path)
+		req.Url = []byte(path)
 		for k, vs := range task.Header {
 			for _, v := range vs {
 				req.Header = append(req.Header, &pb.TaskQueueAddRequest_Header{
diff --git a/go/src/google.golang.org/appengine/taskqueue/taskqueue_test.go b/go/src/google.golang.org/appengine/taskqueue/taskqueue_test.go
index 7a06aa4..0c14015 100644
--- a/go/src/google.golang.org/appengine/taskqueue/taskqueue_test.go
+++ b/go/src/google.golang.org/appengine/taskqueue/taskqueue_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"errors"
+	"fmt"
 	"reflect"
 	"testing"
 
@@ -101,3 +102,15 @@
 		t.Errorf("AddMulti got %v, wanted %v", err, want)
 	}
 }
+
+func TestAddWithEmptyPath(t *testing.T) {
+	c := aetesting.FakeSingleContext(t, "taskqueue", "Add", func(req *pb.TaskQueueAddRequest, res *pb.TaskQueueAddResponse) error {
+		if got, want := string(req.Url), "/_ah/queue/a-queue"; got != want {
+			return fmt.Errorf("req.Url = %q; want %q", got, want)
+		}
+		return nil
+	})
+	if _, err := Add(c, &Task{}, "a-queue"); err != nil {
+		t.Fatalf("Add: %v", err)
+	}
+}
diff --git a/go/src/google.golang.org/appengine/urlfetch/urlfetch.go b/go/src/google.golang.org/appengine/urlfetch/urlfetch.go
index 454fac4..6ffe1e6 100644
--- a/go/src/google.golang.org/appengine/urlfetch/urlfetch.go
+++ b/go/src/google.golang.org/appengine/urlfetch/urlfetch.go
@@ -29,8 +29,7 @@
 // this transport and use the Client rather than using this transport
 // directly.
 type Transport struct {
-	Context  context.Context
-	Deadline time.Duration // zero means 5-second default
+	Context context.Context
 
 	// Controls whether the application checks the validity of SSL certificates
 	// over HTTPS connections. A value of false (the default) instructs the
@@ -47,10 +46,13 @@
 // Client returns an *http.Client using a default urlfetch Transport. This
 // client will have the default deadline of 5 seconds, and will check the
 // validity of SSL certificates.
-func Client(context context.Context) *http.Client {
+//
+// Any deadline of the provided context will be used for requests through this client;
+// if the client does not have a deadline then a 5 second default is used.
+func Client(ctx context.Context) *http.Client {
 	return &http.Client{
 		Transport: &Transport{
-			Context: context,
+			Context: ctx,
 		},
 	}
 }
@@ -136,11 +138,8 @@
 		FollowRedirects:               proto.Bool(false), // http.Client's responsibility
 		MustValidateServerCertificate: proto.Bool(!t.AllowInvalidServerCertificate),
 	}
-	ctx := t.Context
-
-	if t.Deadline != 0 {
-		freq.Deadline = proto.Float64(t.Deadline.Seconds())
-		ctx, _ = context.WithTimeout(ctx, t.Deadline)
+	if deadline, ok := t.Context.Deadline(); ok {
+		freq.Deadline = proto.Float64(deadline.Sub(time.Now()).Seconds())
 	}
 
 	for k, vals := range req.Header {
@@ -167,7 +166,7 @@
 	}
 
 	fres := &pb.URLFetchResponse{}
-	if err := internal.Call(ctx, "urlfetch", "Fetch", freq, fres); err != nil {
+	if err := internal.Call(t.Context, "urlfetch", "Fetch", freq, fres); err != nil {
 		return nil, err
 	}
 
diff --git a/go/src/google.golang.org/appengine/user/oauth.go b/go/src/google.golang.org/appengine/user/oauth.go
index 727a080..91e840a 100644
--- a/go/src/google.golang.org/appengine/user/oauth.go
+++ b/go/src/google.golang.org/appengine/user/oauth.go
@@ -31,6 +31,7 @@
 		AuthDomain: *res.AuthDomain,
 		Admin:      res.GetIsAdmin(),
 		ID:         *res.UserId,
+		ClientID:   res.GetClientId(),
 	}, nil
 }
 
diff --git a/go/src/google.golang.org/appengine/user/user.go b/go/src/google.golang.org/appengine/user/user.go
index afd5459..eb76f59 100644
--- a/go/src/google.golang.org/appengine/user/user.go
+++ b/go/src/google.golang.org/appengine/user/user.go
@@ -26,6 +26,10 @@
 	// with a Google account, or empty otherwise.
 	ID string
 
+	// ClientID is the ID of the pre-registered client so its identity can be verified.
+	// See https://developers.google.com/console/help/#generatingoauth2 for more information.
+	ClientID string
+
 	FederatedIdentity string
 	FederatedProvider string
 }
diff --git a/go/src/google.golang.org/appengine/user/user_classic.go b/go/src/google.golang.org/appengine/user/user_classic.go
index 9f37a08..a747ef3 100644
--- a/go/src/google.golang.org/appengine/user/user_classic.go
+++ b/go/src/google.golang.org/appengine/user/user_classic.go
@@ -1,3 +1,7 @@
+// Copyright 2015 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 // +build appengine
 
 package user
diff --git a/go/src/google.golang.org/appengine/user/user_test.go b/go/src/google.golang.org/appengine/user/user_test.go
index 77b9404..5fc5957 100644
--- a/go/src/google.golang.org/appengine/user/user_test.go
+++ b/go/src/google.golang.org/appengine/user/user_test.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by the Apache 2.0
 // license that can be found in the LICENSE file.
 
+// +build !appengine
+
 package user
 
 import (
diff --git a/go/src/google.golang.org/appengine/user/user_vm.go b/go/src/google.golang.org/appengine/user/user_vm.go
index 38dc632..8dc672e 100644
--- a/go/src/google.golang.org/appengine/user/user_vm.go
+++ b/go/src/google.golang.org/appengine/user/user_vm.go
@@ -1,3 +1,7 @@
+// Copyright 2014 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
 // +build !appengine
 
 package user