1
0
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:
Tim Voronov
2019-10-08 13:49:38 -04:00
committed by GitHub
8 changed files with 146 additions and 5 deletions

View File

@@ -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
}

View File

@@ -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":

View File

@@ -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 {

View File

@@ -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
View 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
}

View File

@@ -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")

View File

@@ -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)
} }
) )

View File

@@ -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)