You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2016/12/04 22:07:29 UTC
svn commit: r1772577 [1/2] - in /httpd/test/mod_h2/trunk: ./ conf/sites/
mh2fuzz/ test/ test/go/
Author: icing
Date: Sun Dec 4 22:07:28 2016
New Revision: 1772577
URL: http://svn.apache.org/viewvc?rev=1772577&view=rev
Log:
additional testing for frame sceanrios using golang's http2 framer
Added:
httpd/test/mod_h2/trunk/test/go/h2fuzz.go
httpd/test/mod_h2/trunk/test/go/in-footer-endless.fuzz
httpd/test/mod_h2/trunk/test/go/in-footer.fuzz
httpd/test/mod_h2/trunk/test/go/in-get.fuzz
httpd/test/mod_h2/trunk/test/go/in-headers-endless.fuzz
httpd/test/mod_h2/trunk/test/go/in-headers-many.fuzz
httpd/test/mod_h2/trunk/test/go/test_fuzz.sh
Modified:
httpd/test/mod_h2/trunk/conf/sites/test.example.org.conf
httpd/test/mod_h2/trunk/configure.ac
httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.c
httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.h
httpd/test/mod_h2/trunk/mh2fuzz/h2c_session.c
httpd/test/mod_h2/trunk/mh2fuzz/mh2f_fuzzer.c
httpd/test/mod_h2/trunk/test/go/get.go
httpd/test/mod_h2/trunk/test/test.sh
httpd/test/mod_h2/trunk/test/test_common.sh
httpd/test/mod_h2/trunk/test/test_fuzz.sh
Modified: httpd/test/mod_h2/trunk/conf/sites/test.example.org.conf
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/conf/sites/test.example.org.conf?rev=1772577&r1=1772576&r2=1772577&view=diff
==============================================================================
--- httpd/test/mod_h2/trunk/conf/sites/test.example.org.conf (original)
+++ httpd/test/mod_h2/trunk/conf/sites/test.example.org.conf Sun Dec 4 22:07:28 2016
@@ -137,7 +137,6 @@
Header add Link "</006/006.js>;rel=preload"
</Location>
-
<Location /hello.py>
SSLOptions +StdEnvVars
</Location>
Modified: httpd/test/mod_h2/trunk/configure.ac
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/configure.ac?rev=1772577&r1=1772576&r2=1772577&view=diff
==============================================================================
--- httpd/test/mod_h2/trunk/configure.ac (original)
+++ httpd/test/mod_h2/trunk/configure.ac Sun Dec 4 22:07:28 2016
@@ -52,7 +52,7 @@ AC_ARG_WITH([serverdir], [AS_HELP_STRING
[Use serverdir directory for setup [default=gen/apache]])],
[request_serverdir=$withval], [request_serverdir=$PWD/gen/apache])
-NGHTTP2_VERSION=1.15.0
+NGHTTP2_VERSION=1.16.0
AC_ARG_WITH([nghttp2-version], [AS_HELP_STRING([--with-nghttp2-version],
[Use nghttp2 of specified version [default=<ignore>]])],
Modified: httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.c
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.c?rev=1772577&r1=1772576&r2=1772577&view=diff
==============================================================================
--- httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.c (original)
+++ httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.c Sun Dec 4 22:07:28 2016
@@ -45,7 +45,7 @@ apr_status_t h2c_request_create(h2c_requ
return APR_SUCCESS;
}
-void h2c_request_headers_set(h2c_request *r, const char *name, const char *value)
+void h2c_request_headers_add(h2c_request *r, const char *name, const char *value)
{
if (!strcmp(":method", name)) {
r->method = value;
@@ -60,16 +60,16 @@ void h2c_request_headers_set(h2c_request
r->scheme = value;
}
else {
- apr_table_setn(r->headers, name, value);
+ apr_table_addn(r->headers, name, value);
}
}
-void h2c_request_headers_setn(h2c_request *r, apr_pool_t *p,
+void h2c_request_headers_addn(h2c_request *r, apr_pool_t *p,
const char *name, size_t namelen,
const char *value, size_t valuelen)
{
- h2c_request_headers_set(r, apr_pstrndup(p, name, namelen),
- apr_pstrndup(p, value, valuelen));
+ h2c_request_headers_add(r, apr_pstrndup(p, name, namelen),
+ apr_pstrndup(p, value, valuelen));
}
apr_status_t h2c_request_ncreate(h2c_request **pr, apr_pool_t *p,
@@ -82,8 +82,8 @@ apr_status_t h2c_request_ncreate(h2c_req
r->headers = apr_table_make(p, 5);
for (i = 0; i < nvlen; ++i) {
- h2c_request_headers_setn(r, p, (char *)nva[i].name, nva[i].namelen,
- (char *)nva[i].value, nva[i].valuelen);
+ h2c_request_headers_addn(r, p, (char *)nva[i].name, nva[i].namelen,
+ (char *)nva[i].value, nva[i].valuelen);
}
*pr = r;
Modified: httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.h
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.h?rev=1772577&r1=1772576&r2=1772577&view=diff
==============================================================================
--- httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.h (original)
+++ httpd/test/mod_h2/trunk/mh2fuzz/h2c_request.h Sun Dec 4 22:07:28 2016
@@ -33,8 +33,8 @@ struct h2c_request {
apr_status_t h2c_request_create(h2c_request **pr, apr_pool_t *p,
const char *method, apr_uri_t *uri);
-void h2c_request_headers_set(h2c_request *r, const char *name, const char *val);
-void h2c_request_headers_setn(h2c_request *r, apr_pool_t *p,
+void h2c_request_headers_add(h2c_request *r, const char *name, const char *val);
+void h2c_request_headers_addn(h2c_request *r, apr_pool_t *p,
const char *name, size_t namelen,
const char *value, size_t valuelen);
Modified: httpd/test/mod_h2/trunk/mh2fuzz/h2c_session.c
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/mh2fuzz/h2c_session.c?rev=1772577&r1=1772576&r2=1772577&view=diff
==============================================================================
--- httpd/test/mod_h2/trunk/mh2fuzz/h2c_session.c (original)
+++ httpd/test/mod_h2/trunk/mh2fuzz/h2c_session.c Sun Dec 4 22:07:28 2016
@@ -449,7 +449,7 @@ static int on_header(nghttp2_session *ng
++s->open_streams;
nghttp2_session_set_stream_user_data(ngh2, frame->push_promise.promised_stream_id, stream);
}
- h2c_request_headers_setn(stream->r, stream->p,
+ h2c_request_headers_addn(stream->r, stream->p,
(const char *)name, namelen,
(const char *)value, valuelen);
}
@@ -490,6 +490,7 @@ static apr_status_t on_conn_write(h2c_co
static apr_status_t on_conn_connect(h2c_conn *c, h2c_conn_obs *obs)
{
h2c_session *s = obs->ctx;
+ nghttp2_option *options;
nghttp2_session_callbacks *cbs;
nghttp2_settings_entry settings[2];
int add_conn_window;
@@ -504,7 +505,11 @@ static apr_status_t on_conn_connect(h2c_
nghttp2_session_callbacks_set_before_frame_send_callback(cbs, before_frame_send);
nghttp2_session_callbacks_set_send_callback(cbs, raw_send);
- nghttp2_session_client_new(&s->ngh2, cbs, s);
+ nghttp2_option_new(&options);
+ nghttp2_option_set_max_deflate_dynamic_table_size(options, 100*1024*1024);
+ nghttp2_option_set_max_send_header_block_length(options, 100*1024*1024);
+
+ nghttp2_session_client_new2(&s->ngh2, cbs, s, options);
nghttp2_session_callbacks_del(cbs);
@@ -569,6 +574,11 @@ apr_status_t h2c_session_submit(h2c_sess
r->method, r->authority, r->path);
stream_id = nghttp2_submit_request(s->ngh2, NULL, ngh->nv, ngh->nvlen,
NULL, stream);
+ if (s->c->verbose > 1) {
+ fprintf(stderr, "%ld: submit %d headers -> STREAM[%d]\n", s->c->id,
+ (int)ngh->nvlen, stream_id);
+ }
+
if (stream_id < 0) {
return APR_EGENERAL;
}
@@ -576,10 +586,6 @@ apr_status_t h2c_session_submit(h2c_sess
s->max_stream_sent = H2_MAX(s->max_stream_sent, stream_id);
stream->id = stream_id;
- if (s->c->verbose > 1) {
- fprintf(stderr, "%ld: submit STREAM[%d]\n", s->c->id, stream_id);
- }
-
if (pid) {
*pid = stream_id;
}
Modified: httpd/test/mod_h2/trunk/mh2fuzz/mh2f_fuzzer.c
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/mh2fuzz/mh2f_fuzzer.c?rev=1772577&r1=1772576&r2=1772577&view=diff
==============================================================================
--- httpd/test/mod_h2/trunk/mh2fuzz/mh2f_fuzzer.c (original)
+++ httpd/test/mod_h2/trunk/mh2fuzz/mh2f_fuzzer.c Sun Dec 4 22:07:28 2016
@@ -304,7 +304,7 @@ static void hh_submit(h2c_session *s, h2
fprintf(stderr, "h2c_conn(%ld-%d): submit :authority as Host\n",
s->c->id, stream->id);
}
- h2c_request_headers_set(r, "Host", r->authority);
+ h2c_request_headers_add(r, "Host", r->authority);
r->authority = NULL;
}
@@ -345,7 +345,7 @@ static void slh_submit(h2c_session *s, h
slh[i] = 'A';
}
slh[hlen] = '\0';
- h2c_request_headers_set(r, "super-long-header", slh);
+ h2c_request_headers_add(r, "super-long-header", slh);
}
static h2c_session_obs *slh_create(const char *name, apr_pool_t *p)
@@ -386,7 +386,7 @@ static void slp_submit(h2c_session *s, h
}
slh[0] = '/';
slh[hlen] = '\0';
- h2c_request_headers_set(r, ":path", slh);
+ h2c_request_headers_add(r, ":path", slh);
}
static h2c_session_obs *slp_create(const char *name, apr_pool_t *p)
@@ -407,13 +407,27 @@ static h2c_session_obs *slp_create(const
******************************************************************************/
typedef struct fuzzer_smh {
h2c_session_obs obs;
+ const char *name;
+ int count;
} fuzzer_smh;
+static const char *STRING_100 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
+static const char *STRING_1000 =
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
+
static void smh_submit(h2c_session *s, h2c_stream *stream, h2c_request *r,
h2c_session_obs *obs)
{
fuzzer_smh *f = obs->ctx;
- apr_size_t hn = 128;
char *name;
if (s->c->verbose > 1) {
@@ -421,21 +435,29 @@ static void smh_submit(h2c_session *s, h
s->c->id, stream->id);
}
- for (int i = 0; i < hn; ++i) {
+ for (int i = 0; i < f->count; ++i) {
name = apr_psprintf(stream->p, "smh-%d", i);
- h2c_request_headers_set(r, name, "super many header fuzzing");
+ h2c_request_headers_add(r, STRING_100, STRING_100);
}
}
static h2c_session_obs *smh_create(const char *name, apr_pool_t *p)
{
- fuzzer_ds *f;
+ fuzzer_smh *f;
size_t prelen = sizeof("super-many-header")-1;
f = apr_pcalloc(p, sizeof(*f));
f->name = apr_pstrdup(p, name);
f->obs.ctx = f;
f->obs.before_submit = smh_submit;
+ f->count = 123;
+ if (strlen(f->name) > prelen) {
+ const char *s = f->name + prelen;
+ int n = (int)apr_atoi64(s);
+ if (n > 0) {
+ f->count = n;
+ }
+ }
return &f->obs;
}
@@ -459,13 +481,12 @@ static void x100_submit(h2c_session *s,
s->c->id, stream->id);
}
- h2c_request_headers_set(r, "Expect", "100-continue");
+ h2c_request_headers_add(r, "Expect", "100-continue");
}
static h2c_session_obs *x100_create(const char *name, apr_pool_t *p)
{
fuzzer_ds *f;
- size_t prelen = sizeof("super-many-header")-1;
f = apr_pcalloc(p, sizeof(*f));
f->name = apr_pstrdup(p, name);
Modified: httpd/test/mod_h2/trunk/test/go/get.go
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/test/go/get.go?rev=1772577&r1=1772576&r2=1772577&view=diff
==============================================================================
--- httpd/test/mod_h2/trunk/test/go/get.go (original)
+++ httpd/test/mod_h2/trunk/test/go/get.go Sun Dec 4 22:07:28 2016
@@ -41,12 +41,7 @@ func main() {
if err != nil {
log.Fatal(err)
} else {
-// defer res.Body.Close()
-// res.Write(os.Stdout)
io.Copy(os.Stdout, res.Body)
-// if err != nil {
-// log.Fatal(err)
-// }
}
}
}
Added: httpd/test/mod_h2/trunk/test/go/h2fuzz.go
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/test/go/h2fuzz.go?rev=1772577&view=auto
==============================================================================
--- httpd/test/mod_h2/trunk/test/go/h2fuzz.go (added)
+++ httpd/test/mod_h2/trunk/test/go/h2fuzz.go Sun Dec 4 22:07:28 2016
@@ -0,0 +1,552 @@
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/tls"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "os"
+ "time"
+ "strconv"
+ "strings"
+
+ "golang.org/x/net/http2"
+ "golang.org/x/net/http2/hpack"
+ )
+
+// Flags
+var (
+ flagSettings = flag.String("settings", "empty", "comma-separated list of KEY=value settings for the initial SETTINGS frame. The magic value 'empty' sends an empty initial settings frame, and the magic value 'omit' causes no initial settings frame to be sent.")
+ )
+
+type command struct {
+ run func(*h2fuzz, []string) error // required
+
+ // complete optionally specifies tokens (case-insensitive) which are
+ // valid for this subcommand.
+ complete func() []string
+}
+
+var commands = map[string]command{
+ "ping": {run: (*h2fuzz).cmdPing},
+ "settings": {run: (*h2fuzz).cmdSettings},
+ "quit": {run: (*h2fuzz).cmdQuit},
+ "stream-get": {run: (*h2fuzz).cmdStreamGet},
+ "stream-end": {run: (*h2fuzz).cmdStreamEnd},
+ "headers-add": {run: (*h2fuzz).cmdHeadersAdd},
+ "headers-end": {run: (*h2fuzz).cmdHeadersEnd},
+ "data": {run: (*h2fuzz).cmdData},
+ "headers-add-endless": {run: (*h2fuzz).cmdHeadersAddEndless},
+ "goaway": {run: (*h2fuzz).cmdGoaway},
+ "wait": {run: (*h2fuzz ).cmdWait},
+}
+
+func lookupCommand(name string) (c command, ok bool) {
+ name = strings.ToLower(name)
+ if c, ok = commands[name]; ok {
+ return c, ok
+ }
+ return c, c.run != nil
+}
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "Usage: h2fuzz <hostname>\n\n")
+ flag.PrintDefaults()
+}
+
+// withPort adds ":443" if another port isn't already present.
+func withPort(host string) string {
+ if _, _, err := net.SplitHostPort(host); err != nil {
+ return net.JoinHostPort(host, "443")
+ }
+ return host
+}
+
+// withoutPort removes ":*" if present.
+func withoutPort(host string) string {
+ if h, _, err := net.SplitHostPort(host); err == nil {
+ return h
+ }
+ return host
+}
+
+func getFlags(args []string, flags map[string]bool) []string{
+ i := 0
+ for i < len(args) {
+ arg := args[i]
+ _, ok := flags[arg]
+ if ok {
+ flags[arg] = true
+ args = append(args[:i], args[i+1:]...)
+ } else {
+ i += 1
+ }
+ }
+ return args
+}
+
+// h2fuzz is the app's state.
+type h2fuzz struct {
+ host string
+ tc *tls.Conn
+ framer *http2.Framer
+ input *bufio.Reader
+ output io.Writer
+ logHeaders bool
+
+ // owned by the command loop:
+ streamID uint32
+ hbuf bytes.Buffer
+ henc *hpack.Encoder
+ eoh bool // end of headers
+ eos bool // end of stream
+ hcont bool // HEADER send, need CONTINUATION
+
+ // owned by the readFrames loop:
+ peerSetting map[http2.SettingID]uint32
+ hdTableSize uint32
+ hdec *hpack.Decoder
+ last_rst uint32
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+ if flag.NArg() != 1 {
+ usage()
+ os.Exit(2)
+ }
+ log.SetFlags(0)
+
+ host := flag.Arg(0)
+ app := &h2fuzz{
+ host: host,
+ peerSetting: make(map[http2.SettingID]uint32),
+ logHeaders : true,
+ hdTableSize: uint32(4 << 10),
+ }
+ app.henc = hpack.NewEncoder(&app.hbuf)
+ app.input = bufio.NewReader(os.Stdin)
+ app.output = os.Stdout
+
+ if err := app.Main(); err != nil {
+ fmt.Fprintf(os.Stderr, "%v\n", err)
+ os.Exit(1)
+ }
+ fmt.Fprintf(os.Stdout, "\n")
+}
+
+func (app *h2fuzz) Main() error {
+ cfg := &tls.Config{
+ ServerName: withoutPort(app.host),
+ NextProtos: strings.Split("h2", ","),
+ InsecureSkipVerify: true,
+ }
+
+ hostAndPort := withPort(app.host)
+ fmt.Fprintf(os.Stdout, "Connecting to %s ...", hostAndPort)
+ tc, err := tls.Dial("tcp", hostAndPort, cfg)
+ if err != nil {
+ return fmt.Errorf("Error dialing %s: %v", withPort(app.host), err)
+ }
+ fmt.Fprintf(os.Stdout, "Connected to %v", tc.RemoteAddr())
+ defer tc.Close()
+
+ if err := tc.Handshake(); err != nil {
+ return fmt.Errorf("TLS handshake: %v", err)
+ }
+ state := tc.ConnectionState()
+ fmt.Fprintf(os.Stdout, "Negotiated protocol %q", state.NegotiatedProtocol)
+ if !state.NegotiatedProtocolIsMutual || state.NegotiatedProtocol == "" {
+ return fmt.Errorf("Could not negotiate protocol mutually")
+ }
+
+ if _, err := io.WriteString(tc, http2.ClientPreface); err != nil {
+ return err
+ }
+
+ app.framer = http2.NewFramer(tc, tc)
+
+ errc := make(chan error, 2)
+ go func() { errc <- app.readFrames() }()
+ go func() { errc <- app.readConsole() }()
+ return <-errc
+}
+
+func (app *h2fuzz) logf(format string, args ...interface{}) {
+ fmt.Fprintf(app.output, format+"\r\n", args...)
+}
+
+var errExitApp = errors.New("internal sentinel error value to quit the console reading loop")
+
+func (app *h2fuzz) readConsole() error {
+ if s := *flagSettings; s != "omit" {
+ var args []string
+ if s != "empty" {
+ args = strings.Split(s, ",")
+ }
+ app.cmdSettings(args)
+ }
+ for {
+ bytes, _, err := app.input.ReadLine()
+ if err != nil {
+ return err
+ }
+ if len(bytes) == 0 {
+ break
+ }
+ line := string(bytes)
+ f := strings.Fields(line)
+ if len(f) == 0 {
+ continue
+ }
+ name, args := f[0], f[1:]
+ if f[0][0] == '#' {
+ continue
+ }
+ cmd, ok := lookupCommand(name)
+ if ok {
+ err = cmd.run(app, args)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New("unknown command: " + name)
+ }
+ }
+ return nil
+}
+
+func (a *h2fuzz) cmdQuit(args []string) error {
+ if len(args) > 0 {
+ a.logf("the QUIT command takes no argument")
+ return nil
+ }
+ return errExitApp
+}
+
+func (a *h2fuzz) cmdSettings(args []string) error {
+ if len(args) == 1 && strings.EqualFold(args[0], "ACK") {
+ return a.framer.WriteSettingsAck()
+ }
+ var settings []http2.Setting
+ for _, arg := range args {
+ if strings.EqualFold(arg, "ACK") {
+ a.logf("Error: ACK must be only argument with the SETTINGS command")
+ return nil
+ }
+ eq := strings.Index(arg, "=")
+ if eq == -1 {
+ a.logf("Error: invalid argument %q (expected SETTING_NAME=nnnn)", arg)
+ return nil
+ }
+ sid, ok := settingByName(arg[:eq])
+ if !ok {
+ a.logf("Error: unknown setting name %q", arg[:eq])
+ return nil
+ }
+ val, err := strconv.ParseUint(arg[eq+1:], 10, 32)
+ if err != nil {
+ a.logf("Error: invalid argument %q (expected SETTING_NAME=nnnn)", arg)
+ return nil
+ }
+ settings = append(settings, http2.Setting{
+ ID: sid,
+ Val: uint32(val),
+ })
+ }
+ a.logf("Sending: %v", settings)
+ return a.framer.WriteSettings(settings...)
+}
+
+func settingByName(name string) (http2.SettingID, bool) {
+ for _, sid := range [...]http2.SettingID{
+ http2.SettingHeaderTableSize,
+ http2.SettingEnablePush,
+ http2.SettingMaxConcurrentStreams,
+ http2.SettingInitialWindowSize,
+ http2.SettingMaxFrameSize,
+ http2.SettingMaxHeaderListSize,
+ } {
+ if strings.EqualFold(sid.String(), name) {
+ return sid, true
+ }
+ }
+ return 0, false
+}
+
+func (app *h2fuzz) cmdPing(args []string) error {
+ if len(args) > 1 {
+ app.logf("invalid PING usage: only accepts 0 or 1 args")
+ return nil // nil means don't end the program
+ }
+ var data [8]byte
+ if len(args) == 1 {
+ copy(data[:], args[0])
+ } else {
+ copy(data[:], "h2fuzz_ping")
+ }
+ app.logf("send PING %v", data)
+ return app.framer.WritePing(false, data)
+}
+
+func (app *h2fuzz) readHeaders() error {
+ for {
+ bytes, _, err := app.input.ReadLine()
+ if err != nil {
+ return err
+ }
+ if len(bytes) == 0 {
+ break
+ }
+ line := string(bytes)
+ f := strings.SplitN(line, ": ", 2)
+ if len(f) != 2 {
+ break
+ }
+ app.writeHeader(f[0], f[1])
+ }
+ return nil;
+}
+
+func (app *h2fuzz) cmdStreamGet(args []string) error {
+ flags := map[string]bool {
+ "end": false,
+ "eos": false,
+ }
+ args = getFlags(args, flags)
+ if len(args) != 1 {
+ app.logf("Error: stream-get needs a path.")
+ return nil
+ }
+ app.startHeaders()
+ app.writeHeader(":authority", app.host)
+ app.writeHeader(":method", "GET")
+ app.writeHeader(":path", args[0])
+ app.writeHeader(":scheme", "https")
+ err := app.readHeaders()
+ if err != nil {
+ return err
+ }
+
+ hbf := app.hbuf.Bytes()
+
+ if app.streamID == 0 {
+ app.streamID = 1
+ } else {
+ app.streamID += 2
+ }
+ app.eos = flags["eos"]
+ app.eoh = flags["eoh"]
+ app.hcont = !app.eoh
+
+ app.logf("-->[HEADER stream=%d, method=GET, path=%s, eoh=%v, eos=%v]",
+ app.streamID, args[0], app.eoh, app.eos)
+ return app.framer.WriteHeaders(http2.HeadersFrameParam{
+ StreamID: app.streamID,
+ BlockFragment: hbf,
+ EndStream: app.eos,
+ EndHeaders: app.eoh,
+ })
+}
+
+func (app *h2fuzz) cmdHeadersAdd(args []string) error {
+ flags := map[string]bool {
+ "end": false,
+ "eos": false,
+ }
+ args = getFlags(args, flags)
+ if len(args) > 0 {
+ app.logf("Error: headers-add reads header from stdin.")
+ return nil
+ }
+
+ if (app.eos && !app.hcont) {
+ app.logf("Error: headers-add stream already closed.")
+ return nil
+ }
+ hcont := app.hcont
+ app.eos = app.eoh || flags["eos"]
+ app.eoh = flags["end"]
+ app.hcont = !app.eoh
+
+ app.startHeaders()
+ err := app.readHeaders()
+ if err != nil {
+ return err
+ }
+
+ hbf := app.hbuf.Bytes()
+ if hcont {
+ app.logf("-->[CONTINUATION stream=%d, eoh=%v, eos=%v]", app.streamID, app.eoh, app.eos)
+ return app.framer.WriteContinuation(app.streamID, app.eoh, hbf)
+ } else {
+ app.logf("-->[HEADER stream=%d: eoh=%v, eos=%v]", app.streamID, app.eoh, app.eos)
+ return app.framer.WriteHeaders(http2.HeadersFrameParam{
+ StreamID: app.streamID,
+ BlockFragment: hbf,
+ EndStream: app.eos,
+ EndHeaders: app.eoh,
+ })
+ }
+}
+
+func (app *h2fuzz) cmdHeadersEnd(args []string) error {
+ if app.hcont {
+ app.eoh = true
+ app.hcont = false
+ app.hbuf.Reset()
+ hbf := app.hbuf.Bytes()
+ app.logf("-->[CONTINUATION stream=%d, eoh=%v]", app.streamID, app.eoh)
+ return app.framer.WriteContinuation(app.streamID, app.eoh, hbf)
+ }
+ return nil
+}
+
+func (app *h2fuzz) cmdStreamEnd(args []string) error {
+ if app.hcont {
+ app.eos = true
+ return app.cmdHeadersEnd(args)
+ } else if !app.eos {
+ app.hbuf.Reset()
+ hbf := app.hbuf.Bytes()
+ app.eos = true
+ app.logf("-->[DATA stream=%d, eos=%v]", app.streamID, app.eos)
+ return app.framer.WriteData(app.streamID, true, hbf)
+ }
+ return nil
+}
+
+func (app *h2fuzz) cmdData(args []string) error {
+ if app.eos {
+ app.logf("Error: DATA after eos.")
+ return nil
+ }
+ if app.hcont {
+ var args []string
+ err := app.cmdHeadersEnd(args)
+ if err != nil {
+ return err
+ }
+ }
+ data := bytes.NewBuffer([]byte{})
+ data.Grow(1024)
+ app.logf("-->[DATA stream=%d, len=%d, eos=%v]", app.streamID, data.Len(), app.eos)
+ return app.framer.WriteData(app.streamID, false, data.Bytes())
+}
+
+func (app *h2fuzz) cmdHeadersAddEndless(args []string) error {
+ if len(args) > 0 {
+ app.logf("Error: get-headers-endless has no arguments.")
+ return nil
+ }
+ d, err := time.ParseDuration("1ms")
+ if err != nil {
+ return err
+ }
+ s := ""
+ for i := 0; i < 109; i++ {
+ s += "aaaaaaaaaa"
+ }
+ app.logHeaders = false
+ for app.last_rst != app.streamID {
+ app.hbuf.Reset()
+ for i := 0; i < 7; i++ {
+ app.writeHeader(s, s)
+ }
+ hbf := app.hbuf.Bytes()
+ if app.logHeaders {
+ app.logf("-->[CONTINUATION stream=%d, eoh=%v]", app.streamID, app.eoh)
+ }
+ err := app.framer.WriteContinuation(app.streamID, false, hbf)
+ if err != nil {
+ break
+ }
+ time.Sleep(d)
+ }
+ return err
+}
+
+func (app *h2fuzz) cmdGoaway(args []string) error {
+ app.logf("-->[GOAWAY")
+ return app.framer.WriteGoAway(0, 0, nil)
+}
+
+func (app *h2fuzz) cmdWait(args []string) error {
+ s := "1m"
+ if len(args) > 0 {
+ s = args[0]
+ }
+ d, err := time.ParseDuration(s)
+ if err == nil {
+ app.logf("Waiting %s", s)
+ time.Sleep(d)
+ }
+ return err
+}
+
+func (app *h2fuzz) readFrames() error {
+ for {
+ f, err := app.framer.ReadFrame()
+ if err == io.EOF {
+ return nil
+ }
+ if err != nil {
+ return fmt.Errorf("ReadFrame: %v", err)
+ }
+ app.logf("<--%v", f)
+ switch f := f.(type) {
+ case *http2.PingFrame:
+ app.logf(" Data = %q", f.Data)
+ case *http2.SettingsFrame:
+ f.ForeachSetting(func(s http2.Setting) error {
+ app.logf(" %v", s)
+ app.peerSetting[s.ID] = s.Val
+ return nil
+ })
+ case *http2.WindowUpdateFrame:
+ app.logf(" Window-Increment = %v", f.Increment)
+ case *http2.GoAwayFrame:
+ app.logf(" Last-Stream-ID = %d; Error-Code = %v (%d)", f.LastStreamID, f.ErrCode, f.ErrCode)
+ case *http2.DataFrame:
+ app.logf(" %q", f.Data())
+ case *http2.HeadersFrame:
+ if f.HasPriority() {
+ app.logf(" PRIORITY = %v", f.Priority)
+ }
+ if app.hdec == nil {
+ app.hdec = hpack.NewDecoder(app.hdTableSize, app.onNewHeaderField)
+ }
+ app.hdec.Write(f.HeaderBlockFragment())
+ case *http2.RSTStreamFrame:
+ app.last_rst = f.StreamID
+ }
+ }
+}
+
+// called from readLoop
+func (app *h2fuzz) onNewHeaderField(f hpack.HeaderField) {
+ if f.Sensitive {
+ app.logf(" %s = %q (SENSITIVE)", f.Name, f.Value)
+ }
+ app.logf(" %s = %q", f.Name, f.Value)
+}
+
+func (app *h2fuzz) writeHeader(name, value string) {
+ app.henc.WriteField(hpack.HeaderField{Name: name, Value: value})
+ if app.logHeaders {
+ app.logf(" %s = %s", name, value)
+ }
+}
+
+func (app *h2fuzz) startHeaders() {
+ app.hbuf.Reset()
+ if app.logHeaders {
+ app.logf(" #start headers")
+ }
+}
Added: httpd/test/mod_h2/trunk/test/go/in-footer-endless.fuzz
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/test/go/in-footer-endless.fuzz?rev=1772577&view=auto
==============================================================================
--- httpd/test/mod_h2/trunk/test/go/in-footer-endless.fuzz (added)
+++ httpd/test/mod_h2/trunk/test/go/in-footer-endless.fuzz Sun Dec 4 22:07:28 2016
@@ -0,0 +1,13 @@
+wait 1ms
+stream-get /
+trailer: x
+
+data
+headers-add
+x: y
+
+headers-add-endless
+stream-end
+wait 1ms
+goaway
+wait
Added: httpd/test/mod_h2/trunk/test/go/in-footer.fuzz
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/test/go/in-footer.fuzz?rev=1772577&view=auto
==============================================================================
--- httpd/test/mod_h2/trunk/test/go/in-footer.fuzz (added)
+++ httpd/test/mod_h2/trunk/test/go/in-footer.fuzz Sun Dec 4 22:07:28 2016
@@ -0,0 +1,11 @@
+wait 1ms
+stream-get / end
+trailer: x
+
+data
+headers-add end
+x: y
+
+wait 100ms
+goaway
+wait
Added: httpd/test/mod_h2/trunk/test/go/in-get.fuzz
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/test/go/in-get.fuzz?rev=1772577&view=auto
==============================================================================
--- httpd/test/mod_h2/trunk/test/go/in-get.fuzz (added)
+++ httpd/test/mod_h2/trunk/test/go/in-get.fuzz Sun Dec 4 22:07:28 2016
@@ -0,0 +1,14 @@
+wait 1ms
+stream-get / eos
+x: y
+y: 3
+z: something
+
+headers-add end
+x2: y
+y3: 3
+z4: something
+
+wait 100ms
+goaway
+wait
Added: httpd/test/mod_h2/trunk/test/go/in-headers-endless.fuzz
URL: http://svn.apache.org/viewvc/httpd/test/mod_h2/trunk/test/go/in-headers-endless.fuzz?rev=1772577&view=auto
==============================================================================
--- httpd/test/mod_h2/trunk/test/go/in-headers-endless.fuzz (added)
+++ httpd/test/mod_h2/trunk/test/go/in-headers-endless.fuzz Sun Dec 4 22:07:28 2016
@@ -0,0 +1,8 @@
+wait 1ms
+stream-get / eos
+
+headers-add-endless
+stream-end
+wait 1ms
+goaway
+wait