Skip to content

Commit

Permalink
Fix documentation on method name and add additional parsing safety ch…
Browse files Browse the repository at this point in the history
…ecks
  • Loading branch information
bufdev committed Jan 31, 2019
1 parent 754008b commit 320fbde
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 25 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# ghz

[![Release](https://img.shields.io/github/release/bojand/ghz.svg?style=flat-square)](https://github.com/bojand/ghz/releases/latest)
[![Release](https://img.shields.io/github/release/bojand/ghz.svg?style=flat-square)](https://github.com/bojand/ghz/releases/latest)
[![Build Status](https://img.shields.io/circleci/project/github/bojand/ghz/master.svg?style=flat-square)](https://circleci.com/gh/bojand/ghz)
[![Go Report Card](https://goreportcard.com/badge/github.com/bojand/ghz?style=flat-square)](https://goreportcard.com/report/github.com/bojand/ghz)
[![License](https://img.shields.io/github/license/bojand/ghz.svg?style=flat-square)](https://raw.githubusercontent.com/bojand/ghz/master/LICENSE)
Expand All @@ -29,7 +29,7 @@ Options:

-proto The Protocol Buffer .proto file.
-protoset The compiled protoset file. Alternative to proto. -proto takes precedence.
-call A fully-qualified method name in 'package/service/method' or 'package.service.method' format.
-call A fully-qualified method name in 'package.Service/method' or 'package.Service.Method' format.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.

Expand Down Expand Up @@ -60,7 +60,7 @@ Options:
-m Request metadata as stringified JSON.
-M Path for call metadata JSON file. Examples: /home/user/metadata.json or ./metadata.json.
-si Stream interval duration. Spread stream sends by given amount.
-si Stream interval duration. Spread stream sends by given amount.
Only applies to client and bidi streaming calls. Example: 100ms
-o Output path. If none provided stdout is used.
Expand Down
10 changes: 5 additions & 5 deletions cmd/ghz/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var (

proto = flag.String("proto", "", `The Protocol Buffer .proto file.`)
protoset = flag.String("protoset", "", `The compiled protoset file. Alternative to proto. -proto takes precedence.`)
call = flag.String("call", "", `A fully-qualified method name in 'package/service/method' or 'package.service.method' format.`)
call = flag.String("call", "", `A fully-qualified method name in 'package.Service/method' or 'package.Service.Method' format.`)
paths = flag.String("i", "", "Comma separated list of proto import paths. The current working directory and the directory of the protocol buffer file are automatically added to the import list.")

cacert = flag.String("cacert", "", "File containing trusted root certificates for verifying the server.")
Expand Down Expand Up @@ -71,10 +71,10 @@ Options:
-proto The Protocol Buffer .proto file.
-protoset The compiled protoset file. Alternative to proto. -proto takes precedence.
-call A fully-qualified method name in 'package/service/method' or 'package.service.method' format.
-call A fully-qualified method name in 'package.Service/Method' or 'package.Service.Method' format.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.
-cacert File containing trusted root certificates for verifying the server.
-cert File containing client certificate (public key), to present to the server. Must also provide -key option.
-key File containing client private key, to present to the server. Must also provide -cert option.
Expand All @@ -83,7 +83,7 @@ Options:
-insecure Use plaintext and insecure connection.
-authority Value to be used as the :authority pseudo-header. Only works if -insecure is used.
-c Number of requests to run concurrently.
-c Number of requests to run concurrently.
Total number of requests cannot be smaller than the concurrency level. Default is 50.
-n Number of requests to run. Default is 200.
-q Rate limit, in queries per second (QPS). Default is no rate limit.
Expand All @@ -102,7 +102,7 @@ Options:
-m Request metadata as stringified JSON.
-M Path for call metadata JSON file. Examples: /home/user/metadata.json or ./metadata.json.
-si Stream interval duration. Spread stream sends by given amount.
-si Stream interval duration. Spread stream sends by given amount.
Only applies to client and bidi streaming calls. Example: 100ms
-o Output path. If none provided stdout is used.
Expand Down
47 changes: 38 additions & 9 deletions protodesc/protodesc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package protodesc

import (
"errors"
"fmt"
"io/ioutil"
"path/filepath"
Expand All @@ -12,6 +13,8 @@ import (
"github.com/jhump/protoreflect/desc/protoparse"
)

var errNoMethodNameSpecified = errors.New("no method name specified")

// GetMethodDescFromProto gets method descritor for the given call symbol from proto file given my path proto
// imports is used for import paths in parsing the proto file
func GetMethodDescFromProto(call, proto string, imports []string) (*desc.MethodDescriptor, error) {
Expand Down Expand Up @@ -64,9 +67,9 @@ func GetMethodDescFromProtoSet(call, protoset string) (*desc.MethodDescriptor, e
}

func getMethodDesc(call string, files map[string]*desc.FileDescriptor) (*desc.MethodDescriptor, error) {
svc, mth := parseSymbol(call)
if svc == "" || mth == "" {
return nil, fmt.Errorf("given method name %q is not in expected format: 'service/method' or 'service.method'", call)
svc, mth, err := parseServiceMethod(call)
if err != nil {
return nil, err
}

dsc, err := findServiceSymbol(files, svc)
Expand Down Expand Up @@ -123,13 +126,39 @@ func findServiceSymbol(resolved map[string]*desc.FileDescriptor, fullyQualifiedN
return nil, fmt.Errorf("cannot find service %q", fullyQualifiedName)
}

func parseSymbol(svcAndMethod string) (string, string) {
pos := strings.LastIndex(svcAndMethod, "/")
if pos < 0 {
pos = strings.LastIndex(svcAndMethod, ".")
// parseServiceMethod parses the fully-qualified service name without a leading "."
// and the method name from the input string.
//
// valid inputs:
// package.Service.Method
// .package.Service.Method
// package.Service/Method
// .package.Service/Method
func parseServiceMethod(svcAndMethod string) (string, string, error) {
if len(svcAndMethod) == 0 {
return "", "", errNoMethodNameSpecified
}
if svcAndMethod[0] == '.' {
svcAndMethod = svcAndMethod[1:]
}
if len(svcAndMethod) == 0 {
return "", "", errNoMethodNameSpecified
}
switch strings.Count(svcAndMethod, "/") {
case 0:
pos := strings.LastIndex(svcAndMethod, ".")
if pos < 0 {
return "", ""
return "", "", newInvalidMethodNameError(svcAndMethod)
}
return svcAndMethod[:pos], svcAndMethod[pos+1:], nil
case 1:
split := strings.Split(svcAndMethod, "/")
return split[0], split[1], nil
default:
return "", "", newInvalidMethodNameError(svcAndMethod)
}
return svcAndMethod[:pos], svcAndMethod[pos+1:]
}

func newInvalidMethodNameError(svcAndMethod string) error {
return fmt.Errorf("method name must be package.Service.Method or package.Service/Method: %q", svcAndMethod)
}
26 changes: 26 additions & 0 deletions protodesc/protodesc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,29 @@ func TestProtodesc_GetMethodDescFromProtoSet(t *testing.T) {
assert.NotNil(t, md)
})
}

func TestParseServiceMethod(t *testing.T) {
testParseServiceMethodSuccess(t, "package.Service.Method", "package.Service", "Method")
testParseServiceMethodSuccess(t, ".package.Service.Method", "package.Service", "Method")
testParseServiceMethodSuccess(t, "package.Service/Method", "package.Service", "Method")
testParseServiceMethodSuccess(t, ".package.Service/Method", "package.Service", "Method")
testParseServiceMethodSuccess(t, "Service.Method", "Service", "Method")
testParseServiceMethodSuccess(t, ".Service.Method", "Service", "Method")
testParseServiceMethodSuccess(t, "Service/Method", "Service", "Method")
testParseServiceMethodSuccess(t, ".Service/Method", "Service", "Method")
testParseServiceMethodError(t, "")
testParseServiceMethodError(t, ".")
testParseServiceMethodError(t, "package/Service/Method")
}

func testParseServiceMethodSuccess(t *testing.T, svcAndMethod string, expectedService string, expectedMethod string) {
service, method, err := parseServiceMethod(svcAndMethod)
assert.NoError(t, err)
assert.Equal(t, expectedService, service)
assert.Equal(t, expectedMethod, method)
}

func testParseServiceMethodError(t *testing.T, svcAndMethod string) {
_, _, err := parseServiceMethod(svcAndMethod)
assert.Error(t, err)
}
12 changes: 6 additions & 6 deletions www/docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: options
title: Options Reference
---

The `ghz` command line has numerous command line options. You can run `ghz --help` to view all available options.
The `ghz` command line has numerous command line options. You can run `ghz --help` to view all available options.

<AUTOGENERATED_TABLE_OF_CONTENTS>

Expand All @@ -22,7 +22,7 @@ protoc --proto_path=. --descriptor_set_out=bundle.protoset *.proto

### `-call`

A fully-qualified method name in 'package/service/method' or 'package.service.method' format. For example: `helloworld.Greeter.SayHello`.
A fully-qualified method name in 'package.Service/Method' or 'package.Service.Method' format. For example: `helloworld.Greeter.SayHello`.

### `-cacert`

Expand Down Expand Up @@ -58,15 +58,15 @@ Path to the JSON or TOML [config file](example_config.md) that specifies all the

### `-c`

Number of requests to run concurrently. Total number of requests cannot be smaller than the concurrency level. Default is `50`. For example to do requests in series without any concurrency set to `1`.
Number of requests to run concurrently. Total number of requests cannot be smaller than the concurrency level. Default is `50`. For example to do requests in series without any concurrency set to `1`.

### `-n`

The total number of requests to run. Default is `200`. The combination of `-c` and `-n` are critical in how the benchmarking is done. `ghz` takes the `-c` argument and spawns that make worker goroutines. In parallel these goroutines each do their share (`c / n`) requests. So for example with the default `-c 50 -n 200` options we would spawn `50` goroutines which in parallel each do `40` requests.
The total number of requests to run. Default is `200`. The combination of `-c` and `-n` are critical in how the benchmarking is done. `ghz` takes the `-c` argument and spawns that make worker goroutines. In parallel these goroutines each do their share (`c / n`) requests. So for example with the default `-c 50 -n 200` options we would spawn `50` goroutines which in parallel each do `40` requests.

### `-q`

Rate limit for each worker goroutine in queries per second (QPS). Default is no rate limit. This essentially spaces out the requests by adding a timeout to each worker before it attempts each call.
Rate limit for each worker goroutine in queries per second (QPS). Default is no rate limit. This essentially spaces out the requests by adding a timeout to each worker before it attempts each call.

### `-t`

Expand All @@ -82,7 +82,7 @@ Maximum duration of application to send requests with `n` setting respected. If

### `-d`

The call data as stringified JSON. If the value is `@` then the request contents are read from standard input (stdin). Example: `-d '{"name":"Bob"}'`.
The call data as stringified JSON. If the value is `@` then the request contents are read from standard input (stdin). Example: `-d '{"name":"Bob"}'`.

For client streaming or bi-directional calls we can accept a JSON array of messages, and each element representing a single message within the stream call. For example: `-d '[{"name":"Joe"},{"name":"Kate"},{"name":"Sara"}]'` can be used as input for a client streaming or bidi call. In case of streaming calls if a single object is given for data then it is automatically converted to an array with single element. For example `-d '{"name":"Joe"}'` is equivalent to `-d '[{"name":"Joe"}]`.

Expand Down
4 changes: 2 additions & 2 deletions www/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Options:

-proto The Protocol Buffer .proto file.
-protoset The compiled protoset file. Alternative to proto. -proto takes precedence.
-call A fully-qualified method name in 'package/service/method' or 'package.service.method' format.
-call A fully-qualified method name in 'package.Service/Method' or 'package.Service.Method' format.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.

Expand Down Expand Up @@ -42,7 +42,7 @@ Options:
-m Request metadata as stringified JSON.
-M Path for call metadata JSON file. Examples: /home/user/metadata.json or ./metadata.json.
-si Stream interval duration. Spread stream sends by given amount.
-si Stream interval duration. Spread stream sends by given amount.
Only applies to client and bidi streaming calls. Example: 100ms
-o Output path. If none provided stdout is used.
Expand Down

0 comments on commit 320fbde

Please sign in to comment.