-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generate and display more useful error message (#5)
- Loading branch information
Showing
6 changed files
with
169 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package grpcproto | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"unicode/utf8" | ||
) | ||
|
||
// This code is copied from google.golang.org/[email protected]/internal/transport/http_util.go, ll.443-494, | ||
// and has been adjusted to make the `EncodeGrpcMessage` function exported. | ||
// The original code is Copyright (c) by the gRPC authors and was distributed under the | ||
// Apache License, version 2.0. | ||
|
||
const ( | ||
spaceByte = ' ' | ||
tildeByte = '~' | ||
percentByte = '%' | ||
) | ||
|
||
// EncodeGrpcMessage is used to encode status code in header field | ||
// "grpc-message". It does percent encoding and also replaces invalid utf-8 | ||
// characters with Unicode replacement character. | ||
// | ||
// It checks to see if each individual byte in msg is an allowable byte, and | ||
// then either percent encoding or passing it through. When percent encoding, | ||
// the byte is converted into hexadecimal notation with a '%' prepended. | ||
func EncodeGrpcMessage(msg string) string { | ||
if msg == "" { | ||
return "" | ||
} | ||
lenMsg := len(msg) | ||
for i := 0; i < lenMsg; i++ { | ||
c := msg[i] | ||
if !(c >= spaceByte && c <= tildeByte && c != percentByte) { | ||
return encodeGrpcMessageUnchecked(msg) | ||
} | ||
} | ||
return msg | ||
} | ||
|
||
func encodeGrpcMessageUnchecked(msg string) string { | ||
var buf bytes.Buffer | ||
for len(msg) > 0 { | ||
r, size := utf8.DecodeRuneInString(msg) | ||
for _, b := range []byte(string(r)) { | ||
if size > 1 { | ||
// If size > 1, r is not ascii. Always do percent encoding. | ||
buf.WriteString(fmt.Sprintf("%%%02X", b)) | ||
continue | ||
} | ||
|
||
// The for loop is necessary even if size == 1. r could be | ||
// utf8.RuneError. | ||
// | ||
// fmt.Sprintf("%%%02X", utf8.RuneError) gives "%FFFD". | ||
if b >= spaceByte && b <= tildeByte && b != percentByte { | ||
buf.WriteByte(b) | ||
} else { | ||
buf.WriteString(fmt.Sprintf("%%%02X", b)) | ||
} | ||
} | ||
msg = msg[size:] | ||
} | ||
return buf.String() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package grpcwebsocket | ||
|
||
const ( | ||
// SubprotocolName is the subprotocol for gRPC-websocket specified in the Sec-Websocket-Protocol | ||
// header. | ||
SubprotocolName = "grpc-ws" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package httputils | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"net/http" | ||
"regexp" | ||
"strings" | ||
"unicode/utf8" | ||
) | ||
|
||
const ( | ||
maxBodyBytes = 1024 | ||
) | ||
|
||
var ( | ||
httpHeaderOptSeparatorRegex = regexp.MustCompile(`;\s*`) | ||
) | ||
|
||
// ExtractResponseError extracts an error from an HTTP response, reading at most 1024 bytes of the | ||
// response body. | ||
func ExtractResponseError(resp *http.Response) error { | ||
if resp.StatusCode < 400 { | ||
return nil | ||
} | ||
contentTypeFields := httpHeaderOptSeparatorRegex.Split(resp.Header.Get("Content-Type"), 2) | ||
if len(contentTypeFields) == 0 { | ||
return errors.New(resp.Status) | ||
} | ||
|
||
if contentTypeFields[0] != "text/plain" { | ||
return fmt.Errorf("%s, content-type %s", resp.Status, contentTypeFields[0]) | ||
} | ||
|
||
bodyReader := io.LimitReader(resp.Body, maxBodyBytes) | ||
contents, err := ioutil.ReadAll(bodyReader) | ||
contentsStr := strings.TrimSpace(string(contents)) | ||
if !utf8.Valid(contents) { | ||
contentsStr = "invalid UTF-8 characters in response" | ||
} | ||
if err != nil { | ||
if contentsStr == "" { | ||
return fmt.Errorf("%s, error reading response body: %v", resp.Status, err) | ||
} | ||
return fmt.Errorf("%s: %s, error reading response body after %d bytes: %v", resp.Status, contentsStr, len(contents), err) | ||
} | ||
|
||
if contentsStr == "" { | ||
return errors.New(resp.Status) | ||
} | ||
return fmt.Errorf("%s: %s", resp.Status, contentsStr) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters