Skip to content

Commit

Permalink
jhttp: allow content-type to include a charset (#111)
Browse files Browse the repository at this point in the history
Accept "utf8" and "utf-8".

Fixes #110.
  • Loading branch information
creachadair authored Nov 16, 2023
1 parent 394dd52 commit 36a71f8
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 13 deletions.
9 changes: 7 additions & 2 deletions jhttp/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"fmt"
"io"
"mime"
"net/http"

"github.com/creachadair/jrpc2"
Expand Down Expand Up @@ -57,8 +58,12 @@ func (b Bridge) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
if req.Header.Get("Content-Type") != "application/json" {
w.WriteHeader(http.StatusUnsupportedMediaType)
mt, params, _ := mime.ParseMediaType(req.Header.Get("Content-Type"))
if mt != "application/json" {
http.Error(w, "content-type must be application/json", http.StatusUnsupportedMediaType)
return
} else if cs, ok := params["charset"]; ok && cs != "utf-8" && cs != "utf8" {
http.Error(w, "invalid content-type charset", http.StatusUnsupportedMediaType)
return
}
}
Expand Down
37 changes: 26 additions & 11 deletions jhttp/jhttp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,24 @@ func TestBridge(t *testing.T) {

// Verify that a valid POST request succeeds.
t.Run("PostOK", func(t *testing.T) {
got := mustPost(t, hsrv.URL, `{
for _, charset := range []string{"", "utf8", "utf-8"} {
got := mustPost(t, hsrv.URL, charset, `{
"jsonrpc": "2.0",
"id": 1,
"method": "Test1",
"params": ["a", "foolish", "consistency", "is", "the", "hobgoblin"]
}`, http.StatusOK)

const want = `{"jsonrpc":"2.0","id":1,"result":6}`
if got != want {
t.Errorf("POST body: got %#q, want %#q", got, want)
const want = `{"jsonrpc":"2.0","id":1,"result":6}`
if got != want {
t.Errorf("POST body: got %#q, want %#q", got, want)
}
}
})

// Verify that the bridge will accept a batch.
t.Run("PostBatchOK", func(t *testing.T) {
got := mustPost(t, hsrv.URL, `[
got := mustPost(t, hsrv.URL, "", `[
{"jsonrpc":"2.0", "id": 3, "method": "Test1", "params": ["first"]},
{"jsonrpc":"2.0", "id": 7, "method": "Test1", "params": ["among", "equals"]}
]`, http.StatusOK)
Expand Down Expand Up @@ -88,9 +90,18 @@ func TestBridge(t *testing.T) {
}
})

// Verify that the charset, if provided, is utf-8.
t.Run("PostInvalidCharset", func(t *testing.T) {
got := mustPost(t, hsrv.URL, "iso-8859-1", "{}", http.StatusUnsupportedMediaType)
const want = "invalid content-type charset\n"
if got != want {
t.Errorf("POST response body: got %q, want %q", got, want)
}
})

// Verify that a POST that generates a JSON-RPC error succeeds.
t.Run("PostErrorReply", func(t *testing.T) {
got := mustPost(t, hsrv.URL, `{
got := mustPost(t, hsrv.URL, "utf-8", `{
"id": 1,
"jsonrpc": "2.0"
}`, http.StatusOK)
Expand All @@ -103,7 +114,7 @@ func TestBridge(t *testing.T) {

// Verify that an invalid ID is not swallowed by the remapping process (see #80).
t.Run("PostInvalidID", func(t *testing.T) {
got := mustPost(t, hsrv.URL, `{
got := mustPost(t, hsrv.URL, "", `{
"jsonrpc": "2.0",
"id": ["this is totally bogus"],
"method": "Test1"
Expand All @@ -117,7 +128,7 @@ func TestBridge(t *testing.T) {

// Verify that a notification returns an empty success.
t.Run("PostNotification", func(t *testing.T) {
got := mustPost(t, hsrv.URL, `{
got := mustPost(t, hsrv.URL, "", `{
"jsonrpc": "2.0",
"method": "TakeNotice",
"params": []
Expand Down Expand Up @@ -224,7 +235,7 @@ func TestBridge_parseGETRequest(t *testing.T) {
})
t.Run("POST", func(t *testing.T) {
const req = `{"jsonrpc":"2.0", "id":1, "method":"str/eq", "params":["foo","foo"]}`
got := mustPost(t, hsrv.URL, req, http.StatusOK)
got := mustPost(t, hsrv.URL, "", req, http.StatusOK)

const want = `{"jsonrpc":"2.0","id":1,"result":true}`
if got != want {
Expand Down Expand Up @@ -302,9 +313,13 @@ func checkClose(t *testing.T, c io.Closer) {
}
}

func mustPost(t *testing.T, url, req string, code int) string {
func mustPost(t *testing.T, url, charset, req string, code int) string {
t.Helper()
rsp, err := http.Post(url, "application/json", strings.NewReader(req))
ctype := "application/json"
if charset != "" {
ctype += "; charset=" + charset
}
rsp, err := http.Post(url, ctype, strings.NewReader(req))
if err != nil {
t.Fatalf("POST request failed: %v", err)
} else if got := rsp.StatusCode; got != code {
Expand Down

0 comments on commit 36a71f8

Please sign in to comment.