mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-28 09:56:31 +02:00
93 lines
2 KiB
Go
93 lines
2 KiB
Go
// Package forms has helper functions for working with HTML forms.
|
|
package forms
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"golang.org/x/net/html"
|
|
)
|
|
|
|
// A Form represents an HTML form.
|
|
type Form struct {
|
|
Action string
|
|
Method string
|
|
Inputs map[string]string
|
|
}
|
|
|
|
// Parse parses all the forms in an HTML document.
|
|
func Parse(r io.Reader) []Form {
|
|
root, err := html.Parse(r)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
var forms []Form
|
|
var currentForm *Form
|
|
var visit func(*html.Node)
|
|
visit = func(node *html.Node) {
|
|
if node.Type == html.ElementNode && node.Data == "form" {
|
|
currentForm = &Form{Action: "", Method: "GET", Inputs: make(map[string]string)}
|
|
for _, attr := range node.Attr {
|
|
switch attr.Key {
|
|
case "action":
|
|
currentForm.Action = attr.Val
|
|
case "method":
|
|
currentForm.Method = strings.ToUpper(attr.Val)
|
|
}
|
|
}
|
|
}
|
|
|
|
if currentForm != nil && node.Type == html.ElementNode && node.Data == "input" {
|
|
var name, value string
|
|
for _, attr := range node.Attr {
|
|
switch attr.Key {
|
|
case "name":
|
|
name = attr.Val
|
|
case "value":
|
|
value = attr.Val
|
|
}
|
|
}
|
|
if name != "" {
|
|
currentForm.Inputs[name] = value
|
|
}
|
|
}
|
|
|
|
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
|
visit(c)
|
|
}
|
|
if node.Type == html.ElementNode && node.Data == "form" {
|
|
if currentForm != nil {
|
|
forms = append(forms, *currentForm)
|
|
}
|
|
currentForm = nil
|
|
}
|
|
}
|
|
visit(root)
|
|
return forms
|
|
}
|
|
|
|
// NewRequestWithContext creates a new request from the form details.
|
|
func (f *Form) NewRequestWithContext(ctx context.Context, baseURL *url.URL) (*http.Request, error) {
|
|
actionURL, err := url.Parse(f.Action)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
actionURL = baseURL.ResolveReference(actionURL)
|
|
|
|
vs := make(url.Values)
|
|
for k, v := range f.Inputs {
|
|
vs.Set(k, v)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, f.Method, actionURL.String(), strings.NewReader(vs.Encode()))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// TODO: handle multipart forms
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
return req, nil
|
|
}
|