func (e HeaderExtractor) Extract(t *Test) (string, error) {
	h, ok := t.Response.Response.Header[e.Name]
	if !ok || len(h) == 0 {
		return "", fmt.Errorf("header %s not received", e.Name)
	}
	return h[0], nil
}
Extract implements Extractor's Extract method.
package ht
import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"regexp"
	"strings"
	"time"
	"github.com/andybalholm/cascadia"
	"github.com/robertkrimen/otto"
	"github.com/vdobler/ht/populate"
	"golang.org/x/net/html"
)
type Extractor interface {
	Extract(t *Test) (string, error)
}
func (t *Test) Extract() map[string]string {
	data := make(map[string]string)
	t.Result.Extractions = make(map[string]Extraction)
	for varname, ex := range t.DataExtraction {
		value, err := ex.Extract(t)
		if err != nil {
			t.Result.Extractions[varname] = Extraction{Error: err}
			t.errorf("Problems extracting %q in %q: %s",
				varname, t.Name, err)
			continue
		}
		data[varname] = value
		t.Result.Extractions[varname] = Extraction{Value: value}
	}
	return data
}
var ExtractorRegistry = make(map[string]reflect.Type)
func RegisterExtractor(ex Extractor) {
	name := NameOf(ex)
	typ := reflect.TypeOf(ex)
	if _, ok := ExtractorRegistry[name]; ok {
		panic(fmt.Sprintf("Extractor with name %q already registered.", name))
	}
	ExtractorRegistry[name] = typ
}
func init() {
	RegisterExtractor(HTMLExtractor{})
	RegisterExtractor(BodyExtractor{})
	RegisterExtractor(JSONExtractor{})
	RegisterExtractor(CookieExtractor{})
	RegisterExtractor(HeaderExtractor{})
	RegisterExtractor(JSExtractor{})
	RegisterExtractor(SetVariable{})
	RegisterExtractor(SetTimestamp{})
}
type ExtractorMap map[string]Extractor
func (em ExtractorMap) MarshalJSON() ([]byte, error) {
	buf := &bytes.Buffer{}
	buf.WriteRune('{')
	i := 0
	for name, ex := range em {
		raw, err := json.Marshal(ex)
		if err != nil {
			return nil, err
		}
		buf.WriteRune('"')
		buf.WriteString(name)
		buf.WriteRune('"')
		buf.WriteRune(':')
		buf.WriteString(`{"Extractor": "`)
		buf.WriteString(NameOf(ex))
		buf.WriteRune('"')
		if string(raw) != "{}" {
			buf.WriteString(", ")
			buf.Write(raw[1 : len(raw)-1])
		}
		buf.WriteRune('}')
		if i < len(em)-1 {
			buf.WriteString(", ")
		}
		i++
	}
	buf.WriteRune('}')
	return buf.Bytes(), nil
}
// Populate implements populate.Populator.Populate.
func (em *ExtractorMap) Populate(src interface{}) error {
	types := make(map[string]struct {
		Extractor string
	})
	err := populate.Lax(&types, src)
	if err != nil {
		return err
	}
	raw := make(map[string]interface{})
	srcMap, ok := src.(map[string]interface{})
	if !ok {
		return fmt.Errorf("ht: cannot populate %T to variable extractor object", src)
	}
	for name := range types {
		r, ok := srcMap[name].(map[string]interface{})
		if !ok {
			return fmt.Errorf("ht: cannot populate extractor for %q from %T", name, srcMap[name])
		}
		delete(r, "Extractor")
		raw[name] = r
	}
	exes := make(map[string]Extractor)
	for name, ex := range types {
		exName := ex.Extractor
		if exName == "" {
			// This happens if Extractor was misspelled, e.g. in
			//    {Extratcor: "CookieExtractor", Name: "sessionid"}
			return fmt.Errorf("missing Extractor name")
		}
		typ, ok := ExtractorRegistry[exName]
		if !ok {
			return noSuchExtractorError(exName)
		}
		if typ.Kind() == reflect.Ptr {
			typ = typ.Elem()
		}
		extractor := reflect.New(typ)
		err = populate.Strict(extractor.Interface(), raw[name])
		if err != nil {
			return fmt.Errorf("cannot build extractor for %q: %s", name, err)
		}
		exes[name] = extractor.Interface().(Extractor)
	}
	*em = exes
	return nil
}
func noSuchExtractorError(name string) error {
	exNames := make([]string, 0, len(ExtractorRegistry))
	for en := range ExtractorRegistry {
		exNames = append(exNames, en)
	}
	if suggestions := possibleNames(name, exNames); len(suggestions) > 0 {
		return fmt.Errorf("no such extractor %s (did you mean %s?)", name,
			strings.Join(suggestions, ", "))
	}
	return fmt.Errorf("no such extractor %s", name)
}
// ----------------------------------------------------------------------------
// HTMLExtractor
// HTMLExtractor allows to extract data from an executed Test.
// It supports extracting HTML attribute values and HTML text node values.
// Examples for CSRF token in the HTML:
//    <meta name="_csrf" content="18f0ca3f-a50a-437f-9bd1-15c0caa28413" />
//    <input type="hidden" name="_csrf"
//        value="18f0ca3f-a50a-437f-9bd1-15c0caa28413"/>
type HTMLExtractor struct {
	// Selector is the CSS selector of an element, e.g.
	//     head meta[name="_csrf"]   or
	//     form#login input[name="tok"]
	//     div.token span
	Selector string
	// Attribute is the name of the attribute from which the
	// value should be extracted.  The magic value "~text~" refers to the
	// normalized text content of the element and ~rawtext~ to the raw
	// text content.
	// E.g. in the examples above the following should be sensible:
	//     content
	//     value
	//     ~text~
	Attribute string
}
// Extract implements Extractor's Extract method.
func (e HTMLExtractor) Extract(t *Test) (string, error) {
	if e.Selector != "" {
		sel, err := cascadia.Compile(e.Selector)
		if err != nil {
			return "", err
		}
		doc, err := html.Parse(t.Response.Body())
		if err != nil {
			return "", err
		}
		node := sel.MatchFirst(doc)
		if node == nil {
			return "", fmt.Errorf("could not find node '%s'", e.Selector)
		}
		if e.Attribute == "~rawtext~" {
			return TextContent(node, true), nil
		} else if e.Attribute == "~text~" {
			return TextContent(node, false), nil
		}
		for _, a := range node.Attr {
			if a.Key == e.Attribute {
				return a.Val, nil
			}
		}
	}
	return "", errors.New("not found")
}
type BodyExtractor struct {
	Regexp string
	Submatch int `json:",omitempty"`
}
func (e BodyExtractor) Extract(t *Test) (string, error) {
	if t.Response.BodyErr != nil {
		return "", ErrBadBody
	}
	re, err := regexp.Compile(e.Regexp)
	if err != nil {
		return "", err
	}
	if e.Submatch < 0 {
		return "", errors.New("BodyExtractor.Submatch < 0")
	}
	submatches := re.FindStringSubmatch(t.Response.BodyStr)
	if len(submatches) > e.Submatch {
		return submatches[e.Submatch], nil
	}
	if len(submatches) == 0 {
		return "", fmt.Errorf("no match found in %q", t.Response.BodyStr)
	}
	return "", fmt.Errorf("got only %d submatches in %q", len(submatches)-1, submatches[0])
}
type JSONExtractor struct {
	Element string `json:",omitempty"`
	Sep string `json:",omitempty"`
	Embedded *JSONExtractor
}
func (e JSONExtractor) Extract(t *Test) (string, error) {
	if t.Response.BodyErr != nil {
		return "", ErrBadBody
	}
	sep := "."
	if e.Sep != "" {
		sep = e.Sep
	}
	raw, err := findJSONelement([]byte(t.Response.BodyStr), e.Element, sep)
	if err != nil {
		return "", err
	}
	s := string(raw)
	if s == "null" {
		if e.Embedded != nil {
			return "", fmt.Errorf("Cannot extract embedded from null")
		}
		return "", nil
	}
	if strings.HasPrefix(s, `"`) && strings.HasSuffix(s, `"`) && len(s) >= 2 {
		unquoted := ""
		err := json.Unmarshal([]byte(s), &unquoted)
		if err != nil {
			return "", fmt.Errorf("element %s == %q is not properly quoted: %s",
				e.Element, LimitString(s), err)
		}
		s = unquoted
	}
	if e.Embedded != nil {
		fakeTest := &Test{Response: Response{BodyStr: s}}
		sub, err := e.Embedded.Extract(fakeTest)
		return sub, err
	}
	return s, nil
}
type CookieExtractor struct {
	Name string  
}
func (e CookieExtractor) Extract(t *Test) (string, error) {
	cookies := findCookiesByName(t, e.Name)
	if len(cookies) == 0 {
		return "", fmt.Errorf("cookie %s not received", e.Name)
	}
	return cookies[0].Value, nil
}
type HeaderExtractor struct {
	Name string  
}