func TestReverseProxyQuery(t *testing.T) { backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Got-Query", r.URL.RawQuery) w.Write([]byte("hi")) })) defer backend.Close() for i, tt := range proxyQueryTests { backendURL, err := url.Parse(backend.URL + tt.baseSuffix) if err != nil { t.Fatal(err) } frontend := httptest.NewServer(NewSingleHostReverseProxy(backendURL)) req, _ := http.NewRequest("GET", frontend.URL+tt.reqSuffix, nil) req.Close = true res, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("%d. Get: %v", i, err) } if g, e := res.Header.Get("X-Got-Query"), tt.want; g != e { t.Errorf("%d. got query %q; expected %q", i, g, e) } res.Body.Close() frontend.Close() } }
func TestFileServerImplicitLeadingSlash(t *testing.T) { defer afterTest(t) tempDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("TempDir: %v", err) } defer mustRemoveAll(tempDir) if err := ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("Hello world"), 0644); err != nil { t.Fatalf("WriteFile: %v", err) } ts := httptest.NewServer(StripPrefix("/bar/", FileServer(Dir(tempDir)))) defer ts.Close() get := func(suffix string) string { res, err := Get(ts.URL + suffix) if err != nil { t.Fatalf("Get %s: %v", suffix, err) } b, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("ReadAll %s: %v", suffix, err) } res.Body.Close() return string(b) } if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") { t.Logf("expected a directory listing with foo.txt, got %q", s) } if s := get("/bar/foo.txt"); s != "Hello world" { t.Logf("expected %q, got %q", "Hello world", s) } }
func TestServeFileContentType(t *testing.T) { defer afterTest(t) const ctype = "icecream/chocolate" ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { switch r.FormValue("override") { case "1": w.Header().Set("Content-Type", ctype) case "2": // Explicitly inhibit sniffing. w.Header()["Content-Type"] = []string{} } ServeFile(w, r, "testdata/file") })) defer ts.Close() get := func(override string, want []string) { resp, err := Get(ts.URL + "?override=" + override) if err != nil { t.Fatal(err) } if h := resp.Header["Content-Type"]; !reflect.DeepEqual(h, want) { t.Errorf("Content-Type mismatch: got %v, want %v", h, want) } resp.Body.Close() } get("0", []string{"text/plain; charset=utf-8"}) get("1", []string{ctype}) get("2", nil) }
// TestCopyError tests that we kill the process if there's an error copying // its output. (for example, from the client having gone away) func TestCopyError(t *testing.T) { check(t) if runtime.GOOS == "windows" { t.Skipf("skipping test on %q", runtime.GOOS) } h := &Handler{ Path: "testdata/test.cgi", Root: "/test.cgi", } ts := httptest.NewServer(h) defer ts.Close() conn, err := net.Dial("tcp", ts.Listener.Addr().String()) if err != nil { t.Fatal(err) } req, _ := http.NewRequest("GET", "http://example.com/test.cgi?bigresponse=1", nil) err = req.Write(conn) if err != nil { t.Fatalf("Write: %v", err) } res, err := http.ReadResponse(bufio.NewReader(conn), req) if err != nil { t.Fatalf("ReadResponse: %v", err) } pidstr := res.Header.Get("X-CGI-Pid") if pidstr == "" { t.Fatalf("expected an X-CGI-Pid header in response") } pid, err := strconv.Atoi(pidstr) if err != nil { t.Fatalf("invalid X-CGI-Pid value") } var buf [5000]byte n, err := io.ReadFull(res.Body, buf[:]) if err != nil { t.Fatalf("ReadFull: %d bytes, %v", n, err) } childRunning := func() bool { return isProcessRunning(t, pid) } if !childRunning() { t.Fatalf("pre-conn.Close, expected child to be running") } conn.Close() tries := 0 for tries < 25 && childRunning() { time.Sleep(50 * time.Millisecond * time.Duration(tries)) tries++ } if childRunning() { t.Fatalf("post-conn.Close, expected child to be gone") } }
func TestBasicAuthWithCurl(t *testing.T) { expected := ":c>" background := httptest.NewServer(ConstantHanlder(expected)) defer background.Close() proxy := goproxy.NewProxyHttpServer() proxy.OnRequest().Do(auth.Basic("my_realm", func(user, passwd string) bool { return user == "user" && passwd == "open sesame" })) _, proxyserver := oneShotProxy(proxy) defer proxyserver.Close() cmd := exec.Command("curl", "--silent", "--show-error", "-x", proxyserver.URL, "-U", "user:open sesame", "--url", background.URL+"/[1-3]", ) out, err := cmd.CombinedOutput() // if curl got error, it'll show up in stderr if err != nil { t.Fatal(err, string(out)) } finalexpected := times(3, expected) if string(out) != finalexpected { t.Error("Expected", finalexpected, "got", string(out)) } }
func TestSniffWriteSize(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { size, _ := strconv.Atoi(r.FormValue("size")) written, err := io.WriteString(w, strings.Repeat("a", size)) if err != nil { t.Errorf("write of %d bytes: %v", size, err) return } if written != size { t.Errorf("write of %d bytes wrote %d bytes", size, written) } })) defer ts.Close() for _, size := range []int{0, 1, 200, 600, 999, 1000, 1023, 1024, 512 << 10, 1 << 20} { res, err := Get(fmt.Sprintf("%s/?size=%d", ts.URL, size)) if err != nil { t.Fatalf("size %d: %v", size, err) } if _, err := io.Copy(ioutil.Discard, res.Body); err != nil { t.Fatalf("size %d: io.Copy of body = %v", size, err) } if err := res.Body.Close(); err != nil { t.Fatalf("size %d: body Close = %v", size, err) } } }
func TestRedirect(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { switch r.URL.Path { case "/": w.Header().Set("Location", "/foo/") w.WriteHeader(StatusSeeOther) case "/foo/": fmt.Fprintf(w, "foo") default: w.WriteHeader(StatusBadRequest) } })) defer ts.Close() var end = regexp.MustCompile("/foo/$") r, err := Get(ts.URL) if err != nil { t.Fatal(err) } r.Body.Close() url := r.Request.URL.String() if r.StatusCode != 200 || !end.MatchString(url) { t.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r.StatusCode, url) } }
// Verify Response.ContentLength is populated. http://golang.org/issue/4126 func TestClientHeadContentLength(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if v := r.FormValue("cl"); v != "" { w.Header().Set("Content-Length", v) } })) defer ts.Close() tests := []struct { suffix string want int64 }{ {"/?cl=1234", 1234}, {"/?cl=0", 0}, {"", -1}, } for _, tt := range tests { req, _ := NewRequest("HEAD", ts.URL+tt.suffix, nil) res, err := DefaultClient.Do(req) if err != nil { t.Fatal(err) } if res.ContentLength != tt.want { t.Errorf("Content-Length = %d; want %d", res.ContentLength, tt.want) } bs, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatal(err) } if len(bs) != 0 { t.Errorf("Unexpected content: %q", bs) } } }
func TestEmptyPasswordAuth(t *testing.T) { defer afterTest(t) gopher := "gopher" ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { auth := r.Header.Get("Authorization") if strings.HasPrefix(auth, "Basic ") { encoded := auth[6:] decoded, err := base64.StdEncoding.DecodeString(encoded) if err != nil { t.Fatal(err) } expected := gopher + ":" s := string(decoded) if expected != s { t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected) } } else { t.Errorf("Invalid auth %q", auth) } })) defer ts.Close() c := &Client{} req, err := NewRequest("GET", ts.URL, nil) if err != nil { t.Fatal(err) } req.URL.User = url.User(gopher) resp, err := c.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() }
// TestClientWrites verifies that client requests are buffered and we // don't send a TCP packet per line of the http request + body. func TestClientWrites(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { })) defer ts.Close() writes := 0 dialer := func(netz string, addr string) (net.Conn, error) { c, err := net.Dial(netz, addr) if err == nil { c = &writeCountingConn{c, &writes} } return c, err } c := &Client{Transport: &Transport{Dial: dialer}} _, err := c.Get(ts.URL) if err != nil { t.Fatal(err) } if writes != 1 { t.Errorf("Get request did %d Write calls, want 1", writes) } writes = 0 _, err = c.PostForm(ts.URL, url.Values{"foo": {"bar"}}) if err != nil { t.Fatal(err) } if writes != 1 { t.Errorf("Post request did %d Write calls, want 1", writes) } }
func TestContentTypeWithCopy(t *testing.T) { defer afterTest(t) const ( input = "\n<html>\n\t<head>\n" expected = "text/html; charset=utf-8" ) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { // Use io.Copy from a bytes.Buffer to trigger ReadFrom. buf := bytes.NewBuffer([]byte(input)) n, err := io.Copy(w, buf) if int(n) != len(input) || err != nil { t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input)) } })) defer ts.Close() resp, err := Get(ts.URL) if err != nil { t.Fatalf("Get: %v", err) } if ct := resp.Header.Get("Content-Type"); ct != expected { t.Errorf("Content-Type = %q, want %q", ct, expected) } data, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("reading body: %v", err) } else if !bytes.Equal(data, []byte(input)) { t.Errorf("data is %q, want %q", data, input) } resp.Body.Close() }
func TestServerContentType(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { i, _ := strconv.Atoi(r.FormValue("i")) tt := sniffTests[i] n, err := w.Write(tt.data) if n != len(tt.data) || err != nil { log.Fatalf("%v: Write(%q) = %v, %v want %d, nil", tt.desc, tt.data, n, err, len(tt.data)) } })) defer ts.Close() for i, tt := range sniffTests { resp, err := Get(ts.URL + "/?i=" + strconv.Itoa(i)) if err != nil { t.Errorf("%v: %v", tt.desc, err) continue } if ct := resp.Header.Get("Content-Type"); ct != tt.contentType { t.Errorf("%v: Content-Type = %q, want %q", tt.desc, ct, tt.contentType) } data, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("%v: reading body: %v", tt.desc, err) } else if !bytes.Equal(data, tt.data) { t.Errorf("%v: data is %q, want %q", tt.desc, data, tt.data) } resp.Body.Close() } }
func oneShotProxy(proxy *goproxy.ProxyHttpServer, t *testing.T) (client *http.Client, s *httptest.Server) { s = httptest.NewServer(proxy) proxyUrl, _ := url.Parse(s.URL) tr := &http.Transport{TLSClientConfig: acceptAllCerts, Proxy: http.ProxyURL(proxyUrl)} client = &http.Client{Transport: tr} return }
func oneShotProxy(proxy *goproxy.ProxyHttpServer) (client *http.Client, s *httptest.Server) { s = httptest.NewServer(proxy) proxyUrl, _ := url.Parse(s.URL) tr := &http.Transport{Proxy: http.ProxyURL(proxyUrl)} client = &http.Client{Transport: tr} return }
func proxyWithLog() (*http.Client, *bytes.Buffer) { proxy := NewJqueryVersionProxy() proxyServer := httptest.NewServer(proxy) buf := new(bytes.Buffer) proxy.Logger = log.New(buf, "", 0) proxyUrl, _ := url.Parse(proxyServer.URL) tr := &http.Transport{Proxy: http.ProxyURL(proxyUrl)} client := &http.Client{Transport: tr} return client, buf }
func TestNoProxyHeaders(t *testing.T) { s := httptest.NewServer(VerifyNoProxyHeaders{t}) client, l := oneShotProxy(goproxy.NewProxyHttpServer(), t) defer l.Close() req, err := http.NewRequest("GET", s.URL, nil) panicOnErr(err, "bad request") req.Header.Add("Connection", "close") req.Header.Add("Proxy-Connection", "close") req.Header.Add("Proxy-Authenticate", "auth") req.Header.Add("Proxy-Authorization", "auth") client.Do(req) }
func TestXForwardedFor(t *testing.T) { const prevForwardedFor = "client ip" const backendResponse = "I am the backend" const backendStatus = 404 backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-Forwarded-For") == "" { t.Errorf("didn't get X-Forwarded-For header") } if !strings.Contains(r.Header.Get("X-Forwarded-For"), prevForwardedFor) { t.Errorf("X-Forwarded-For didn't contain prior data") } w.WriteHeader(backendStatus) w.Write([]byte(backendResponse)) })) defer backend.Close() backendURL, err := url.Parse(backend.URL) if err != nil { t.Fatal(err) } proxyHandler := NewSingleHostReverseProxy(backendURL) frontend := httptest.NewServer(proxyHandler) defer frontend.Close() getReq, _ := http.NewRequest("GET", frontend.URL, nil) getReq.Host = "some-name" getReq.Header.Set("Connection", "close") getReq.Header.Set("X-Forwarded-For", prevForwardedFor) getReq.Close = true res, err := http.DefaultClient.Do(getReq) if err != nil { t.Fatalf("Get: %v", err) } if g, e := res.StatusCode, backendStatus; g != e { t.Errorf("got res.StatusCode %d; expected %d", g, e) } bodyBytes, _ := ioutil.ReadAll(res.Body) if g, e := string(bodyBytes), backendResponse; g != e { t.Errorf("got body %q; expected %q", g, e) } }
func TestCharset(t *testing.T) { s := httptest.NewServer(ConstantServer(1)) defer s.Close() ch := make(chan string, 2) proxy := goproxy.NewProxyHttpServer() proxy.OnResponse().Do(goproxy_html.HandleString( func(s string, ctx *goproxy.ProxyCtx) string { ch <- s return s })) proxyServer := httptest.NewServer(proxy) defer proxyServer.Close() proxyUrl, _ := url.Parse(proxyServer.URL) client := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}} resp, err := client.Get(s.URL + "/cp1255.txt") if err != nil { t.Fatal("GET:", err) } b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal("readAll:", err) } resp.Body.Close() inHandleString := "" select { case inHandleString = <-ch: default: } if len(b) != 2 || b[0] != 0xe3 || b[1] != 0xf3 { t.Error("Did not translate back to 0xe3,0xf3, instead", b) } if inHandleString != "דף" { t.Error("HandleString did not convert DALET & PEH SOFIT (דף) from ISO-8859-8 to utf-8, got", []byte(inHandleString)) } }
func TestReverseProxyFlushInterval(t *testing.T) { const expected = "hi" backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(expected)) })) defer backend.Close() backendURL, err := url.Parse(backend.URL) if err != nil { t.Fatal(err) } proxyHandler := NewSingleHostReverseProxy(backendURL) proxyHandler.FlushInterval = time.Microsecond done := make(chan bool) onExitFlushLoop = func() { done <- true } defer func() { onExitFlushLoop = nil }() frontend := httptest.NewServer(proxyHandler) defer frontend.Close() req, _ := http.NewRequest("GET", frontend.URL, nil) req.Close = true res, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("Get: %v", err) } defer res.Body.Close() if bodyBytes, _ := ioutil.ReadAll(res.Body); string(bodyBytes) != expected { t.Errorf("got body %q; expected %q", bodyBytes, expected) } select { case <-done: // OK case <-time.After(5 * time.Second): t.Error("maxLatencyWriter flushLoop() never exited") } }
func TestClientHead(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(robotsTxtHandler) defer ts.Close() r, err := Head(ts.URL) if err != nil { t.Fatal(err) } if _, ok := r.Header["Last-Modified"]; !ok { t.Error("Last-Modified header not found.") } }
func TestProxyService(t *testing.T) { var fs = httptest.NewServer(http.FileServer(http.Dir("."))) defer fs.Close() client, buf := proxyWithLog() get(t, fs, client, "/jquery_homepage.html") warnings := buf.String() if !strings.Contains(warnings, "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js") || !strings.Contains(warnings, "http://code.jquery.com/jquery-1.4.2.min.js") || !strings.Contains(warnings, "Contradicting") { t.Error("contradicting jquery versions does not issue warning") } }
func TestServeFileFromCWD(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "fs_test.go") })) defer ts.Close() r, err := Get(ts.URL) if err != nil { t.Fatal(err) } r.Body.Close() if r.StatusCode != 200 { t.Fatalf("expected 200 OK, got %s", r.Status) } }
func TestFSRedirect(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir(".")))) defer ts.Close() for _, data := range fsRedirectTestData { res, err := Get(ts.URL + data.original) if err != nil { t.Fatal(err) } res.Body.Close() if g, e := res.Request.URL.Path, data.redirect; g != e { t.Errorf("redirect from %s: got %s, want %s", data.original, g, e) } } }
func TestHasGoproxyCA(t *testing.T) { proxy := goproxy.NewProxyHttpServer() proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm) s := httptest.NewServer(proxy) proxyUrl, _ := url.Parse(s.URL) goproxyCA := x509.NewCertPool() goproxyCA.AddCert(goproxy.GoproxyCa.Leaf) tr := &http.Transport{TLSClientConfig: &tls.Config{RootCAs: goproxyCA}, Proxy: http.ProxyURL(proxyUrl)} client := &http.Client{Transport: tr} if resp := string(getOrFail(https.URL+"/bobo", client, t)); resp != "bobo" { t.Error("Wrong response when mitm", resp, "expected bobo") } }
func TestServeFileMimeType(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "testdata/style.css") })) defer ts.Close() resp, err := Get(ts.URL) if err != nil { t.Fatal(err) } resp.Body.Close() want := "text/css; charset=utf-8" if h := resp.Header.Get("Content-Type"); h != want { t.Errorf("Content-Type mismatch: got %q, want %q", h, want) } }
func TestServeFileWithContentEncoding(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Content-Encoding", "foo") ServeFile(w, r, "testdata/file") })) defer ts.Close() resp, err := Get(ts.URL) if err != nil { t.Fatal(err) } resp.Body.Close() if g, e := resp.ContentLength, int64(-1); g != e { t.Errorf("Content-Length mismatch: got %d, want %d", g, e) } }
func TestPostRedirects(t *testing.T) { defer afterTest(t) var log struct { sync.Mutex bytes.Buffer } var ts *httptest.Server ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { log.Lock() fmt.Fprintf(&log.Buffer, "%s %s ", r.Method, r.RequestURI) log.Unlock() if v := r.URL.Query().Get("code"); v != "" { code, _ := strconv.Atoi(v) if code/100 == 3 { w.Header().Set("Location", ts.URL) } w.WriteHeader(code) } })) defer ts.Close() tests := []struct { suffix string want int // response code }{ {"/", 200}, {"/?code=301", 301}, {"/?code=302", 200}, {"/?code=303", 200}, {"/?code=404", 404}, } for _, tt := range tests { res, err := Post(ts.URL+tt.suffix, "text/plain", strings.NewReader("Some content")) if err != nil { t.Fatal(err) } if res.StatusCode != tt.want { t.Errorf("POST %s: status code = %d; want %d", tt.suffix, res.StatusCode, tt.want) } } log.Lock() got := log.String() log.Unlock() want := "POST / POST /?code=301 POST /?code=302 GET / POST /?code=303 GET / POST /?code=404 " if got != want { t.Errorf("Log differs.\n Got: %q\nWant: %q", got, want) } }
func TestFileServerEscapesNames(t *testing.T) { defer afterTest(t) const dirListPrefix = "<pre>\n" const dirListSuffix = "\n</pre>\n" tests := []struct { name, escaped string }{ {`simple_name`, `<a href="simple_name">simple_name</a>`}, {`"'<>&`, `<a href="%22%27%3C%3E&">"'<>&</a>`}, {`?foo=bar#baz`, `<a href="%3Ffoo=bar%23baz">?foo=bar#baz</a>`}, {`<combo>?foo`, `<a href="%3Ccombo%3E%3Ffoo"><combo>?foo</a>`}, } // We put each test file in its own directory in the fakeFS so we can look at it in isolation. fs := make(fakeFS) for i, test := range tests { testFile := &fakeFileInfo{basename: test.name} fs[fmt.Sprintf("/%d", i)] = &fakeFileInfo{ dir: true, modtime: time.Unix(1000000000, 0).UTC(), ents: []*fakeFileInfo{testFile}, } fs[fmt.Sprintf("/%d/%s", i, test.name)] = testFile } ts := httptest.NewServer(FileServer(&fs)) defer ts.Close() for i, test := range tests { url := fmt.Sprintf("%s/%d", ts.URL, i) res, err := Get(url) if err != nil { t.Fatalf("test %q: Get: %v", test.name, err) } b, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("test %q: read Body: %v", test.name, err) } s := string(b) if !strings.HasPrefix(s, dirListPrefix) || !strings.HasSuffix(s, dirListSuffix) { t.Errorf("test %q: listing dir, full output is %q, want prefix %q and suffix %q", test.name, s, dirListPrefix, dirListSuffix) } if trimmed := strings.TrimSuffix(strings.TrimPrefix(s, dirListPrefix), dirListSuffix); trimmed != test.escaped { t.Errorf("test %q: listing dir, filename escaped to %q, want %q", test.name, trimmed, test.escaped) } res.Body.Close() } }
func TestRedirectCookiesJar(t *testing.T) { defer afterTest(t) var ts *httptest.Server ts = httptest.NewServer(echoCookiesRedirectHandler) defer ts.Close() c := &Client{ Jar: new(TestJar), } u, _ := url.Parse(ts.URL) c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]}) resp, err := c.Get(ts.URL) if err != nil { t.Fatalf("Get: %v", err) } resp.Body.Close() matchReturnedCookies(t, expectedCookies, resp.Cookies()) }
func TestClient(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(robotsTxtHandler) defer ts.Close() r, err := Get(ts.URL) var b []byte if err == nil { b, err = pedanticReadAll(r.Body) r.Body.Close() } if err != nil { t.Error(err) } else if s := string(b); !strings.HasPrefix(s, "User-agent:") { t.Errorf("Incorrect page body (did not begin with User-agent): %q", s) } }