Beispiel #1
0
func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	start := time.Now()

	defer req.Body.Close()
	if req.Method != "POST" {
		http.NotFound(w, req)
		return
	}

	service := req.Header.Get(ServiceHeader)
	procedure := req.Header.Get(ProcedureHeader)

	err := h.callHandler(w, req, start)
	if err == nil {
		return
	}

	err = errors.AsHandlerError(service, procedure, err)
	status := http.StatusInternalServerError
	if transport.IsBadRequestError(err) {
		status = http.StatusBadRequest
	} else if transport.IsTimeoutError(err) {
		status = http.StatusGatewayTimeout
	}
	http.Error(w, err.Error(), status)
}
Beispiel #2
0
// remoteTimeout tests if a yarpc client returns a remote timeout error behind
// the TimeoutError interface when a remote tchannel handler returns a handler
// timeout.
func remoteTimeout(t crossdock.T, dispatcher yarpc.Dispatcher) {
	assert := crossdock.Assert(t)

	headers := yarpc.NewHeaders()
	token := random.Bytes(5)

	_, _, err := rawCall(dispatcher, headers, "handlertimeout/raw", token)
	if skipOnConnRefused(t, err) {
		return
	}
	if !assert.Error(err, "expected an error") {
		return
	}

	if transport.IsBadRequestError(err) {
		t.Skipf("handlertimeout/raw procedure not implemented: %v", err)
		return
	}

	assert.True(transport.IsTimeoutError(err), "returns a TimeoutError: %T", err)

	form := strings.HasPrefix(err.Error(),
		`Timeout: call to procedure "handlertimeout/raw" of service "service" from caller "caller" timed out after`)
	assert.True(form, "must be a remote handler timeout: %q", err.Error())
}
Beispiel #3
0
func isUnrecognizedProcedure(err error) bool {
	if transport.IsBadRequestError(err) {
		// TODO: Once all other languages implement the gauntlet test
		// subject, we can remove this check.
		return strings.Contains(err.Error(), "unrecognized procedure")
	}
	return false
}
Beispiel #4
0
func (h handler) handle(ctx context.Context, call inboundCall) {
	start := time.Now()
	err := h.callHandler(ctx, call, start)
	if err == nil {
		return
	}

	if _, ok := err.(tchannel.SystemError); ok {
		call.Response().SendSystemError(err)
		return
	}

	err = errors.AsHandlerError(call.ServiceName(), call.MethodString(), err)
	status := tchannel.ErrCodeUnexpected
	if transport.IsBadRequestError(err) {
		status = tchannel.ErrCodeBadRequest
	} else if transport.IsTimeoutError(err) {
		status = tchannel.ErrCodeTimeout
	}

	call.Response().SendSystemError(tchannel.NewSystemError(status, err.Error()))
}
Beispiel #5
0
// runRaw tests if a yarpc client returns a remote timeout error behind the
// TimeoutError interface when a remote http handler returns a handler timeout.
func runRaw(t crossdock.T, disp yarpc.Dispatcher) {
	assert := crossdock.Assert(t)
	fatals := crossdock.Fatals(t)

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	ch := raw.New(disp.Channel("yarpc-test"))
	_, _, err := ch.Call(ctx, yarpc.NewReqMeta().Procedure("handlertimeout/raw"), nil)
	fatals.Error(err, "expected an error")

	if transport.IsBadRequestError(err) {
		t.Skipf("handlertimeout/raw method not implemented: %v", err)
		return
	}

	assert.True(transport.IsTimeoutError(err), "returns a TimeoutError: %T", err)

	form := strings.HasPrefix(err.Error(),
		`Timeout: call to procedure "handlertimeout/raw" of service "service" from caller "caller" timed out after`)
	assert.True(form, "must be a remote handler timeout: %q", err.Error())
}
Beispiel #6
0
// Run tests if a yarpc client returns correctly a client timeout error behind
// the TimeoutError interface when the context deadline is reached while the
// server is taking too long to respond.
func Run(t crossdock.T) {
	assert := crossdock.Assert(t)
	fatals := crossdock.Fatals(t)

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	dispatcher := disp.Create(t)
	fatals.NoError(dispatcher.Start(), "could not start Dispatcher")
	defer dispatcher.Stop()

	ch := raw.New(dispatcher.Channel("yarpc-test"))
	_, _, err := ch.Call(ctx, yarpc.NewReqMeta().Procedure("sleep/raw"), nil)
	fatals.Error(err, "expected a failure for timeout")

	if transport.IsBadRequestError(err) {
		t.Skipf("sleep/raw method not implemented: %v", err)
		return
	}

	assert.True(transport.IsTimeoutError(err), "returns a TimeoutError: %T", err)

	trans := t.Param(params.Transport)
	switch trans {
	case "http":
		form := strings.HasPrefix(err.Error(),
			`client timeout for procedure "sleep/raw" of service "yarpc-test" after`)
		assert.True(form, "should be a client timeout: %q", err.Error())
	case "tchannel":
		form := strings.HasPrefix(err.Error(), `timeout`)
		assert.True(form,
			"should be a remote timeout (we cant represent client timeout with tchannel): %q",
			err.Error())
	default:
		fatals.Fail("", "unknown transport %q", trans)
	}
}
Beispiel #7
0
func TestSimpleRoundTrip(t *testing.T) {
	transports := []roundTripTransport{
		httpTransport{t},
		tchannelTransport{t},
	}

	tests := []struct {
		requestHeaders  transport.Headers
		requestBody     string
		responseHeaders transport.Headers
		responseBody    string
		responseError   error

		wantError func(error)
	}{
		{
			requestHeaders:  transport.NewHeaders().With("token", "1234"),
			requestBody:     "world",
			responseHeaders: transport.NewHeaders().With("status", "ok"),
			responseBody:    "hello, world",
		},
		{
			requestBody:   "foo",
			responseError: errors.HandlerUnexpectedError(fmt.Errorf("great sadness")),
			wantError: func(err error) {
				assert.True(t, transport.IsUnexpectedError(err), err)
				assert.Equal(t, "UnexpectedError: great sadness", err.Error())
			},
		},
		{
			requestBody:   "bar",
			responseError: errors.HandlerBadRequestError(fmt.Errorf("missing service name")),
			wantError: func(err error) {
				assert.True(t, transport.IsBadRequestError(err))
				assert.Equal(t, "BadRequest: missing service name", err.Error())
			},
		},
		{
			requestBody: "baz",
			responseError: errors.RemoteUnexpectedError(
				`UnexpectedError: error for procedure "foo" of service "bar": great sadness`,
			),
			wantError: func(err error) {
				assert.True(t, transport.IsUnexpectedError(err))
				assert.Equal(t,
					`UnexpectedError: error for procedure "hello" of service "testService": `+
						`UnexpectedError: error for procedure "foo" of service "bar": great sadness`,
					err.Error())
			},
		},
		{
			requestBody: "qux",
			responseError: errors.RemoteBadRequestError(
				`BadRequest: unrecognized procedure "echo" for service "derp"`,
			),
			wantError: func(err error) {
				assert.True(t, transport.IsUnexpectedError(err))
				assert.Equal(t,
					`UnexpectedError: error for procedure "hello" of service "testService": `+
						`BadRequest: unrecognized procedure "echo" for service "derp"`,
					err.Error())
			},
		},
	}

	rootCtx := context.Background()
	for _, tt := range tests {
		for _, trans := range transports {
			requestMatcher := transporttest.NewRequestMatcher(t, &transport.Request{
				Caller:    testCaller,
				Service:   testService,
				Procedure: testProcedure,
				Encoding:  raw.Encoding,
				Headers:   tt.requestHeaders,
				Body:      bytes.NewReader([]byte(tt.requestBody)),
			})

			handler := unaryHandlerFunc(func(_ context.Context, r *transport.Request, w transport.ResponseWriter) error {
				assert.True(t, requestMatcher.Matches(r), "request mismatch: received %v", r)

				if tt.responseError != nil {
					return tt.responseError
				}

				if tt.responseHeaders.Len() > 0 {
					w.AddHeaders(tt.responseHeaders)
				}

				_, err := w.Write([]byte(tt.responseBody))
				assert.NoError(t, err, "failed to write response for %v", r)
				return err
			})

			ctx, cancel := context.WithTimeout(rootCtx, 200*time.Millisecond)
			defer cancel()

			registry := staticRegistry{Handler: handler}
			trans.WithRegistry(registry, func(o transport.UnaryOutbound) {
				res, err := o.Call(ctx, &transport.Request{
					Caller:    testCaller,
					Service:   testService,
					Procedure: testProcedure,
					Encoding:  raw.Encoding,
					Headers:   tt.requestHeaders,
					Body:      bytes.NewReader([]byte(tt.requestBody)),
				})

				if tt.wantError != nil {
					if assert.Error(t, err, "%T: expected error, got %v", trans, res) {
						tt.wantError(err)

						// none of the errors returned by Call can be valid
						// Handler errors.
						_, ok := err.(errors.HandlerError)
						assert.False(t, ok, "%T: %T must not be a HandlerError", trans, err)
					}
				} else {
					responseMatcher := transporttest.NewResponseMatcher(t, &transport.Response{
						Headers: tt.responseHeaders,
						Body:    ioutil.NopCloser(bytes.NewReader([]byte(tt.responseBody))),
					})

					if assert.NoError(t, err, "%T: call failed", trans) {
						assert.True(t, responseMatcher.Matches(res), "%T: response mismatch", trans)
					}
				}
			})
		}
	}
}