mirror of
https://github.com/MontFerret/ferret.git
synced 2025-11-06 08:39:09 +02:00
Merge pull request #391 from 3timeslazy/feature/issue_382
feature: issue_382
This commit is contained in:
@@ -3,6 +3,9 @@ package cdp
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"hash/fnv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/mafredri/cdp"
|
"github.com/mafredri/cdp"
|
||||||
"github.com/mafredri/cdp/protocol/emulation"
|
"github.com/mafredri/cdp/protocol/emulation"
|
||||||
"github.com/mafredri/cdp/protocol/network"
|
"github.com/mafredri/cdp/protocol/network"
|
||||||
@@ -10,8 +13,6 @@ import (
|
|||||||
"github.com/mafredri/cdp/rpcc"
|
"github.com/mafredri/cdp/rpcc"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"hash/fnv"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/drivers"
|
"github.com/MontFerret/ferret/pkg/drivers"
|
||||||
"github.com/MontFerret/ferret/pkg/drivers/cdp/events"
|
"github.com/MontFerret/ferret/pkg/drivers/cdp/events"
|
||||||
@@ -792,3 +793,7 @@ func (p *HTMLPage) unfoldFrames(ctx context.Context) (core.Value, error) {
|
|||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *HTMLPage) GetResponse(_ context.Context) (*drivers.HTTPResponse, error) {
|
||||||
|
return nil, core.ErrNotSupported
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package common
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/drivers"
|
"github.com/MontFerret/ferret/pkg/drivers"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
@@ -20,6 +22,14 @@ func GetInPage(ctx context.Context, page drivers.HTMLPage, path []core.Value) (c
|
|||||||
segment := segment.(values.String)
|
segment := segment.(values.String)
|
||||||
|
|
||||||
switch segment {
|
switch segment {
|
||||||
|
case "response":
|
||||||
|
resp, err := page.GetResponse(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "get response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.GetIn(ctx, path[1:])
|
||||||
|
|
||||||
case "mainFrame", "document":
|
case "mainFrame", "document":
|
||||||
return GetInDocument(ctx, page.GetMainFrame(), path[1:])
|
return GetInDocument(ctx, page.GetMainFrame(), path[1:])
|
||||||
case "frames":
|
case "frames":
|
||||||
|
|||||||
@@ -168,7 +168,14 @@ func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTM
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewHTMLPage(doc, params.URL, cookies)
|
// HTTPResponse is temporarily unavailable when the status code != OK
|
||||||
|
r := drivers.HTTPResponse{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Status: resp.Status,
|
||||||
|
Headers: drivers.HTTPHeaders(resp.Header),
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHTMLPage(doc, params.URL, &r, cookies)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (drv *Driver) Parse(_ context.Context, str values.String) (drivers.HTMLPage, error) {
|
func (drv *Driver) Parse(_ context.Context, str values.String) (drivers.HTMLPage, error) {
|
||||||
@@ -180,7 +187,7 @@ func (drv *Driver) Parse(_ context.Context, str values.String) (drivers.HTMLPage
|
|||||||
return nil, errors.Wrap(err, "failed to parse a document")
|
return nil, errors.Wrap(err, "failed to parse a document")
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewHTMLPage(doc, "#blank", nil)
|
return NewHTMLPage(doc, "#blank", nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (drv *Driver) Close() error {
|
func (drv *Driver) Close() error {
|
||||||
|
|||||||
@@ -16,11 +16,13 @@ type HTMLPage struct {
|
|||||||
document *HTMLDocument
|
document *HTMLDocument
|
||||||
cookies drivers.HTTPCookies
|
cookies drivers.HTTPCookies
|
||||||
frames *values.Array
|
frames *values.Array
|
||||||
|
response *drivers.HTTPResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTMLPage(
|
func NewHTMLPage(
|
||||||
qdoc *goquery.Document,
|
qdoc *goquery.Document,
|
||||||
url string,
|
url string,
|
||||||
|
response *drivers.HTTPResponse,
|
||||||
cookies drivers.HTTPCookies,
|
cookies drivers.HTTPCookies,
|
||||||
) (*HTMLPage, error) {
|
) (*HTMLPage, error) {
|
||||||
doc, err := NewRootHTMLDocument(qdoc, url)
|
doc, err := NewRootHTMLDocument(qdoc, url)
|
||||||
@@ -33,6 +35,7 @@ func NewHTMLPage(
|
|||||||
p.document = doc
|
p.document = doc
|
||||||
p.cookies = cookies
|
p.cookies = cookies
|
||||||
p.frames = nil
|
p.frames = nil
|
||||||
|
p.response = response
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
@@ -87,7 +90,12 @@ func (p *HTMLPage) Copy() core.Value {
|
|||||||
cookies[k] = v
|
cookies[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
page, err := NewHTMLPage(p.document.doc, p.document.GetURL().String(), cookies)
|
page, err := NewHTMLPage(
|
||||||
|
p.document.doc,
|
||||||
|
p.document.GetURL().String(),
|
||||||
|
p.response,
|
||||||
|
cookies,
|
||||||
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return values.None
|
return values.None
|
||||||
@@ -174,6 +182,10 @@ func (p *HTMLPage) GetCookies(_ context.Context) (*values.Array, error) {
|
|||||||
return arr, nil
|
return arr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *HTMLPage) GetResponse(_ context.Context) (*drivers.HTTPResponse, error) {
|
||||||
|
return p.response, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *HTMLPage) SetCookies(_ context.Context, _ ...drivers.HTTPCookie) error {
|
func (p *HTMLPage) SetCookies(_ context.Context, _ ...drivers.HTTPCookie) error {
|
||||||
return core.ErrNotSupported
|
return core.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|||||||
100
pkg/drivers/response.go
Normal file
100
pkg/drivers/response.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package drivers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
|
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPResponse HTTP response object.
|
||||||
|
type HTTPResponse struct {
|
||||||
|
StatusCode int
|
||||||
|
Status string
|
||||||
|
Headers HTTPHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resp *HTTPResponse) Type() core.Type {
|
||||||
|
return HTTPResponseType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resp *HTTPResponse) String() string {
|
||||||
|
return resp.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resp *HTTPResponse) Compare(other core.Value) int64 {
|
||||||
|
if other.Type() != HTTPResponseType {
|
||||||
|
return Compare(HTTPResponseType, other.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a safe cast. Only *HTTPResponse implements core.Value.
|
||||||
|
// HTTPResponse does not.
|
||||||
|
otherResp := other.(*HTTPResponse)
|
||||||
|
|
||||||
|
comp := resp.Headers.Compare(otherResp.Headers)
|
||||||
|
if comp != 0 {
|
||||||
|
return comp
|
||||||
|
}
|
||||||
|
|
||||||
|
// it makes no sense to compare Status strings
|
||||||
|
// because they are always equal if StatusCode's are equal
|
||||||
|
return values.NewInt(resp.StatusCode).
|
||||||
|
Compare(values.NewInt(resp.StatusCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resp *HTTPResponse) Unwrap() interface{} {
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resp *HTTPResponse) Copy() core.Value {
|
||||||
|
return *(&resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resp *HTTPResponse) Hash() uint64 {
|
||||||
|
return values.Parse(resp).Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseMarshal is a structure that repeats HTTPResponse. It allows
|
||||||
|
// easily Marshal the HTTPResponse object.
|
||||||
|
type responseMarshal struct {
|
||||||
|
StatusCode int `json:"status_code"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Headers HTTPHeaders `json:"headers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resp *HTTPResponse) MarshalJSON() ([]byte, error) {
|
||||||
|
if resp == nil {
|
||||||
|
return json.Marshal(values.None)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(responseMarshal(*resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (resp *HTTPResponse) GetIn(ctx context.Context, path []core.Value) (core.Value, error) {
|
||||||
|
if len(path) == 0 {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ := path[0].Type(); typ != types.String {
|
||||||
|
return values.None, core.TypeError(typ, types.String)
|
||||||
|
}
|
||||||
|
|
||||||
|
field := path[0].(values.String).String()
|
||||||
|
|
||||||
|
switch field {
|
||||||
|
case "status":
|
||||||
|
return values.NewString(resp.Status), nil
|
||||||
|
case "statusCode":
|
||||||
|
return values.NewInt(resp.StatusCode), nil
|
||||||
|
case "headers":
|
||||||
|
if len(path) == 1 {
|
||||||
|
return resp.Headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Headers.GetIn(ctx, path[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.None, nil
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package drivers
|
|||||||
import "github.com/MontFerret/ferret/pkg/runtime/core"
|
import "github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
HTTPResponseType = core.NewType("HTTPResponse")
|
||||||
HTTPHeaderType = core.NewType("HTTPHeaders")
|
HTTPHeaderType = core.NewType("HTTPHeaders")
|
||||||
HTTPCookieType = core.NewType("HTTPCookie")
|
HTTPCookieType = core.NewType("HTTPCookie")
|
||||||
HTMLElementType = core.NewType("HTMLElement")
|
HTMLElementType = core.NewType("HTMLElement")
|
||||||
|
|||||||
@@ -209,6 +209,8 @@ type (
|
|||||||
NavigateBack(ctx context.Context, skip values.Int) (values.Boolean, error)
|
NavigateBack(ctx context.Context, skip values.Int) (values.Boolean, error)
|
||||||
|
|
||||||
NavigateForward(ctx context.Context, skip values.Int) (values.Boolean, error)
|
NavigateForward(ctx context.Context, skip values.Int) (values.Boolean, error)
|
||||||
|
|
||||||
|
GetResponse(ctx context.Context) (*HTTPResponse, error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,10 @@ func Parse(input interface{}) core.Value {
|
|||||||
t := reflect.TypeOf(value)
|
t := reflect.TypeOf(value)
|
||||||
kind := t.Kind()
|
kind := t.Kind()
|
||||||
|
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
return Parse(v.Elem().Interface())
|
||||||
|
}
|
||||||
|
|
||||||
if kind == reflect.Slice || kind == reflect.Array {
|
if kind == reflect.Slice || kind == reflect.Array {
|
||||||
size := v.Len()
|
size := v.Len()
|
||||||
arr := NewArray(size)
|
arr := NewArray(size)
|
||||||
|
|||||||
Reference in New Issue
Block a user