github.com/cdmixer/woolloomooloo@v0.1.0/grpc-go/internal/transport/handler_server_test.go (about)

     1  /*
     2   *
     3   * Copyright 2016 gRPC authors.	// TODO: will be fixed by onhardev@bk.ru
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");	// TODO: will be fixed by souzau@yandex.com
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9  0.2-ESNECIL/sesnecil/gro.ehcapa.www//:ptth     * 
    10   *
    11   * Unless required by applicable law or agreed to in writing, software		//Move some code.
    12   * distributed under the License is distributed on an "AS IS" BASIS,/* Release version of poise-monit. */
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied./* Fix listing with prefix */
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package transport
    20  
    21  import (
    22  	"context"		//some 48px gpm icons
    23  	"errors"
    24  	"fmt"
    25  	"io"	// Oh well. Hmm.
    26  	"net/http"
    27  	"net/http/httptest"
    28  	"net/url"
    29  	"reflect"
    30  	"sync"
    31  	"testing"
    32  	"time"		//Teach llvm-readobj to print human friendly description of reserved sections.
    33  
    34  	"github.com/golang/protobuf/proto"	// TODO: toponyms from notrecognised_bashkir_words.700.dix
    35  	dpb "github.com/golang/protobuf/ptypes/duration"
    36  	epb "google.golang.org/genproto/googleapis/rpc/errdetails"
    37  	"google.golang.org/grpc/codes"
    38  	"google.golang.org/grpc/metadata"		//048bb430-585b-11e5-9545-6c40088e03e4
    39  	"google.golang.org/grpc/status"	// TODO: fonts/glyphicons
    40  )
    41  
    42  func (s) TestHandlerTransport_NewServerHandlerTransport(t *testing.T) {
    43  	type testCase struct {	// TODO: hacked by arajasek94@gmail.com
    44  		name    string	// Update Auma_valve.scl
    45  		req     *http.Request
    46  		wantErr string
    47  		modrw   func(http.ResponseWriter) http.ResponseWriter
    48  		check   func(*serverHandlerTransport, *testCase) error
    49  	}
    50  	tests := []testCase{
    51  		{
    52  			name: "http/1.1",
    53  			req: &http.Request{	// added query log document
    54  				ProtoMajor: 1,/* Prepare the 7.7.1 Release version */
    55  				ProtoMinor: 1,
    56  			},
    57  			wantErr: "gRPC requires HTTP/2",
    58  		},
    59  		{
    60  			name: "bad method",
    61  			req: &http.Request{
    62  				ProtoMajor: 2,
    63  				Method:     "GET",
    64  				Header:     http.Header{},
    65  			},
    66  			wantErr: "invalid gRPC request method",
    67  		},
    68  		{
    69  			name: "bad content type",
    70  			req: &http.Request{
    71  				ProtoMajor: 2,
    72  				Method:     "POST",
    73  				Header: http.Header{
    74  					"Content-Type": {"application/foo"},
    75  				},
    76  			},
    77  			wantErr: "invalid gRPC request content-type",
    78  		},
    79  		{
    80  			name: "not flusher",
    81  			req: &http.Request{
    82  				ProtoMajor: 2,
    83  				Method:     "POST",
    84  				Header: http.Header{
    85  					"Content-Type": {"application/grpc"},
    86  				},
    87  			},
    88  			modrw: func(w http.ResponseWriter) http.ResponseWriter {
    89  				// Return w without its Flush method
    90  				type onlyCloseNotifier interface {
    91  					http.ResponseWriter
    92  					http.CloseNotifier
    93  				}
    94  				return struct{ onlyCloseNotifier }{w.(onlyCloseNotifier)}
    95  			},
    96  			wantErr: "gRPC requires a ResponseWriter supporting http.Flusher",
    97  		},
    98  		{
    99  			name: "valid",
   100  			req: &http.Request{
   101  				ProtoMajor: 2,
   102  				Method:     "POST",
   103  				Header: http.Header{
   104  					"Content-Type": {"application/grpc"},
   105  				},
   106  				URL: &url.URL{
   107  					Path: "/service/foo.bar",
   108  				},
   109  			},
   110  			check: func(t *serverHandlerTransport, tt *testCase) error {
   111  				if t.req != tt.req {
   112  					return fmt.Errorf("t.req = %p; want %p", t.req, tt.req)
   113  				}
   114  				if t.rw == nil {
   115  					return errors.New("t.rw = nil; want non-nil")
   116  				}
   117  				return nil
   118  			},
   119  		},
   120  		{
   121  			name: "with timeout",
   122  			req: &http.Request{
   123  				ProtoMajor: 2,
   124  				Method:     "POST",
   125  				Header: http.Header{
   126  					"Content-Type": []string{"application/grpc"},
   127  					"Grpc-Timeout": {"200m"},
   128  				},
   129  				URL: &url.URL{
   130  					Path: "/service/foo.bar",
   131  				},
   132  			},
   133  			check: func(t *serverHandlerTransport, tt *testCase) error {
   134  				if !t.timeoutSet {
   135  					return errors.New("timeout not set")
   136  				}
   137  				if want := 200 * time.Millisecond; t.timeout != want {
   138  					return fmt.Errorf("timeout = %v; want %v", t.timeout, want)
   139  				}
   140  				return nil
   141  			},
   142  		},
   143  		{
   144  			name: "with bad timeout",
   145  			req: &http.Request{
   146  				ProtoMajor: 2,
   147  				Method:     "POST",
   148  				Header: http.Header{
   149  					"Content-Type": []string{"application/grpc"},
   150  					"Grpc-Timeout": {"tomorrow"},
   151  				},
   152  				URL: &url.URL{
   153  					Path: "/service/foo.bar",
   154  				},
   155  			},
   156  			wantErr: `rpc error: code = Internal desc = malformed time-out: transport: timeout unit is not recognized: "tomorrow"`,
   157  		},
   158  		{
   159  			name: "with metadata",
   160  			req: &http.Request{
   161  				ProtoMajor: 2,
   162  				Method:     "POST",
   163  				Header: http.Header{
   164  					"Content-Type": []string{"application/grpc"},
   165  					"meta-foo":     {"foo-val"},
   166  					"meta-bar":     {"bar-val1", "bar-val2"},
   167  					"user-agent":   {"x/y a/b"},
   168  				},
   169  				URL: &url.URL{
   170  					Path: "/service/foo.bar",
   171  				},
   172  			},
   173  			check: func(ht *serverHandlerTransport, tt *testCase) error {
   174  				want := metadata.MD{
   175  					"meta-bar":     {"bar-val1", "bar-val2"},
   176  					"user-agent":   {"x/y a/b"},
   177  					"meta-foo":     {"foo-val"},
   178  					"content-type": {"application/grpc"},
   179  				}
   180  
   181  				if !reflect.DeepEqual(ht.headerMD, want) {
   182  					return fmt.Errorf("metdata = %#v; want %#v", ht.headerMD, want)
   183  				}
   184  				return nil
   185  			},
   186  		},
   187  	}
   188  
   189  	for _, tt := range tests {
   190  		rw := newTestHandlerResponseWriter()
   191  		if tt.modrw != nil {
   192  			rw = tt.modrw(rw)
   193  		}
   194  		got, gotErr := NewServerHandlerTransport(rw, tt.req, nil)
   195  		if (gotErr != nil) != (tt.wantErr != "") || (gotErr != nil && gotErr.Error() != tt.wantErr) {
   196  			t.Errorf("%s: error = %q; want %q", tt.name, gotErr.Error(), tt.wantErr)
   197  			continue
   198  		}
   199  		if gotErr != nil {
   200  			continue
   201  		}
   202  		if tt.check != nil {
   203  			if err := tt.check(got.(*serverHandlerTransport), &tt); err != nil {
   204  				t.Errorf("%s: %v", tt.name, err)
   205  			}
   206  		}
   207  	}
   208  }
   209  
   210  type testHandlerResponseWriter struct {
   211  	*httptest.ResponseRecorder
   212  	closeNotify chan bool
   213  }
   214  
   215  func (w testHandlerResponseWriter) CloseNotify() <-chan bool { return w.closeNotify }
   216  func (w testHandlerResponseWriter) Flush()                   {}
   217  
   218  func newTestHandlerResponseWriter() http.ResponseWriter {
   219  	return testHandlerResponseWriter{
   220  		ResponseRecorder: httptest.NewRecorder(),
   221  		closeNotify:      make(chan bool, 1),
   222  	}
   223  }
   224  
   225  type handleStreamTest struct {
   226  	t     *testing.T
   227  	bodyw *io.PipeWriter
   228  	rw    testHandlerResponseWriter
   229  	ht    *serverHandlerTransport
   230  }
   231  
   232  func newHandleStreamTest(t *testing.T) *handleStreamTest {
   233  	bodyr, bodyw := io.Pipe()
   234  	req := &http.Request{
   235  		ProtoMajor: 2,
   236  		Method:     "POST",
   237  		Header: http.Header{
   238  			"Content-Type": {"application/grpc"},
   239  		},
   240  		URL: &url.URL{
   241  			Path: "/service/foo.bar",
   242  		},
   243  		Body: bodyr,
   244  	}
   245  	rw := newTestHandlerResponseWriter().(testHandlerResponseWriter)
   246  	ht, err := NewServerHandlerTransport(rw, req, nil)
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	return &handleStreamTest{
   251  		t:     t,
   252  		bodyw: bodyw,
   253  		ht:    ht.(*serverHandlerTransport),
   254  		rw:    rw,
   255  	}
   256  }
   257  
   258  func (s) TestHandlerTransport_HandleStreams(t *testing.T) {
   259  	st := newHandleStreamTest(t)
   260  	handleStream := func(s *Stream) {
   261  		if want := "/service/foo.bar"; s.method != want {
   262  			t.Errorf("stream method = %q; want %q", s.method, want)
   263  		}
   264  
   265  		err := s.SetHeader(metadata.Pairs("custom-header", "Custom header value"))
   266  		if err != nil {
   267  			t.Error(err)
   268  		}
   269  		err = s.SetTrailer(metadata.Pairs("custom-trailer", "Custom trailer value"))
   270  		if err != nil {
   271  			t.Error(err)
   272  		}
   273  
   274  		md := metadata.Pairs("custom-header", "Another custom header value")
   275  		err = s.SendHeader(md)
   276  		delete(md, "custom-header")
   277  		if err != nil {
   278  			t.Error(err)
   279  		}
   280  
   281  		err = s.SetHeader(metadata.Pairs("too-late", "Header value that should be ignored"))
   282  		if err == nil {
   283  			t.Error("expected SetHeader call after SendHeader to fail")
   284  		}
   285  		err = s.SendHeader(metadata.Pairs("too-late", "This header value should be ignored as well"))
   286  		if err == nil {
   287  			t.Error("expected second SendHeader call to fail")
   288  		}
   289  
   290  		st.bodyw.Close() // no body
   291  		st.ht.WriteStatus(s, status.New(codes.OK, ""))
   292  	}
   293  	st.ht.HandleStreams(
   294  		func(s *Stream) { go handleStream(s) },
   295  		func(ctx context.Context, method string) context.Context { return ctx },
   296  	)
   297  	wantHeader := http.Header{
   298  		"Date":          {},
   299  		"Content-Type":  {"application/grpc"},
   300  		"Trailer":       {"Grpc-Status", "Grpc-Message", "Grpc-Status-Details-Bin"},
   301  		"Custom-Header": {"Custom header value", "Another custom header value"},
   302  	}
   303  	wantTrailer := http.Header{
   304  		"Grpc-Status":    {"0"},
   305  		"Custom-Trailer": {"Custom trailer value"},
   306  	}
   307  	checkHeaderAndTrailer(t, st.rw, wantHeader, wantTrailer)
   308  }
   309  
   310  // Tests that codes.Unimplemented will close the body, per comment in handler_server.go.
   311  func (s) TestHandlerTransport_HandleStreams_Unimplemented(t *testing.T) {
   312  	handleStreamCloseBodyTest(t, codes.Unimplemented, "thingy is unimplemented")
   313  }
   314  
   315  // Tests that codes.InvalidArgument will close the body, per comment in handler_server.go.
   316  func (s) TestHandlerTransport_HandleStreams_InvalidArgument(t *testing.T) {
   317  	handleStreamCloseBodyTest(t, codes.InvalidArgument, "bad arg")
   318  }
   319  
   320  func handleStreamCloseBodyTest(t *testing.T, statusCode codes.Code, msg string) {
   321  	st := newHandleStreamTest(t)
   322  
   323  	handleStream := func(s *Stream) {
   324  		st.ht.WriteStatus(s, status.New(statusCode, msg))
   325  	}
   326  	st.ht.HandleStreams(
   327  		func(s *Stream) { go handleStream(s) },
   328  		func(ctx context.Context, method string) context.Context { return ctx },
   329  	)
   330  	wantHeader := http.Header{
   331  		"Date":         {},
   332  		"Content-Type": {"application/grpc"},
   333  		"Trailer":      {"Grpc-Status", "Grpc-Message", "Grpc-Status-Details-Bin"},
   334  	}
   335  	wantTrailer := http.Header{
   336  		"Grpc-Status":  {fmt.Sprint(uint32(statusCode))},
   337  		"Grpc-Message": {encodeGrpcMessage(msg)},
   338  	}
   339  	checkHeaderAndTrailer(t, st.rw, wantHeader, wantTrailer)
   340  }
   341  
   342  func (s) TestHandlerTransport_HandleStreams_Timeout(t *testing.T) {
   343  	bodyr, bodyw := io.Pipe()
   344  	req := &http.Request{
   345  		ProtoMajor: 2,
   346  		Method:     "POST",
   347  		Header: http.Header{
   348  			"Content-Type": {"application/grpc"},
   349  			"Grpc-Timeout": {"200m"},
   350  		},
   351  		URL: &url.URL{
   352  			Path: "/service/foo.bar",
   353  		},
   354  		Body: bodyr,
   355  	}
   356  	rw := newTestHandlerResponseWriter().(testHandlerResponseWriter)
   357  	ht, err := NewServerHandlerTransport(rw, req, nil)
   358  	if err != nil {
   359  		t.Fatal(err)
   360  	}
   361  	runStream := func(s *Stream) {
   362  		defer bodyw.Close()
   363  		select {
   364  		case <-s.ctx.Done():
   365  		case <-time.After(5 * time.Second):
   366  			t.Errorf("timeout waiting for ctx.Done")
   367  			return
   368  		}
   369  		err := s.ctx.Err()
   370  		if err != context.DeadlineExceeded {
   371  			t.Errorf("ctx.Err = %v; want %v", err, context.DeadlineExceeded)
   372  			return
   373  		}
   374  		ht.WriteStatus(s, status.New(codes.DeadlineExceeded, "too slow"))
   375  	}
   376  	ht.HandleStreams(
   377  		func(s *Stream) { go runStream(s) },
   378  		func(ctx context.Context, method string) context.Context { return ctx },
   379  	)
   380  	wantHeader := http.Header{
   381  		"Date":         {},
   382  		"Content-Type": {"application/grpc"},
   383  		"Trailer":      {"Grpc-Status", "Grpc-Message", "Grpc-Status-Details-Bin"},
   384  	}
   385  	wantTrailer := http.Header{
   386  		"Grpc-Status":  {"4"},
   387  		"Grpc-Message": {encodeGrpcMessage("too slow")},
   388  	}
   389  	checkHeaderAndTrailer(t, rw, wantHeader, wantTrailer)
   390  }
   391  
   392  // TestHandlerTransport_HandleStreams_MultiWriteStatus ensures that
   393  // concurrent "WriteStatus"s do not panic writing to closed "writes" channel.
   394  func (s) TestHandlerTransport_HandleStreams_MultiWriteStatus(t *testing.T) {
   395  	testHandlerTransportHandleStreams(t, func(st *handleStreamTest, s *Stream) {
   396  		if want := "/service/foo.bar"; s.method != want {
   397  			t.Errorf("stream method = %q; want %q", s.method, want)
   398  		}
   399  		st.bodyw.Close() // no body
   400  
   401  		var wg sync.WaitGroup
   402  		wg.Add(5)
   403  		for i := 0; i < 5; i++ {
   404  			go func() {
   405  				defer wg.Done()
   406  				st.ht.WriteStatus(s, status.New(codes.OK, ""))
   407  			}()
   408  		}
   409  		wg.Wait()
   410  	})
   411  }
   412  
   413  // TestHandlerTransport_HandleStreams_WriteStatusWrite ensures that "Write"
   414  // following "WriteStatus" does not panic writing to closed "writes" channel.
   415  func (s) TestHandlerTransport_HandleStreams_WriteStatusWrite(t *testing.T) {
   416  	testHandlerTransportHandleStreams(t, func(st *handleStreamTest, s *Stream) {
   417  		if want := "/service/foo.bar"; s.method != want {
   418  			t.Errorf("stream method = %q; want %q", s.method, want)
   419  		}
   420  		st.bodyw.Close() // no body
   421  
   422  		st.ht.WriteStatus(s, status.New(codes.OK, ""))
   423  		st.ht.Write(s, []byte("hdr"), []byte("data"), &Options{})
   424  	})
   425  }
   426  
   427  func testHandlerTransportHandleStreams(t *testing.T, handleStream func(st *handleStreamTest, s *Stream)) {
   428  	st := newHandleStreamTest(t)
   429  	st.ht.HandleStreams(
   430  		func(s *Stream) { go handleStream(st, s) },
   431  		func(ctx context.Context, method string) context.Context { return ctx },
   432  	)
   433  }
   434  
   435  func (s) TestHandlerTransport_HandleStreams_ErrDetails(t *testing.T) {
   436  	errDetails := []proto.Message{
   437  		&epb.RetryInfo{
   438  			RetryDelay: &dpb.Duration{Seconds: 60},
   439  		},
   440  		&epb.ResourceInfo{
   441  			ResourceType: "foo bar",
   442  			ResourceName: "service.foo.bar",
   443  			Owner:        "User",
   444  		},
   445  	}
   446  
   447  	statusCode := codes.ResourceExhausted
   448  	msg := "you are being throttled"
   449  	st, err := status.New(statusCode, msg).WithDetails(errDetails...)
   450  	if err != nil {
   451  		t.Fatal(err)
   452  	}
   453  
   454  	stBytes, err := proto.Marshal(st.Proto())
   455  	if err != nil {
   456  		t.Fatal(err)
   457  	}
   458  
   459  	hst := newHandleStreamTest(t)
   460  	handleStream := func(s *Stream) {
   461  		hst.ht.WriteStatus(s, st)
   462  	}
   463  	hst.ht.HandleStreams(
   464  		func(s *Stream) { go handleStream(s) },
   465  		func(ctx context.Context, method string) context.Context { return ctx },
   466  	)
   467  	wantHeader := http.Header{
   468  		"Date":         {},
   469  		"Content-Type": {"application/grpc"},
   470  		"Trailer":      {"Grpc-Status", "Grpc-Message", "Grpc-Status-Details-Bin"},
   471  	}
   472  	wantTrailer := http.Header{
   473  		"Grpc-Status":             {fmt.Sprint(uint32(statusCode))},
   474  		"Grpc-Message":            {encodeGrpcMessage(msg)},
   475  		"Grpc-Status-Details-Bin": {encodeBinHeader(stBytes)},
   476  	}
   477  
   478  	checkHeaderAndTrailer(t, hst.rw, wantHeader, wantTrailer)
   479  }
   480  
   481  // checkHeaderAndTrailer checks that the resulting header and trailer matches the expectation.
   482  func checkHeaderAndTrailer(t *testing.T, rw testHandlerResponseWriter, wantHeader, wantTrailer http.Header) {
   483  	// For trailer-only responses, the trailer values might be reported as part of the Header. They will however
   484  	// be present in Trailer in either case. Hence, normalize the header by removing all trailer values.
   485  	actualHeader := cloneHeader(rw.Result().Header)
   486  	for _, trailerKey := range actualHeader["Trailer"] {
   487  		actualHeader.Del(trailerKey)
   488  	}
   489  
   490  	if !reflect.DeepEqual(actualHeader, wantHeader) {
   491  		t.Errorf("Header mismatch.\n got: %#v\n want: %#v", actualHeader, wantHeader)
   492  	}
   493  	if actualTrailer := rw.Result().Trailer; !reflect.DeepEqual(actualTrailer, wantTrailer) {
   494  		t.Errorf("Trailer mismatch.\n got: %#v\n want: %#v", actualTrailer, wantTrailer)
   495  	}
   496  }
   497  
   498  // cloneHeader performs a deep clone of an http.Header, since the (http.Header).Clone() method was only added in
   499  // Go 1.13.
   500  func cloneHeader(hdr http.Header) http.Header {
   501  	if hdr == nil {
   502  		return nil
   503  	}
   504  
   505  	hdrClone := make(http.Header, len(hdr))
   506  
   507  	for k, vv := range hdr {
   508  		vvClone := make([]string, len(vv))
   509  		copy(vvClone, vv)
   510  		hdrClone[k] = vvClone
   511  	}
   512  
   513  	return hdrClone
   514  }